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
107 changes: 107 additions & 0 deletions src/ui/Features/Options/Shortcuts/ShortcutGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Avalonia.Media;
using Nikse.SubtitleEdit.Logic;
using Nikse.SubtitleEdit.Logic.Config;

namespace Nikse.SubtitleEdit.Features.Options.Shortcuts;

/// <summary>
/// Thematic grouping of shortcuts, used only for browsing/filtering in the shortcuts
/// window. Unlike <see cref="ShortcutCategory"/> it has no effect on dispatch - the
/// category still decides which focused control a shortcut is active in.
/// </summary>
public enum ShortcutGroup
{
General,
File,
Video,
Waveform,
SubtitleGrid,
TextBox,
SubtitleGridAndTextBox,
Sync,
Translate,
Search,
Tools,
Ai,
}

public static class ShortcutGroupUi
{
public static ShortcutGroup FromCategory(ShortcutCategory category)
{
return category switch
{
ShortcutCategory.Waveform => ShortcutGroup.Waveform,
ShortcutCategory.SubtitleGrid => ShortcutGroup.SubtitleGrid,
ShortcutCategory.TextBox => ShortcutGroup.TextBox,
ShortcutCategory.SubtitleGridAndTextBox => ShortcutGroup.SubtitleGridAndTextBox,
_ => ShortcutGroup.General,
};
}

public static string GetName(ShortcutGroup group)
{
var language = Se.Language.Options.Shortcuts;
return group switch
{
ShortcutGroup.File => language.CategoryFile,
ShortcutGroup.Video => language.CategoryVideo,
ShortcutGroup.Waveform => language.CategoryWaveform,
ShortcutGroup.SubtitleGrid => language.CategorySubtitleGrid,
ShortcutGroup.TextBox => language.CategoryTextBox,
ShortcutGroup.SubtitleGridAndTextBox => language.CategorySubtitleGridAndTextBox,
ShortcutGroup.Sync => language.CategorySync,
ShortcutGroup.Translate => language.CategoryTranslate,
ShortcutGroup.Search => language.CategorySearch,
ShortcutGroup.Tools => language.CategoryTools,
ShortcutGroup.Ai => language.CategoryAi,
_ => language.CategoryGeneral,
};
}

public static string GetIconName(ShortcutGroup group)
{
return group switch
{
ShortcutGroup.File => IconNames.FileOutline,
ShortcutGroup.Video => IconNames.MovieOpenOutline,
ShortcutGroup.Waveform => IconNames.Waveform,
ShortcutGroup.SubtitleGrid => IconNames.ViewList,
ShortcutGroup.TextBox => IconNames.FormTextBox,
ShortcutGroup.SubtitleGridAndTextBox => IconNames.ViewSplitVertical,
ShortcutGroup.Sync => IconNames.Sync,
ShortcutGroup.Translate => IconNames.Translate,
ShortcutGroup.Search => IconNames.Find,
ShortcutGroup.Tools => IconNames.Tools,
ShortcutGroup.Ai => IconNames.Creation,
_ => IconNames.Settings,
};
}

// Soft tint of the group color, used as background behind the colored icon.
public static IBrush GetSoftBrush(ShortcutGroup group)
{
var color = ((SolidColorBrush)GetBrush(group)).Color;
return new SolidColorBrush(color, 0.28);
}

// Mid-saturation tones picked to stay readable on both the dark and light theme.
public static IBrush GetBrush(ShortcutGroup group)
{
return group switch
{
ShortcutGroup.File => new SolidColorBrush(Color.Parse("#5fa8e0")),
ShortcutGroup.Video => new SolidColorBrush(Color.Parse("#a78bfa")),
ShortcutGroup.Waveform => new SolidColorBrush(Color.Parse("#f0b429")),
ShortcutGroup.SubtitleGrid => new SolidColorBrush(Color.Parse("#4ec98a")),
ShortcutGroup.TextBox => new SolidColorBrush(Color.Parse("#4fc3e8")),
ShortcutGroup.SubtitleGridAndTextBox => new SolidColorBrush(Color.Parse("#7fa8f0")),
ShortcutGroup.Sync => new SolidColorBrush(Color.Parse("#58c9b4")),
ShortcutGroup.Translate => new SolidColorBrush(Color.Parse("#6bb84e")),
ShortcutGroup.Search => new SolidColorBrush(Color.Parse("#d9a03f")),
ShortcutGroup.Tools => new SolidColorBrush(Color.Parse("#f0885a")),
ShortcutGroup.Ai => new SolidColorBrush(Color.Parse("#e668c4")),
_ => new SolidColorBrush(Color.Parse("#8494a4")),
};
}
}
36 changes: 36 additions & 0 deletions src/ui/Features/Options/Shortcuts/ShortcutGroupTile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Avalonia.Media;
using Nikse.SubtitleEdit.Logic;
using Nikse.SubtitleEdit.Logic.Config;

