Skip to content

Commit 8e71ff3

Browse files
committed
Add ability to toggle dark/light theme, support dark mode for plot
Resolves #1
1 parent e63c25b commit 8e71ff3

4 files changed

Lines changed: 107 additions & 22 deletions

File tree

src/ManiaKeyPresses.UI/GlobalConfig.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.ComponentModel;
33
using System.IO;
44
using System.Text.Json;
5+
using Avalonia.Styling;
56

67
namespace ManiaKeyPresses.UI;
78

@@ -19,6 +20,8 @@ public static class GlobalConfig
1920

2021
public static string? OsuClientSecret { get; private set; }
2122

23+
public static ThemeVariant Theme { get; private set; } = null!;
24+
2225
public static void Load()
2326
{
2427
if (!Directory.Exists(ManiaKeyPressesFolder))
@@ -34,38 +37,44 @@ public static void Load()
3437

3538
OsuClientId = config.OsuClientId;
3639
OsuClientSecret = config.OsuClientSecret;
40+
Theme = config.Theme ?? ThemeVariant.Default;
3741
}
3842

3943
public static void UpdateOsuClientId(string? clientId)
4044
{
4145
OsuClientId = clientId;
4246

4347
NotifyPropertyChanged(nameof(OsuClientId));
44-
45-
var config = new Config
46-
{
47-
OsuClientId = clientId,
48-
OsuClientSecret = OsuClientSecret,
49-
};
50-
51-
File.WriteAllText(ConfigFile, JsonSerializer.Serialize(config));
48+
UpdateConfigFile();
5249
}
5350

5451
public static void UpdateOsuClientSecret(string? clientSecret)
5552
{
5653
OsuClientSecret = clientSecret;
5754

5855
NotifyPropertyChanged(nameof(OsuClientSecret));
56+
UpdateConfigFile();
57+
}
5958

59+
public static void UpdateTheme(ThemeVariant theme)
60+
{
61+
Theme = theme;
62+
63+
NotifyPropertyChanged(nameof(Theme));
64+
UpdateConfigFile();
65+
}
66+
67+
private static void UpdateConfigFile()
68+
{
6069
var config = new Config
6170
{
6271
OsuClientId = OsuClientId,
63-
OsuClientSecret = clientSecret,
72+
OsuClientSecret = OsuClientSecret,
73+
Theme = Theme,
6474
};
65-
75+
6676
File.WriteAllText(ConfigFile, JsonSerializer.Serialize(config));
6777
}
68-
6978

7079
public static event PropertyChangedEventHandler? PropertyChanged;
7180

@@ -80,4 +89,6 @@ file class Config
8089
public string? OsuClientId { get; init; }
8190

8291
public string? OsuClientSecret { get; init; }
92+
93+
public ThemeVariant? Theme { get; init; }
8394
}

src/ManiaKeyPresses.UI/MainViewModel.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public MainViewModel()
2222

2323
public bool HasReplay => !string.IsNullOrWhiteSpace(CurrentReplayFileName);
2424

25+
public bool IsDarkMode { get; private set; }
26+
2527
public void UpdateReplay(string? replayFileName)
2628
{
2729
CurrentReplayFileName = replayFileName;
@@ -30,5 +32,12 @@ public void UpdateReplay(string? replayFileName)
3032
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasReplay)));
3133
}
3234

35+
public void UpdateIsDarkMode(bool isDarkMode)
36+
{
37+
IsDarkMode = isDarkMode;
38+
39+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDarkMode)));
40+
}
41+
3342
public event PropertyChangedEventHandler? PropertyChanged;
3443
}

src/ManiaKeyPresses.UI/MainWindow.axaml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@
1212
</StackPanel>
1313

