Browse Source

Shortcuts binder basic functionality ported

Krzysztof Krysiński 1 year ago
parent
commit
7c4f055e2a

+ 5 - 5
src/PixiEditor.AvaloniaUI/Models/UserData/RecentlyOpenedDocument.cs

@@ -1,11 +1,14 @@
 using System.Diagnostics;
 using System.IO;
+using System.Linq;
 using Avalonia;
 using Avalonia.Media.Imaging;
+using ChunkyImageLib;
 using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers.Extensions;
 using PixiEditor.AvaloniaUI.Models.IO;
+using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Exceptions;
 using PixiEditor.Parser;
 using PixiEditor.Parser.Deprecated;
@@ -116,15 +119,12 @@ internal class RecentlyOpenedDocument : ObservableObject
                 }
             }
 
-            //TODO: Fix this
-            /*using Surface surface = Surface.Combine(serializableDocument.Width, serializableDocument.Height,
+            using Surface surface = Surface.Combine(serializableDocument.Width, serializableDocument.Height,
                 serializableDocument.Layers
                     .Where(x => x.Opacity > 0.8)
                     .Select(x => (x.ToImage(), new VecI(x.OffsetX, x.OffsetY))).ToList());
 
-            return DownscaleToMaxSize(surface.ToBitmap());*/
-
-            return null;
+            return DownscaleToMaxSize(surface.ToWriteableBitmap());
         }
 
         if (SupportedFilesHelper.IsExtensionSupported(FileExtension))

+ 0 - 13
src/PixiEditor.AvaloniaUI/PixiEditor.AvaloniaUI.csproj

@@ -19,7 +19,6 @@
       <AvaloniaResource Include="Images\Lock-alpha.svg" />
       <None Remove="Images\Merge-downwards.svg" />
       <AvaloniaResource Include="Images\Merge-downwards.svg" />
-      <None Remove="Nunito.ttf" />
     </ItemGroup>
 
     <ItemGroup>
@@ -77,18 +76,6 @@
       <Compile Remove="Views\Buttons\**" />
       <EmbeddedResource Remove="Views\Buttons\**" />
       <None Remove="Views\Buttons\**" />
-      <Compile Update="Views\Main\ViewportControls\Viewport.axaml.cs">
-        <DependentUpon>Viewport.axaml</DependentUpon>
-        <SubType>Code</SubType>
-      </Compile>
-      <Compile Update="Views\Dialogs\Debug\Localization\LocalizationDebugWindow.axaml.cs">
-        <DependentUpon>LocalizationDebugWindow.axaml</DependentUpon>
-        <SubType>Code</SubType>
-      </Compile>
-      <Compile Update="Views\Windows\PalettesBrowser.axaml.cs">
-        <DependentUpon>PalettesBrowser.axaml</DependentUpon>
-        <SubType>Code</SubType>
-      </Compile>
     </ItemGroup>
   
     <ItemGroup>

+ 1 - 0
src/PixiEditor.AvaloniaUI/Styles/PixiEditor.Controls.axaml

@@ -7,6 +7,7 @@
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/CommandsMenu.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/BlendModeComboBox.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Buttons/DialogButtonTheme.axaml"/>
+                <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/ShortcutBoxTemplate.axaml"/>
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
     </Styles.Resources>

+ 0 - 8
src/PixiEditor.AvaloniaUI/Styles/Templates/SearchResultTemplate.axaml

@@ -1,8 +0,0 @@
-<ResourceDictionary xmlns="https://github.com/avaloniaui"
-                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-                    xmlns:search="clr-namespace:PixiEditor.AvaloniaUI.Models.Commands.Search">
-
-    <!--<ControlTheme TargetType="search:SearchResult">
-
-    </ControlTheme>-->
-</ResourceDictionary>

+ 13 - 0
src/PixiEditor.AvaloniaUI/Styles/Templates/ShortcutBoxTemplate.axaml

@@ -0,0 +1,13 @@
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:shortcuts="clr-namespace:PixiEditor.AvaloniaUI.Views.Shortcuts">
+    <ControlTheme x:Key="{x:Type shortcuts:ShortcutBox}" TargetType="shortcuts:ShortcutBox">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="shortcuts:ShortcutBox">
+                   <ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </ControlTheme>
+</ResourceDictionary>

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Dialogs/OptionPopup.axaml

