Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions AutoDarkModeApp/Strings/en-us/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,15 @@ Would you like to create an issue on the Auto Dark Mode repository?</value>
<data name="Hotkeys" xml:space="preserve">
<value>Hotkeys</value>
</data>
<data name="HotkeyDuplicateErrorTitle" xml:space="preserve">
<value>Duplicate Hotkey</value>
</data>
<data name="HotkeyDuplicateErrorMessage" xml:space="preserve">
<value>This hotkey is already used by "{0}". Please choose a different key combination.</value>
</data>
<data name="HotkeyInvalidErrorMessage" xml:space="preserve">
<value>Invalid hotkey combination. Please include at least one non-modifier key (e.g., a letter, number, or function key).</value>
</data>
<data name="IdleTime" xml:space="preserve">
<value>Time in minutes before the system is considered idle</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@

<StackPanel Orientation="Vertical" Spacing="16">

<InfoBar
x:Name="ErrorInfoBar"
IsClosable="False"
IsOpen="{x:Bind IsErrorVisible, Mode=OneWay}"
Message="{x:Bind ErrorMessage, Mode=OneWay}"
Comment thread
ChenYiLins marked this conversation as resolved.
Severity="Error" />

<TextBlock Text="{helpers:ResourceString Name=SelectTheKeyCombination}" TextWrapping="Wrap" />

<ItemsControl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,32 @@ public sealed partial class ShortcutDialogContentControl : UserControl
[GeneratedDependencyProperty]
public partial string? CapturedHotkeys { get; set; }

[GeneratedDependencyProperty]
public partial bool HasNonModifierKey { get; set; }

[GeneratedDependencyProperty]
public partial bool IsErrorVisible { get; set; }

[GeneratedDependencyProperty]
public partial string? ErrorMessage { get; set; }

public Func<string?, (bool isDuplicate, string? conflictingName)>? ValidateHotkey { get; set; }

private KeyboardHook? _keyboardHook;
private bool _isCapturing;

public void ShowError(string message)
{
ErrorMessage = message;
IsErrorVisible = true;
}

public void HideError()
{
IsErrorVisible = false;
ErrorMessage = null;
}

public void LoadFromKeyValue(string? hotkeyValue)
{
if (string.IsNullOrWhiteSpace(hotkeyValue))
Expand Down Expand Up @@ -86,13 +109,19 @@ private void OnKeyboardEvent(object? sender, KeyboardHookEventArgs e)
}

List<string> displayParts = [];
if (isCtrl) displayParts.Add("Ctrl");
if (isShift) displayParts.Add("Shift");
if (isAlt) displayParts.Add("Alt");
if (isWin) displayParts.Add("Win");
if (isCtrl)
displayParts.Add("Ctrl");
if (isShift)
displayParts.Add("Shift");
if (isAlt)
displayParts.Add("Alt");
if (isWin)
displayParts.Add("Win");

var key = (VirtualKey)e.VirtualKeyCode;
if (!IsModifierKey(key))
HasNonModifierKey = !IsModifierKey(key);

if (HasNonModifierKey)
{
displayParts.Add(HotkeyStringConverter.GetKeyDisplayName(key));
}
Expand All @@ -101,6 +130,23 @@ private void OnKeyboardEvent(object? sender, KeyboardHookEventArgs e)
{
CapturedHotkeys = string.Join(" + ", displayParts);
HotkeyCombination = displayParts.Select(p => new SingleHotkeyDataObject { Key = p }).ToList();

if (ValidateHotkey is not null)
{
var (isDuplicate, conflictingName) = ValidateHotkey(CapturedHotkeys);
if (isDuplicate)
{
ShowError(string.Format(ResourceExtensions.GetLocalized("HotkeyDuplicateErrorMessage"), conflictingName));
}
else
{
HideError();
}
}
else
{
HideError();
}
});

e.Handled = true;
Expand Down
41 changes: 41 additions & 0 deletions AutoDarkModeApp/ViewModels/HotkeysViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,47 @@ public void UpdateHotkeyValue(string tag, string? value)
SafeSaveBuilder();
}

public (bool isDuplicate, string? conflictingName) IsDuplicateHotkey(string currentTag, string? newKeys)
{
if (string.IsNullOrWhiteSpace(newKeys))
{
return (false, null);
}

var normalizedNewKeys = newKeys.Trim();

var allHotkeys = new[]
{
(Tag: "ForceLight", Keys: _builder.Config.Hotkeys.ForceLight, Name: "ForceLight"),
(Tag: "ForceDark", Keys: _builder.Config.Hotkeys.ForceDark, Name: "ForceDark"),
(Tag: "StopForcing", Keys: _builder.Config.Hotkeys.NoForce, Name: "StopForcing"),
(Tag: "ToggleTheme", Keys: _builder.Config.Hotkeys.ToggleTheme, Name: "ToggleTheme"),
(Tag: "AutomaticThemeSwitch", Keys: _builder.Config.Hotkeys.ToggleAutoThemeSwitch, Name: "AutomaticThemeSwitch"),
(Tag: "PauseAutoThemeSwitching", Keys: _builder.Config.Hotkeys.TogglePostpone, Name: "PauseAutoThemeSwitching"),
};

foreach (var hotkey in allHotkeys)
{
if (hotkey.Tag == currentTag)
{
continue;
}

if (string.IsNullOrWhiteSpace(hotkey.Keys))
{
continue;
}

if (hotkey.Keys.Trim().Equals(normalizedNewKeys, StringComparison.OrdinalIgnoreCase))
{
var displayName = HotkeysCollection?.FirstOrDefault(h => h.Tag == hotkey.Tag)?.DisplayName ?? hotkey.Name;
return (true, displayName);
}
}

return (false, null);
}

public HotkeysViewModel(IErrorService errorService)
{
_dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
Expand Down
23 changes: 19 additions & 4 deletions AutoDarkModeApp/Views/HotkeysPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private async void EditHotkeysButton_Click(object sender, RoutedEventArgs e)

var keyValue = ViewModel.GetHotkeyValue(hotkeyData.Tag);
var dialogContent = new ShortcutDialogContentControl();
dialogContent.ValidateHotkey = (newKeys) => ViewModel.IsDuplicateHotkey(hotkeyData.Tag, newKeys);

if (keyValue is not null)
{
Expand All @@ -42,18 +43,32 @@ private async void EditHotkeysButton_Click(object sender, RoutedEventArgs e)
Content = dialogContent,
};

shortcutDialog.Closing += (dialog, args) =>
{
if (args.Result == ContentDialogResult.Primary)
{
if (!dialogContent.HasNonModifierKey)
{
dialogContent.ShowError("HotkeyInvalidErrorMessage".GetLocalized());
}
if (dialogContent.IsErrorVisible)
{
args.Cancel = true;
}
}
};

var result = await shortcutDialog.ShowAsync();
if (result == ContentDialogResult.Secondary)
{
dialogContent.HotkeyCombination?.Clear();
dialogContent.CapturedHotkeys = null;
}
else if (result != ContentDialogResult.Primary)

if (result == ContentDialogResult.Primary || result == ContentDialogResult.Secondary)
Comment thread
ChenYiLins marked this conversation as resolved.
{
return;
ViewModel.UpdateHotkeyValue(hotkeyData.Tag, dialogContent.CapturedHotkeys);
}

ViewModel.UpdateHotkeyValue(hotkeyData.Tag, dialogContent.CapturedHotkeys);
}

private async void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
Expand Down