From e8c0721c3919300f8ddde9855a8c8543d4a7efc4 Mon Sep 17 00:00:00 2001
From: Ray Berezowski <6616212+rberezowski@users.noreply.github.com>
Date: Tue, 2 Jun 2026 17:28:25 -0400
Subject: [PATCH] Initial TaskbarLauncher release baseline
---
.gitignore | 16 +
App.xaml | 182 +++++
App.xaml.cs | 23 +
AssemblyInfo.cs | 10 +
MainWindow.xaml | 166 +++++
MainWindow.xaml.cs | 951 +++++++++++++++++++++++++++
Models/LauncherConfig.cs | 44 ++
Services/ConfigService.cs | 158 +++++
Services/DisplayModeMetrics.cs | 30 +
Services/HotkeyService.cs | 116 ++++
Services/IconService.cs | 127 ++++
Services/LauncherController.cs | 109 +++
Services/StartupService.cs | 68 ++
Services/ThemeService.cs | 84 +++
TaskbarLauncher.csproj | 17 +
Tools/New-TaskbarLauncherRelease.ps1 | 98 +++
Views/LauncherPopup.xaml | 36 +
Views/LauncherPopup.xaml.cs | 331 ++++++++++
18 files changed, 2566 insertions(+)
create mode 100644 .gitignore
create mode 100644 App.xaml
create mode 100644 App.xaml.cs
create mode 100644 AssemblyInfo.cs
create mode 100644 MainWindow.xaml
create mode 100644 MainWindow.xaml.cs
create mode 100644 Models/LauncherConfig.cs
create mode 100644 Services/ConfigService.cs
create mode 100644 Services/DisplayModeMetrics.cs
create mode 100644 Services/HotkeyService.cs
create mode 100644 Services/IconService.cs
create mode 100644 Services/LauncherController.cs
create mode 100644 Services/StartupService.cs
create mode 100644 Services/ThemeService.cs
create mode 100644 TaskbarLauncher.csproj
create mode 100644 Tools/New-TaskbarLauncherRelease.ps1
create mode 100644 Views/LauncherPopup.xaml
create mode 100644 Views/LauncherPopup.xaml.cs
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f91cad3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+bin/
+obj/
+*.user
+*.suo
+*.pdb
+
+# Portable runtime data
+config.json
+backups/
+
+# Local/editor noise
+.vs/
+.vscode/
+
+# Generated release output kept outside the repo when possible
+*.zip
diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..fef3362
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..a926271
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,23 @@
+using TaskbarLauncher.Services;
+
+namespace TaskbarLauncher;
+
+public partial class App : System.Windows.Application
+{
+ private LauncherController? controller;
+
+ protected override void OnStartup(System.Windows.StartupEventArgs e)
+ {
+ base.OnStartup(e);
+
+ ThemeService.Apply();
+ controller = new LauncherController();
+ controller.Start();
+ }
+
+ protected override void OnExit(System.Windows.ExitEventArgs e)
+ {
+ controller?.Dispose();
+ base.OnExit(e);
+ }
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..cc29e7f
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly:ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..7f07c34
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..4928b40
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,951 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Forms;
+using System.Windows.Media;
+using System.IO;
+using System.Windows.Input;
+using System.Reflection;
+using TaskbarLauncher.Models;
+using TaskbarLauncher.Services;
+using MessageBox = System.Windows.MessageBox;
+using TreeView = System.Windows.Controls.TreeView;
+using TreeViewItem = System.Windows.Controls.TreeViewItem;
+
+namespace TaskbarLauncher;
+
+public partial class MainWindow : Window
+{
+ private readonly LauncherController? controller;
+ private LauncherItem? selectedItem;
+ private bool isLoadingSelection;
+ private bool isLoadingStartupState;
+ private List? selectedPath;
+ private string? loadedAutoIconPath;
+ private System.Windows.Point dragStartPoint;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ public MainWindow(LauncherController controller)
+ {
+ InitializeComponent();
+ this.controller = controller;
+ DisplayModeBox.ItemsSource = Enum.GetValues();
+ ItemDisplayModeBox.ItemsSource = Enum.GetValues();
+ TypeBox.ItemsSource = Enum.GetValues();
+ VersionText.Text = $"Version {GetAppVersion()}";
+ RefreshView();
+ }
+
+ private void RefreshView(LauncherItem? itemToSelect = null)
+ {
+ if (controller is null)
+ {
+ return;
+ }
+
+ ItemsTree.Items.Clear();
+ foreach (LauncherItem item in controller.Config.Items)
+ {
+ ItemsTree.Items.Add(CreateTreeItem(item));
+ }
+
+ HotkeyBox.Text = controller.Config.Hotkey;
+ DisplayModeBox.SelectedItem = controller.Config.DisplayMode;
+ LoadStartupState();
+ StatusText.Text = $"Config file: {ConfigService.ConfigPath}";
+ if (itemToSelect is not null && SelectTreeItem(itemToSelect))
+ {
+ selectedItem = itemToSelect;
+ selectedPath = FindItemPath(itemToSelect);
+ LoadItemFields(itemToSelect);
+ }
+ else
+ {
+ ClearItemFields();
+ }
+ }
+
+ private static string GetAppVersion()
+ {
+ return Assembly.GetExecutingAssembly()
+ .GetCustomAttribute()?
+ .InformationalVersion ?? "development";
+ }
+
+ private static TreeViewItem CreateTreeItem(LauncherItem item)
+ {
+ var treeItem = new TreeViewItem { Header = $"{item.Title} ({item.Type})", Tag = item };
+ foreach (LauncherItem child in item.Children)
+ {
+ treeItem.Items.Add(CreateTreeItem(child));
+ }
+
+ return treeItem;
+ }
+
+ private bool SelectTreeItem(LauncherItem item)
+ {
+ foreach (object root in ItemsTree.Items)
+ {
+ if (root is TreeViewItem treeItem && SelectTreeItem(treeItem, item))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool SelectTreeItem(TreeViewItem treeItem, LauncherItem item)
+ {
+ if (ReferenceEquals(treeItem.Tag, item))
+ {
+ treeItem.IsSelected = true;
+ treeItem.BringIntoView();
+ return true;
+ }
+
+ foreach (object child in treeItem.Items)
+ {
+ if (child is TreeViewItem childTreeItem && SelectTreeItem(childTreeItem, item))
+ {
+ treeItem.IsExpanded = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void ItemsTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs