Skip to content

Commit a94aaa8

Browse files
Add one-click theme presets for the clock's look (#142)
* Add clock theme presets and a picker control ClockTheme bundles the font, colors, opacity, and shape settings into named looks (System, Terminal, Chalk, Card, Paper, Neon, Sunset) that apply in one click. ThemePresetPicker renders each as a true miniature of the clock via OutlinedTextBlock on a desktop-stand-in backdrop, so users pick a look by seeing it rather than coordinating eight controls. * Add a Theme card to the settings window New card between Window and Typography hosting the preset picker, with its own sidebar nav entry. * Make the theme chips look like clocks on a desktop The flat grey swatch backdrop made every theme look alike; use a wallpaper-like gradient so each look pops the way it would in place, enlarge the samples, and rework the lineup: drop Neon and Card (weak at chip scale), add Minimal (thin lock-screen white) and Midnight, and warm up Sunset. * Rework the theme lineup to the picked looks Restore the flat swatch backdrop (the wallpaper gradient missed) and replace the lineup with the selected set: System, Accent (white on the system accent color), Smoke (translucent dark glass), Terminal, Midnight, Paper, Minimal, and Chalk. * Rename the card to Theme Presets and fix the Minimal chip Clearer name in the card header and nav, plainer helper text, and larger chip samples: OutlinedTextBlock draws text as geometry, so the Light-weight Minimal sample dissolved into anti-aliasing at 17px. * Clear the default stroke on theme chip samples OutlinedTextBlock defaults to a 1px black stroke, so every chip was drawing a dark halo around its digits - most visible on the thin Minimal sample. Only outline themes set a stroke now.
1 parent ff6d3a8 commit a94aaa8

4 files changed

Lines changed: 232 additions & 0 deletions

File tree

