diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs
index 1d8954e..8faf23a 100644
--- a/demo/Ursa.Demo/Models/MenuKeys.cs
+++ b/demo/Ursa.Demo/Models/MenuKeys.cs
@@ -8,9 +8,11 @@ public static class MenuKeys
public const string MenuKeyDivider = "Divider";
public const string MenuKeyDualBadge = "DualBadge";
public const string MenuKeyIpBox = "IPv4Box";
+ public const string MenuKeyKeyGestureInput = "KeyGestureInput";
public const string MenuKeyLoading = "Loading";
public const string MenuKeyNavigation = "Navigation";
public const string MenuKeyPagination = "Pagination";
public const string MenuKeyTagInput = "TagInput";
public const string MenuKeyTimeline = "Timeline";
+
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml b/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml
new file mode 100644
index 0000000..406c353
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml.cs b/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml.cs
new file mode 100644
index 0000000..f213756
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/KeyGestureInputDemo.axaml.cs
@@ -0,0 +1,18 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class KeyGestureInputDemo : UserControl
+{
+ public KeyGestureInputDemo()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Ursa.Demo.csproj b/demo/Ursa.Demo/Ursa.Demo.csproj
index 4f0188d..d11b6e7 100644
--- a/demo/Ursa.Demo/Ursa.Demo.csproj
+++ b/demo/Ursa.Demo/Ursa.Demo.csproj
@@ -7,23 +7,23 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
-
+
+
diff --git a/demo/Ursa.Demo/ViewModels/KeyGestureInputDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/KeyGestureInputDemoViewModel.cs
new file mode 100644
index 0000000..b69f20c
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/KeyGestureInputDemoViewModel.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using Avalonia.Input;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public class KeyGestureInputDemoViewModel: ObservableObject
+{
+ public List AcceptableKeys { get; set; } = new List()
+ {
+ Key.A, Key.B, Key.C,
+ };
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index 54109ae..8487910 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -30,6 +30,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyDivider => new DividerDemoViewModel(),
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
MenuKeys.MenuKeyIpBox => new IPv4BoxDemoViewModel(),
+ MenuKeys.MenuKeyKeyGestureInput => new KeyGestureInputDemoViewModel(),
MenuKeys.MenuKeyLoading => new LoadingDemoViewModel(),
MenuKeys.MenuKeyNavigation => new NavigationMenuDemoViewModel(),
MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(),
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index 011375b..f55365f 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -17,6 +17,7 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider },
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
new() { MenuHeader = "IPv4Box", Key = MenuKeys.MenuKeyIpBox },
+ new() { MenuHeader = "KeyGestureInput", Key = MenuKeys.MenuKeyKeyGestureInput },
new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading },
new() { MenuHeader = "Navigation", Key = MenuKeys.MenuKeyNavigation },
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
diff --git a/src/Ursa.Themes.Semi/Controls/KeyGestureInput.axaml b/src/Ursa.Themes.Semi/Controls/KeyGestureInput.axaml
new file mode 100644
index 0000000..2bbbd31
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/KeyGestureInput.axaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index dee9b3c..2038cd0 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -7,6 +7,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/KeyGestureInput.axaml b/src/Ursa.Themes.Semi/Themes/Dark/KeyGestureInput.axaml
new file mode 100644
index 0000000..ed7c331
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Dark/KeyGestureInput.axaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
index aa85239..c2ff413 100644
--- a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
@@ -7,6 +7,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Light/KeyGestureInput.axaml b/src/Ursa.Themes.Semi/Themes/Light/KeyGestureInput.axaml
new file mode 100644
index 0000000..490e1a4
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Light/KeyGestureInput.axaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
index aa85239..c2ff413 100644
--- a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
@@ -7,6 +7,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/KeyGestureInput.axaml b/src/Ursa.Themes.Semi/Themes/Shared/KeyGestureInput.axaml
new file mode 100644
index 0000000..19752fa
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Shared/KeyGestureInput.axaml
@@ -0,0 +1,7 @@
+
+
+ 80
+ 32
+ 3
+ 1
+
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
index 7bf0b4d..3d93ea5 100644
--- a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
@@ -7,6 +7,7 @@
+
diff --git a/src/Ursa/Controls/KeyGestureInput.cs b/src/Ursa/Controls/KeyGestureInput.cs
new file mode 100644
index 0000000..5c4e535
--- /dev/null
+++ b/src/Ursa/Controls/KeyGestureInput.cs
@@ -0,0 +1,97 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Converters;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Layout;
+
+namespace Ursa.Controls;
+
+public class KeyGestureInput: TemplatedControl
+{
+ static KeyGestureInput()
+ {
+ InputElement.FocusableProperty.OverrideDefaultValue(true);
+ }
+
+ public static readonly StyledProperty GestureProperty = AvaloniaProperty.Register(
+ nameof(Gesture));
+
+ public KeyGesture Gesture
+ {
+ get => GetValue(GestureProperty);
+ set => SetValue(GestureProperty, value);
+ }
+
+ public static readonly StyledProperty?> AcceptableKeysProperty = AvaloniaProperty.Register?>(
+ nameof(AcceptableKeys));
+
+ public IList? AcceptableKeys
+ {
+ get => GetValue(AcceptableKeysProperty);
+ set => SetValue(AcceptableKeysProperty, value);
+ }
+
+ public static readonly StyledProperty ConsiderKeyModifiersProperty = AvaloniaProperty.Register(
+ nameof(ConsiderKeyModifiers), true);
+
+ public bool ConsiderKeyModifiers
+ {
+ get => GetValue(ConsiderKeyModifiersProperty);
+ set => SetValue(ConsiderKeyModifiersProperty, value);
+ }
+
+ public static readonly StyledProperty HorizontalContentAlignmentProperty =
+ ContentControl.HorizontalContentAlignmentProperty.AddOwner(
+ new StyledPropertyMetadata(HorizontalAlignment.Center));
+
+ public HorizontalAlignment HorizontalContentAlignment
+ {
+ get => GetValue(HorizontalContentAlignmentProperty);
+ set => SetValue(HorizontalContentAlignmentProperty, value);
+ }
+
+ public static readonly StyledProperty VerticalContentAlignmentProperty =
+ ContentControl.VerticalContentAlignmentProperty.AddOwner(
+ new StyledPropertyMetadata(VerticalAlignment.Center));
+
+ public VerticalAlignment VerticalContentAlignment
+ {
+ get => GetValue(VerticalContentAlignmentProperty);
+ set => SetValue(VerticalContentAlignmentProperty, value);
+ }
+
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ // base.OnKeyDown(e);
+ if (AcceptableKeys is not null && !AcceptableKeys.Contains(e.Key))
+ {
+ return;
+ }
+
+ if (!ConsiderKeyModifiers)
+ {
+ if(e.Key is Key.LeftCtrl or Key.RightCtrl or Key.LeftAlt or Key.RightAlt or Key.LeftShift or Key.RightShift or Key.LWin or Key.RWin)
+ {
+ return;
+ }
+ Gesture = new KeyGesture(e.Key);
+ }
+ KeyGesture gesture;
+ switch (e.KeyModifiers)
+ {
+ case KeyModifiers.Control when e.Key is Key.LeftCtrl or Key.RightCtrl:
+ case KeyModifiers.Alt when e.Key is Key.LeftAlt or Key.RightAlt:
+ case KeyModifiers.Shift when e.Key is Key.LeftShift or Key.RightShift:
+ case KeyModifiers.Meta when e.Key is Key.LWin or Key.RWin:
+ gesture = new KeyGesture(e.Key);
+ break;
+ default:
+ gesture = new KeyGesture(e.Key, e.KeyModifiers);
+ break;
+ }
+ Gesture = gesture;
+ e.Handled = true;
+ }
+}
\ No newline at end of file