@@ -13,7 +13,7 @@
     Title="OptionPopup"
     SizeToContent="WidthAndHeight"
     Name="popup"
-    ui:Translator.Key="{Binding #popup.Title}">
+    ui:Translator.Key="{Binding #popup.Title, Mode=OneTime}">
     <Grid>
         <Grid.RowDefinitions>
             <RowDefinition/>

+ 65 - 0
src/PixiEditor.AvaloniaUI/Views/Shortcuts/KeyCombinationBox.axaml

@@ -0,0 +1,65 @@
+<UserControl x:Class="PixiEditor.AvaloniaUI.Views.Shortcuts.KeyCombinationBox"
+             x:ClassModifier="internal"
+             xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             mc:Ignorable="d" 
+             d:DesignHeight="30" d:DesignWidth="80">
+    <Grid Focusable="True" Name="focusGrid">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition/>
+            <ColumnDefinition Width="20"/>
+        </Grid.ColumnDefinitions>
+        <TextBox
+                 KeyDown="TextBox_PreviewKeyDown"
+                 KeyUp="TextBox_PreviewKeyUp"
+                 x:Name="textBox"
+                 CaretBrush="Transparent"
+                 GotFocus="TextBox_GotKeyboardFocus"
+                 LostFocus="TextBox_LostKeyboardFocus">
+            <TextBox.Styles>
+                <Style Selector="Border">
+                    <Setter Property="CornerRadius" Value="5,0,0,5"/>
+                </Style>
+            </TextBox.Styles>
+        </TextBox>
+        <Button Grid.Column="1" x:Name="button" Content="―"
+                Click="Button_Click">
+            <Button.Styles>
+                <Style Selector="Button">
+                    <Setter Property="Template">
+                        <Setter.Value>
+                            <ControlTemplate TargetType="Button">
+                                <Border CornerRadius="0,5,5,0" BorderThickness="1"
+                                        Background="{TemplateBinding Background}"
+                                        BorderBrush="{TemplateBinding BorderBrush}">
+                                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
+                                                      ContentTemplate="{TemplateBinding ContentTemplate}"
+                                                      Content="{TemplateBinding Content}"/>
+                                </Border>
+                            </ControlTemplate>
+                        </Setter.Value>
+                    </Setter>
+                    <!--<Style.Triggers>
+                        <Trigger Property="IsMouseOver" Value="False">
+                            <Setter Property="Background" Value="{StaticResource BrighterAccentColor}"/>
+                            <Setter Property="BorderBrush" Value="{StaticResource BrighterAccentColor}"/>
+                            <Setter Property="Foreground" Value="White"/>
+                        </Trigger>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter Property="Background" Value="{StaticResource AlmostLightModeAccentColor}"/>
+                            <Setter Property="BorderBrush" Value="{StaticResource AlmostLightModeAccentColor}"/>
+                            <Setter Property="Foreground" Value="White"/>
+                        </Trigger>
+                        <Trigger Property="IsEnabled" Value="False">
+                            <Setter Property="Background" Value="{StaticResource AccentColor}"/>
+                            <Setter Property="BorderBrush" Value="{StaticResource BrighterAccentColor}"/>
+                            <Setter Property="Foreground" Value="Gray"/>
+                        </Trigger>
+                    </Style.Triggers>-->
+                </Style>
+            </Button.Styles>
+        </Button>
+    </Grid>
+</UserControl>

+ 182 - 0
src/PixiEditor.AvaloniaUI/Views/Shortcuts/KeyCombinationBox.axaml.cs

