Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a 3-way theme switcher instead of a 2-way switcher #444

Merged
merged 2 commits into from
May 13, 2024
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
18 changes: 16 additions & 2 deletions YoutubeDownloader/App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,34 @@
xmlns:materialAssists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
xmlns:materialControls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles">
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"
ActualThemeVariantChanged="Application_OnActualThemeVariantChanged">
<Application.DataTemplates>
<framework:ViewManager />
</Application.DataTemplates>

<Application.Styles>
<materialStyles:MaterialTheme />
<!-- Hack: this brush is missing on initialization for some reason -->
<Style>
<Style.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="MaterialPaperBrush" Color="#FF303030" />
</ResourceDictionary>
</Style.Resources>
</Style>

<materialStyles:MaterialThemeBase />
<materialIcons:MaterialIconStyles />
<dialogHostAvalonia:DialogHostStyles />

<!-- Combo box -->
<Style Selector="ComboBox">
<Setter Property="FontSize" Value="14" />

<Style Selector="^ /template/ Panel#PART_RootPanel">
<Setter Property="Height" Value="22" />
</Style>

<Style Selector="^ /template/ ToggleButton">
<Style Selector="^:checked, ^:unchecked">
<Setter Property="Margin" Value="0" />
Expand Down
132 changes: 80 additions & 52 deletions YoutubeDownloader/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
using Microsoft.Extensions.DependencyInjection;
using YoutubeDownloader.Framework;
using YoutubeDownloader.Services;
using YoutubeDownloader.Utils;
using YoutubeDownloader.Utils.Extensions;
using YoutubeDownloader.ViewModels;
using YoutubeDownloader.ViewModels.Components;
using YoutubeDownloader.ViewModels.Dialogs;
using YoutubeDownloader.Views;

namespace YoutubeDownloader;

public partial class App : Application, IDisposable
public class App : Application, IDisposable
{
private readonly ServiceProvider _services;
private readonly SettingsService _settingsService;
private readonly MainViewModel _mainViewModel;

private readonly DisposableCollector _eventRoot = new();

public App()
{
var services = new ServiceCollection();
Expand All @@ -47,83 +52,106 @@ public App()
services.AddTransient<SettingsViewModel>();

_services = services.BuildServiceProvider(true);
_settingsService = _services.GetRequiredService<SettingsService>();
_mainViewModel = _services.GetRequiredService<ViewModelManager>().CreateMainViewModel();

// Re-initialize the theme when the user changes it
_eventRoot.Add(
_settingsService.WatchProperty(
o => o.Theme,
() =>
{
RequestedThemeVariant = _settingsService.Theme switch
{
ThemeVariant.System => Avalonia.Styling.ThemeVariant.Default,
ThemeVariant.Light => Avalonia.Styling.ThemeVariant.Light,
ThemeVariant.Dark => Avalonia.Styling.ThemeVariant.Dark,
_
=> throw new InvalidOperationException(
$"Unknown theme '{_settingsService.Theme}'."
)
};

InitializeTheme();
},
false
)
);
}

public override void Initialize()
{
base.Initialize();

// Increase maximum concurrent connections
ServicePointManager.DefaultConnectionLimit = 20;

AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainView { DataContext = _mainViewModel };

base.OnFrameworkInitializationCompleted();

// Set custom theme colors
SetDefaultTheme();
}

public override void RegisterServices()
{
base.RegisterServices();

AvaloniaWebViewBuilder.Initialize(config => config.IsInPrivateModeEnabled = true);
}

public void Dispose() => _services.Dispose();
}

