Browse Source

Merge pull request #379 from PixiEditor/crash-report-fixes

Improvements
Krzysztof Krysiński 3 years ago
parent
commit
848957c545
31 changed files with 215 additions and 94 deletions
  1. 8 1
      PixiEditor/Helpers/Behaviours/ClearFocusOnClickBehavior.cs
  2. 1 9
      PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs
  3. 4 2
      PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs
  4. 7 5
      PixiEditor/Models/DataHolders/CrashReport.cs
  5. 9 6
      PixiEditor/Models/DataHolders/Document/Document.Layers.cs
  6. 2 0
      PixiEditor/Models/DataHolders/Document/Document.Operations.cs
  7. 1 1
      PixiEditor/Models/DataHolders/Document/Document.cs
  8. 12 12
      PixiEditor/Models/Layers/LayerStructure.cs
  9. 1 1
      PixiEditor/PixiEditor.csproj
  10. 2 2
      PixiEditor/Properties/AssemblyInfo.cs
  11. 3 2
      PixiEditor/Styles/AvalonDock/Themes/Generic.xaml
  12. 1 0
      PixiEditor/Styles/ImageCheckBoxStyle.xaml
  13. 1 0
      PixiEditor/Styles/ListSwitchButtonStyle.xaml
  14. 14 0
      PixiEditor/Styles/ThemeStyle.xaml
  15. 1 0
      PixiEditor/ViewModels/CrashReportViewModel.cs
  16. 64 24
      PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs
  17. 13 7
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  18. 6 0
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  19. 3 0
      PixiEditor/ViewModels/ViewModelMain.cs
  20. 1 1
      PixiEditor/Views/Dialogs/CrashReportDialog.xaml
  21. 1 0
      PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml
  22. 1 0
      PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml
  23. 4 10
      PixiEditor/Views/MainWindow.xaml
  24. 1 1
      PixiEditor/Views/UserControls/EditableTextBlock.xaml
  25. 2 2
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  26. 4 4
      PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml
  27. 1 1
      PixiEditor/Views/UserControls/NumberInput.xaml
  28. 27 1
      PixiEditor/Views/UserControls/NumberInput.xaml.cs
  29. 13 0
      PixiEditor/Views/UserControls/SizeInput.xaml.cs
  30. 5 0
      PixiEditor/Views/UserControls/SizePicker.xaml.cs
  31. 2 2
      PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

+ 8 - 1
PixiEditor/Helpers/Behaviours/ClearFocusOnClickBehavior.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Models.Controllers.Shortcuts;
 using PixiEditor.Models.Controllers.Shortcuts;
 using System.Windows;
 using System.Windows;
+using System.Windows.Input;
 using System.Windows.Interactivity;
 using System.Windows.Interactivity;
 
 
 namespace PixiEditor.Helpers.Behaviours
 namespace PixiEditor.Helpers.Behaviours
@@ -8,8 +9,14 @@ namespace PixiEditor.Helpers.Behaviours
     {
     {
         protected override void OnAttached()
         protected override void OnAttached()
         {
         {
-            AssociatedObject.MouseDown += AssociatedObject_MouseDown;
             base.OnAttached();
             base.OnAttached();
+            AssociatedObject.MouseDown += AssociatedObject_MouseDown;
+            AssociatedObject.LostKeyboardFocus += AssociatedObject_LostKeyboardFocus;
+        }
+
+        private void AssociatedObject_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
+        {
+            
         }
         }
 
 
         protected override void OnDetaching()
         protected override void OnDetaching()

+ 1 - 9
PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs

@@ -76,15 +76,7 @@ namespace PixiEditor.Helpers.Behaviours
 
 
         private void RemoveFocus()
         private void RemoveFocus()
         {
         {
-            DependencyObject scope = FocusManager.GetFocusScope(AssociatedObject);
-            FrameworkElement parent = (FrameworkElement)AssociatedObject.Parent;
-
-            while (parent != null && parent is IInputElement element && !element.Focusable)
-            {
-                parent = (FrameworkElement)parent.Parent;
-            }
-
-            FocusManager.SetFocusedElement(scope, parent);
+            MainWindow.Current.mainGrid.Focus();
         }
         }
 
 
         private void AssociatedObjectGotKeyboardFocus(
         private void AssociatedObjectGotKeyboardFocus(

+ 4 - 2
PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs

@@ -1,4 +1,6 @@
-using System;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools.Tools;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
@@ -22,7 +24,7 @@ namespace PixiEditor.Models.Controllers.Shortcuts
 
 
         public Shortcut LastShortcut { get; private set; }
         public Shortcut LastShortcut { get; private set; }
 
 
-        public const Key MoveViewportToolTransientChangeKey = Key.Space;
+        public Dictionary<Key, Tool> TransientShortcuts { get; set; } = new Dictionary<Key, Tool>();
 
 
         public static void BlockShortcutExection(string blocker)
         public static void BlockShortcutExection(string blocker)
         {
         {

+ 7 - 5
PixiEditor/Models/DataHolders/CrashReport.cs

@@ -91,8 +91,9 @@ namespace PixiEditor.Models.DataHolders
 
 
         public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
         public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
 
 
-        public IEnumerable<Document> RecoverDocuments()
+        public List<Document> RecoverDocuments()
         {
         {
+            List<Document> documents = new();
             foreach (ZipArchiveEntry entry in ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")))
             foreach (ZipArchiveEntry entry in ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")))
             {
             {
                 using Stream stream = entry.Open();
                 using Stream stream = entry.Open();
@@ -109,8 +110,10 @@ namespace PixiEditor.Models.DataHolders
                     continue;
                     continue;
                 }
                 }
 
 
-                yield return document;
+                documents.Add(document);
             }
             }
+
+            return documents;
         }
         }
 
 
         public void Dispose()
         public void Dispose()
@@ -159,15 +162,14 @@ namespace PixiEditor.Models.DataHolders
                 try
                 try
                 {
                 {
                     string documentPath =
                     string documentPath =
-                        $"{(string.IsNullOrWhiteSpace(document.DocumentFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.DocumentFilePath))}-{document.OpenedUTC}.pixi";
+                        $"{(string.IsNullOrWhiteSpace(document.DocumentFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.DocumentFilePath))}-{document.OpenedUTC}.pixi".Replace(':', '_');
 
 
                     byte[] serialized = PixiParser.Serialize(document.ToSerializable());
                     byte[] serialized = PixiParser.Serialize(document.ToSerializable());
 
 
                     using Stream documentStream = archive.CreateEntry($"Documents/{documentPath}").Open();
                     using Stream documentStream = archive.CreateEntry($"Documents/{documentPath}").Open();
                     documentStream.Write(serialized);
                     documentStream.Write(serialized);
                 }
                 }
-                catch
-                { }
+                catch { }
             }
             }
         }
         }
 
 

+ 9 - 6
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -492,6 +492,8 @@ namespace PixiEditor.Models.DataHolders
 
 
             UndoManager.SquashUndoChanges(2, "Undo merge layers", false);
             UndoManager.SquashUndoChanges(2, "Undo merge layers", false);
 
 
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(layer.GuidValue, LayerAction.Add));
+
             return layer;
             return layer;
         }
         }
 
 
