diff --git a/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs b/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs
index ac2c9ed24..b7dc91525 100644
--- a/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs
+++ b/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs
@@ -1,4 +1,5 @@
-using Artemis.Core.Services;
+using System;
+using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Core;
@@ -16,6 +17,14 @@ public class Hotkey : CorePropertyChanged, IStorageModel
Entity = new ProfileConfigurationHotkeyEntity();
}
+ ///
+ public Hotkey(KeyboardKey? key, KeyboardModifierKey? modifiers)
+ {
+ Key = key;
+ Modifiers = modifiers;
+ Entity = new ProfileConfigurationHotkeyEntity();
+ }
+
///
/// Creates a new instance of based on the provided entity
///
@@ -46,7 +55,7 @@ public class Hotkey : CorePropertyChanged, IStorageModel
/// if the event args match the hotkey; otherwise
public bool MatchesEventArgs(ArtemisKeyboardKeyEventArgs eventArgs)
{
- return eventArgs.Key == Key && eventArgs.Modifiers == Modifiers;
+ return eventArgs.Key == Key && (eventArgs.Modifiers == Modifiers || (eventArgs.Modifiers == KeyboardModifierKey.None && Modifiers == null));
}
#region Implementation of IStorageModel
diff --git a/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs
index e6193c515..4738bab71 100644
--- a/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs
+++ b/src/Artemis.Core/Services/Input/Events/ArtemisKeyboardKeyEventArgs.cs
@@ -34,4 +34,13 @@ public class ArtemisKeyboardKeyEventArgs : EventArgs
/// Gets the modifiers that are pressed
///
public KeyboardModifierKey Modifiers { get; }
+
+ ///
+ /// Creates a hotkey matching the event.
+ ///
+ /// The resulting hotkey.
+ public Hotkey ToHotkey()
+ {
+ return new Hotkey {Key = Key, Modifiers = Modifiers};
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs b/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs
index d85066451..0e18f6c68 100644
--- a/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs
+++ b/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs
@@ -11,7 +11,12 @@ public interface IMainWindowService : IArtemisSharedUIService
/// Gets a boolean indicating whether the main window is currently open
///
bool IsMainWindowOpen { get; }
-
+
+ ///
+ /// Gets a boolean indicating whether the main window is currently focused
+ ///
+ bool IsMainWindowFocused { get; }
+
///
/// Sets up the main window provider that controls the state of the main window
///
diff --git a/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs b/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs
index a95126c66..0a95a0087 100644
--- a/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs
+++ b/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs
@@ -10,6 +10,9 @@ internal class MainWindowService : IMainWindowService
///
public bool IsMainWindowOpen { get; private set; }
+ ///
+ public bool IsMainWindowFocused { get; private set; }
+
protected virtual void OnMainWindowOpened()
{
MainWindowOpened?.Invoke(this, EventArgs.Empty);
@@ -24,11 +27,13 @@ internal class MainWindowService : IMainWindowService
protected virtual void OnMainWindowFocused()
{
MainWindowFocused?.Invoke(this, EventArgs.Empty);
+ IsMainWindowFocused = true;
}
protected virtual void OnMainWindowUnfocused()
{
MainWindowUnfocused?.Invoke(this, EventArgs.Empty);
+ IsMainWindowFocused = false;
}
private void SyncWithManager()
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs
index e34febdfe..7b1bf30d8 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs
@@ -1,4 +1,6 @@
using System;
+using Artemis.Core;
+using Avalonia.Input;
using Material.Icons;
namespace Artemis.UI.Shared.Services.ProfileEditor;
@@ -43,6 +45,11 @@ public interface IToolViewModel : IDisposable
/// Gets the tooltip which this tool should show in the toolbar.
///
public string ToolTip { get; }
+
+ ///
+ /// Gets the keyboard hotkey that activates the tool.
+ ///
+ Hotkey? Hotkey { get; }
}
///
@@ -98,5 +105,8 @@ public abstract class ToolViewModel : ActivatableViewModelBase, IToolViewModel
///
public abstract string ToolTip { get; }
+ ///
+ public abstract Hotkey? Hotkey { get; }
+
#endregion
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Utilities.cs b/src/Artemis.UI.Shared/Utilities.cs
index 3b1c58495..48e0ae2b3 100644
--- a/src/Artemis.UI.Shared/Utilities.cs
+++ b/src/Artemis.UI.Shared/Utilities.cs
@@ -22,10 +22,11 @@ public static class UI
static UI()
{
- KeyBindingsEnabled = InputElement.GotFocusEvent.Raised.Select(e => e.Item2.Source is not TextBox).StartWith(true);
+ CurrentKeyBindingsEnabled = InputElement.GotFocusEvent.Raised.Select(e => e.Item2.Source is not TextBox).StartWith(true);
+ CurrentKeyBindingsEnabled.Subscribe(b => KeyBindingsEnabled = b);
MicaEnabled = MicaEnabledSubject.AsObservable();
}
-
+
///
/// Gets the current IoC locator.
///
@@ -36,10 +37,15 @@ public static class UI
///
public static IClipboard Clipboard { get; set; } = null!;
+ ///
+ /// Gets an observable boolean indicating whether hotkeys are to be disabled.
+ ///
+ public static IObservable CurrentKeyBindingsEnabled { get; }
+
///
/// Gets a boolean indicating whether hotkeys are to be disabled.
///
- public static IObservable KeyBindingsEnabled { get; }
+ public static bool KeyBindingsEnabled { get; private set; }
///
/// Gets a boolean indicating whether the Mica effect should be enabled.
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs
index 9209789a1..7b323ab6e 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs
@@ -58,7 +58,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
_focusNone = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.None).ToProperty(this, vm => vm.FocusNone).DisposeWith(d);
_focusFolder = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Folder).ToProperty(this, vm => vm.FocusFolder).DisposeWith(d);
_focusSelection = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Selection).ToProperty(this, vm => vm.FocusSelection).DisposeWith(d);
- _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
+ _keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
});
AddFolder = ReactiveCommand.Create(ExecuteAddFolder);
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
index 9f6009934..d7fc9516e 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs
@@ -53,7 +53,7 @@ public class PlaybackViewModel : ActivatableViewModelBase
_currentTime = _profileEditorService.Time.ToProperty(this, vm => vm.CurrentTime).DisposeWith(d);
_formattedCurrentTime = _profileEditorService.Time.Select(t => $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}").ToProperty(this, vm => vm.FormattedCurrentTime).DisposeWith(d);
_playing = _profileEditorService.Playing.ToProperty(this, vm => vm.Playing).DisposeWith(d);
- _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
+ _keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
// Update timer
Timer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000));
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
index 4dcb93b82..82c559e98 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
@@ -46,7 +46,7 @@ public class ProfileTreeViewModel : TreeItemViewModel
_focusNone = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.None).ToProperty(this, vm => vm.FocusNone).DisposeWith(d);
_focusFolder = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Folder).ToProperty(this, vm => vm.FocusFolder).DisposeWith(d);
_focusSelection = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Selection).ToProperty(this, vm => vm.FocusSelection).DisposeWith(d);
- _keyBindingsEnabled = Shared.UI.KeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
+ _keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
});
ClearSelection = ReactiveCommand.Create(() => profileEditorService.ChangeCurrentProfileElement(null), this.WhenAnyValue(vm => vm.KeyBindingsEnabled));
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionAddToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionAddToolViewModel.cs
index 39be6c84f..484bca7f4 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionAddToolViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionAddToolViewModel.cs
@@ -7,6 +7,7 @@ using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
+using Avalonia.Input;
using Material.Icons;
using ReactiveUI;
using SkiaSharp;
@@ -45,9 +46,12 @@ public class SelectionAddToolViewModel : ToolViewModel
///
public override MaterialIconKind Icon => MaterialIconKind.SelectionDrag;
+
+ ///
+ public override Hotkey? Hotkey { get; } = new(KeyboardKey.OemPlus, KeyboardModifierKey.Control);
///
- public override string ToolTip => "Add LEDs to the current layer";
+ public override string ToolTip => "Add LEDs to the current layer (Ctrl + +)";
public void AddLedsInRectangle(SKRect rect, bool expand, bool inverse)
{
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolViewModel.cs
index 2c7d0a995..806b9279a 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolViewModel.cs
@@ -4,8 +4,10 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.Core;
+using Artemis.Core.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
+using Avalonia.Input;
using Material.Icons;
using ReactiveUI;
using SkiaSharp;
@@ -38,12 +40,15 @@ public class SelectionRemoveToolViewModel : ToolViewModel
///
public override int Order => 3;
+
+ ///
+ public override Hotkey? Hotkey { get; } = new(KeyboardKey.OemMinus, KeyboardModifierKey.Control);
///
public override MaterialIconKind Icon => MaterialIconKind.SelectOff;
///
- public override string ToolTip => "Remove LEDs from the current layer";
+ public override string ToolTip => "Remove LEDs from the current layer (Ctrl + -)";
public void RemoveLedsInRectangle(SKRect rect)
{
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs
index 7475f15eb..dbb5c75bf 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs
@@ -3,11 +3,13 @@ using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.Core;
+using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia;
+using Avalonia.Input;
using Material.Icons;
using ReactiveUI;
using SkiaSharp;
@@ -95,12 +97,15 @@ public class TransformToolViewModel : ToolViewModel
///
public override int Order => 3;
+
+ ///
+ public override Hotkey? Hotkey { get; } = new(KeyboardKey.T, KeyboardModifierKey.Control);
///
public override MaterialIconKind Icon => MaterialIconKind.TransitConnectionVariant;
///
- public override string ToolTip => "Transform the shape of the current layer";
+ public override string ToolTip => "Transform the shape of the current layer (Ctrl+T)";
public Rect ShapeBounds
{
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml
index 1939fbbae..0ea17649e 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml
@@ -8,19 +8,11 @@
xmlns:shared="clr-namespace:Artemis.UI.Shared.Services.ProfileEditor;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileEditorView"
- x:DataType="profileEditor:ProfileEditorViewModel">
+ x:DataType="profileEditor:ProfileEditorViewModel"
+ Focusable="True">
-
-
-
-
-
-
-
-
-