Fixed shortcut key monitoring

This commit is contained in:
Thorsten Sommer 2026-01-22 18:21:13 +01:00
parent 18fa36c32b
commit 7ff53ebcae
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
2 changed files with 52 additions and 66 deletions

View File

@ -12,44 +12,37 @@
@T("Press the desired key combination to set the shortcut. The shortcut will be registered globally and will work even when the app is not focused.")
</MudJustifiedText>
<MudPaper Class="pa-4 mb-3 d-flex align-center justify-center shortcut-capture-area"
Style="min-height: 80px; cursor: pointer;"
Elevation="2"
@onclick="@this.FocusInput">
@* Hidden input to capture keyboard events *@
<input type="text"
@ref="this.hiddenInput"
@onkeydown="@this.HandleKeyDown"
style="position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;"
autocomplete="off"
readonly />
@if (string.IsNullOrWhiteSpace(this.currentShortcut))
{
<MudText Typo="Typo.h6" Color="Color.Secondary">
@T("Press a key combination...")
</MudText>
}
else
{
<MudText Typo="Typo.h5" Color="Color.Primary">
@this.GetDisplayShortcut()
</MudText>
}
</MudPaper>
<MudFocusTrap DefaultFocus="DefaultFocus.FirstChild">
<MudTextField
@ref="@this.inputField"
T="string"
Text="@this.ShowText"
Variant="Variant.Outlined"
Label="@T("Define a shortcut")"
Placeholder="@T("Press a key combination...")"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Keyboard"
Immediate="@true"
TextUpdateSuppression="false"
OnKeyDown="@this.HandleKeyDown"
OnBlur="@this.HandleBlur"
UserAttributes="@USER_INPUT_ATTRIBUTES"
AutoFocus="true"
KeyDownPreventDefault="true"
KeyUpPreventDefault="true"
HelperText="@T("Supported modifiers: Ctrl/Cmd, Shift, Alt.")"
Class="me-3"/>
</MudFocusTrap>
@if (!string.IsNullOrWhiteSpace(this.validationMessage))
{
<MudAlert Severity="@this.validationSeverity" Class="mb-3">
<MudAlert Severity="@this.validationSeverity" Variant="Variant.Filled" Class="mb-3">
@this.validationMessage
</MudAlert>
}
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="mb-2">
@T("Supported modifiers: Ctrl/Cmd, Shift, Alt. Example: Ctrl+Shift+R")
</MudText>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.ClearShortcut" Variant="Variant.Text" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Clear">
<MudButton OnClick="@this.ClearShortcut" Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Clear">
@T("Clear Shortcut")
</MudButton>
<MudSpacer/>

View File

@ -29,44 +29,42 @@ public partial class ShortcutDialog : MSGComponentBase
[Parameter]
public string ShortcutName { get; set; } = string.Empty;
private ElementReference hiddenInput;
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
private string currentShortcut = string.Empty;
private string validationMessage = string.Empty;
private Severity validationSeverity = Severity.Info;
private bool hasValidationError;
// Current key state
//
// Current key state:
//
private bool hasCtrl;
private bool hasShift;
private bool hasAlt;
private bool hasMeta;
private string? currentKey;
private bool isFirstRender = true;
private MudTextField<string>? inputField;
#region Overrides of ComponentBase
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
base.OnInitialized();
await base.OnInitializedAsync();
// Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
this.currentShortcut = this.InitialShortcut;
this.ParseExistingShortcut();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
// Auto-focus the hidden input when the dialog opens
if (this.isFirstRender)
{
this.isFirstRender = false;
await this.hiddenInput.FocusAsync();
}
}
#endregion
private string ShowText => string.IsNullOrWhiteSpace(this.currentShortcut)
? T("Press a key combination...")
: this.GetDisplayShortcut();
private void ParseExistingShortcut()
{
if (string.IsNullOrWhiteSpace(this.currentShortcut))
@ -107,15 +105,9 @@ public partial class ShortcutDialog : MSGComponentBase
}
}
private async Task FocusInput()
{
// Focus the hidden input to capture keyboard events
await this.hiddenInput.FocusAsync();
}
private async Task HandleKeyDown(KeyboardEventArgs e)
{
// Ignore pure modifier key presses
// Ignore pure modifier key presses:
if (IsModifierKey(e.Code))
{
this.UpdateModifiers(e);
@ -124,10 +116,9 @@ public partial class ShortcutDialog : MSGComponentBase
return;
}
// Update modifiers
this.UpdateModifiers(e);
// Get the key
// Get the key:
this.currentKey = TranslateKeyCode(e.Code);
// Validate: must have at least one modifier + a key
@ -140,11 +131,10 @@ public partial class ShortcutDialog : MSGComponentBase
return;
}
// Build the shortcut string
this.UpdateShortcutString();
// Validate the shortcut
await this.ValidateShortcut();
this.StateHasChanged();
}
private void UpdateModifiers(KeyboardEventArgs e)
@ -213,10 +203,7 @@ public partial class ShortcutDialog : MSGComponentBase
private string GetDisplayShortcut()
{
if (string.IsNullOrWhiteSpace(this.currentShortcut))
return string.Empty;
// Convert internal format to display format
// Convert internal format to display format:
return this.currentShortcut
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
.Replace("CommandOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
@ -376,4 +363,10 @@ public partial class ShortcutDialog : MSGComponentBase
// Default: return as-is
_ => code,
};
private void HandleBlur()
{
// Re-focus the input field to keep capturing keys:
this.inputField?.FocusAsync();
}
}