@@ -0,0 +1,182 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Hardware.Info;
+using PixiEditor.AvaloniaUI.Models.Input;
+using PixiEditor.AvaloniaUI.ViewModels;
+using PixiEditor.Extensions.Common.Localization;
+
+namespace PixiEditor.AvaloniaUI.Views.Shortcuts;
+
+/// <summary>
+/// Interaction logic for KeyCombinationBox.xaml
+/// </summary>
+internal partial class KeyCombinationBox : UserControl
+{
+    private KeyCombination currentCombination;
+    private bool ignoreButtonPress;
+
+    public static readonly StyledProperty<KeyCombination> KeyCombinationProperty =
+        AvaloniaProperty.Register<KeyCombinationBox, KeyCombination>(
+            nameof(KeyCombination));
+
+    public KeyCombination KeyCombination
+    {
+        get => GetValue(KeyCombinationProperty);
+        set => SetValue(KeyCombinationProperty, value);
+    }
+
+    public event EventHandler<KeyCombination> KeyCombinationChanged;
+
+    public static readonly StyledProperty<KeyCombination> DefaultCombinationProperty = AvaloniaProperty.Register<KeyCombinationBox, KeyCombination>(
+        nameof(DefaultCombination));
+
+    public KeyCombination DefaultCombination
+    {
+        get => GetValue(DefaultCombinationProperty);
+        set => SetValue(DefaultCombinationProperty, value);
+    }
+
+    static KeyCombinationBox()
+    {
+        KeyCombinationProperty.Changed.Subscribe(CombinationUpdate);
+        DefaultCombinationProperty.Changed.Subscribe(DefaultCombinationUpdate);
+    }
+
+    public KeyCombinationBox()
+    {
+        InitializeComponent();
+
+        UpdateText();
+        UpdateButton();
+
+        ViewModelMain.Current.LocalizationProvider.OnLanguageChanged += _ => UpdateText();
+
+        //TOOD: Fix
+        //InputLanguageManager.Current.InputLanguageChanged += (_, _) => UpdateText();
+    }
+
+    private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
+    {
+        e.Handled = true;
+
+        //TODO: e.Key == Key.System ? e.SystemKey : e.Key was here, but SystemKey is not available in Avalonia
+        if (GetModifier(e.Key) is { } modifier)
+        {
+            currentCombination = new(currentCombination.Key, currentCombination.Modifiers | modifier);
+        }
+        else
+        {
+            currentCombination = new(e.Key, currentCombination.Modifiers);
+        }
+
+        UpdateText();
+        UpdateButton();
+    }
+
+    private void TextBox_PreviewKeyUp(object sender, KeyEventArgs e)
+    {
+        e.Handled = true;
+
+        //TODO: There was (e.Key == Key.System ? e.SystemKey : e.Key), but SystemKey is not available in Avalonia
+        if (GetModifier(e.Key) is { } modifier)
+        {
+            currentCombination = new(currentCombination.Key, currentCombination.Modifiers ^ modifier);
+            UpdateText();
+        }
+        else
+        {
+            KeyCombination = new(e.Key, currentCombination.Modifiers);
+            focusGrid.Focus();
+            //Keyboard.ClearFocus();
+        }
+
+        UpdateButton();
+    }
+
+    private void TextBox_GotKeyboardFocus(object sender, GotFocusEventArgs e)
+    {
+        currentCombination = new();
+        textBox.Text = new LocalizedString("PRESS_ANY_KEY");
+        UpdateButton();
+    }
+
+    private void TextBox_LostKeyboardFocus(object sender, RoutedEventArgs e)
+    {
+        // TODO: Validate if right
+        ignoreButtonPress = TopLevel.GetTopLevel(this).FocusManager.GetFocusedElement() == button;
+        currentCombination = KeyCombination;
+
+        UpdateText();
+        UpdateButton();
+        focusGrid.Focus();
+        //FocusHelper.MoveFocusToParent((FrameworkElement)sender);
+    }
+
+    private void Button_Click(object sender, RoutedEventArgs e)
+    {
+        if (ignoreButtonPress)
+        {
+            ignoreButtonPress = false;
+            return;
+        }
+
+        if (KeyCombination != DefaultCombination)
+        {
+            KeyCombination = DefaultCombination;
+        }
+        else
+        {
+            KeyCombination = default;
+        }
+    }
+
+    private void UpdateText() => textBox.Text = currentCombination != default ? currentCombination.ToString() : new LocalizedString("NONE_SHORTCUT");
+
+    private void UpdateButton()
+    {
+        //TODO: Maybe make better icons
+        if (textBox.IsFocused)
+        {
+            button.IsEnabled = true;
+            button.Content = "\ud83d\uddd9";
+        }
+        else if (KeyCombination != DefaultCombination)
+        {
+            button.IsEnabled = true;
+            button.Content = "\u2190";
+        }
+        else
+        {
+            button.IsEnabled = KeyCombination != default;
+            button.Content = "―";
+        }
+    }
+
+    private static void CombinationUpdate(AvaloniaPropertyChangedEventArgs<KeyCombination> e)
+    {
+        var box = (KeyCombinationBox)e.Sender;
+
+        box.currentCombination = box.KeyCombination;
+        box.textBox.Text = box.KeyCombination.ToString();
+        box.KeyCombinationChanged.Invoke(box, box.currentCombination);
+
+        box.UpdateText();
+        box.UpdateButton();
+    }
+
+    private static void DefaultCombinationUpdate(AvaloniaPropertyChangedEventArgs<KeyCombination> e)
+    {
+        var box = (KeyCombinationBox)e.Sender;
+        box.UpdateButton();
+    }
+
+    private static KeyModifiers? GetModifier(Key key) => key switch
+    {
+        Key.LeftCtrl or Key.RightCtrl => KeyModifiers.Control,
+        Key.LeftAlt or Key.RightAlt => KeyModifiers.Alt,
+        Key.LeftShift or Key.RightShift => KeyModifiers.Shift,
+        _ => null
+    };
+}