public partial class App
{
public static void SetLightTheme()
private void InitializeTheme()
{
if (Current is null)
return;

Current.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
Theme.Light,
Color.Parse("#343838"),
Color.Parse("#F9A825")
);

Current.Resources["SuccessBrush"] = new SolidColorBrush(Colors.DarkGreen);
Current.Resources["CanceledBrush"] = new SolidColorBrush(Colors.DarkOrange);
Current.Resources["FailedBrush"] = new SolidColorBrush(Colors.DarkRed);
var actualTheme = RequestedThemeVariant?.Key switch
{
"Light" => PlatformThemeVariant.Light,
"Dark" => PlatformThemeVariant.Dark,
_ => PlatformSettings?.GetColorValues().ThemeVariant
};

if (actualTheme == PlatformThemeVariant.Light)
{
this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
Theme.Light,
Color.Parse("#343838"),
Color.Parse("#F9A825")
);

Resources["SuccessBrush"] = new SolidColorBrush(Colors.DarkGreen);
Resources["CanceledBrush"] = new SolidColorBrush(Colors.DarkOrange);
Resources["FailedBrush"] = new SolidColorBrush(Colors.DarkRed);
}
else
{
this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
Theme.Dark,
Color.Parse("#E8E8E8"),
Color.Parse("#F9A825")
);

Resources["SuccessBrush"] = new SolidColorBrush(Colors.LightGreen);
Resources["CanceledBrush"] = new SolidColorBrush(Colors.Orange);
Resources["FailedBrush"] = new SolidColorBrush(Colors.OrangeRed);
}
}

public static void SetDarkTheme()
public override void OnFrameworkInitializationCompleted()
{
if (Current is null)
return;
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainView { DataContext = _mainViewModel };

Current.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
Theme.Dark,
Color.Parse("#E8E8E8"),
Color.Parse("#F9A825")
);
base.OnFrameworkInitializationCompleted();

Current.Resources["SuccessBrush"] = new SolidColorBrush(Colors.LightGreen);
Current.Resources["CanceledBrush"] = new SolidColorBrush(Colors.Orange);
Current.Resources["FailedBrush"] = new SolidColorBrush(Colors.OrangeRed);
}
// Set up custom theme colors
InitializeTheme();

public static void SetDefaultTheme()
{
if (Current is null)
return;
// Load settings
_settingsService.Load();
}

var isDarkModeEnabledByDefault =
Current.PlatformSettings?.GetColorValues().ThemeVariant == PlatformThemeVariant.Dark;
private void Application_OnActualThemeVariantChanged(object? sender, EventArgs args) =>
// Re-initialize the theme when the system theme changes
InitializeTheme();

if (isDarkModeEnabledByDefault)
SetDarkTheme();
else
SetLightTheme();
public void Dispose()
{
_eventRoot.Dispose();
_services.Dispose();
}
}
8 changes: 8 additions & 0 deletions YoutubeDownloader/Framework/ThemeVariant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace YoutubeDownloader.Framework;

public enum ThemeVariant
{
System,
Light,
Dark
}
18 changes: 3 additions & 15 deletions YoutubeDownloader/Services/SettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using Avalonia;
using Avalonia.Platform;
using Cogwheel;
using CommunityToolkit.Mvvm.ComponentModel;
using YoutubeDownloader.Core.Downloading;
using YoutubeDownloader.Framework;
using Container = YoutubeExplode.Videos.Streams.Container;

namespace YoutubeDownloader.Services;
Expand All @@ -21,10 +20,10 @@ public partial class SettingsService()
private bool _isUkraineSupportMessageEnabled = true;

[ObservableProperty]
private bool _isAutoUpdateEnabled = true;
private ThemeVariant _theme;

[ObservableProperty]
private bool _isDarkModeEnabled;
private bool _isAutoUpdateEnabled = true;

[ObservableProperty]
private bool _isAuthPersisted = true;
Expand Down Expand Up @@ -54,17 +53,6 @@ public partial class SettingsService()
[ObservableProperty]
private VideoQualityPreference _lastVideoQualityPreference = VideoQualityPreference.Highest;

public override void Reset()
{
base.Reset();

// Reset the dark mode setting separately because its default value is evaluated dynamically
// and cannot be set by the field initializer.
IsDarkModeEnabled =
Application.Current?.PlatformSettings?.GetColorValues().ThemeVariant
== PlatformThemeVariant.Dark;
}