1414
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10" HorizontalAlignment="Right">
15+
<Button Name="ThemeToggleButton"
16+
Background="Transparent"
17+
BorderThickness="0"
18+
Margin="0,0,10,0"
19+
Click="ThemeToggleButton_Click"
20+
ToolTip.Tip="Toggle Light/Dark Mode">
21+
<Grid>
22+
<Path Name="SunIcon"
23+
Data="M12,8A4,4 0 0,0 8,12A4,4 0 0,0 12,16A4,4 0 0,0 16,12A4,4 0 0,0 12,8M12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6A6,6 0 0,1 18,12A6,6 0 0,1 12,18M20,8.69V4H15.31L12,0.69L8.69,4H4V8.69L0.69,12L4,15.31V20H8.69L12,23.31L15.31,20H20V15.31L23.31,12L20,8.69Z"
24+
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
25+
Width="20"
26+
Height="20"
27+
IsVisible="{Binding IsDarkMode}"/>
28+
29+
<Path Name="MoonIcon"
30+
Data="M17.75,4.09L15.22,6.03L16.13,9.09L13.5,7.28L10.87,9.09L11.78,6.03L9.25,4.09L12.44,4L13.5,1L14.56,4L17.75,4.09M21.25,11L19.61,12.25L20.2,14.23L18.5,13.06L16.8,14.23L17.39,12.25L15.75,11L17.81,10.95L18.5,9L19.19,10.95L21.25,11M18.97,15.95C19.8,15.87 20.69,17.05 20.16,17.8C19.84,18.25 19.5,18.67 19.08,19.07C15.17,23 8.84,23 4.94,19.07C1.03,15.17 1.03,8.83 4.94,4.93C5.34,4.53 5.76,4.17 6.21,3.85C6.96,3.32 8.14,4.21 8.06,5.04C7.79,7.9 8.75,10.87 10.95,13.06C13.14,15.26 16.1,16.22 18.97,15.95M17.33,17.97C14.5,17.81 11.7,16.64 9.53,14.5C7.36,12.31 6.2,9.5 6.04,6.68C3.23,9.82 3.34,14.4 6.35,17.41C9.37,20.43 14,20.54 17.33,17.97Z"
31+
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
32+
Width="20"
33+
Height="20"
34+
IsVisible="{Binding !IsDarkMode}"/>
35+
</Grid>
36+
</Button>
37+
1538
<Button Name="SettingsButton" Background="Transparent" BorderThickness="0">
1639
<Button.Flyout>
1740
<Flyout Placement="BottomEdgeAlignedRight">
@@ -47,7 +70,7 @@
4770
</Button.Flyout>
4871

4972
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11.03L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.22,8.95 2.27,9.22 2.46,9.37L4.57,11.03C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.22,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
50-
Fill="White"
73+
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
5174
Width="20"
5275
Height="20"/>
5376
</Button>

src/ManiaKeyPresses.UI/MainWindow.axaml.cs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.IO;
33
using System.Linq;
4+
using Avalonia;
45
using Avalonia.Controls;
56
using Avalonia.Input;
67
using Avalonia.Interactivity;
78
using Avalonia.Platform.Storage;
9+
using Avalonia.Styling;
810
using OxyPlot;
911
using OxyPlot.Avalonia;
1012
using OxyPlot.Axes;
@@ -21,6 +23,12 @@ public MainWindow()
2123
{
2224
InitializeComponent();
2325
DataContext = new MainViewModel();
26+
27+
if (Application.Current is not null)
28+
{
29+
Application.Current.RequestedThemeVariant = GlobalConfig.Theme;
30+
ViewModel.UpdateIsDarkMode(GlobalConfig.Theme == ThemeVariant.Dark);
31+
}
2432

2533
DragDrop.SetAllowDrop(this, true);
2634
AddHandler(DragDrop.DropEvent, Drop);
@@ -70,6 +78,21 @@ private async void LoadReplayButton_Click(object? sender, RoutedEventArgs e)
7078
var replayPath = files.Single().TryGetLocalPath()!;
7179
AnalyseReplay(replayPath);
7280
}
81+
82+
private void ThemeToggleButton_Click(object sender, RoutedEventArgs e)
83+
{
84+
var app = Application.Current;
85+
86+
if (app is null)
87+
return;
88+
89+
var currentTheme = app.ActualThemeVariant;
90+
var newTheme = currentTheme == ThemeVariant.Dark ? ThemeVariant.Light : ThemeVariant.Dark;
91+
app.RequestedThemeVariant = newTheme;
92+
93+
ViewModel.UpdateIsDarkMode(newTheme == ThemeVariant.Dark);
94+
GlobalConfig.UpdateTheme(newTheme);
95+
}
7396