DesktopClock/ClockTheme.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Collections.Generic;
2+
using System.Windows.Media;
3+
using DesktopClock.Properties;
4+
using DesktopClock.Utilities;
5+
6+
namespace DesktopClock;
7+
8+
/// <summary>
9+
/// A coherent visual identity for the clock — font, colors, opacity, and shape — applied in one click.
10+
/// </summary>
11+
public sealed class ClockTheme
12+
{
13+
public ClockTheme(string name, string fontFamily, string fontWeight, Color textColor, Color outerColor,
14+
bool backgroundEnabled, double backgroundOpacity, double backgroundCornerRadius, double outlineThickness,
15+
string fontStyle = "Normal")
16+
{
17+
Name = name;
18+
FontFamily = fontFamily;
19+
FontWeight = fontWeight;
20+
FontStyle = fontStyle;
21+
TextColor = textColor;
22+
OuterColor = outerColor;
23+
BackgroundEnabled = backgroundEnabled;
24+
BackgroundOpacity = backgroundOpacity;
25+
BackgroundCornerRadius = backgroundCornerRadius;
26+
OutlineThickness = outlineThickness;
27+
}
28+
29+
public string Name { get; }
30+
public string FontFamily { get; }
31+
public string FontWeight { get; }
32+
public string FontStyle { get; }
33+
public Color TextColor { get; }
34+
public Color OuterColor { get; }
35+
public bool BackgroundEnabled { get; }
36+
public double BackgroundOpacity { get; }
37+
public double BackgroundCornerRadius { get; }
38+
public double OutlineThickness { get; }
39+
40+
/// <summary>
41+
/// Built-in looks, starting with the system-seeded default.
42+
/// </summary>
43+
public static IReadOnlyList<ClockTheme> GetBuiltInThemes()
44+
{
45+
return new[]
46+
{
47+
CreateSystemTheme(),
48+
new ClockTheme("Accent", "Segoe UI", "SemiBold",
49+
Color.FromRgb(0xFF, 0xFF, 0xFF), SystemThemeService.GetSystemAccentColor(),
50+
backgroundEnabled: true, backgroundOpacity: 1, backgroundCornerRadius: 8, outlineThickness: 0.2),
51+
new ClockTheme("Smoke", "Segoe UI", "Normal",
52+
Color.FromRgb(0xF2, 0xF2, 0xF2), Color.FromRgb(0x0A, 0x0A, 0x10),
53+
backgroundEnabled: true, backgroundOpacity: 0.55, backgroundCornerRadius: 10, outlineThickness: 0.2),
54+
new ClockTheme("Terminal", "Consolas", "Bold",
55+
Color.FromRgb(0x00, 0xE5, 0xFF), Color.FromRgb(0x0C, 0x0C, 0x0C),
56+
backgroundEnabled: true, backgroundOpacity: 0.85, backgroundCornerRadius: 6, outlineThickness: 0.2),
57+
new ClockTheme("Midnight", "Segoe UI", "SemiBold",
58+
Color.FromRgb(0x4C, 0xC2, 0xFF), Color.FromRgb(0x1B, 0x1B, 0x1B),
59+
backgroundEnabled: true, backgroundOpacity: 0.95, backgroundCornerRadius: 8, outlineThickness: 0.2),
60+
new ClockTheme("Paper", "Georgia", "Normal",
61+
Color.FromRgb(0x1A, 0x1A, 0x1A), Color.FromRgb(0xFA, 0xF9, 0xF6),
62+
backgroundEnabled: true, backgroundOpacity: 0.97, backgroundCornerRadius: 8, outlineThickness: 0.2),
63+
new ClockTheme("Minimal", "Segoe UI", "Light",
64+
Color.FromRgb(0xFF, 0xFF, 0xFF), Color.FromRgb(0x00, 0x00, 0x00),
65+
backgroundEnabled: false, backgroundOpacity: 0, backgroundCornerRadius: 1, outlineThickness: 0),
66+
new ClockTheme("Chalk", "Segoe UI", "SemiBold",
67+
Color.FromRgb(0xFF, 0xFF, 0xFF), Color.FromRgb(0x00, 0x00, 0x00),
68+
backgroundEnabled: false, backgroundOpacity: 1, backgroundCornerRadius: 1, outlineThickness: 1.5),
69+
};
70+
}
71+
72+
/// <summary>
73+
/// Applies this look to the given settings; the clock updates immediately.
74+
/// </summary>
75+
public void Apply(Settings settings)
76+
{
77+
settings.FontFamily = FontFamily;
78+
settings.FontWeight = FontWeight;
79+
settings.FontStyle = FontStyle;
80+
settings.TextColor = TextColor;
81+
settings.TextOpacity = 1;
82+
settings.OuterColor = OuterColor;
83+
settings.BackgroundEnabled = BackgroundEnabled;
84+
settings.BackgroundOpacity = BackgroundOpacity;
85+
settings.BackgroundCornerRadius = BackgroundCornerRadius;
86+
settings.OutlineThickness = OutlineThickness;
87+
}
88+
89+
/// <summary>
90+
/// The factory look: accent-colored text seeded from the Windows theme, like a fresh install.
91+
/// </summary>
92+
private static ClockTheme CreateSystemTheme()
93+
{
94+
if (!SystemThemeService.TryGetThemeDefaults(out var textColor, out var outerColor))
95+
{
96+
// Match the hardcoded defaults in Settings when the system theme can't be read.
97+
textColor = Color.FromRgb(33, 33, 33);
98+
outerColor = Color.FromRgb(247, 247, 247);
99+
}
100+
101+
return new ClockTheme("System", "Consolas", "Normal", textColor, outerColor,
102+
backgroundEnabled: true, backgroundOpacity: 0.9, backgroundCornerRadius: 1, outlineThickness: 0.2);
103+
}
104+
}

DesktopClock/SettingsWindow.xaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
Style="{StaticResource SidebarButton}"
6161
Tag="{Binding ElementName=WindowSection}"
6262
Click="NavigateToSection" />
63+
<Button Content="T_heme Presets"
64+
Style="{StaticResource SidebarButton}"
65+
Tag="{Binding ElementName=ThemeSection}"
66+
Click="NavigateToSection" />
6367
<Button Content="_Typography"
6468
Style="{StaticResource SidebarButton}"
6569
Tag="{Binding ElementName=TypographySection}"
@@ -244,6 +248,16 @@
244248
</StackPanel>
245249
</GroupBox>
246250