namespace Nikse.SubtitleEdit.Features.Options.Shortcuts;

/// <summary>
/// One clickable tile in the group filter strip. Group == null means "all groups".
/// </summary>
public class ShortcutGroupTile
{
public ShortcutGroup? Group { get; }
public string Name { get; }
public string IconName { get; }
public IBrush Brush { get; }
public int Count { get; }

public ShortcutGroupTile(ShortcutGroup group, int count)
{
Group = group;
Name = ShortcutGroupUi.GetName(group);
IconName = ShortcutGroupUi.GetIconName(group);
Brush = ShortcutGroupUi.GetBrush(group);
Count = count;
}

public ShortcutGroupTile(int count) // "All" tile
{
Group = null;
Name = Se.Language.General.All;
IconName = IconNames.ViewGrid;
// System accent so "All" cannot be mistaken for the gray "General" group.
Brush = UiUtil.GetAccentBrush();
Count = count;
}
}
31 changes: 26 additions & 5 deletions src/ui/Features/Options/Shortcuts/ShortcutTreeNode.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
using Avalonia.Controls;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Nikse.SubtitleEdit.Logic;
using System;
using System.Collections.Generic;

namespace Nikse.SubtitleEdit.Features.Options.Shortcuts;

public partial class ShortcutTreeNode : ObservableObject
{
[ObservableProperty] private string _category;
[ObservableProperty] private string _activeIn;
[ObservableProperty] private string _groupName;
[ObservableProperty] private string _groupIconName;
[ObservableProperty] private IBrush _groupBrush;
[ObservableProperty] private IBrush _groupSoftBrush;
[ObservableProperty] private string _title;
[ObservableProperty] private string _displayShortcut;
[ObservableProperty] private List<string> _keyParts;
[ObservableProperty] private bool _isAssigned;
[ObservableProperty] private bool _isUnassigned;
[ObservableProperty] private double _activeInOpacity;

public ShortCut? ShortCut { get; set; }

public ShortcutTreeNode(string category, string title, string displayShortCut, ShortCut shortcut)
public ShortcutTreeNode(string activeIn, string title, string displayShortCut, ShortCut shortcut)
{
Category = category;
ActiveIn = activeIn;
Title = title;
ShortCut = shortcut;
DisplayShortcut = displayShortCut;
KeyParts = string.IsNullOrEmpty(displayShortCut)
? new List<string>()
: new List<string>(displayShortCut.Split(" + ", StringSplitOptions.None));
IsAssigned = KeyParts.Count > 0;
IsUnassigned = !IsAssigned;
GroupName = ShortcutGroupUi.GetName(shortcut.Group);
GroupIconName = ShortcutGroupUi.GetIconName(shortcut.Group);
GroupBrush = ShortcutGroupUi.GetBrush(shortcut.Group);
GroupSoftBrush = ShortcutGroupUi.GetSoftBrush(shortcut.Group);
// "Everywhere" pills are dimmed so context-scoped shortcuts stand out while scrolling.
ActiveInOpacity = shortcut.Category == ShortcutCategory.General ? 0.55 : 1.0;
}
}
}
52 changes: 41 additions & 11 deletions src/ui/Features/Options/Shortcuts/ShortcutsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace Nikse.SubtitleEdit.Features.Options.Shortcuts;
public partial class ShortcutsViewModel : ObservableObject
{
public ObservableCollection<ShortcutTreeNode> FlatNodes { get; } = new();
public ObservableCollection<ShortcutGroupTile> GroupTiles { get; } = new();
[ObservableProperty] private ShortcutGroupTile? _selectedGroupTile;
[ObservableProperty] private ObservableCollection<string> _shortcuts;
[ObservableProperty] private string? _selectedShortcut;
[ObservableProperty] private ObservableCollection<string> _filters;
Expand Down Expand Up @@ -108,6 +110,11 @@ public ShortcutsViewModel(IWindowService windowService, IFileHelper fileHelper)
_actorSlots[9] = Se.Settings.Actor10;
}

