From 749a856faad7b45b9acdf8b85ac080d817d8639b Mon Sep 17 00:00:00 2001 From: Ray Berezowski <6616212+rberezowski@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:58:03 -0400 Subject: [PATCH] Add recent launcher items --- Models/LauncherConfig.cs | 2 ++ README.md | 1 + Services/ConfigService.cs | 18 +++++++++++++++ Services/LauncherController.cs | 41 ++++++++++++++++++++++++++++++++++ TaskbarLauncher.csproj | 4 ++-- Views/LauncherPopup.xaml.cs | 35 ++++++++++++++++++++++++++--- config.example.json | 2 ++ docs/INSTALL.md | 2 ++ 8 files changed, 100 insertions(+), 5 deletions(-) diff --git a/Models/LauncherConfig.cs b/Models/LauncherConfig.cs index 66752f5..abb5490 100644 --- a/Models/LauncherConfig.cs +++ b/Models/LauncherConfig.cs @@ -8,6 +8,8 @@ public sealed class LauncherConfig public LauncherThemeScheme ThemeScheme { get; set; } = LauncherThemeScheme.MarkHaven; + public List RecentItems { get; set; } = []; + public List Items { get; set; } = []; } diff --git a/README.md b/README.md index 9497a71..358006c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Download the compiled Windows release from: - System tray launcher - Global hotkey, default `Ctrl+Alt+Space` - Search/filter in the launcher popup +- Recent items section for quickly relaunching entries - Nested menus, drag/drop ordering, and drag/drop item creation in Settings - Right-click launcher entries to edit them in Settings - Right-click item duplication in Settings diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs index 1f1b8b0..160abf1 100644 --- a/Services/ConfigService.cs +++ b/Services/ConfigService.cs @@ -190,10 +190,20 @@ public static class ConfigService throw new InvalidDataException("The selected config is missing launcher items."); } + if (config.RecentItems is null) + { + config.RecentItems = []; + } + foreach (LauncherItem item in config.Items) { ValidateItem(item); } + + foreach (LauncherItem item in config.RecentItems) + { + ValidateItem(item); + } } private static void ValidateItem(LauncherItem item) @@ -226,6 +236,14 @@ public static class ConfigService config.ThemeScheme = LauncherThemeScheme.MarkHaven; } + config.RecentItems ??= []; + config.RecentItems.RemoveAll(item => item.Type == LauncherItemType.Menu || string.IsNullOrWhiteSpace(item.Target)); + foreach (LauncherItem item in config.RecentItems) + { + item.Children = []; + NormalizeDisplayModes(item); + } + foreach (LauncherItem item in config.Items) { NormalizeDisplayModes(item); diff --git a/Services/LauncherController.cs b/Services/LauncherController.cs index 9fda252..6832271 100644 --- a/Services/LauncherController.cs +++ b/Services/LauncherController.cs @@ -9,6 +9,8 @@ namespace TaskbarLauncher.Services; public sealed class LauncherController : IDisposable { + private const int MaxRecentItems = 8; + private readonly HotkeyService hotkeyService = new(); private NotifyIcon? notifyIcon; private LauncherPopup? popup; @@ -136,6 +138,7 @@ public sealed class LauncherController : IDisposable { if (TryLaunchItem(item, out string? error)) { + RecordRecentItem(item); popup?.Close(); return; } @@ -146,6 +149,43 @@ public sealed class LauncherController : IDisposable } } + private void RecordRecentItem(LauncherItem item) + { + if (item.Type == LauncherItemType.Menu || string.IsNullOrWhiteSpace(item.Target)) + { + return; + } + + Config.RecentItems.RemoveAll(recent => IsSameRecentItem(recent, item)); + Config.RecentItems.Insert(0, CreateRecentItem(item)); + if (Config.RecentItems.Count > MaxRecentItems) + { + Config.RecentItems.RemoveRange(MaxRecentItems, Config.RecentItems.Count - MaxRecentItems); + } + + ConfigService.Save(Config); + } + + private static bool IsSameRecentItem(LauncherItem recent, LauncherItem item) + { + return string.Equals(recent.Target, item.Target, StringComparison.OrdinalIgnoreCase) && + string.Equals(recent.Arguments ?? "", item.Arguments ?? "", StringComparison.OrdinalIgnoreCase); + } + + private static LauncherItem CreateRecentItem(LauncherItem item) + { + return new LauncherItem + { + Title = item.Title, + Type = item.Type, + Target = item.Target, + Arguments = item.Arguments, + IconPath = item.IconPath, + DisplayMode = item.DisplayMode, + Children = [] + }; + } + private void OpenAllItems(LauncherItem menuItem) { List failures = []; @@ -161,6 +201,7 @@ public sealed class LauncherController : IDisposable if (TryLaunchItem(child, out string? error)) { launchedCount++; + RecordRecentItem(child); } else if (!string.IsNullOrWhiteSpace(error)) { diff --git a/TaskbarLauncher.csproj b/TaskbarLauncher.csproj index e49dca1..b942def 100644 --- a/TaskbarLauncher.csproj +++ b/TaskbarLauncher.csproj @@ -12,8 +12,8 @@ Ray Berezowski Assets\AppIcon.ico 1.0.1 - 1.0.1.4 - 1.0.1.4 + 1.0.1.5 + 1.0.1.5 1.0.1 diff --git a/Views/LauncherPopup.xaml.cs b/Views/LauncherPopup.xaml.cs index fc783bd..f9224e3 100644 --- a/Views/LauncherPopup.xaml.cs +++ b/Views/LauncherPopup.xaml.cs @@ -93,6 +93,12 @@ public partial class LauncherPopup : Window { ItemsPanel.Children.Clear(); visibleLaunchButtons.Clear(); + + if (config.RecentItems.Count > 0) + { + ItemsPanel.Children.Add(CreateRecentItemsControl()); + } + bool expandMenusByDefault = config.Items.Count <= 2; foreach (LauncherItem item in config.Items) { @@ -100,7 +106,26 @@ public partial class LauncherPopup : Window } } - private FrameworkElement CreateItemControl(LauncherItem item, int depth, bool expandMenusByDefault) + private Expander CreateRecentItemsControl() + { + Expander expander = new() + { + Header = new TextBlock { Text = "Recent" }, + IsExpanded = true, + Margin = new Thickness(0, 4, 0, 8) + }; + + StackPanel recentItems = new(); + foreach (LauncherItem item in config.RecentItems) + { + recentItems.Children.Add(CreateItemControl(item, 1, expandMenusByDefault: false, allowEdit: false)); + } + + expander.Content = recentItems; + return expander; + } + + private FrameworkElement CreateItemControl(LauncherItem item, int depth, bool expandMenusByDefault, bool allowEdit = true) { if (item.Type == LauncherItemType.Menu) { @@ -114,7 +139,7 @@ public partial class LauncherPopup : Window StackPanel children = new(); foreach (LauncherItem child in item.Children) { - children.Children.Add(CreateItemControl(child, depth + 1, expandMenusByDefault)); + children.Children.Add(CreateItemControl(child, depth + 1, expandMenusByDefault, allowEdit)); } expander.Content = children; @@ -131,7 +156,11 @@ public partial class LauncherPopup : Window }; button.Click += (_, _) => launchItem(item); - button.ContextMenu = CreateItemContextMenu(item); + if (allowEdit) + { + button.ContextMenu = CreateItemContextMenu(item); + } + button.KeyDown += (_, e) => { if (e.Key == Key.Enter) diff --git a/config.example.json b/config.example.json index 93055a7..316c610 100644 --- a/config.example.json +++ b/config.example.json @@ -1,6 +1,8 @@ { "Hotkey": "Ctrl+Alt+Space", "DisplayMode": "CompactList", + "ThemeScheme": "MarkHaven", + "RecentItems": [], "Items": [ { "Title": "Folders", diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 7da7ee3..1080cf7 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -21,6 +21,8 @@ Install the **.NET Desktop Runtime** for **Windows x64**. 4. Press `Ctrl+Alt+Space` to open the launcher. 5. Right-click a launcher entry and choose **Edit in Settings** to jump to that item. +Recently launched entries appear in the launcher popup under **Recent**. + ## Configure Open the tray menu and choose **Settings**.