diff --git a/Marechai.App/App.xaml b/Marechai.App/App.xaml
index 77a46865..115bc560 100644
--- a/Marechai.App/App.xaml
+++ b/Marechai.App/App.xaml
@@ -8,6 +8,7 @@
+
diff --git a/Marechai.App/App.xaml.cs b/Marechai.App/App.xaml.cs
index 70d711ea..8a6f71c5 100644
--- a/Marechai.App/App.xaml.cs
+++ b/Marechai.App/App.xaml.cs
@@ -22,6 +22,7 @@ using NewsViewModel = Marechai.App.Presentation.ViewModels.NewsViewModel;
using PhotoDetailViewModel = Marechai.App.Presentation.ViewModels.PhotoDetailViewModel;
using ProcessorDetailViewModel = Marechai.App.Presentation.ViewModels.ProcessorDetailViewModel;
using ProcessorsListViewModel = Marechai.App.Presentation.ViewModels.ProcessorsListViewModel;
+using SettingsViewModel = Marechai.App.Presentation.ViewModels.SettingsViewModel;
using SoundSynthDetailViewModel = Marechai.App.Presentation.ViewModels.SoundSynthDetailViewModel;
using SoundSynthsListViewModel = Marechai.App.Presentation.ViewModels.SoundSynthsListViewModel;
@@ -114,6 +115,10 @@ public partial class App : Application
.ConfigureServices((context, services) =>
{
// Register application services
+ services
+ .AddSingleton();
+
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -149,6 +154,7 @@ public partial class App : Application
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
})
.UseNavigation(RegisterRoutes));
@@ -181,6 +187,7 @@ public partial class App : Application
new ViewMap(),
new ViewMap(),
new ViewMap(),
+ new ViewMap(),
new DataViewMap());
routes.Register(new RouteMap("",
@@ -197,7 +204,8 @@ public partial class App : Application
true),
new RouteMap("computers",
views.FindByViewModel(),
- Nested:
+ Nested
+ :
[
new RouteMap("list-computers",
views.FindByViewModel<
@@ -208,7 +216,8 @@ public partial class App : Application
]),
new RouteMap("consoles",
views.FindByViewModel(),
- Nested:
+ Nested
+ :
[
new RouteMap("list-consoles",
views.FindByViewModel<
@@ -216,7 +225,8 @@ public partial class App : Application
]),
new RouteMap("companies",
views.FindByViewModel(),
- Nested:
+ Nested
+ :
[
new RouteMap("company-details",
views.FindByViewModel<
@@ -239,7 +249,8 @@ public partial class App : Application
ProcessorDetailViewModel>())
]),
new RouteMap("sound-synths",
- views.FindByViewModel(),
+ views.FindByViewModel<
+ SoundSynthsListViewModel>(),
Nested:
[
new RouteMap("sound-synth-details",
@@ -249,6 +260,8 @@ public partial class App : Application
views.FindByViewModel<
MachineViewViewModel>())
]),
+ new RouteMap("settings",
+ views.FindByViewModel()),
new RouteMap("Second",
views.FindByViewModel())
])
diff --git a/Marechai.App/Marechai.App.csproj b/Marechai.App/Marechai.App.csproj
index 00335748..5788ea25 100644
--- a/Marechai.App/Marechai.App.csproj
+++ b/Marechai.App/Marechai.App.csproj
@@ -35,6 +35,7 @@
Localization;
Navigation;
ThemeService;
+ Storage;
SkiaRenderer;
Svg;
diff --git a/Marechai.App/Presentation/ViewModels/MainViewModel.cs b/Marechai.App/Presentation/ViewModels/MainViewModel.cs
index 4ecdb5d5..bdc3912d 100644
--- a/Marechai.App/Presentation/ViewModels/MainViewModel.cs
+++ b/Marechai.App/Presentation/ViewModels/MainViewModel.cs
@@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
+using Marechai.App.Services;
using Uno.Extensions.Navigation;
+using Uno.Extensions.Toolkit;
namespace Marechai.App.Presentation.ViewModels;
@@ -25,7 +27,7 @@ public partial class MainViewModel : ObservableObject
private bool _sidebarContentVisible = true;
public MainViewModel(IStringLocalizer localizer, IOptions appInfo, INavigator navigator,
- NewsViewModel newsViewModel)
+ NewsViewModel newsViewModel, IColorThemeService colorThemeService, IThemeService themeService)
{
_navigator = navigator;
_localizer = localizer;
@@ -36,6 +38,9 @@ public partial class MainViewModel : ObservableObject
GoToSecond = new AsyncRelayCommand(GoToSecondView);
+ // Initialize color theme service with theme service
+ _ = InitializeThemeServicesAsync(colorThemeService, themeService);
+
// Initialize localized strings
InitializeLocalizedStrings();
@@ -81,6 +86,22 @@ public partial class MainViewModel : ObservableObject
public ICommand LoginLogoutCommand { get; }
public ICommand ToggleSidebarCommand { get; }
+ private async Task InitializeThemeServicesAsync(IColorThemeService colorThemeService, IThemeService themeService)
+ {
+ try
+ {
+ // Wait for theme service to be ready
+ await themeService.InitializeAsync();
+
+ // Set the theme service reference and reapply the saved theme
+ colorThemeService.SetThemeService(themeService);
+ }
+ catch
+ {
+ // Silently fail - theme will work but without refresh on startup
+ }
+ }
+
private void InitializeLocalizedStrings()
{
LocalizedStrings = new Dictionary
diff --git a/Marechai.App/Presentation/ViewModels/SettingsViewModel.cs b/Marechai.App/Presentation/ViewModels/SettingsViewModel.cs
new file mode 100644
index 00000000..f5303109
--- /dev/null
+++ b/Marechai.App/Presentation/ViewModels/SettingsViewModel.cs
@@ -0,0 +1,154 @@
+using System.Collections.Generic;
+using System.Linq;
+using Uno.Extensions.Toolkit;
+using Marechai.App.Services;
+
+namespace Marechai.App.Presentation.ViewModels;
+
+public partial class SettingsViewModel : ObservableObject
+{
+ private readonly IStringLocalizer _localizer;
+ private readonly IThemeService _themeService;
+ private readonly IColorThemeService _colorThemeService;
+
+ [ObservableProperty]
+ private List _availableThemes = new();
+
+ [ObservableProperty]
+ private ThemeOption _selectedTheme;
+
+ [ObservableProperty]
+ private List _availableColorThemes = new();
+
+ [ObservableProperty]
+ private ColorThemeOption _selectedColorTheme;
+
+ public SettingsViewModel(IStringLocalizer localizer, IThemeService themeService, IColorThemeService colorThemeService)
+ {
+ _localizer = localizer;
+ _themeService = themeService;
+ _colorThemeService = colorThemeService;
+ Title = _localizer["Settings"];
+
+ // Initialize immediately to ensure UI is populated
+ InitializeOptions();
+
+ // Wait for theme service to initialize
+ _ = InitializeThemeServiceAsync();
+ }
+
+ private async System.Threading.Tasks.Task InitializeThemeServiceAsync()
+ {
+ try
+ {
+ await _themeService.InitializeAsync();
+
+ // Ensure the color theme service has a reference to the theme service
+ _colorThemeService.SetThemeService(_themeService);
+ }
+ catch
+ {
+ // Theme service might already be initialized
+ }
+ }
+
+ public string Title { get; }
+
+ private void InitializeOptions()
+ {
+ // Initialize Light/Dark/System Themes
+ AvailableThemes = new List
+ {
+ new() { Theme = AppTheme.Light, DisplayName = _localizer["LightTheme"] },
+ new() { Theme = AppTheme.Dark, DisplayName = _localizer["DarkTheme"] },
+ new() { Theme = AppTheme.System, DisplayName = _localizer["SystemTheme"] }
+ };
+
+ // Initialize Color Themes
+ AvailableColorThemes = new List
+ {
+ new() { ThemeName = "Default", DisplayName = _localizer["DefaultColorTheme"] },
+ new() { ThemeName = "Windows311", DisplayName = _localizer["Windows311Theme"] }
+ };
+
+ // Try to load saved preferences
+ LoadSavedPreferences();
+ }
+
+ private async void LoadSavedPreferences()
+ {
+ try
+ {
+ // Load current theme from ThemeService
+ var currentTheme = _themeService.Theme;
+ SelectedTheme = AvailableThemes.FirstOrDefault(t => t.Theme == currentTheme)
+ ?? AvailableThemes.FirstOrDefault(t => t.Theme == AppTheme.System)
+ ?? AvailableThemes.First();
+
+ // Load current color theme
+ var currentColorTheme = _colorThemeService.CurrentColorTheme;
+ SelectedColorTheme = AvailableColorThemes.FirstOrDefault(t => t.ThemeName == currentColorTheme)
+ ?? AvailableColorThemes.First();
+ }
+ catch
+ {
+ // If loading fails, use defaults
+ SelectedTheme = AvailableThemes.FirstOrDefault(t => t.Theme == AppTheme.System)
+ ?? AvailableThemes.First();
+ SelectedColorTheme = AvailableColorThemes.First();
+ }
+ }
+
+ partial void OnSelectedThemeChanged(ThemeOption value)
+ {
+ if (value != null)
+ {
+ ApplyTheme(value);
+ }
+ }
+
+ partial void OnSelectedColorThemeChanged(ColorThemeOption value)
+ {
+ if (value != null)
+ {
+ ApplyColorTheme(value);
+ }
+ }
+
+ private async void ApplyTheme(ThemeOption theme)
+ {
+ try
+ {
+ // Apply theme immediately using ThemeService
+ await _themeService.SetThemeAsync(theme.Theme);
+ }
+ catch
+ {
+ // Silently fail
+ }
+ }
+
+ private void ApplyColorTheme(ColorThemeOption colorTheme)
+ {
+ try
+ {
+ _colorThemeService.ApplyColorTheme(colorTheme.ThemeName);
+ }
+ catch
+ {
+ // Silently fail
+ }
+ }
+}
+
+public class ThemeOption
+{
+ public AppTheme Theme { get; set; }
+ public string DisplayName { get; set; } = string.Empty;
+}
+
+public class ColorThemeOption
+{
+ public string ThemeName { get; set; } = string.Empty;
+ public string DisplayName { get; set; } = string.Empty;
+}
diff --git a/Marechai.App/Presentation/Views/SettingsPage.xaml b/Marechai.App/Presentation/Views/SettingsPage.xaml
new file mode 100644
index 00000000..190bf6bb
--- /dev/null
+++ b/Marechai.App/Presentation/Views/SettingsPage.xaml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Marechai.App/Presentation/Views/SettingsPage.xaml.cs b/Marechai.App/Presentation/Views/SettingsPage.xaml.cs
new file mode 100644
index 00000000..fd90416e
--- /dev/null
+++ b/Marechai.App/Presentation/Views/SettingsPage.xaml.cs
@@ -0,0 +1,14 @@
+using Marechai.App.Presentation.ViewModels;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Marechai.App.Presentation.Views;
+
+public sealed partial class SettingsPage : Page
+{
+ public SettingsPage()
+ {
+ InitializeComponent();
+ }
+
+ public SettingsViewModel? ViewModel => DataContext as SettingsViewModel;
+}
\ No newline at end of file
diff --git a/Marechai.App/Services/ColorThemeService.cs b/Marechai.App/Services/ColorThemeService.cs
new file mode 100644
index 00000000..d7a9a5b6
--- /dev/null
+++ b/Marechai.App/Services/ColorThemeService.cs
@@ -0,0 +1,148 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Storage;
+using Microsoft.UI.Xaml;
+using Uno.Extensions.Toolkit;
+
+namespace Marechai.App.Services;
+
+public interface IColorThemeService
+{
+ string CurrentColorTheme { get; }
+ IReadOnlyList AvailableColorThemes { get; }
+ void ApplyColorTheme(string themeName);
+ void SetThemeService(IThemeService themeService);
+ void ReapplyCurrentTheme();
+}
+
+public class ColorThemeService : IColorThemeService
+{
+ private const string COLOR_THEME_KEY = "ColorTheme";
+ private const string DEFAULT_THEME = "Default";
+ private IThemeService _themeService;
+
+ public ColorThemeService()
+ {
+ LoadSavedTheme();
+ }
+
+ public string CurrentColorTheme { get; private set; } = DEFAULT_THEME;
+
+ public IReadOnlyList AvailableColorThemes => new List
+ {
+ DEFAULT_THEME,
+ "Windows311"
+ };
+
+ public void SetThemeService(IThemeService themeService)
+ {
+ _themeService = themeService;
+
+ // Reapply the current theme now that we have the theme service
+ if(CurrentColorTheme != DEFAULT_THEME) ReapplyCurrentTheme();
+ }
+
+ public void ReapplyCurrentTheme()
+ {
+ // Force refresh of the current theme
+ ApplyColorTheme(CurrentColorTheme);
+ }
+
+ public void ApplyColorTheme(string themeName)
+ {
+ if(!AvailableColorThemes.Contains(themeName)) return;
+
+ CurrentColorTheme = themeName;
+
+ // Save preference
+ try
+ {
+ ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
+ localSettings.Values[COLOR_THEME_KEY] = themeName;
+ }
+ catch
+ {
+ // Silently fail
+ }
+
+ // Apply the theme by rebuilding merged dictionaries
+ Application app = Application.Current;
+
+ if(app?.Resources == null) return;
+
+ // Store the existing merged dictionaries (except color overrides)
+ var existingDictionaries = app.Resources.MergedDictionaries
+ .Where(d => d.Source?.OriginalString?.Contains("ColorPaletteOverride") != true)
+ .ToList();
+
+ // Clear all merged dictionaries
+ app.Resources.MergedDictionaries.Clear();
+
+ // Re-add the existing dictionaries
+ foreach(ResourceDictionary dict in existingDictionaries) app.Resources.MergedDictionaries.Add(dict);
+
+ // Add the new color theme if not default
+ if(themeName != DEFAULT_THEME)
+ {
+ var newDictionary = new ResourceDictionary
+ {
+ Source = new Uri("ms-appx:///Styles/ColorPaletteOverride.xaml")
+ };
+
+ app.Resources.MergedDictionaries.Add(newDictionary);
+ }
+
+ // Force UI refresh by toggling the theme temporarily
+ ForceThemeRefresh();
+ }
+
+ private void LoadSavedTheme()
+ {
+ try
+ {
+ ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
+
+ if(localSettings.Values.ContainsKey(COLOR_THEME_KEY))
+ {
+ var savedTheme = localSettings.Values[COLOR_THEME_KEY] as string;
+
+ if(!string.IsNullOrEmpty(savedTheme) && AvailableColorThemes.Contains(savedTheme))
+ {
+ CurrentColorTheme = savedTheme;
+ ApplyColorTheme(CurrentColorTheme);
+ }
+ }
+ }
+ catch
+ {
+ // If loading fails, use default theme
+ }
+ }
+
+ private async void ForceThemeRefresh()
+ {
+ if(_themeService == null) return;
+
+ try
+ {
+ // Get current theme
+ AppTheme currentTheme = _themeService.Theme;
+
+ // Toggle to opposite theme briefly
+ AppTheme tempTheme = currentTheme == AppTheme.Light ? AppTheme.Dark : AppTheme.Light;
+ await _themeService.SetThemeAsync(tempTheme);
+
+ // Small delay to ensure the change is applied
+ await Task.Delay(50);
+
+ // Switch back to original theme
+ await _themeService.SetThemeAsync(currentTheme);
+ }
+ catch
+ {
+ // Silently fail
+ }
+ }
+}
\ No newline at end of file
diff --git a/Marechai.App/Strings/en/Resources.resw b/Marechai.App/Strings/en/Resources.resw
index 16dff704..ef94999f 100644
--- a/Marechai.App/Strings/en/Resources.resw
+++ b/Marechai.App/Strings/en/Resources.resw
@@ -166,4 +166,50 @@
Company was renamed on an unknown date to an unknown name.
+
+
+ Appearance
+
+
+ Select your preferred theme
+
+
+ Theme
+
+
+ Theme changes will be applied the next time you start the application.
+
+
+ About
+
+
+ Version 1.0
+
+
+ Light
+
+
+ Dark
+
+
+ System
+
+
+ Default
+
+
+ Windows 3.11
+
+
+ Brightness
+
+
+ Color Theme
+
+
+ Design System
+
+
+ Design system changes will be applied the next time you start the application. Color theme changes are applied immediately.
+
diff --git a/Marechai.App/Strings/es/Resources.resw b/Marechai.App/Strings/es/Resources.resw
index 72018021..f6c89c59 100644
--- a/Marechai.App/Strings/es/Resources.resw
+++ b/Marechai.App/Strings/es/Resources.resw
@@ -166,4 +166,50 @@
La empresa fue renombrada en una fecha desconocida a un nombre desconocido.
+
+
+ Apariencia
+
+
+ Seleccione su tema preferido
+
+
+ Tema
+
+
+ Los cambios de tema se aplicarán la próxima vez que inicie la aplicación.
+
+
+ Acerca de
+
+
+ Versión 1.0
+
+
+ Claro
+
+
+ Oscuro
+
+
+ Sistema
+
+
+ Predeterminado
+
+
+ Windows 3.11
+
+
+ Brillo
+
+
+ Tema de Color
+
+
+ Sistema de Diseño
+
+
+ Los cambios del sistema de diseño se aplicarán la próxima vez que inicie la aplicación. Los cambios de tema de color se aplican inmediatamente.
+
diff --git a/Marechai.App/Strings/fr/Resources.resw b/Marechai.App/Strings/fr/Resources.resw
index d5298761..781dabb3 100644
--- a/Marechai.App/Strings/fr/Resources.resw
+++ b/Marechai.App/Strings/fr/Resources.resw
@@ -166,4 +166,50 @@
L'entreprise a été renommée à une date inconnue en un nom inconnu.
+
+
+ Apparence
+
+
+ Sélectionnez votre thème préféré
+
+
+ Thème
+
+
+ Les modifications du thème seront appliquées au prochain démarrage de l'application.
+
+
+ À propos
+
+
+ Version 1.0
+
+
+ Clair
+
+
+ Sombre
+
+
+ Système
+
+
+ Par défaut
+
+
+ Windows 3.11
+
+
+ Luminosité
+
+
+ Thème de Couleur
+
+
+ Système de Conception
+
+
+ Les modifications du système de conception seront appliquées au prochain démarrage de l'application. Les modifications du thème de couleur sont appliquées immédiatement.
+
diff --git a/Marechai.App/Strings/pt-BR/Resources.resw b/Marechai.App/Strings/pt-BR/Resources.resw
index f2f52baa..422db224 100644
--- a/Marechai.App/Strings/pt-BR/Resources.resw
+++ b/Marechai.App/Strings/pt-BR/Resources.resw
@@ -166,4 +166,50 @@
A empresa foi renomeada em uma data desconhecida para um nome desconhecido.
+
+
+ Aparência
+
+
+ Selecione o seu tema preferido
+
+
+ Tema
+
+
+ As alterações de tema serão aplicadas na próxima vez que você iniciar o aplicativo.
+
+
+ Sobre
+
+
+ Versão 1.0
+
+
+ Claro
+
+
+ Escuro
+
+
+ Sistema
+
+
+ Padrão
+
+
+ Windows 3.11
+
+
+ Brilho
+
+
+ Tema de Cor
+
+
+ Sistema de Design
+
+
+ As alterações do sistema de design serão aplicadas na próxima vez que você iniciar o aplicativo. As alterações de tema de cor são aplicadas imediatamente.
+
diff --git a/Marechai.App/Styles/ColorPaletteOverride.xaml b/Marechai.App/Styles/ColorPaletteOverride.xaml
new file mode 100644
index 00000000..c1e92ec7
--- /dev/null
+++ b/Marechai.App/Styles/ColorPaletteOverride.xaml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+ #008080
+ #00A0A0
+ #00C0C0
+ #00E0E0
+ #006060
+ #004040
+ #002020
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+