@@ -535,13 +537,13 @@ namespace PixiEditor.Models.DataHolders
 
 
             var startGroup = LayerStructure.GetGroupByLayer(layerGuid);
             var startGroup = LayerStructure.GetGroupByLayer(layerGuid);
 
 
-            LayerStructure.PreMoveReassignBounds(new GroupData(startGroup?.GroupGuid), layerGuid);
+            LayerStructure.Unassign(new GroupData(startGroup?.GroupGuid), layerGuid);
 
 
             Layers.Move(Layers.IndexOf(Layers.First(x => x.GuidValue == layerGuid)), indexTo);
             Layers.Move(Layers.IndexOf(Layers.First(x => x.GuidValue == layerGuid)), indexTo);
 
 
             var newGroup = LayerStructure.GetGroupByLayer(layerAtOldIndex);
             var newGroup = LayerStructure.GetGroupByLayer(layerAtOldIndex);
 
 
-            LayerStructure.PostMoveReassignBounds(new GroupData(newGroup?.GroupGuid), layerGuid);
+            LayerStructure.Assign(new GroupData(newGroup?.GroupGuid), layerGuid);
 
 
             RaisePropertyChanged(nameof(LayerStructure));
             RaisePropertyChanged(nameof(LayerStructure));
         }
         }
@@ -680,7 +682,7 @@ namespace PixiEditor.Models.DataHolders
 
 
             LayerStructure.ReassignParent(group, referenceLayerGroup);
             LayerStructure.ReassignParent(group, referenceLayerGroup);
 
 
-            LayerStructure.PostMoveReassignBounds(new GroupData(group?.Parent?.GroupGuid), new GroupData(group?.GroupGuid));
+            LayerStructure.Assign(new GroupData(group?.Parent?.GroupGuid), new GroupData(group?.GroupGuid));
         }
         }
 
 
         private int CalculateNewIndex(int layerIndex, bool above, int oldIndex)
         private int CalculateNewIndex(int layerIndex, bool above, int oldIndex)
@@ -713,13 +715,13 @@ namespace PixiEditor.Models.DataHolders
 
 
             var startGroup = LayerStructure.GetGroupByLayer(layer);
             var startGroup = LayerStructure.GetGroupByLayer(layer);
 
 
-            LayerStructure.PreMoveReassignBounds(new GroupData(startGroup?.GroupGuid), layer);
+            LayerStructure.Unassign(new GroupData(startGroup?.GroupGuid), layer);
 
 
             Layers.Move(oldIndex, newIndex);
             Layers.Move(oldIndex, newIndex);
 
 
             var newFolder = LayerStructure.GetGroupByLayer(referenceLayer);
             var newFolder = LayerStructure.GetGroupByLayer(referenceLayer);
 
 
-            LayerStructure.PostMoveReassignBounds(new GroupData(newFolder?.GroupGuid), layer);
+            LayerStructure.Assign(new GroupData(newFolder?.GroupGuid), layer);
 
 
             if (Layers.IndexOf(ActiveLayer) == oldIndex)
             if (Layers.IndexOf(ActiveLayer) == oldIndex)
             {
             {
@@ -758,8 +760,9 @@ namespace PixiEditor.Models.DataHolders
 
 
                 if (layerGroup?.Parent != null && LayerStructure.GroupContainsOnlyLayer(layer.GuidValue, layerGroup))
                 if (layerGroup?.Parent != null && LayerStructure.GroupContainsOnlyLayer(layer.GuidValue, layerGroup))
                 {
                 {
-                    LayerStructure.PreMoveReassignBounds(new GroupData(layerGroup.Parent.GroupGuid), new GroupData(layerGroup.GroupGuid));
+                    LayerStructure.Unassign(new GroupData(layerGroup.Parent.GroupGuid), new GroupData(layerGroup.GroupGuid));
                 }
                 }
+
                 LayerStructure.AssignParent(Layers[index].GuidValue, null);
                 LayerStructure.AssignParent(Layers[index].GuidValue, null);
                 RemoveGroupsIfEmpty(layer, layerGroup);
                 RemoveGroupsIfEmpty(layer, layerGroup);
 
 

+ 2 - 0
PixiEditor/Models/DataHolders/Document/Document.Operations.cs

@@ -54,6 +54,8 @@ namespace PixiEditor.Models.DataHolders
                 ResizeCanvas(newOffsets, width, height);
                 ResizeCanvas(newOffsets, width, height);
             }
             }
 
 
+            if (oldWidth == Width && Height == oldHeight) return;
+
             DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
             DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
         }
         }
 
 

+ 1 - 1
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -207,7 +207,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void SetAsActiveOnClick(object obj)
         private void SetAsActiveOnClick(object obj)
         {
         {
-            if (XamlAccesibleViewModel.BitmapManager.ActiveDocument != this)
+            if (XamlAccesibleViewModel?.BitmapManager?.ActiveDocument != this)
             {
             {
                 XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;
                 XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;
             }
             }

+ 12 - 12
PixiEditor/Models/Layers/LayerStructure.cs

@@ -191,7 +191,7 @@ namespace PixiEditor.Models.Layers
                 return;
                 return;
             }
             }
 
 
-            PreMoveReassignBounds(parentGroup, group);
+            Unassign(parentGroup, group);
 
 
             List<Guid> layersInOrder = GetLayersInOrder(new GroupData(groupTopIndex, groupBottomIndex));
             List<Guid> layersInOrder = GetLayersInOrder(new GroupData(groupTopIndex, groupBottomIndex));
 
 
@@ -250,9 +250,9 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         /// </summary>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="group">Group which data should be reassigned.</param>
         /// <param name="group">Group which data should be reassigned.</param>
-        public void PreMoveReassignBounds(GroupData parentGroup, GroupData group)
+        public void Unassign(GroupData parentGroup, GroupData group)
         {
         {
-            PreMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), GetGroupByGuid(group.GroupGuid));
+            Unassign(GetGroupByGuid(parentGroup.GroupGuid), GetGroupByGuid(group.GroupGuid));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -260,7 +260,7 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         /// </summary>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="layer">Layer which data should be reassigned.</param>
         /// <param name="layer">Layer which data should be reassigned.</param>