+ 113 - 0
src/PixiEditor.AvaloniaUI/Views/Shortcuts/ShortcutBox.cs

@@ -0,0 +1,113 @@
+using System.Linq;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using PixiEditor.AvaloniaUI.Models.Commands;
+using PixiEditor.AvaloniaUI.Models.Commands.Commands;
+using PixiEditor.AvaloniaUI.Models.Dialogs;
+using PixiEditor.AvaloniaUI.Models.Input;
+using PixiEditor.Extensions.Common.Localization;
+
+namespace PixiEditor.AvaloniaUI.Views.Shortcuts;
+
+internal class ShortcutBox : ContentControl
+{
+    private readonly KeyCombinationBox box;
+    private bool changingCombination;
+
+    public static readonly StyledProperty<Command> CommandProperty = AvaloniaProperty.Register<ShortcutBox, Command>(
+        nameof(Command));
+
+    public Command Command
+    {
+        get => GetValue(CommandProperty);
+        set => SetValue(CommandProperty, value);
+    }
+
+    static ShortcutBox()
+    {
+        CommandProperty.Changed.Subscribe(CommandUpdated);
+    }
+
+    public ShortcutBox()
+    {
+        Content = box = new KeyCombinationBox();
+        box.KeyCombinationChanged += Box_KeyCombinationChanged;
+    }
+
+    private void Command_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+    {
+        if (e.PropertyName == nameof(Command.Shortcut))
+        {
+            UpdateBoxCombination();
+        }
+    }
+
+    private void Box_KeyCombinationChanged(object sender, KeyCombination e)
+    {
+        if (changingCombination)
+        {
+            return;
+        }
+
+        changingCombination = true;
+        var controller = CommandController.Current;
+
+        if (e != KeyCombination.None)
+        {
+            if (controller.Commands[e].SkipWhile(x => x == Command).FirstOrDefault() is { } oldCommand)
+            {
+                var oldShortcut = Command.Shortcut;
+                bool enableSwap = oldShortcut is not { Key: Key.None, Modifiers: KeyModifiers.None };
+
+                LocalizedString text = enableSwap ?
+                    new LocalizedString("SHORTCUT_ALREADY_ASSIGNED_SWAP", oldCommand.DisplayName) :
+                    new LocalizedString("SHORTCUT_ALREADY_ASSIGNED_OVERWRITE", oldCommand.DisplayName);
+                OptionsDialog<string> dialog = new OptionsDialog<string>("ALREADY_ASSIGNED", text, MainWindow.Current);
+
+                dialog.Add(new LocalizedString("REPLACE"), x => controller.ReplaceShortcut(Command, e));
+                if (enableSwap)
+                {
+                    dialog.Add(new LocalizedString("SWAP"), x =>
+                    {
+                        controller.ReplaceShortcut(Command, e);
+                        controller.ReplaceShortcut(oldCommand, oldShortcut);
+                    });
+                }
+                dialog.Add(new LocalizedString("CANCEL"), x => box.KeyCombination = Command.Shortcut);
+
+                dialog.ShowDialog();
+                changingCombination = false;
+                return;
+            }
+        }
+
+        changingCombination = false;
+        controller.UpdateShortcut(Command, e);
+    }
+
+    private void UpdateBoxCombination()
+    {
+        changingCombination = true;
+        box.KeyCombination = Command?.Shortcut ?? default;
+        box.DefaultCombination = Command?.DefaultShortcut ?? default;
+        changingCombination = false;
+    }
+
+    private static void CommandUpdated(AvaloniaPropertyChangedEventArgs<Command> e)
+    {
+        var box = e.Sender as ShortcutBox;
+
+        if (e.OldValue.Value is { } oldValue)
+        {
+            oldValue.PropertyChanged -= box.Command_PropertyChanged;
+        }
+
+        if (e.NewValue.Value is { } newValue)
+        {
+            newValue.PropertyChanged += box.Command_PropertyChanged;
+        }
+
+        box.UpdateBoxCombination();
+    }
+}