public override void Save()
{
// Clear the cookies if they are not supposed to be persisted
Expand Down
3 changes: 1 addition & 2 deletions YoutubeDownloader/Utils/Extensions/AvaloniaExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.VisualTree;

Expand Down
15 changes: 9 additions & 6 deletions YoutubeDownloader/ViewModels/Dialogs/SettingsViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using YoutubeDownloader.Framework;
using YoutubeDownloader.Services;
using YoutubeDownloader.Utils;
Expand All @@ -19,16 +20,18 @@ public SettingsViewModel(SettingsService settingsService)
_eventRoot.Add(_settingsService.WatchAllProperties(OnAllPropertiesChanged));
}

public bool IsAutoUpdateEnabled
public IReadOnlyList<ThemeVariant> AvailableThemes { get; } = Enum.GetValues<ThemeVariant>();

public ThemeVariant Theme
{
get => _settingsService.IsAutoUpdateEnabled;
set => _settingsService.IsAutoUpdateEnabled = value;
get => _settingsService.Theme;
set => _settingsService.Theme = value;
}

public bool IsDarkModeEnabled
public bool IsAutoUpdateEnabled
{
get => _settingsService.IsDarkModeEnabled;
set => _settingsService.IsDarkModeEnabled = value;
get => _settingsService.IsAutoUpdateEnabled;
set => _settingsService.IsAutoUpdateEnabled = value;
}

public bool IsAuthPersisted
Expand Down
12 changes: 0 additions & 12 deletions YoutubeDownloader/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,6 @@ private async Task CheckForUpdatesAsync()
[RelayCommand]
private async Task InitializeAsync()
{
// Reset settings (needed to resolve the default dark mode setting)
settingsService.Reset();

// Load settings
settingsService.Load();

// Set the correct theme
if (settingsService.IsDarkModeEnabled)
App.SetDarkTheme();
else
App.SetLightTheme();

await ShowUkraineSupportMessageAsync();
await CheckForUpdatesAsync();
}
Expand Down
26 changes: 13 additions & 13 deletions YoutubeDownloader/Views/Dialogs/SettingsView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@
BorderThickness="0,1">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- Theme -->
<DockPanel
Margin="16,8"
LastChildFill="False"
ToolTip.Tip="Preferred user interface theme">
<TextBlock DockPanel.Dock="Left" Text="Theme" />
<ComboBox
Width="150"
DockPanel.Dock="Right"
ItemsSource="{Binding AvailableThemes}"
SelectedItem="{Binding Theme}" />
</DockPanel>

<!-- Auto-updates -->
<DockPanel
Margin="16,8"
Expand All @@ -44,19 +57,6 @@
<ToggleSwitch DockPanel.Dock="Right" IsChecked="{Binding IsAutoUpdateEnabled}" />
</DockPanel>

<!-- Dark mode -->
<DockPanel
Margin="16,8"
LastChildFill="False"
ToolTip.Tip="Use darker colors in the UI">
<TextBlock DockPanel.Dock="Left" Text="Dark mode" />
<ToggleSwitch
x:Name="DarkModeToggleSwitch"
DockPanel.Dock="Right"
IsChecked="{Binding IsDarkModeEnabled}"
IsCheckedChanged="DarkModeToggleSwitch_OnIsCheckedChanged" />
</DockPanel>

<!-- Persist authentication -->
<DockPanel
IsVisible="{OnPlatform False,
Expand Down
17 changes: 0 additions & 17 deletions YoutubeDownloader/Views/Dialogs/SettingsView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Avalonia.Interactivity;
using YoutubeDownloader.Framework;
using YoutubeDownloader.ViewModels.Dialogs;

Expand All @@ -7,20 +6,4 @@ namespace YoutubeDownloader.Views.Dialogs;
public partial class SettingsView : UserControl<SettingsViewModel>
{
public SettingsView() => InitializeComponent();

private void DarkModeToggleSwitch_OnIsCheckedChanged(object? sender, RoutedEventArgs args)
{
if (DarkModeToggleSwitch.IsChecked is true)
{
App.SetDarkTheme();
}
else if (DarkModeToggleSwitch.IsChecked is false)
{
App.SetLightTheme();
}
else
{
App.SetDefaultTheme();
}
}
}
Loading