7497
private void Drop(object? sender, DragEventArgs e)
7598
{
@@ -102,18 +125,29 @@ private void AnalyseReplay(string replayPath)
102125
var plotModel = new PlotModel
103126
{
104127
Title = "Key Hold Time Distribution",
105-
Background = OxyColors.White
128+
Background = ViewModel.IsDarkMode ? OxyColors.Black : OxyColors.White,
129+
TextColor = ViewModel.IsDarkMode ? OxyColors.White : OxyColors.Black,
130+
TitleColor = ViewModel.IsDarkMode ? OxyColors.White : OxyColors.Black,
131+
PlotAreaBorderColor = ViewModel.IsDarkMode ? OxyColors.Gray : OxyColors.Black
106132
};
107133

134+
var gridColour = ViewModel.IsDarkMode ? OxyColors.DarkGray : OxyColors.LightGray;
135+
var axisColour = ViewModel.IsDarkMode ? OxyColors.White : OxyColors.Black;
136+
108137
plotModel.Axes.Add(new LinearAxis
109138
{
110139
Position = AxisPosition.Bottom,
111140
Title = "pressing time (ms)",
112141
TitleFontSize = 15,
142+
TitleColor = axisColour,
143+
TextColor = axisColour,
144+
AxislineColor = axisColour,
145+
TicklineColor = axisColour,
113146
Minimum = 0,
114147
Maximum = 160,
115148
MajorGridlineStyle = LineStyle.Solid,
116-
MajorGridlineColor = OxyColors.LightGray,
149+
MajorGridlineColor = gridColour,
150+
MinorGridlineColor = gridColour,
117151
IsZoomEnabled = false,
118152
IsPanEnabled = false,
119153
});
@@ -125,9 +159,14 @@ private void AnalyseReplay(string replayPath)
125159
Position = AxisPosition.Left,
126160
Title = "count",
127161
TitleFontSize = 15,
162+
TitleColor = axisColour,
163+
TextColor = axisColour,
164+
AxislineColor = axisColour,
165+
TicklineColor = axisColour,
128166
Maximum = maxHoldTimeCount + maxHoldTimeCount * 0.1,
129167
MajorGridlineStyle = LineStyle.Solid,
130-
MajorGridlineColor = OxyColors.LightGray,
168+
MajorGridlineColor = gridColour,
169+
MinorGridlineColor = gridColour,
131170
IsZoomEnabled = false,
132171
IsPanEnabled = false,
133172
});
@@ -138,15 +177,18 @@ private void AnalyseReplay(string replayPath)
138177
{
139178
LegendPosition = LegendPosition.RightTop,
140179
LegendPlacement = LegendPlacement.Inside,
141-
LegendFontSize = 10
180+
LegendFontSize = 10,
181+
LegendTextColor = ViewModel.IsDarkMode ? OxyColors.White : OxyColors.Black,
182+
LegendBackground = OxyColors.Transparent,
183+
LegendBorder = ViewModel.IsDarkMode ? OxyColors.Gray : OxyColors.LightGray
142184
});
143185

144186
for (var i = 0; i < analysis.HoldTimes.Length; i++)
145187
{
146188
var lineSeries = new LineSeries
147189
{
148190
Title = $"key {i + 1}",
149-
Color = GetRainbowColor(i, analysis.HoldTimes.Length),
191+
Color = GetRainbowColor(i, analysis.HoldTimes.Length, ViewModel.IsDarkMode),
150192
StrokeThickness = 2,
151193
TrackerFormatString = "{0}\nHold Time: {2:0} ms\nCount: {4:0}",
152194
};
@@ -163,13 +205,13 @@ private void AnalyseReplay(string replayPath)
163205
PlotView.Model = plotModel;
164206
}
165207

166-
private static OxyColor GetRainbowColor(int index, int total)
208+
private static OxyColor GetRainbowColor(int index, int total, bool isDarkTheme)
167209
{
168-
const double saturation = 0.9;
169-
const double value = 0.9;
210+
var saturation = isDarkTheme ? 0.8 : 0.9;
211+
var value = isDarkTheme ? 0.9 : 0.8;
170212

171-
const double c = value * saturation;
172-
const double m = value - c;
213+
var c = value * saturation;
214+
var m = value - c;
173215

174216
// Create a hue using the index & total
175217
// Then do standard HSV->RGB conversion

0 commit comments

Comments
 (0)