+ 0 - 12
src/PixiEditor.AvaloniaUI/Views/Windows/Settings/SettingsWindow.axaml.cs

@@ -11,17 +11,5 @@ public partial class SettingsWindow : PixiEditorPopup
         var viewModel = DataContext as SettingsWindowViewModel;
         viewModel!.CurrentPage = page;
     }
-
-    //TODO figure out what's the purpose of this
-    /*
-    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
-    {
-        e.CanExecute = true;
-    }
-
-    private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
-    {
-        SystemCommands.CloseWindow(this);
-    }*/
 }
 

+ 4 - 3
src/PixiEditor.AvaloniaUI/Views/Windows/Settings/ShortcutsBinder.axaml

@@ -8,6 +8,7 @@
     xmlns:commands="clr-namespace:PixiEditor.AvaloniaUI.Models.Commands"
     xmlns:commands1="clr-namespace:PixiEditor.AvaloniaUI.Models.Commands.Commands"
     xmlns:viewModels="clr-namespace:PixiEditor.AvaloniaUI.ViewModels"
+    xmlns:shortcuts="clr-namespace:PixiEditor.AvaloniaUI.Views.Shortcuts"
     mc:Ignorable="d"
     d:DesignHeight="600" d:DesignWidth="400"
     x:Class="PixiEditor.AvaloniaUI.Views.Windows.Settings.ShortcutsBinder">
@@ -66,10 +67,10 @@
                 </ControlTemplate>
             </ScrollViewer.Template>-->
             <Grid>
-                <TextBlock Foreground="LightGray" HorizontalAlignment="Center" TextAlignment="Center"
+                <TextBlock HorizontalAlignment="Center" TextAlignment="Center"
                            IsVisible="{Binding !VisibleGroups, Mode=OneWay}"
                            ui:Translator.Key="NOTHING_FOUND"  d:Text="Nothing found."/>
-                <ItemsControl ItemsSource="{Binding Commands}" Foreground="White" Focusable="False">
+                <ItemsControl ItemsSource="{Binding Commands}" Focusable="False">
                     <ItemsControl.ItemTemplate>
                         <DataTemplate DataType="{x:Type viewModels:GroupSearchResult}">
                             <StackPanel Margin="0,0,0,20" IsVisible="{Binding IsVisible}">
@@ -79,7 +80,7 @@
                                         <DataTemplate DataType="{x:Type viewModels:CommandSearchResult}">
                                             <Grid Margin="0,5,5,0" IsVisible="{Binding IsVisible}">
                                                 <TextBlock Text="{Binding Command.DisplayName}" ToolTip.Tip="{Binding Command.Description}"/>
-                                                <!--<userControls:ShortcutBox Width="120" Command="{Binding Command}" HorizontalAlignment="Right" Focusable="False"/>-->
+                                                <shortcuts:ShortcutBox Width="120" Command="{Binding Command}" HorizontalAlignment="Right" Focusable="False"/>
                                             </Grid>
                                         </DataTemplate>
                                     </ItemsControl.ItemTemplate>