-        public void PreMoveReassignBounds(GroupData parentGroup, Guid layer)
+        public void Unassign(GroupData parentGroup, Guid layer)
         {
         {
             PreMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), layer);
             PreMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), layer);
         }
         }
@@ -270,9 +270,9 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         /// </summary>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="layerGuid">Group which data should be reassigned.</param>
         /// <param name="layerGuid">Group which data should be reassigned.</param>
-        public void PostMoveReassignBounds(GroupData parentGroup, Guid layerGuid)
+        public void Assign(GroupData parentGroup, Guid layerGuid)
         {
         {
-            PostMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), layerGuid);
+            Assign(GetGroupByGuid(parentGroup.GroupGuid), layerGuid);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -280,9 +280,9 @@ namespace PixiEditor.Models.Layers
         /// </summary>
         /// </summary>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="parentGroup">Parent group to reassign data in.</param>
         /// <param name="group">Group which data should be reassigned.</param>
         /// <param name="group">Group which data should be reassigned.</param>
-        public void PostMoveReassignBounds(GroupData parentGroup, GroupData group)
+        public void Assign(GroupData parentGroup, GroupData group)
         {
         {
-            PostMoveReassignBounds(GetGroupByGuid(parentGroup?.GroupGuid), GetGroupByGuid(group.GroupGuid));
+            Assign(GetGroupByGuid(parentGroup?.GroupGuid), GetGroupByGuid(group.GroupGuid));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -450,7 +450,7 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        private void PreMoveReassignBounds(GuidStructureItem parentGroup, GuidStructureItem group)
+        private void Unassign(GuidStructureItem parentGroup, GuidStructureItem group)
         {
         {
             if (parentGroup != null)
             if (parentGroup != null)
             {
             {
@@ -481,7 +481,7 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        private void PostMoveReassignBounds(GuidStructureItem parentGroup, Guid layerGuid)
+        private void Assign(GuidStructureItem parentGroup, Guid layerGuid)
         {
         {
             if (parentGroup != null)
             if (parentGroup != null)
             {
             {
@@ -529,7 +529,7 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        private void PostMoveReassignBounds(GuidStructureItem parentGroup, GuidStructureItem group)
+        private void Assign(GuidStructureItem parentGroup, GuidStructureItem group)
         {
         {
             if (parentGroup != null)
             if (parentGroup != null)
             {
             {
@@ -572,7 +572,7 @@ namespace PixiEditor.Models.Layers
                 PreMoveReassignBounds(currentParent, layer);
                 PreMoveReassignBounds(currentParent, layer);
             }
             }
 
 
-            PostMoveReassignBounds(parent, layer);
+            Assign(parent, layer);
 
 
             LayerStructureChanged?.Invoke(this, new LayerStructureChangedEventArgs(layer));
             LayerStructureChanged?.Invoke(this, new LayerStructureChangedEventArgs(layer));
         }
         }

+ 1 - 1
PixiEditor/PixiEditor.csproj

@@ -197,7 +197,7 @@
 		</PackageReference>
 		</PackageReference>
 		<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
 		<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
 		<PackageReference Include="PixiEditor.ColorPicker" Version="3.2.0" />
 		<PackageReference Include="PixiEditor.ColorPicker" Version="3.2.0" />
-		<PackageReference Include="PixiEditor.Parser" Version="2.0.0" />
+		<PackageReference Include="PixiEditor.Parser" Version="2.0.0.1" />
 		<PackageReference Include="PixiEditor.Parser.Skia" Version="2.0.0.1" />
 		<PackageReference Include="PixiEditor.Parser.Skia" Version="2.0.0.1" />
 		<PackageReference Include="SkiaSharp" Version="2.80.3" />
 		<PackageReference Include="SkiaSharp" Version="2.80.3" />
 		<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
 		<PackageReference Include="System.Drawing.Common" Version="6.0.0" />

+ 2 - 2
PixiEditor/Properties/AssemblyInfo.cs

@@ -50,5 +50,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.1.7.4")]
-[assembly: AssemblyFileVersion("0.1.7.4")]
+[assembly: AssemblyVersion("0.1.7.5")]
+[assembly: AssemblyFileVersion("0.1.7.5")]

+ 3 - 2
PixiEditor/Styles/AvalonDock/Themes/Generic.xaml

@@ -162,8 +162,9 @@
 	<Style x:Key="PixiEditorDockThemeButtonStyle" TargetType="Button">
 	<Style x:Key="PixiEditorDockThemeButtonStyle" TargetType="Button">
 		<Setter Property="Background" Value="Transparent" />
 		<Setter Property="Background" Value="Transparent" />
 		<Setter Property="BorderThickness" Value="1" />
 		<Setter Property="BorderThickness" Value="1" />
-		<Setter Property="BorderBrush" Value="Transparent" />
-		<Setter Property="Padding" Value="0" />
+        <Setter Property="BorderBrush" Value="Transparent" />
+        <Setter Property="Focusable" Value="False" />
+        <Setter Property="Padding" Value="0" />
 		<Setter Property="Template">
 		<Setter Property="Template">
 			<Setter.Value>
 			<Setter.Value>
 				<ControlTemplate TargetType="Button">
 				<ControlTemplate TargetType="Button">

+ 1 - 0
PixiEditor/Styles/ImageCheckBoxStyle.xaml

@@ -2,6 +2,7 @@
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:local="clr-namespace:PixiEditor.Styles">
                     xmlns:local="clr-namespace:PixiEditor.Styles">
     <Style TargetType="{x:Type CheckBox}" x:Key="ImageCheckBox">
     <Style TargetType="{x:Type CheckBox}" x:Key="ImageCheckBox">
+        <Setter Property="Focusable" Value="False"/>
         <Setter Property="Template">
         <Setter Property="Template">
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type CheckBox}">
                 <ControlTemplate TargetType="{x:Type CheckBox}">

+ 1 - 0
PixiEditor/Styles/ListSwitchButtonStyle.xaml

@@ -7,6 +7,7 @@
         <Setter Property="BorderThickness" Value="1"/>
         <Setter Property="BorderThickness" Value="1"/>
         <Setter Property="FontSize" Value="12"/>
         <Setter Property="FontSize" Value="12"/>
         <Setter Property="Cursor" Value="Hand"/>
         <Setter Property="Cursor" Value="Hand"/>
+        <Setter Property="Focusable" Value="False"/>
         <Setter Property="Padding" Value="2, 0"/>
         <Setter Property="Padding" Value="2, 0"/>
         <Setter Property="Foreground" Value="White"/>
         <Setter Property="Foreground" Value="White"/>
         <Setter Property="Template">
         <Setter Property="Template">

+ 14 - 0
PixiEditor/Styles/ThemeStyle.xaml

@@ -13,9 +13,14 @@
         <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
         <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
     </Style>
     </Style>
 
 
+    <Style TargetType="ToggleButton">
+        <Setter Property="Focusable" Value="False"/>
+    </Style>
+    
     <Style TargetType="Button" x:Key="BaseDarkButton">
     <Style TargetType="Button" x:Key="BaseDarkButton">
         <Setter Property="Background" Value="#404040" />
         <Setter Property="Background" Value="#404040" />
         <Setter Property="Foreground" Value="White" />
         <Setter Property="Foreground" Value="White" />
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="FontSize" Value="15" />
         <Setter Property="FontSize" Value="15" />
         <Setter Property="SnapsToDevicePixels" Value="True" />
         <Setter Property="SnapsToDevicePixels" Value="True" />
         <Setter Property="Template">
         <Setter Property="Template">
@@ -49,6 +54,7 @@
     <Style TargetType="Button" x:Key="DarkRoundButton" BasedOn="{StaticResource BaseDarkButton}">
     <Style TargetType="Button" x:Key="DarkRoundButton" BasedOn="{StaticResource BaseDarkButton}">
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="Background" Value="#303030" />
         <Setter Property="Background" Value="#303030" />
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="Height" Value="28"/>
         <Setter Property="Height" Value="28"/>
         <Setter Property="Width" Value="70"/>
         <Setter Property="Width" Value="70"/>
         <Setter Property="Template">
         <Setter Property="Template">
@@ -113,6 +119,7 @@
 
 
     <Style TargetType="Button" x:Key="ImageButtonStyle">
     <Style TargetType="Button" x:Key="ImageButtonStyle">
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="OverridesDefaultStyle" Value="True" />
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="Template">
         <Setter Property="Template">
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate TargetType="Button">
                 <ControlTemplate TargetType="Button">
@@ -134,6 +141,7 @@
            BasedOn="{StaticResource BaseDarkButton}">
            BasedOn="{StaticResource BaseDarkButton}">
         <Setter Property="TextBlock.FontFamily" Value="Segoe MDL2 Assets"/>
         <Setter Property="TextBlock.FontFamily" Value="Segoe MDL2 Assets"/>
         <Setter Property="TextBlock.FontSize" Value="15"/>
         <Setter Property="TextBlock.FontSize" Value="15"/>
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="TextBlock.Width" Value="30"/>
         <Setter Property="TextBlock.Width" Value="30"/>
 
 
         <Style.Triggers>
         <Style.Triggers>
@@ -149,6 +157,10 @@
         </Style.Triggers>
         </Style.Triggers>
     </Style>
     </Style>
 
 
+    <Style TargetType="CheckBox">
+        <Setter Property="Focusable" Value="False"/>
+    </Style>
+
 
 
     <Style TargetType="TextBox" x:Key="DarkTextBoxStyle">
     <Style TargetType="TextBox" x:Key="DarkTextBoxStyle">
         <Setter Property="BorderThickness" Value="1" />
         <Setter Property="BorderThickness" Value="1" />
@@ -187,6 +199,7 @@
 
 
     <Style TargetType="Button" x:Key="OpacityButtonStyle">
     <Style TargetType="Button" x:Key="OpacityButtonStyle">
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="OverridesDefaultStyle" Value="True" />
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="Template">
         <Setter Property="Template">
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate TargetType="Button">
                 <ControlTemplate TargetType="Button">
@@ -213,6 +226,7 @@
 
 
     <Style TargetType="Button" x:Key="ToolButtonStyle">
     <Style TargetType="Button" x:Key="ToolButtonStyle">
         <Setter Property="Height" Value="32" />
         <Setter Property="Height" Value="32" />
+        <Setter Property="Focusable" Value="False" />
         <Setter Property="Width" Value="32" />
         <Setter Property="Width" Value="32" />
         <Setter Property="VerticalAlignment" Value="Top" />
         <Setter Property="VerticalAlignment" Value="Top" />
         <Setter Property="HorizontalAlignment" Value="Center" />
         <Setter Property="HorizontalAlignment" Value="Center" />

+ 1 - 0
PixiEditor/ViewModels/CrashReportViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 using System.Diagnostics;
 using System.Diagnostics;
+using System.Linq;
 using System.Windows;
 using System.Windows;
 
 
 namespace PixiEditor.ViewModels
 namespace PixiEditor.ViewModels

+ 64 - 24
PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -1,8 +1,10 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers.Shortcuts;
 using PixiEditor.Models.Controllers.Shortcuts;
+using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
 using System;
 using System;
+using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
@@ -17,10 +19,6 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand MouseUpCommand { get; set; }
         public RelayCommand MouseUpCommand { get; set; }
 
 
-        public RelayCommand KeyDownCommand { get; set; }
-
-        public RelayCommand KeyUpCommand { get; set; }
-
         private bool restoreToolOnKeyUp = false;
         private bool restoreToolOnKeyUp = false;
 
 
         private MouseInputFilter filter = new();
         private MouseInputFilter filter = new();
@@ -33,17 +31,42 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             MouseUpCommand = new RelayCommand(filter.MouseUp);
             MouseUpCommand = new RelayCommand(filter.MouseUp);
             PreviewMouseMiddleButtonCommand = new RelayCommand(OnPreviewMiddleMouseButton);
             PreviewMouseMiddleButtonCommand = new RelayCommand(OnPreviewMiddleMouseButton);
             GlobalMouseHook.OnMouseUp += filter.MouseUp;
             GlobalMouseHook.OnMouseUp += filter.MouseUp;
-            KeyDownCommand = new RelayCommand(OnKeyDown);
-            KeyUpCommand = new RelayCommand(OnKeyUp);
+
+            InputManager.Current.PreProcessInput += Current_PreProcessInput;
 
 
             filter.OnMouseDown += OnMouseDown;
             filter.OnMouseDown += OnMouseDown;
             filter.OnMouseMove += OnMouseMove;
             filter.OnMouseMove += OnMouseMove;
             filter.OnMouseUp += OnMouseUp;
             filter.OnMouseUp += OnMouseUp;
         }
         }
 
 
-        private void OnKeyDown(object parameter)
+        private void Current_PreProcessInput(object sender, PreProcessInputEventArgs e)
+        {
+            if (e != null && e.StagingItem != null && e.StagingItem.Input != null)
+            {
+                InputEventArgs inputEvent = e.StagingItem.Input;
+
+                if (inputEvent is KeyboardEventArgs)
+                {
+                    KeyboardEventArgs k = inputEvent as KeyboardEventArgs;
+                    RoutedEvent r = k.RoutedEvent;
+                    KeyEventArgs keyEvent = k as KeyEventArgs;
+
+                    if (keyEvent != null && keyEvent?.InputSource?.RootVisual != MainWindow.Current) return;
+                    if (r == Keyboard.KeyDownEvent)
+                    {
+                        OnKeyDown(keyEvent);
+                    }
+
+                    if (r == Keyboard.KeyUpEvent)
+                    {
+                        OnKeyUp(keyEvent);
+                    }
+                }
+            }
+        }
+
+        private void OnKeyDown(KeyEventArgs args)
         {
         {
-            KeyEventArgs args = (KeyEventArgs)parameter;
             var key = args.Key;
             var key = args.Key;
             if (key == Key.System)
             if (key == Key.System)
                 key = args.SystemKey;
                 key = args.SystemKey;
@@ -55,8 +78,23 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 Owner.BitmapManager.InputTarget.OnKeyDown(key);
                 Owner.BitmapManager.InputTarget.OnKeyDown(key);
             }
             }
 
 
-            if (args.Key == ShortcutController.MoveViewportToolTransientChangeKey)
-                ChangeMoveViewportToolState(true);
+            HandleTransientKey(args, true);
+        }
+
+        private void HandleTransientKey(KeyEventArgs args, bool state)
+        {
+            var controller = Owner.ShortcutController;
+
+            Key finalKey = args.Key;
+            if (finalKey == Key.System)
+            {
+                finalKey = args.SystemKey;
+            }
+
+            if (controller.TransientShortcuts.ContainsKey(finalKey))
+            {
+                ChangeToolState(controller.TransientShortcuts[finalKey].GetType(), state);
+            }
         }
         }
 
 
         private void ProcessShortcutDown(bool isRepeat, Key key)
         private void ProcessShortcutDown(bool isRepeat, Key key)
@@ -71,9 +109,8 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             Owner.ShortcutController.KeyPressed(key, Keyboard.Modifiers);
             Owner.ShortcutController.KeyPressed(key, Keyboard.Modifiers);
         }
         }
 
 
-        private void OnKeyUp(object parameter)
+        private void OnKeyUp(KeyEventArgs args)
         {
         {
-            KeyEventArgs args = (KeyEventArgs)parameter;
             var key = args.Key;
             var key = args.Key;
             if (key == Key.System)
             if (key == Key.System)
                 key = args.SystemKey;
                 key = args.SystemKey;
@@ -83,10 +120,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             if (Owner.BitmapManager.ActiveDocument != null)
             if (Owner.BitmapManager.ActiveDocument != null)
                 Owner.BitmapManager.InputTarget.OnKeyUp(key);
                 Owner.BitmapManager.InputTarget.OnKeyUp(key);
 
 
-            if (args.Key == ShortcutController.MoveViewportToolTransientChangeKey)
-            {
-                ChangeMoveViewportToolState(false);     
-            }
+            HandleTransientKey(args, false);
         }
         }
 
 
         private void ProcessShortcutUp(Key key)
         private void ProcessShortcutUp(Key key)
@@ -115,21 +149,27 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         private void OnPreviewMiddleMouseButton(object sender)
         private void OnPreviewMiddleMouseButton(object sender)
         {
         {
-            ChangeMoveViewportToolState(true);
+            ChangeToolState<MoveViewportTool>(true);
+        }
+
+        private void ChangeToolState<T>(bool setOn)
+            where T : Tool
+        {
+            ChangeToolState(typeof(T), setOn);
         }
         }
 
 
-        void ChangeMoveViewportToolState(bool setOn)
+        private void ChangeToolState(Type type, bool setOn)
         {
         {
             if (setOn)
             if (setOn)
             {
             {
-                var moveViewportToolIsActive = Owner.ToolsSubViewModel.ActiveTool is MoveViewportTool;
-                if (!moveViewportToolIsActive)
+                var transientToolIsActive = Owner.ToolsSubViewModel.ActiveTool.GetType() == type;
+                if (!transientToolIsActive)
                 {
                 {
-                    Owner.ToolsSubViewModel.SetActiveTool<MoveViewportTool>();
-                    Owner.ToolsSubViewModel.MoveToolIsTransient = true;
+                    Owner.ToolsSubViewModel.SetActiveTool(type);
+                    Owner.ToolsSubViewModel.ActiveToolIsTransient = true;
                 }
                 }
             }
             }
-            else if (Owner.ToolsSubViewModel.LastActionTool != null && Owner.ToolsSubViewModel.MoveToolIsTransient)
+            else if (Owner.ToolsSubViewModel.LastActionTool != null && Owner.ToolsSubViewModel.ActiveToolIsTransient)
             {
             {
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
                 Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
                 restoreToolOnKeyUp = false;
                 restoreToolOnKeyUp = false;
@@ -155,7 +195,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
             else if (button == MouseButton.Middle)
             else if (button == MouseButton.Middle)
             {
             {
-                ChangeMoveViewportToolState(false);
+                ChangeToolState<MoveViewportTool>(false);
             }
             }
         }
         }
     }
     }

+ 13 - 7
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -24,7 +24,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public Tool LastActionTool { get; private set; }
         public Tool LastActionTool { get; private set; }
 
 
-        public bool MoveToolIsTransient { get; set; }
+        public bool ActiveToolIsTransient { get; set; }
 
 
         public Cursor ToolCursor
         public Cursor ToolCursor
         {
         {
@@ -57,7 +57,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
 
 
-        public IEnumerable<Tool> ToolSet { get; private set; }
+        public List<Tool> ToolSet { get; private set; }
 
 
         public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
         public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
 
 
@@ -70,7 +70,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public void SetupTools(IServiceProvider services)
         public void SetupTools(IServiceProvider services)
         {
         {
-            ToolSet = services.GetServices<Tool>();
+            ToolSet = services.GetServices<Tool>().ToList();
             SetActiveTool<PenTool>();
             SetActiveTool<PenTool>();
 
 
             Owner.BitmapManager.BitmapOperations.BitmapChanged += (_, _) => TriggerCacheOutdated();
             Owner.BitmapManager.BitmapOperations.BitmapChanged += (_, _) => TriggerCacheOutdated();
@@ -93,11 +93,16 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public void SetActiveTool(Tool tool)
         public void SetActiveTool(Tool tool)
         {
         {
-            MoveToolIsTransient = false;
+            if (ActiveTool == tool) return;
+            ActiveToolIsTransient = false;
+            bool shareToolbar = IPreferences.Current.GetPreference<bool>("EnableSharedToolbar");
             if (ActiveTool != null)
             if (ActiveTool != null)
             {
             {
                 activeTool.IsActive = false;
                 activeTool.IsActive = false;
-                ActiveTool.Toolbar.SaveToolbarSettings();
+                if (shareToolbar)
+                {
+                    ActiveTool.Toolbar.SaveToolbarSettings();
+                }
             }
             }
 
 
             LastActionTool = ActiveTool;
             LastActionTool = ActiveTool;
@@ -105,7 +110,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             ActiveTool = tool;
             ActiveTool = tool;
 
 
-            if (IPreferences.Current.GetPreference<bool>("EnableSharedToolbar"))
+            if (shareToolbar)
             {
             {
                 ActiveTool.Toolbar.LoadSharedSettings();
                 ActiveTool.Toolbar.LoadSharedSettings();
             }
             }
@@ -191,8 +196,9 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
 
 
-        private void SetActiveTool(Type toolType)
+        public void SetActiveTool(Type toolType)
         {
         {
+            if (!typeof(Tool).IsAssignableFrom(toolType)) { throw new ArgumentException($"'{toolType}' does not inherit from {typeof(Tool)}"); }
             Tool foundTool = ToolSet.First(x => x.GetType() == toolType);
             Tool foundTool = ToolSet.First(x => x.GetType() == toolType);
             SetActiveTool(foundTool);
             SetActiveTool(foundTool);
         }
         }

+ 6 - 0
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -34,7 +34,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             //sometimes CanRedo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
             //sometimes CanRedo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
             if (CanRedo(null))
             if (CanRedo(null))
+            {
                 Owner.BitmapManager.ActiveDocument.UndoManager.Redo();
                 Owner.BitmapManager.ActiveDocument.UndoManager.Redo();
+                Owner.BitmapManager.ActiveDocument.ChangesSaved = false;
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -47,7 +50,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             //sometimes CanUndo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
             //sometimes CanUndo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
             if (CanUndo(null))
             if (CanUndo(null))
+            {
                 Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
                 Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
+                Owner.BitmapManager.ActiveDocument.ChangesSaved = false;
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 3 - 0
PixiEditor/ViewModels/ViewModelMain.cs

@@ -217,6 +217,9 @@ namespace PixiEditor.ViewModels
                         "Misc",
                         "Misc",
                         new Shortcut(Key.F1, MiscSubViewModel.OpenShortcutWindowCommand, "Open shortcuts window", true)));
                         new Shortcut(Key.F1, MiscSubViewModel.OpenShortcutWindowCommand, "Open shortcuts window", true)));
 
 
+            ShortcutController.TransientShortcuts[Key.Space] = ToolsSubViewModel.ToolSet.First(x => x is MoveViewportTool);
+            ShortcutController.TransientShortcuts[Key.LeftAlt] = ToolsSubViewModel.ToolSet.First(x => x is ColorPickerTool);
+
             BitmapManager.PrimaryColor = ColorsSubViewModel.PrimaryColor;
             BitmapManager.PrimaryColor = ColorsSubViewModel.PrimaryColor;
 
 
             ToolsSubViewModel?.SetupToolsTooltipShortcuts(services);
             ToolsSubViewModel?.SetupToolsTooltipShortcuts(services);

+ 1 - 1
PixiEditor/Views/Dialogs/CrashReportDialog.xaml

@@ -33,7 +33,7 @@
             <StackPanel>
             <StackPanel>
                 <Grid Background="{StaticResource MainColor}">
                 <Grid Background="{StaticResource MainColor}">
                     <StackPanel Margin="7" VerticalAlignment="Center">
                     <StackPanel Margin="7" VerticalAlignment="Center">
-                        <TextBlock Text="{Binding DocumentCount, StringFormat={}{0} file(s) can be recovered}"
+                        <TextBlock Text="{Binding DocumentCount, StringFormat={}{0} file(s) might be recoverable}"
                        d:Text="2 file(s) can be recovered"/>
                        d:Text="2 file(s) can be recovered"/>
                         <TextBlock TextWrapping="Wrap">You can help the developers fix this bug by sending a crash report that was generated (you will still be able to recover the files).</TextBlock>
                         <TextBlock TextWrapping="Wrap">You can help the developers fix this bug by sending a crash report that was generated (you will still be able to recover the files).</TextBlock>
                     </StackPanel>
                     </StackPanel>

+ 1 - 0
PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml

@@ -42,6 +42,7 @@
                               Width="240"
                               Width="240"
                               Height="170"
                               Height="170"
                               x:Name="sizePicker"
                               x:Name="sizePicker"
+                              Focusable="True"
                               ChosenHeight="{Binding NewAbsoluteHeight, Mode=TwoWay, ElementName=window}"
                               ChosenHeight="{Binding NewAbsoluteHeight, Mode=TwoWay, ElementName=window}"
                               ChosenWidth="{Binding NewAbsoluteWidth, Mode=TwoWay, ElementName=window}" 
                               ChosenWidth="{Binding NewAbsoluteWidth, Mode=TwoWay, ElementName=window}" 
                               ChosenPercentageSize="{Binding NewPercentageSize, Mode=TwoWay, ElementName=window}"
                               ChosenPercentageSize="{Binding NewPercentageSize, Mode=TwoWay, ElementName=window}"

+ 1 - 0
PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml

@@ -37,6 +37,7 @@
         <local:SizePicker HorizontalAlignment="Center" Width="240" Height="180" Margin="0,30,0,0"
         <local:SizePicker HorizontalAlignment="Center" Width="240" Height="180" Margin="0,30,0,0"
             x:Name="sizePicker"
             x:Name="sizePicker"
             PreserveAspectRatio="True"
             PreserveAspectRatio="True"
+                          Focusable="True"
             ChosenHeight="{Binding NewAbsoluteHeight, Mode=TwoWay, ElementName=window}"
             ChosenHeight="{Binding NewAbsoluteHeight, Mode=TwoWay, ElementName=window}"
             ChosenWidth="{Binding NewAbsoluteWidth, Mode=TwoWay, ElementName=window}" 
             ChosenWidth="{Binding NewAbsoluteWidth, Mode=TwoWay, ElementName=window}" 
             ChosenPercentageSize="{Binding NewPercentageSize, Mode=TwoWay, ElementName=window}"
             ChosenPercentageSize="{Binding NewPercentageSize, Mode=TwoWay, ElementName=window}"

+ 4 - 10
PixiEditor/Views/MainWindow.xaml

@@ -54,12 +54,6 @@
     </Window.CommandBindings>
     </Window.CommandBindings>
 
 
     <i:Interaction.Triggers>
     <i:Interaction.Triggers>
-        <i:EventTrigger EventName="KeyDown">
-            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyDownCommand}" PassEventArgsToCommand="True" />
-        </i:EventTrigger>
-        <i:EventTrigger EventName="KeyUp">
-            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyUpCommand}" PassEventArgsToCommand="True"/>
-        </i:EventTrigger>
         <i:EventTrigger EventName="ContentRendered">
         <i:EventTrigger EventName="ContentRendered">
             <i:InvokeCommandAction Command="{Binding OnStartupCommand}" />
             <i:InvokeCommandAction Command="{Binding OnStartupCommand}" />
         </i:EventTrigger>
         </i:EventTrigger>
@@ -209,7 +203,7 @@
             <Button Command="{Binding UndoSubViewModel.RedoCommand}" ToolTip="Redo"
             <Button Command="{Binding UndoSubViewModel.RedoCommand}" ToolTip="Redo"
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A6;"/>
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A6;"/>
             <ToggleButton Width="30" BorderThickness="0"
             <ToggleButton Width="30" BorderThickness="0"
-                          ToolTip="Pen Mode"
+                          ToolTip="Pen Mode" Focusable="False"
                           IsChecked="{Binding StylusSubViewModel.IsPenModeEnabled}">
                           IsChecked="{Binding StylusSubViewModel.IsPenModeEnabled}">
                 <ToggleButton.Style>
                 <ToggleButton.Style>
                     <Style TargetType="ToggleButton">
                     <Style TargetType="ToggleButton">
@@ -217,9 +211,9 @@
                             <Setter.Value>
                             <Setter.Value>
                                 <ControlTemplate TargetType="ToggleButton">
                                 <ControlTemplate TargetType="ToggleButton">
                                     <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                     <Border BorderBrush="{TemplateBinding BorderBrush}" 
-                                            Background="{TemplateBinding Background}">
+                                            Background="{TemplateBinding Background}" Focusable="False">
                                         <ContentPresenter HorizontalAlignment="Center"
                                         <ContentPresenter HorizontalAlignment="Center"
-                                              VerticalAlignment="Center"/>
+                                              VerticalAlignment="Center" Focusable="False"/>
                                     </Border>
                                     </Border>
                                 </ControlTemplate>
                                 </ControlTemplate>
                             </Setter.Value>
                             </Setter.Value>
@@ -229,7 +223,7 @@
                                 <Setter Property="Background" Value="Transparent"/>
                                 <Setter Property="Background" Value="Transparent"/>
                             </Trigger>
                             </Trigger>
                             <Trigger Property="IsMouseOver" Value="True">
                             <Trigger Property="IsMouseOver" Value="True">
-                                <Setter Property="Background" Value="#606060"/>
+                                <Setter Property="Background" Value="#404040"/>
                             </Trigger>
                             </Trigger>
                             <Trigger Property="IsChecked" Value="True">
                             <Trigger Property="IsChecked" Value="True">
                                 <Setter Property="Background" Value="#707070"/>
                                 <Setter Property="Background" Value="#707070"/>

+ 1 - 1
PixiEditor/Views/UserControls/EditableTextBlock.xaml

@@ -5,7 +5,7 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              mc:Ignorable="d"
              mc:Ignorable="d"
-             d:DesignHeight="60" d:DesignWidth="100">
+             d:DesignHeight="60" d:DesignWidth="100" Focusable="True">
     <Grid>
     <Grid>
         <TextBlock Foreground="Snow" MouseLeftButtonDown="TextBlock_MouseDown"
         <TextBlock Foreground="Snow" MouseLeftButtonDown="TextBlock_MouseDown"
                    TextTrimming="CharacterEllipsis" Name="textBlock"
                    TextTrimming="CharacterEllipsis" Name="textBlock"

+ 2 - 2
PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -74,6 +74,7 @@ namespace PixiEditor.Views.UserControls.Layers
         public LayersManager()
         public LayersManager()
         {
         {
             InitializeComponent();
             InitializeComponent();
+            numberInput.OnScrollAction = () => NumberInput_LostFocus(null, null);
         }
         }
 
 
         private static void LayerTreeRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         private static void LayerTreeRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -348,7 +349,6 @@ namespace PixiEditor.Views.UserControls.Layers
 
 
             if (item is Layer || item is LayerStructureItemContainer)
             if (item is Layer || item is LayerStructureItemContainer)
             {
             {
-
                 Layer layer = null;
                 Layer layer = null;
 
 
                 if (item is Layer lr)
                 if (item is Layer lr)
@@ -463,4 +463,4 @@ namespace PixiEditor.Views.UserControls.Layers
             SelectedItem = sender;
             SelectedItem = sender;
         }
         }
     }
     }
-}
+}

+ 4 - 4
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml

@@ -51,7 +51,7 @@
                         </Button.Background>
                         </Button.Background>
                     </Button>
                     </Button>
                 </StackPanel>
                 </StackPanel>
-                <CheckBox Panel.ZIndex="10" Name="visibilityCheckbox" Grid.Column="1" Margin="0,0,5,0" Height="16" HorizontalAlignment="Right">
+                <CheckBox Focusable="False" Panel.ZIndex="10" Name="visibilityCheckbox" Grid.Column="1" Margin="0,0,5,0" Height="16" HorizontalAlignment="Right">
                     <CheckBox.Triggers>
                     <CheckBox.Triggers>
                         <EventTrigger RoutedEvent="CheckBox.Checked">
                         <EventTrigger RoutedEvent="CheckBox.Checked">
                             <BeginStoryboard>
                             <BeginStoryboard>
@@ -73,13 +73,13 @@
                     </CheckBox.Triggers>
                     </CheckBox.Triggers>
                     <CheckBox.Template>
                     <CheckBox.Template>
                         <ControlTemplate TargetType="{x:Type CheckBox}">
                         <ControlTemplate TargetType="{x:Type CheckBox}">
-                            <StackPanel Orientation="Horizontal">
-                                <Image Width="14" Cursor="Hand" x:Name="checkboxImage" Source="/Images/ChevronDown.png">
+                            <StackPanel Orientation="Horizontal" Focusable="False">
+                                <Image Focusable="False" Width="14" Cursor="Hand" x:Name="checkboxImage" Source="/Images/ChevronDown.png">
                                     <Image.RenderTransform>
                                     <Image.RenderTransform>
                                         <RotateTransform Angle="0"/>
                                         <RotateTransform Angle="0"/>
                                     </Image.RenderTransform>
                                     </Image.RenderTransform>
                                 </Image>
                                 </Image>
-                                <ContentPresenter/>
+                                <ContentPresenter Focusable="False"/>
                             </StackPanel>
                             </StackPanel>
                             <ControlTemplate.Triggers>
                             <ControlTemplate.Triggers>
                                 <Trigger Property="IsChecked" Value="True">
                                 <Trigger Property="IsChecked" Value="True">

+ 1 - 1
PixiEditor/Views/UserControls/NumberInput.xaml

@@ -6,7 +6,7 @@
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              mc:Ignorable="d"
              mc:Ignorable="d"
-             d:DesignHeight="20" d:DesignWidth="40" x:Name="numberInput" Focusable="True">
+             d:DesignHeight="20" d:DesignWidth="40" x:Name="numberInput" Focusable="True" FocusVisualStyle="{x:Null}">
     <TextBox TextAlignment="Center" Style="{StaticResource DarkTextBoxStyle}" Focusable="True"
     <TextBox TextAlignment="Center" Style="{StaticResource DarkTextBoxStyle}" Focusable="True"
              InputScope="Number" MouseWheel="TextBox_MouseWheel"
              InputScope="Number" MouseWheel="TextBox_MouseWheel"
              PreviewTextInput="TextBox_PreviewTextInput" Text="{Binding ElementName=numberInput, Path=Value}" Padding="0" VerticalContentAlignment="Center">
              PreviewTextInput="TextBox_PreviewTextInput" Text="{Binding ElementName=numberInput, Path=Value}" Padding="0" VerticalContentAlignment="Center">

+ 27 - 1
PixiEditor/Views/UserControls/NumberInput.xaml.cs

@@ -38,6 +38,30 @@ namespace PixiEditor.Views
 
 
         private readonly Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$", RegexOptions.Compiled);
         private readonly Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$", RegexOptions.Compiled);
 
 
+
+        public int Decimals
+        {
+            get { return (int)GetValue(DecimalsProperty); }
+            set { SetValue(DecimalsProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for Precision.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty DecimalsProperty =
+            DependencyProperty.Register("Decimals", typeof(int), typeof(NumberInput), new PropertyMetadata(2));
+
+
+        public Action OnScrollAction
+        {
+            get { return (Action)GetValue(OnScrollActionProperty); }
+            set { SetValue(OnScrollActionProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for OnScrollAction.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty OnScrollActionProperty =
+            DependencyProperty.Register("OnScrollAction", typeof(Action), typeof(NumberInput), new PropertyMetadata(null));
+
+
+
         public NumberInput()
         public NumberInput()
         {
         {
             InitializeComponent();
             InitializeComponent();
@@ -64,7 +88,7 @@ namespace PixiEditor.Views
         private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
         {
             NumberInput input = (NumberInput)d;
             NumberInput input = (NumberInput)d;
-            input.Value = Math.Clamp((float)e.NewValue, input.Min, input.Max);
+            input.Value = (float)Math.Round(Math.Clamp((float)e.NewValue, input.Min, input.Max), input.Decimals);
         }
         }
 
 
         private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
         private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
@@ -89,6 +113,8 @@ namespace PixiEditor.Views
             {
             {
                 Value += step;
                 Value += step;
             }
             }
+
+            OnScrollAction?.Invoke();
         }
         }
     }
     }
 }
 }

+ 13 - 0
PixiEditor/Views/UserControls/SizeInput.xaml.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
+using System;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Input;
@@ -22,6 +23,16 @@ namespace PixiEditor.Views
         public static readonly DependencyProperty UnitProperty =
         public static readonly DependencyProperty UnitProperty =
             DependencyProperty.Register(nameof(Unit), typeof(SizeUnit), typeof(SizeInput), new PropertyMetadata(SizeUnit.Pixel));
             DependencyProperty.Register(nameof(Unit), typeof(SizeUnit), typeof(SizeInput), new PropertyMetadata(SizeUnit.Pixel));
 
 
+        public Action OnScrollAction
+        {
+            get { return (Action)GetValue(OnScrollActionProperty); }
+            set { SetValue(OnScrollActionProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for OnScrollAction.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty OnScrollActionProperty =
+            DependencyProperty.Register("OnScrollAction", typeof(Action), typeof(SizeInput), new PropertyMetadata(null));
+
         public SizeInput()
         public SizeInput()
         {
         {
             InitializeComponent();
             InitializeComponent();
@@ -119,6 +130,8 @@ namespace PixiEditor.Views
             {
             {
                 Size += step;
                 Size += step;
             }
             }
+
+            OnScrollAction?.Invoke();
         }
         }
     }
     }
 }
 }

+ 5 - 0
PixiEditor/Views/UserControls/SizePicker.xaml.cs

@@ -85,7 +85,12 @@ namespace PixiEditor.Views
             WidthLostFocusCommand = new(WidthLostFocus);
             WidthLostFocusCommand = new(WidthLostFocus);
             HeightLostFocusCommand = new(HeightLostFocus);
             HeightLostFocusCommand = new(HeightLostFocus);
             PercentageLostFocusCommand = new(PercentageLostFocus);
             PercentageLostFocusCommand = new(PercentageLostFocus);
+
             InitializeComponent();
             InitializeComponent();
+
+            WidthPicker.OnScrollAction = () => OnSizeUpdate(true);
+            HeightPicker.OnScrollAction = () => OnSizeUpdate(false);
+            PercentageSizePicker.OnScrollAction = () => PercentageLostFocus(null);
         }
         }
 
 
         public void FocusWidthPicker()
         public void FocusWidthPicker()

+ 2 - 2
PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

@@ -150,7 +150,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
 
 
-            doc.LayerStructure.PreMoveReassignBounds(new GroupData(group1.GroupGuid), doc.Layers[0].GuidValue);
+            doc.LayerStructure.Unassign(new GroupData(group1.GroupGuid), doc.Layers[0].GuidValue);
 
 
             Assert.Empty(doc.LayerStructure.Groups);
             Assert.Empty(doc.LayerStructure.Groups);
         }
         }
@@ -168,7 +168,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
-            doc.LayerStructure.PostMoveReassignBounds(new GroupData(group1.GroupGuid), layer.GuidValue);
+            doc.LayerStructure.Assign(new GroupData(group1.GroupGuid), layer.GuidValue);
 
 
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);