diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs b/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs similarity index 85% rename from src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs rename to src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs index 0a735f875..aaa5485e6 100644 --- a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfigurationHotkey.cs +++ b/src/Artemis.Core/Models/ProfileConfiguration/Hotkey.cs @@ -6,18 +6,18 @@ namespace Artemis.Core /// /// Represents a key or combination of keys that changes the suspension status of a /// - public class ProfileConfigurationHotkey : CorePropertyChanged, IStorageModel + public class Hotkey : CorePropertyChanged, IStorageModel { /// - /// Creates a new instance of + /// Creates a new instance of /// - public ProfileConfigurationHotkey() + public Hotkey() { Entity = new ProfileConfigurationHotkeyEntity(); } - internal ProfileConfigurationHotkey(ProfileConfigurationHotkeyEntity entity) + internal Hotkey(ProfileConfigurationHotkeyEntity entity) { Entity = entity; Load(); diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs index a278443cd..942f1af4c 100644 --- a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs +++ b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs @@ -104,12 +104,12 @@ namespace Artemis.Core /// /// Gets or sets the hotkey used to enable or toggle the profile /// - public ProfileConfigurationHotkey? EnableHotkey { get; set; } + public Hotkey? EnableHotkey { get; set; } /// /// Gets or sets the hotkey used to disable the profile /// - public ProfileConfigurationHotkey? DisableHotkey { get; set; } + public Hotkey? DisableHotkey { get; set; } /// /// Gets the ID of the profile of this profile configuration @@ -246,8 +246,8 @@ namespace Artemis.Core ? new NodeScript("Activate profile", "Whether or not the profile should be active", Entity.ActivationCondition, this) : new NodeScript("Activate profile", "Whether or not the profile should be active", this); - EnableHotkey = Entity.EnableHotkey != null ? new ProfileConfigurationHotkey(Entity.EnableHotkey) : null; - DisableHotkey = Entity.DisableHotkey != null ? new ProfileConfigurationHotkey(Entity.DisableHotkey) : null; + EnableHotkey = Entity.EnableHotkey != null ? new Hotkey(Entity.EnableHotkey) : null; + DisableHotkey = Entity.DisableHotkey != null ? new Hotkey(Entity.DisableHotkey) : null; } /// diff --git a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs index 2ebde89e7..306822572 100644 --- a/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs +++ b/src/Artemis.Core/Services/Input/Enums/KeyboardModifierKey.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; namespace Artemis.Core.Services { @@ -9,18 +10,23 @@ namespace Artemis.Core.Services public enum KeyboardModifierKey { /// No modifiers are pressed. + [Description("None")] None = 0, /// The ALT key. + [Description("Alt")] Alt = 1, /// The CTRL key. + [Description("Ctrl")] Control = 2, /// The SHIFT key. + [Description("Shift")] Shift = 4, /// The Windows logo key. + [Description("Win")] Windows = 8 } } \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia.Shared/Artemis.UI.Avalonia.Shared.xml b/src/Artemis.UI.Avalonia.Shared/Artemis.UI.Avalonia.Shared.xml index 36b985041..3ee84f85d 100644 --- a/src/Artemis.UI.Avalonia.Shared/Artemis.UI.Avalonia.Shared.xml +++ b/src/Artemis.UI.Avalonia.Shared/Artemis.UI.Avalonia.Shared.xml @@ -96,6 +96,36 @@ + + + Represents a control that can be used to display or edit instances. + + + + + Creates a new instance of the class + + + + + Gets or sets the currently displayed icon as either a or an + pointing + to an SVG + + + + + Gets or sets the currently displayed icon as either a or an + pointing + to an SVG + + + + + + + + Gets or sets the to display diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs index 183447733..fbba87b5a 100644 --- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileEdit/ProfileConfigurationHotkeyViewModel.cs @@ -22,7 +22,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit UpdateHotkeyDisplay(); } - public ProfileConfigurationHotkey Hotkey => _isDisableHotkey ? _profileConfiguration.DisableHotkey : _profileConfiguration.EnableHotkey; + public Hotkey Hotkey => _isDisableHotkey ? _profileConfiguration.DisableHotkey : _profileConfiguration.EnableHotkey; public string Hint { @@ -66,13 +66,13 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit if (_isDisableHotkey) { - _profileConfiguration.DisableHotkey ??= new ProfileConfigurationHotkey(); + _profileConfiguration.DisableHotkey ??= new Hotkey(); _profileConfiguration.DisableHotkey.Key = (KeyboardKey?) e.Key; _profileConfiguration.DisableHotkey.Modifiers = (KeyboardModifierKey?) Keyboard.Modifiers; } else { - _profileConfiguration.EnableHotkey ??= new ProfileConfigurationHotkey(); + _profileConfiguration.EnableHotkey ??= new Hotkey(); _profileConfiguration.EnableHotkey.Key = (KeyboardKey?) e.Key; _profileConfiguration.EnableHotkey.Modifiers = (KeyboardModifierKey?) Keyboard.Modifiers; } diff --git a/src/Avalonia/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Avalonia/Artemis.UI.Shared/Artemis.UI.Shared.csproj index 7dbbc699f..180b48704 100644 --- a/src/Avalonia/Artemis.UI.Shared/Artemis.UI.Shared.csproj +++ b/src/Avalonia/Artemis.UI.Shared/Artemis.UI.Shared.csproj @@ -44,6 +44,9 @@ + + HotkeyBox.axaml + %(Filename) diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs b/src/Avalonia/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs index 8ea3417ad..cc68222dd 100644 --- a/src/Avalonia/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs +++ b/src/Avalonia/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs @@ -21,43 +21,7 @@ namespace Artemis.UI.Shared.Controls public static readonly StyledProperty IconProperty = AvaloniaProperty.Register(nameof(Icon), notifying: IconChanging); - private static void IconChanging(IAvaloniaObject sender, bool before) - { - if (before) - ((ArtemisIcon) sender).Update(); - } - - private void Update() - { - try - { - // First look for an enum value instead of a string - if (Icon is MaterialIconKind materialIcon) - Content = new MaterialIcon {Kind = materialIcon, Width = Bounds.Width, Height = Bounds.Height }; - // If it's a string there are several options - else if (Icon is string iconString) - { - // An enum defined as a string - if (Enum.TryParse(iconString, true, out MaterialIconKind parsedIcon)) - Content = new MaterialIcon {Kind = parsedIcon, Width = Bounds.Width, Height = Bounds.Height}; - // An URI pointing to an SVG - else if (iconString.EndsWith(".svg")) - { - SvgSource source = new(); - source.Load(iconString); - Content = new SvgImage {Source = source}; - } - // An URI pointing to a different kind of image - else - Content = new Image {Source = new Bitmap(iconString), Width = Bounds.Width, Height = Bounds.Height }; - } - } - catch - { - Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark, Width = Bounds.Width, Height = Bounds.Height }; - } - } - + /// /// Gets or sets the currently displayed icon as either a or an pointing /// to an SVG @@ -70,6 +34,44 @@ namespace Artemis.UI.Shared.Controls #endregion + private static void IconChanging(IAvaloniaObject sender, bool before) + { + if (before) + ((ArtemisIcon)sender).Update(); + } + + private void Update() + { + try + { + // First look for an enum value instead of a string + if (Icon is MaterialIconKind materialIcon) + Content = new MaterialIcon { Kind = materialIcon, Width = Bounds.Width, Height = Bounds.Height }; + // If it's a string there are several options + else if (Icon is string iconString) + { + // An enum defined as a string + if (Enum.TryParse(iconString, true, out MaterialIconKind parsedIcon)) + Content = new MaterialIcon { Kind = parsedIcon, Width = Bounds.Width, Height = Bounds.Height }; + // An URI pointing to an SVG + else if (iconString.EndsWith(".svg")) + { + SvgSource source = new(); + source.Load(iconString); + Content = new SvgImage { Source = source }; + } + // An URI pointing to a different kind of image + else + Content = new Image { Source = new Bitmap(iconString), Width = Bounds.Width, Height = Bounds.Height }; + } + } + catch + { + Content = new MaterialIcon { Kind = MaterialIconKind.QuestionMark, Width = Bounds.Width, Height = Bounds.Height }; + } + } + + public ArtemisIcon() { InitializeComponent(); diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml b/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml new file mode 100644 index 000000000..29ae38f38 --- /dev/null +++ b/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml.cs b/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml.cs new file mode 100644 index 000000000..a78ff19d5 --- /dev/null +++ b/src/Avalonia/Artemis.UI.Shared/Controls/HotkeyBox.axaml.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using Artemis.Core; +using Artemis.Core.Services; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Humanizer; +using Material.Icons; + +namespace Artemis.UI.Shared.Controls +{ + /// + /// Represents a control that can be used to display or edit instances. + /// + public class HotkeyBox : UserControl + { + private readonly TextBox _displayTextBox; + + /// + /// Creates a new instance of the class + /// + public HotkeyBox() + { + InitializeComponent(); + + _displayTextBox = this.Find("DisplayTextBox"); + _displayTextBox.KeyDown += DisplayTextBoxOnKeyDown; + _displayTextBox.KeyUp += DisplayTextBoxOnKeyUp; + UpdateDisplayTextBox(); + } + + private static void HotkeyChanging(IAvaloniaObject sender, bool before) + { + ((HotkeyBox) sender).UpdateDisplayTextBox(); + } + + private void DisplayTextBoxOnKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key >= Key.LeftShift && e.Key <= Key.RightAlt) + return; + + Hotkey ??= new Hotkey(); + Hotkey.Key = (KeyboardKey?) e.Key; + Hotkey.Modifiers = (KeyboardModifierKey?) e.KeyModifiers; + UpdateDisplayTextBox(); + + e.Handled = true; + } + + private void DisplayTextBoxOnKeyUp(object? sender, KeyEventArgs e) + { + if (e.KeyModifiers == KeyModifiers.None) + FocusManager.Instance.Focus(null); + + e.Handled = true; + } + + private void UpdateDisplayTextBox() + { + string? display = null; + if (Hotkey?.Modifiers != null) + display = string.Join("+", Enum.GetValues().Skip(1).Where(m => Hotkey.Modifiers.Value.HasFlag(m)).Select(v => v.Humanize())); + if (Hotkey?.Key != null) + display = string.IsNullOrEmpty(display) ? Hotkey.Key.ToString() : $"{display}+{Hotkey.Key}"; + + _displayTextBox.Text = display; + _displayTextBox.CaretIndex = _displayTextBox.Text?.Length ?? 0; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void Button_OnClick(object? sender, RoutedEventArgs e) + { + Hotkey = null; + FocusManager.Instance.Focus(null); + + UpdateDisplayTextBox(); + } + + #region Properties + + /// + /// Gets or sets the currently displayed icon as either a or an + /// pointing + /// to an SVG + /// + public static readonly StyledProperty HotkeyProperty = + AvaloniaProperty.Register(nameof(Hotkey), notifying: HotkeyChanging); + + public static readonly StyledProperty WatermarkProperty = + AvaloniaProperty.Register(nameof(Watermark)); + + public static readonly StyledProperty UseFloatingWatermarkProperty = + AvaloniaProperty.Register(nameof(UseFloatingWatermark)); + + /// + /// Gets or sets the currently displayed icon as either a or an + /// pointing + /// to an SVG + /// + public Hotkey? Hotkey + { + get => GetValue(HotkeyProperty); + set => SetValue(HotkeyProperty, value); + } + + public string? Watermark + { + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); + } + + public bool UseFloatingWatermark + { + get => GetValue(UseFloatingWatermarkProperty); + set => SetValue(UseFloatingWatermarkProperty, value); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/NoInputTextBox.cs b/src/Avalonia/Artemis.UI.Shared/Controls/NoInputTextBox.cs new file mode 100644 index 000000000..6d20bb00e --- /dev/null +++ b/src/Avalonia/Artemis.UI.Shared/Controls/NoInputTextBox.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Styling; + +namespace Artemis.UI.Shared.Controls +{ + internal class NoInputTextBox : TextBox, IStyleable + { + /// + protected override void OnKeyDown(KeyEventArgs e) + { + // Don't call the base method on purpose + } + + /// + protected override void OnKeyUp(KeyEventArgs e) + { + // Don't call the base method on purpose + } + + Type IStyleable.StyleKey => typeof(TextBox); + } +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml index f46281660..6a71044e9 100644 --- a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml +++ b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:builders="clr-namespace:Artemis.UI.Shared.Services.Builders;assembly=Artemis.UI.Shared" + xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.Workshop.WorkshopView"> @@ -22,6 +23,8 @@ + +