251+
<GroupBox x:Name="ThemeSection"
252+
Header="Theme Presets"
253+
Style="{StaticResource CardGroupBox}">
254+
<StackPanel Style="{StaticResource CardBodyPanel}">
255+
<local:ThemePresetPicker />
256+
<TextBlock Text="Pick a starting look for the clock. Every detail can still be adjusted in the sections below."
257+
Style="{StaticResource DescriptionTextBlock}" />
258+
</StackPanel>
259+
</GroupBox>
260+
247261
<GroupBox x:Name="TypographySection"
248262
Header="Typography"
249263
Style="{StaticResource CardGroupBox}">
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<UserControl x:Class="DesktopClock.ThemePresetPicker"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
4+
<WrapPanel x:Name="PresetsPanel" />
5+
</UserControl>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Windows;
2+
using System.Windows.Controls;
3+
using System.Windows.Media;
4+
using DesktopClock.Properties;
5+
6+
namespace DesktopClock;
7+
8+
/// <summary>
9+
/// A row of one-click clock looks, each chip rendering a true miniature of the
10+
/// clock — same fonts, colors, and shape the real window would use.
11+
/// </summary>
12+
public partial class ThemePresetPicker : UserControl
13+
{
14+
private static readonly FontWeightConverter _fontWeightConverter = new();
15+
private static readonly FontStyleConverter _fontStyleConverter = new();
16+
17+
public ThemePresetPicker()
18+
{
19+
InitializeComponent();
20+
21+
foreach (var theme in ClockTheme.GetBuiltInThemes())
22+
{
23+
PresetsPanel.Children.Add(CreatePresetButton(theme));
24+
}
25+
}
26+
27+
private Button CreatePresetButton(ClockTheme theme)
28+
{
29+
var name = new TextBlock
30+
{
31+
Text = theme.Name,
32+
FontSize = 11.5,
33+
HorizontalAlignment = HorizontalAlignment.Center,
34+
Margin = new Thickness(0, 4, 0, 0),
35+
};
36+
name.SetResourceReference(TextBlock.ForegroundProperty, "TextSecondaryBrush");
37+
38+
var content = new StackPanel();
39+
content.Children.Add(CreateMiniClock(theme));
40+
content.Children.Add(name);
41+
42+
var button = new Button
43+
{
44+
Content = content,
45+
Padding = new Thickness(5),
46+
Margin = new Thickness(0, 0, 8, 8),
47+
ToolTip = "Sets the font, colors, opacity, and corners. Fine-tune in Typography and Appearance.",
48+
};
49+
button.Click += (_, _) => theme.Apply(Settings.Default);
50+
51+
return button;
52+
}
53+
54+
/// <summary>
55+
/// Renders the theme the same way the clock window would, at chip scale.
56+
/// </summary>
57+
private static FrameworkElement CreateMiniClock(ClockTheme theme)
58+
{
59+
// Rendered as geometry, so keep the sample large enough that thin weights
60+
// don't dissolve into anti-aliasing at small stroke widths. The control
61+
// defaults to a 1px black stroke, so clear it unless the theme wants one.
62+
var text = new OutlinedTextBlock
63+
{
64+
Text = "10:08",
65+
FontSize = 21,
66+
FontFamily = new FontFamily(theme.FontFamily),
67+
FontWeight = (FontWeight)_fontWeightConverter.ConvertFromString(theme.FontWeight),
68+
FontStyle = (FontStyle)_fontStyleConverter.ConvertFromString(theme.FontStyle),
69+
Fill = new SolidColorBrush(theme.TextColor),
70+
Stroke = System.Windows.Media.Brushes.Transparent,
71+
StrokeThickness = 0,
72+
HorizontalAlignment = HorizontalAlignment.Center,
73+
VerticalAlignment = VerticalAlignment.Center,
74+
};
75+
76+
var clock = new Border
77+
{
78+
Child = text,
79+
Padding = new Thickness(10, 4, 10, 4),
80+
HorizontalAlignment = HorizontalAlignment.Center,
81+
VerticalAlignment = VerticalAlignment.Center,
82+
};
83+
84+
if (theme.BackgroundEnabled)
85+
{
86+
// Solid background, scaled down like the clock itself would be in a small window.
87+
clock.Background = new SolidColorBrush(theme.OuterColor) { Opacity = theme.BackgroundOpacity };
88+
clock.CornerRadius = new CornerRadius(theme.BackgroundCornerRadius * 0.75);
89+
}
90+
else if (theme.OutlineThickness > 0)
91+
{
92+
text.Stroke = new SolidColorBrush(theme.OuterColor) { Opacity = theme.BackgroundOpacity };
93+
text.StrokeThickness = theme.OutlineThickness;
94+
}
95+
96+
// Neutral backdrop standing in for the desktop behind the clock.
97+
var backdrop = new Border
98+
{
99+
Child = clock,
100+
Width = 140,
101+
Height = 64,
102+
CornerRadius = new CornerRadius(5),
103+
SnapsToDevicePixels = true,
104+
};
105+
backdrop.SetResourceReference(Border.BackgroundProperty, "SubtleBrush");
106+
107+
return backdrop;
108+
}
109+
}

0 commit comments

Comments
 (0)