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
Binary file added ScreenSound/Assets/kofi-button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions ScreenSound/ScreenSound.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@
-->
<Resource Include="app.ico" />
<Resource Include="Assets\app-icon.png" />
<!--
Official Ko-fi "Buy me a coffee" branded button from
https://storage.ko-fi.com/cdn/kofi2.png. Ko-fi explicitly provides this
asset for embedding by supporters, so bundling it in the app is within
their brand guidelines. Used as the Donate page CTA — the branded
button reads more as a real call-to-action than a generic text link.
-->
<Resource Include="Assets\kofi-button.png" />
</ItemGroup>

</Project>
91 changes: 91 additions & 0 deletions ScreenSound/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
</StackPanel>
</RadioButton>

<!--
Bottom-docked children stack upward from the bottom in XAML order:
About docks first → very bottom; Donate next → above About;
Settings last → above Donate. Visual top-to-bottom in the
sidebar's bottom cluster is Settings, Donate, About.
-->
<RadioButton DockPanel.Dock="Bottom" GroupName="Nav"
Checked="NavButton_Checked" Tag="3"
Style="{StaticResource SidebarNavButton}">
Expand All @@ -100,6 +106,17 @@
</StackPanel>
</RadioButton>

<RadioButton DockPanel.Dock="Bottom" GroupName="Nav"
Checked="NavButton_Checked" Tag="4"
Style="{StaticResource SidebarNavButton}">
<StackPanel HorizontalAlignment="Center">
<wpfui:SymbolIcon Symbol="Heart24" FontSize="22" HorizontalAlignment="Center"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=RadioButton}}"/>
<TextBlock Text="Donate" FontSize="10" HorizontalAlignment="Center" Margin="0,4,0,0"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=RadioButton}}"/>
</StackPanel>
</RadioButton>

<RadioButton DockPanel.Dock="Bottom" GroupName="Nav"
Checked="NavButton_Checked" Tag="2"
Style="{StaticResource SidebarNavButton}">
Expand Down Expand Up @@ -598,6 +615,80 @@
</ScrollViewer>
</Border>

<!-- ═══ DONATE PAGE ═══ -->
<Border x:Name="DonatePage" Visibility="Collapsed" Opacity="0">
<Border.RenderTransform><TranslateTransform/></Border.RenderTransform>
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="24,20,24,8">
<StackPanel>
<TextBlock Text="Donate" FontSize="20" FontWeight="Medium" Margin="0,0,0,16"/>
<wpfui:Card Padding="24">
<StackPanel>
<!-- ── Header: accent heart + section title ─────────
Mirrors the About page's header pattern (icon +
identity block) so the two bottom-cluster pages
read as visually related.
-->
<StackPanel Orientation="Horizontal" Margin="0,0,0,16">
<wpfui:SymbolIcon Symbol="Heart24" FontSize="28"
Foreground="{DynamicResource SystemAccentColorPrimaryBrush}"
VerticalAlignment="Center" Margin="0,0,14,0"/>
<TextBlock Text="Support ScreenSound" FontSize="16" FontWeight="SemiBold"
VerticalAlignment="Center"/>
</StackPanel>

<TextBlock TextWrapping="Wrap" FontSize="13"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
Margin="0,0,0,20">
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.
</TextBlock>

<!-- Ko-fi CTA — the official branded "Buy me a coffee" button
image from Ko-fi's CDN, bundled as a WPF Resource (see
csproj) so it renders offline.

The Template is stripped to a bare ContentPresenter so the
image IS the button face: no default WPF button chrome,
no hover rectangle painting over the yellow Ko-fi art.
Hover / pressed feedback is done via Opacity on the outer
Border so the branded colours stay intact while still
giving the user "this is clickable" affordance.

Cursor=Hand and AutomationProperties.Name give mouse +
screen-reader affordances the bare image wouldn't carry.
ToolTip spells out the destination so users can see where
a click will take them before committing. -->
<Button Click="KofiDonate_Click"
HorizontalAlignment="Left" Cursor="Hand"
ToolTip="Opens ko-fi.com/twibster in your browser"
AutomationProperties.Name="Donate via Ko-fi — opens ko-fi.com/twibster in your browser">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Root" Background="Transparent">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Root" Property="Opacity" Value="0.85"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Root" Property="Opacity" Value="0.7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<Image Source="/Assets/kofi-button.png" Width="200"
RenderOptions.BitmapScalingMode="HighQuality"/>
</Button>
</StackPanel>
</wpfui:Card>
</StackPanel>
</ScrollViewer>
</Border>

</Grid>
</Grid>
</wpfui:UiWindow>
38 changes: 36 additions & 2 deletions ScreenSound/Views/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -363,6 +363,40 @@ private void RemovePinFromList_Click(object sender, RoutedEventArgs e)
}
}

// ── Donate page ───────────────────────────────────────────────────────

/// <summary>
/// Opens the Ko-fi donation page in the user's default browser. The
/// Donate tab's button is a bare image-button (no <c>wpfui:Hyperlink</c>)
/// 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.
///
/// <c>UseShellExecute=true</c> is required for opening https:// URLs in
/// .NET 8 — without it <see cref="System.Diagnostics.Process.Start(string)"/>
/// 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.
/// </summary>
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)
Expand Down
Loading