From 3e94457951cc2d87d17b30612d420ac8492f30d2 Mon Sep 17 00:00:00 2001 From: Alex <48759429+Aurumaker72@users.noreply.github.com> Date: Sun, 12 Nov 2023 11:15:22 +0100 Subject: [PATCH] Finalize multiselection support --- RsrcArchitect.Services/IInputService.cs | 16 + .../RsrcArchitect.Services.csproj | 4 + RsrcArchitect.ViewModels.Types/Key.cs | 1034 +++++++++++++++++ .../DialogEditorViewModel.cs | 5 +- .../DialogEditorViewModel_Commands.cs | 8 +- RsrcArchitect.ViewModels/MainViewModel.cs | 10 +- RsrcArchitect.Views.WPF/MainWindow.xaml.cs | 6 +- .../Rendering/StyledObjectRenderer.cs | 3 +- 8 files changed, 1076 insertions(+), 10 deletions(-) create mode 100644 RsrcArchitect.Services/IInputService.cs create mode 100644 RsrcArchitect.ViewModels.Types/Key.cs diff --git a/RsrcArchitect.Services/IInputService.cs b/RsrcArchitect.Services/IInputService.cs new file mode 100644 index 0000000..f0cd23f --- /dev/null +++ b/RsrcArchitect.Services/IInputService.cs @@ -0,0 +1,16 @@ +using RsrcArchitect.ViewModels.Types; + +namespace RsrcArchitect.Services; + +/// +/// The default for a service that provides input polling functionality +/// +public interface IInputService +{ + /// + /// Gets a key's held state + /// + /// The key to check against + /// Whether the key is currently being held + public bool IsKeyHeld(Key key); +} \ No newline at end of file diff --git a/RsrcArchitect.Services/RsrcArchitect.Services.csproj b/RsrcArchitect.Services/RsrcArchitect.Services.csproj index 9f94831..53d8807 100644 --- a/RsrcArchitect.Services/RsrcArchitect.Services.csproj +++ b/RsrcArchitect.Services/RsrcArchitect.Services.csproj @@ -7,4 +7,8 @@ RsrcArchitect.Services + + + + diff --git a/RsrcArchitect.ViewModels.Types/Key.cs b/RsrcArchitect.ViewModels.Types/Key.cs new file mode 100644 index 0000000..4bc02b1 --- /dev/null +++ b/RsrcArchitect.ViewModels.Types/Key.cs @@ -0,0 +1,1034 @@ +namespace RsrcArchitect.ViewModels.Types; + +// from wpf + +/// +/// Represents a keyboard key +/// +public enum Key +{ + /// + /// No key pressed. + /// + None, + + /// + /// The CANCEL key. + /// + Cancel, + + /// + /// The BACKSPACE key. + /// + Back, + + /// + /// The TAB key. + /// + Tab, + + /// + /// The LineFeed key. + /// + LineFeed, + + /// + /// The CLEAR key. + /// + Clear, + + /// + /// The RETURN key. + /// + Return, + + /// + /// The ENTER key. + /// + Enter = Return, + + /// + /// The PAUSE key. + /// + Pause, + + /// + /// The CAPS LOCK key. + /// + Capital, + + /// + /// The CAPS LOCK key. + /// + CapsLock = Capital, + + /// + /// The IME Kana mode key. + /// + KanaMode, + + /// + /// The IME Hangul mode key. + /// + HangulMode = KanaMode, + + /// + /// The IME Junja mode key. + /// + JunjaMode, + + /// + /// The IME Final mode key. + /// + FinalMode, + + /// + /// The IME Hanja mode key. + /// + HanjaMode, + + /// + /// The IME Kanji mode key. + /// + KanjiMode = HanjaMode, + + /// + /// The ESC key. + /// + Escape, + + /// + /// The IME Convert key. + /// + ImeConvert, + + /// + /// The IME NonConvert key. + /// + ImeNonConvert, + + /// + /// The IME Accept key. + /// + ImeAccept, + + /// + /// The IME Mode change request. + /// + ImeModeChange, + + /// + /// The SPACEBAR key. + /// + Space, + + /// + /// The PAGE UP key. + /// + Prior, + + /// + /// The PAGE UP key. + /// + PageUp = Prior, + + /// + /// The PAGE DOWN key. + /// + Next, + + /// + /// The PAGE DOWN key. + /// + PageDown = Next, + + /// + /// The END key. + /// + End, + + /// + /// The HOME key. + /// + Home, + + /// + /// The LEFT ARROW key. + /// + Left, + + /// + /// The UP ARROW key. + /// + Up, + + /// + /// The RIGHT ARROW key. + /// + Right, + + /// + /// The DOWN ARROW key. + /// + Down, + + /// + /// The SELECT key. + /// + Select, + + /// + /// The PRINT key. + /// + Print, + + /// + /// The EXECUTE key. + /// + Execute, + + /// + /// The PRINT SCREEN key. + /// + Snapshot, + + /// + /// The PRINT SCREEN key. + /// + PrintScreen = Snapshot, + + /// + /// The INS key. + /// + Insert, + + /// + /// The DEL key. + /// + Delete, + + /// + /// The HELP key. + /// + Help, + + /// + /// The 0 key. + /// + D0, // 0 + + /// + /// The 1 key. + /// + D1, // 1 + + /// + /// The 2 key. + /// + D2, // 2 + + /// + /// The 3 key. + /// + D3, // 3 + + /// + /// The 4 key. + /// + D4, // 4 + + /// + /// The 5 key. + /// + D5, // 5 + + /// + /// The 6 key. + /// + D6, // 6 + + /// + /// The 7 key. + /// + D7, // 7 + + /// + /// The 8 key. + /// + D8, // 8 + + /// + /// The 9 key. + /// + D9, // 9 + + /// + /// The A key. + /// + A, + + /// + /// The B key. + /// + B, + + /// + /// The C key. + /// + C, + + /// + /// The D key. + /// + D, + + /// + /// The E key. + /// + E, + + /// + /// The F key. + /// + F, + + /// + /// The G key. + /// + G, + + /// + /// The H key. + /// + H, + + /// + /// The I key. + /// + I, + + /// + /// The J key. + /// + J, + + /// + /// The K key. + /// + K, + + /// + /// The L key. + /// + L, + + /// + /// The M key. + /// + M, + + /// + /// The N key. + /// + N, + + /// + /// The O key. + /// + O, + + /// + /// The P key. + /// + P, + + /// + /// The Q key. + /// + Q, + + /// + /// The R key. + /// + R, + + /// + /// The S key. + /// + S, + + /// + /// The T key. + /// + T, + + /// + /// The U key. + /// + U, + + /// + /// The V key. + /// + V, + + /// + /// The W key. + /// + W, + + /// + /// The X key. + /// + X, + + /// + /// The Y key. + /// + Y, + + /// + /// The Z key. + /// + Z, + + /// + /// The left Windows logo key (Microsoft Natural Keyboard). + /// + LWin, + + /// + /// The right Windows logo key (Microsoft Natural Keyboard). + /// + RWin, + + /// + /// The Application key (Microsoft Natural Keyboard). + /// + Apps, + + // Missing VK_POWER? + + /// + /// The Computer Sleep key. + /// + Sleep, + + /// + /// The 0 key on the numeric keypad. + /// + NumPad0, + + /// + /// The 1 key on the numeric keypad. + /// + NumPad1, + + /// + /// The 2 key on the numeric keypad. + /// + NumPad2, + + /// + /// The 3 key on the numeric keypad. + /// + NumPad3, + + /// + /// The 4 key on the numeric keypad. + /// + NumPad4, + + /// + /// The 5 key on the numeric keypad. + /// + NumPad5, + + /// + /// The 6 key on the numeric keypad. + /// + NumPad6, + + /// + /// The 7 key on the numeric keypad. + /// + NumPad7, + + /// + /// The 8 key on the numeric keypad. + /// + NumPad8, + + /// + /// The 9 key on the numeric keypad. + /// + NumPad9, + + /// + /// The Multiply key. + /// + Multiply, + + /// + /// The Add key. + /// + Add, + + /// + /// The Separator key. + /// + Separator, + + /// + /// The Subtract key. + /// + Subtract, + + /// + /// The Decimal key. + /// + Decimal, + + /// + /// The Divide key. + /// + Divide, + + /// + /// The F1 key. + /// + F1, + + /// + /// The F2 key. + /// + F2, + + /// + /// The F3 key. + /// + F3, + + /// + /// The F4 key. + /// + F4, + + /// + /// The F5 key. + /// + F5, + + /// + /// The F6 key. + /// + F6, + + /// + /// The F7 key. + /// + F7, + + /// + /// The F8 key. + /// + F8, + + /// + /// The F9 key. + /// + F9, + + /// + /// The F10 key. + /// + F10, + + /// + /// The F11 key. + /// + F11, + + /// + /// The F12 key. + /// + F12, + + /// + /// The F13 key. + /// + F13, + + /// + /// The F14 key. + /// + F14, + + /// + /// The F15 key. + /// + F15, + + /// + /// The F16 key. + /// + F16, + + /// + /// The F17 key. + /// + F17, + + /// + /// The F18 key. + /// + F18, + + /// + /// The F19 key. + /// + F19, + + /// + /// The F20 key. + /// + F20, + + /// + /// The F21 key. + /// + F21, + + /// + /// The F22 key. + /// + F22, + + /// + /// The F23 key. + /// + F23, + + /// + /// The F24 key. + /// + F24, + + /// + /// The NUM LOCK key. + /// + NumLock, + + /// + /// The SCROLL LOCK key. + /// + Scroll, + + /// + /// The left SHIFT key. + /// + LeftShift, + + /// + /// The right SHIFT key. + /// + RightShift, + + /// + /// The left CTRL key. + /// + LeftCtrl, + + /// + /// The right CTRL key. + /// + RightCtrl, + + /// + /// The left ALT key. + /// + LeftAlt, + + /// + /// The right ALT key. + /// + RightAlt, + + /// + /// The Browser Back key. + /// + BrowserBack, + + /// + /// The Browser Forward key. + /// + BrowserForward, + + /// + /// The Browser Refresh key. + /// + BrowserRefresh, + + /// + /// The Browser Stop key. + /// + BrowserStop, + + /// + /// The Browser Search key. + /// + BrowserSearch, + + /// + /// The Browser Favorites key. + /// + BrowserFavorites, + + /// + /// The Browser Home key. + /// + BrowserHome, + + /// + /// The Volume Mute key. + /// + VolumeMute, + + /// + /// The Volume Down key. + /// + VolumeDown, + + /// + /// The Volume Up key. + /// + VolumeUp, + + /// + /// The Media Next Track key. + /// + MediaNextTrack, + + /// + /// The Media Previous Track key. + /// + MediaPreviousTrack, + + /// + /// The Media Stop key. + /// + MediaStop, + + /// + /// The Media Play Pause key. + /// + MediaPlayPause, + + /// + /// The Launch Mail key. + /// + LaunchMail, + + /// + /// The Select Media key. + /// + SelectMedia, + + /// + /// The Launch Application1 key. + /// + LaunchApplication1, + + /// + /// The Launch Application2 key. + /// + LaunchApplication2, + + /// + /// The Oem 1 key. + /// + Oem1, + + /// + /// The Oem Semicolon key. + /// + OemSemicolon = Oem1, + + /// + /// The Oem plus key. + /// + OemPlus, + + /// + /// The Oem comma key. + /// + OemComma, + + /// + /// The Oem Minus key. + /// + OemMinus, + + /// + /// The Oem Period key. + /// + OemPeriod, + + /// + /// The Oem 2 key. + /// + Oem2, + + /// + /// The Oem Question key. + /// + OemQuestion = Oem2, + + /// + /// The Oem 3 key. + /// + Oem3, + + /// + /// The Oem tilde key. + /// + OemTilde = Oem3, + + /// + /// The ABNT_C1 Portuguese (Brazilian) key. + /// + AbntC1, + + /// + /// The ABNT_C2 Portuguese (Brazilian) key. + /// + AbntC2, + + /// + /// The Oem 4 key. + /// + Oem4, + + /// + /// The Oem Open Brackets key. + /// + OemOpenBrackets = Oem4, + + /// + /// The Oem 5 key. + /// + Oem5, + + /// + /// The Oem Pipe key. + /// + OemPipe = Oem5, + + /// + /// The Oem 6 key. + /// + Oem6, + + /// + /// The Oem Close Brackets key. + /// + OemCloseBrackets = Oem6, + + /// + /// The Oem 7 key. + /// + Oem7, + + /// + /// The Oem Quotes key. + /// + OemQuotes = Oem7, + + /// + /// The Oem8 key. + /// + Oem8, + + // Future: VK_OEM_AX = 0xE1 + + /// + /// The Oem 102 key. + /// + Oem102, + + /// + /// The Oem Backslash key. + /// + OemBackslash = Oem102, + + // Future: VK_ICO_HELP = 0xE3; + // Future: VK_ICO_00 = 0xE4; + + /// + /// A special key masking the real key being processed by an IME. + /// + ImeProcessed, + + /// + /// A special key masking the real key being processed as a system key. + /// + System, + + // Future: VK_OEM_RESET = 0xE9; + // Future: VK_OEM_JUMP = 0xEA; + // Future: VK_OEM_PA1 = 0xEB; + // Future: VK_OEM_PA2 = 0xEC; + // Future: VK_OEM_PA3 = 0xED; + // Future: VK_OEM_WSCTRL = 0xEE; + // Future: VK_OEM_CUSEL = 0xEF; + + /// + /// The OEM_ATTN key. + /// + OemAttn, + + /// + /// The DBE_ALPHANUMERIC key. + /// + DbeAlphanumeric = OemAttn, + + /// + /// The OEM_FINISH key. + /// + OemFinish, + + /// + /// The DBE_KATAKANA key. + /// + DbeKatakana = OemFinish, + + /// + /// The OEM_COPY key. + /// + OemCopy, + + /// + /// The DBE_HIRAGANA key. + /// + DbeHiragana = OemCopy, + + /// + /// The OEM_AUTO key. + /// + OemAuto, + + /// + /// The DBE_SBCSCHAR key. + /// + DbeSbcsChar = OemAuto, + + /// + /// The OEM_ENLW key. + /// + OemEnlw, + + /// + /// The DBE_DBCSCHAR key. + /// + DbeDbcsChar = OemEnlw, + + /// + /// The OEM_BACKTAB key. + /// + OemBackTab, + + /// + /// The DBE_ROMAN key. + /// + DbeRoman = OemBackTab, + + /// + /// The ATTN key. + /// + Attn, + + /// + /// The DBE_NOROMAN key. + /// + DbeNoRoman = Attn, + + /// + /// The CRSEL key. + /// + CrSel, + + /// + /// The DBE_ENTERWORDREGISTERMODE key. + /// + DbeEnterWordRegisterMode = CrSel, + + /// + /// The EXSEL key. + /// + ExSel, + + /// + /// The DBE_ENTERIMECONFIGMODE key. + /// + DbeEnterImeConfigureMode = ExSel, + + /// + /// The ERASE EOF key. + /// + EraseEof, + + /// + /// The DBE_FLUSHSTRING key. + /// + DbeFlushString = EraseEof, + + /// + /// The PLAY key. + /// + Play, + + /// + /// The DBE_CODEINPUT key. + /// + DbeCodeInput = Play, + + /// + /// The ZOOM key. + /// + Zoom, + + /// + /// The DBE_NOCODEINPUT key. + /// + DbeNoCodeInput = Zoom, + + /// + /// A constant reserved for future use. + /// + NoName, + + /// + /// The DBE_DETERMINESTRING key. + /// + DbeDetermineString = NoName, + + /// + /// The PA1 key. + /// + Pa1, + + /// + /// The DBE_ENTERDLGCONVERSIONMODE key. + /// + DbeEnterDialogConversionMode = Pa1, + + /// + /// The CLEAR key. + /// + OemClear, + + /// + /// Indicates the key is part of a dead-key composition + /// + DeadCharProcessed, +} \ No newline at end of file diff --git a/RsrcArchitect.ViewModels/DialogEditorViewModel.cs b/RsrcArchitect.ViewModels/DialogEditorViewModel.cs index 5b5b74a..19ae743 100644 --- a/RsrcArchitect.ViewModels/DialogEditorViewModel.cs +++ b/RsrcArchitect.ViewModels/DialogEditorViewModel.cs @@ -21,6 +21,7 @@ namespace RsrcArchitect.ViewModels; public partial class DialogEditorViewModel : ObservableObject { private readonly IFilePickerService _filePickerService; + private readonly IInputService _inputService; private readonly DialogEditorSettingsViewModel _dialogEditorSettingsViewModel; private TransformationOperation _transformationOperation = TransformationOperation.Empty; @@ -33,11 +34,13 @@ public partial class DialogEditorViewModel : ObservableObject private bool _isPanning; public DialogEditorViewModel(Dialog dialog, string friendlyName, - DialogEditorSettingsViewModel dialogEditorSettingsViewModel, IFilePickerService filePickerService) + DialogEditorSettingsViewModel dialogEditorSettingsViewModel, IFilePickerService filePickerService, IInputService inputService) { FriendlyName = friendlyName; _dialogEditorSettingsViewModel = dialogEditorSettingsViewModel; _filePickerService = filePickerService; + _inputService = inputService; + DialogViewModel = new DialogViewModel(dialog); ToolboxItemViewModels = new List { diff --git a/RsrcArchitect.ViewModels/DialogEditorViewModel_Commands.cs b/RsrcArchitect.ViewModels/DialogEditorViewModel_Commands.cs index 148297b..aca3a15 100644 --- a/RsrcArchitect.ViewModels/DialogEditorViewModel_Commands.cs +++ b/RsrcArchitect.ViewModels/DialogEditorViewModel_Commands.cs @@ -44,7 +44,11 @@ private void PointerPress(Vector2 position) { Debug.Print("No grip hit"); var newSelectedNode = GetControlNodeAtPosition(dialogPosition); - SelectedNodes.Clear(); + if (!_inputService.IsKeyHeld(Key.LeftShift)) + { + SelectedNodes.Clear(); + } + if (newSelectedNode != null) { SelectedNodes.Add(newSelectedNode); @@ -87,7 +91,7 @@ private void PointerMove(Vector2 position) WeakReferenceMessenger.Default.Send(new CanvasInvalidationMessage(0)); return; } - + if (SelectedNodes.Count == 0) return; position = RelativePositionToDialog(position); diff --git a/RsrcArchitect.ViewModels/MainViewModel.cs b/RsrcArchitect.ViewModels/MainViewModel.cs index 250f713..216753a 100644 --- a/RsrcArchitect.ViewModels/MainViewModel.cs +++ b/RsrcArchitect.ViewModels/MainViewModel.cs @@ -13,15 +13,17 @@ namespace RsrcArchitect.ViewModels; public partial class MainViewModel : ObservableObject, IRecipient { private readonly IFilePickerService _filePickerService; - + private readonly IInputService _inputService; + public ObservableCollection DialogEditorViewModels { get; } = new(); public DialogEditorSettingsViewModel DialogEditorSettingsViewModel { get; } = new(); [ObservableProperty] private DialogEditorViewModel? _selectedDialogEditorViewModel = null; - public MainViewModel(IFilePickerService filePickerService) + public MainViewModel(IFilePickerService filePickerService, IInputService inputService) { _filePickerService = filePickerService; + _inputService = inputService; WeakReferenceMessenger.Default.RegisterAll(this); } @@ -34,7 +36,7 @@ private void CreateProject() Width = 600, Height = 400, Root = new TreeNode(new Panel()) - }, "New Dialog Project", DialogEditorSettingsViewModel, _filePickerService)); + }, "New Dialog Project", DialogEditorSettingsViewModel, _filePickerService, _inputService)); } [RelayCommand] @@ -122,7 +124,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTI END """); - DialogEditorViewModels.Add(new DialogEditorViewModel(dialog, "New Dialog Project", DialogEditorSettingsViewModel, _filePickerService)); + DialogEditorViewModels.Add(new DialogEditorViewModel(dialog, "New Dialog Project", DialogEditorSettingsViewModel, _filePickerService, _inputService)); // var result = await _filePickerService.TryPickOpenFileAsync(new[] { ".rc" }); // // if (result == null) diff --git a/RsrcArchitect.Views.WPF/MainWindow.xaml.cs b/RsrcArchitect.Views.WPF/MainWindow.xaml.cs index e8b16f8..1f0d557 100644 --- a/RsrcArchitect.Views.WPF/MainWindow.xaml.cs +++ b/RsrcArchitect.Views.WPF/MainWindow.xaml.cs @@ -22,7 +22,7 @@ namespace RsrcArchitect.Views.WPF; /// Interaction logic for MainWindow.xaml /// [INotifyPropertyChanged] -public partial class MainWindow : Window, IRecipient, IRecipient +public partial class MainWindow : Window, IRecipient, IRecipient, IInputService { private const float ZoomIncrement = 0.25f; @@ -35,7 +35,7 @@ public MainWindow() { InitializeComponent(); - MainViewModel = new MainViewModel(App.FilesService); + MainViewModel = new MainViewModel(App.FilesService, this); DataContext = this; @@ -163,4 +163,6 @@ private async void SetVisualStyle_Click(object sender, RoutedEventArgs e) } DialogRenderer.StyledObjectRenderer.LoadAtlas(path); } + + public bool IsKeyHeld(ViewModels.Types.Key key) => Keyboard.IsKeyDown((System.Windows.Input.Key)key); } \ No newline at end of file diff --git a/RsrcArchitect.Views.WPF/Rendering/StyledObjectRenderer.cs b/RsrcArchitect.Views.WPF/Rendering/StyledObjectRenderer.cs index 34d05f2..03f7bf8 100644 --- a/RsrcArchitect.Views.WPF/Rendering/StyledObjectRenderer.cs +++ b/RsrcArchitect.Views.WPF/Rendering/StyledObjectRenderer.cs @@ -301,6 +301,7 @@ public void RenderDecorations(SKCanvas canvas, DialogEditorViewModel dialogEdito selectedControlViewModel.Rectangle.Width, selectedControlViewModel.Rectangle.Height); + canvas.Save(); canvas.Translate( selectedControlViewModel.Rectangle.X, selectedControlViewModel.Rectangle.Y); @@ -331,7 +332,7 @@ public void RenderDecorations(SKCanvas canvas, DialogEditorViewModel dialogEdito canvas.Translate(-point.X, -point.Y); } - canvas.Translate(dialogEditorSettingsViewModel.GripSize / 2f, dialogEditorSettingsViewModel.GripSize / 2f); + canvas.Restore(); } }