partial void OnSelectedGroupTileChanged(ShortcutGroupTile? value)
{
UpdateVisibleShortcuts(SearchText);
}

partial void OnCtrlIsSelectedChanged(bool value)
{
if (!_isLoadingSelection)
Expand Down Expand Up @@ -198,6 +205,7 @@ public void LoadShortCuts(MainViewModel vm)
{
MainViewModel = vm;
_allShortcuts = ShortcutsMain.GetAllShortcuts(vm);
BuildGroupTiles();
UpdateVisibleShortcuts(string.Empty);

_configurableCommands.Add(vm.SetColor1Command);
Expand Down Expand Up @@ -231,28 +239,50 @@ public void LoadShortCuts(MainViewModel vm)
_configurableCommands.Add(vm.SetActor10Command);
}

internal void UpdateVisibleShortcuts(string searchText)
private void BuildGroupTiles()
{
FlatNodes.Clear();
AddShortcuts(ShortcutCategory.General, Se.Language.Options.Shortcuts.CategoryGeneral, searchText);
AddShortcuts(ShortcutCategory.SubtitleGridAndTextBox,
Se.Language.Options.Shortcuts.CategorySubtitleGridAndTextBox, searchText);
AddShortcuts(ShortcutCategory.SubtitleGrid, Se.Language.Options.Shortcuts.CategorySubtitleGrid, searchText);
AddShortcuts(ShortcutCategory.Waveform, Se.Language.Options.Shortcuts.CategoryWaveform, searchText);
AddShortcuts(ShortcutCategory.TextBox, Se.Language.Options.Shortcuts.CategoryTextBox, searchText);
GroupTiles.Clear();
GroupTiles.Add(new ShortcutGroupTile(_allShortcuts.Count));
foreach (var group in Enum.GetValues<ShortcutGroup>())
{
var count = _allShortcuts.Count(p => p.Group == group);
if (count > 0)
{
GroupTiles.Add(new ShortcutGroupTile(group, count));
}
}

SelectedGroupTile = GroupTiles[0];
}

private void AddShortcuts(ShortcutCategory category, string categoryName, string searchText)
internal void UpdateVisibleShortcuts(string searchText)
{
var shortcuts = _allShortcuts.Where(p => p.Category == category && Search(searchText, p)).ToList();
FlatNodes.Clear();
var group = SelectedGroupTile?.Group;
var shortcuts = _allShortcuts
.Where(p => (group == null || p.Group == group) && Search(searchText, p))
.OrderBy(p => (int)p.Group);

foreach (var x in shortcuts)
{
var leaf = new ShortcutTreeNode(categoryName, MakeDisplayName(x, false), MakeDisplayShortCut(x), x);
var leaf = new ShortcutTreeNode(GetActiveInName(x.Category), MakeDisplayName(x, false), MakeDisplayShortCut(x), x);
FlatNodes.Add(leaf);
}
}

private static string GetActiveInName(ShortcutCategory category)
{
var language = Se.Language.Options.Shortcuts;
return category switch
{
ShortcutCategory.SubtitleGridAndTextBox => language.CategorySubtitleGridAndTextBox,
ShortcutCategory.SubtitleGrid => language.CategorySubtitleGrid,
ShortcutCategory.Waveform => language.CategoryWaveform,
ShortcutCategory.TextBox => language.CategoryTextBox,
_ => language.ActiveInEverywhere,
};
}

private static string MakeDisplayName(ShortCut x, bool includeShortCutKeys = true)
{
var name = ShortcutsMain.CommandTranslationLookup.TryGetValue(x.Name, out var displayName)
Expand Down
Loading