From 391684e3ad1e6e13519491e85c29d4bfa8486842 Mon Sep 17 00:00:00 2001 From: twibster Date: Wed, 15 Apr 2026 22:32:02 +0200 Subject: [PATCH 1/2] Add Donate sidebar tab with PayPal CTA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New nav tab between Settings and About (Heart24 icon, Tag=4) opens a dedicated page with a PayPal donate button — accent-filled Hyperlink that ShellExecutes to paypal.me/omara2002 in the default browser. Sidebar ordering note: DockPanel Bottom-docked children stack upward from the bottom in XAML order, so About must remain first, Donate is inserted between, and Settings stays last. Visual top-to-bottom in the bottom cluster is Settings, Donate, About. --- ScreenSound/Views/MainWindow.xaml | 64 ++++++++++++++++++++++++++++ ScreenSound/Views/MainWindow.xaml.cs | 4 +- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/ScreenSound/Views/MainWindow.xaml b/ScreenSound/Views/MainWindow.xaml index 4e8cbfe..23cc08d 100644 --- a/ScreenSound/Views/MainWindow.xaml +++ b/ScreenSound/Views/MainWindow.xaml @@ -89,6 +89,12 @@ + @@ -100,6 +106,17 @@ + + + + + + + @@ -598,6 +615,53 @@ + + + + + + + + + + + + + + + + ScreenSound is a solo developer project built in spare time. If it's + made your multi-monitor setup a little nicer, a one-off tip helps + justify the hours spent on bug fixes and new features — the app + stays free either way. + + + + + + + + + + diff --git a/ScreenSound/Views/MainWindow.xaml.cs b/ScreenSound/Views/MainWindow.xaml.cs index b6e81a3..9c3e355 100644 --- a/ScreenSound/Views/MainWindow.xaml.cs +++ b/ScreenSound/Views/MainWindow.xaml.cs @@ -31,8 +31,8 @@ public MainWindow() DataContext = _viewModel; // Order must match the Tag indices on the sidebar RadioButtons: - // 0 = Home, 1 = Pinned, 2 = Settings, 3 = About. - _pages = new[] { HomePage, PinnedPage, SettingsPage, AboutPage }; + // 0 = Home, 1 = Pinned, 2 = Settings, 3 = About, 4 = Donate. + _pages = new[] { HomePage, PinnedPage, SettingsPage, AboutPage, DonatePage }; SetupTrayIcon(); From d54bb44dcb411920ff28782f596805d5c8b8d5c6 Mon Sep 17 00:00:00 2001 From: twibster Date: Wed, 15 Apr 2026 22:39:41 +0200 Subject: [PATCH 2/2] Swap PayPal link for Ko-fi branded button on Donate page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use Ko-fi's official "Buy me a coffee" PNG (downloaded from Ko-fi's CDN, bundled as a WPF Resource) instead of the PayPal text hyperlink. A plain Button with a stripped ControlTemplate — just a Border wrapping a ContentPresenter — makes the yellow branded image the button face with no chrome repainting over it. Hover / pressed give subtle opacity feedback so users still get "this is clickable" affordance. KofiDonate_Click uses Process.Start with UseShellExecute=true to open https://ko-fi.com/twibster in the default browser; .NET 8 requires the shell-execute flag for https URLs. Any launch failure is swallowed — a missing default browser shouldn't crash the UI, and the ToolTip already shows the URL for manual copy. Accessibility: Cursor=Hand, AutomationProperties.Name, and a descriptive ToolTip cover mouse, keyboard, and screen-reader paths. --- ScreenSound/Assets/kofi-button.png | Bin 0 -> 4794 bytes ScreenSound/ScreenSound.csproj | 8 +++++ ScreenSound/Views/MainWindow.xaml | 49 +++++++++++++++++++++------ ScreenSound/Views/MainWindow.xaml.cs | 34 +++++++++++++++++++ 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 ScreenSound/Assets/kofi-button.png diff --git a/ScreenSound/Assets/kofi-button.png b/ScreenSound/Assets/kofi-button.png new file mode 100644 index 0000000000000000000000000000000000000000..8926e9e68818b611df158da4a4aea2ee0474e42f GIT binary patch literal 4794 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 + + diff --git a/ScreenSound/Views/MainWindow.xaml b/ScreenSound/Views/MainWindow.xaml index 23cc08d..717434c 100644 --- a/ScreenSound/Views/MainWindow.xaml +++ b/ScreenSound/Views/MainWindow.xaml @@ -645,17 +645,44 @@ stays free either way. - - + + diff --git a/ScreenSound/Views/MainWindow.xaml.cs b/ScreenSound/Views/MainWindow.xaml.cs index 9c3e355..b277d84 100644 --- a/ScreenSound/Views/MainWindow.xaml.cs +++ b/ScreenSound/Views/MainWindow.xaml.cs @@ -363,6 +363,40 @@ private void RemovePinFromList_Click(object sender, RoutedEventArgs e) } } + // ── Donate page ─────────────────────────────────────────────────────── + + /// + /// Opens the Ko-fi donation page in the user's default browser. The + /// Donate tab's button is a bare image-button (no wpfui:Hyperlink) + /// so the ko-fi-yellow branded PNG renders pixel-perfect without any + /// Hyperlink chrome repainting over it — the trade-off is that URL + /// launching has to be wired here rather than inherited from NavigateUri. + /// + /// UseShellExecute=true is required for opening https:// URLs in + /// .NET 8 — without it + /// treats the URL as an executable path and throws Win32Exception. Any + /// failure (no default browser, shell error) is swallowed: the button + /// click is a best-effort nice-to-have, not something to crash the UI + /// over. + /// + private void KofiDonate_Click(object sender, RoutedEventArgs e) + { + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = "https://ko-fi.com/twibster", + UseShellExecute = true, + }); + } + catch + { + // Intentionally swallowed — a failed browser launch shouldn't + // crash the app or even surface an error dialog. The ToolTip + // already shows the URL so the user can copy/paste if needed. + } + } + protected override void OnClosing(CancelEventArgs e) { if (!_forceClose && _viewModel.MinimizeOnClose)