From ec7917625ccca96a3ec92361364cbe90031712a2 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Sun, 28 Dec 2025 01:46:16 +0100 Subject: [PATCH] UI overhaul --- src/DesktopMagic/App.xaml | 17 +- .../BuiltInPlugins/WeatherPlugin.cs | 2 +- .../DataContexts/MainWindowDataContext.cs | 9 + .../DataContexts/PluginEntryDataContext.cs | 10 +- src/DesktopMagic/DesktopMagic.csproj | 2 + .../Helpers/SettingElementGenerator.cs | 65 +- .../Helpers/UnderscoreEscapingConverter.cs | 23 + src/DesktopMagic/MainWindow.xaml | 226 ++---- src/DesktopMagic/MainWindow.xaml.cs | 758 +++--------------- src/DesktopMagic/Manager.cs | 365 +++++++++ src/DesktopMagic/Pages/MainPage.xaml | 92 +++ src/DesktopMagic/Pages/MainPage.xaml.cs | 261 ++++++ src/DesktopMagic/Pages/ThemePage.xaml | 47 ++ src/DesktopMagic/Pages/ThemePage.xaml.cs | 103 +++ src/DesktopMagic/Plugins/PluginEntry.xaml | 88 +- src/DesktopMagic/Plugins/PluginManager.xaml | 134 ++-- .../Plugins/PluginManager.xaml.cs | 115 +-- src/DesktopMagic/Plugins/PluginWindow.xaml | 27 +- src/DesktopMagic/Plugins/PluginWindow.xaml.cs | 2 + src/DesktopMagic/Plugins/WebPluginWindow.xaml | 24 +- .../Plugins/WebPluginWindow.xaml.cs | 2 + .../Resources/Strings/StringResources.de.xaml | 12 +- .../Resources/Strings/StringResources.en.xaml | 12 +- .../Styles/ToggleSwitchContentLeftStyle.xaml | 131 +++ .../Settings/DesktopMagicSettings.cs | 2 +- src/DesktopMagic/Settings/Layout.cs | 2 +- src/DesktopMagic/Settings/PluginSettings.cs | 2 +- 27 files changed, 1537 insertions(+), 996 deletions(-) create mode 100644 src/DesktopMagic/Helpers/UnderscoreEscapingConverter.cs create mode 100644 src/DesktopMagic/Manager.cs create mode 100644 src/DesktopMagic/Pages/MainPage.xaml create mode 100644 src/DesktopMagic/Pages/MainPage.xaml.cs create mode 100644 src/DesktopMagic/Pages/ThemePage.xaml create mode 100644 src/DesktopMagic/Pages/ThemePage.xaml.cs create mode 100644 src/DesktopMagic/Resources/Styles/ToggleSwitchContentLeftStyle.xaml diff --git a/src/DesktopMagic/App.xaml b/src/DesktopMagic/App.xaml index e943dca..83dca8a 100644 --- a/src/DesktopMagic/App.xaml +++ b/src/DesktopMagic/App.xaml @@ -1,20 +1,25 @@  - - + + - - + - + + + + + + \ No newline at end of file diff --git a/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs b/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs index 300ca18..45f52e9 100644 --- a/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs +++ b/src/DesktopMagic/BuiltInPlugins/WeatherPlugin.cs @@ -16,7 +16,7 @@ public class WeatherPlugin : AsyncPlugin [Setting("city-name", "City Name")] private readonly TextBox cityInput = new TextBox("Tokyo"); - [Setting("search-btn")] + [Setting("search-btn", "Update Location")] private readonly Button searchButton = new Button("Update Location"); [Setting("show-city", "Show City Name")] diff --git a/src/DesktopMagic/DataContexts/MainWindowDataContext.cs b/src/DesktopMagic/DataContexts/MainWindowDataContext.cs index 183d23b..88aa449 100644 --- a/src/DesktopMagic/DataContexts/MainWindowDataContext.cs +++ b/src/DesktopMagic/DataContexts/MainWindowDataContext.cs @@ -13,6 +13,15 @@ internal class MainWindowDataContext : INotifyPropertyChanged private static DesktopMagicSettings settings = new(); private bool isLoading = true; + public string Title => +#if DEBUG + $"{App.AppName} - Dev {System.Windows.Forms.Application.ProductVersion}"; +#else + $"{App.AppName} - {System.Windows.Forms.Application.ProductVersion}"; +#endif + + public string AppName => App.AppName; + public DesktopMagicSettings Settings { get => settings; diff --git a/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs b/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs index 9fd8356..30e7ceb 100644 --- a/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs +++ b/src/DesktopMagic/DataContexts/PluginEntryDataContext.cs @@ -1,8 +1,6 @@ using DesktopMagic.Helpers; using DesktopMagic.Plugins; -using MaterialDesignThemes.Wpf; - using System; using System.ComponentModel; using System.Diagnostics; @@ -39,7 +37,7 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co public ICommand Command => command; - public ButtonData InstallUninstallButtonData => new(mode == Mode.Install ? PackIconKind.Download : PackIconKind.Remove, GetInstallUninstallButtonText(), true, Command); + public ButtonData InstallUninstallButtonData => new(mode == Mode.Install ? "Download24" : "Delete24", GetInstallUninstallButtonText(), true, Command); public ButtonData OpenButtonData { @@ -47,11 +45,11 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co { if (string.IsNullOrWhiteSpace(pluginMetadata.ProfileUri?.ToString())) { - return new(PackIconKind.FolderOutline, (string)App.LanguageDictionary["folder"], path is not null, new CommandHandler(() => Process.Start("explorer.exe", path!))); + return new("Folder24", (string)App.LanguageDictionary["folder"], path is not null, new CommandHandler(() => Process.Start("explorer.exe", path!))); } else { - return new(PackIconKind.ExternalLink, "mod.io", true, new CommandHandler(OpenModIoPage)); + return new("Open24", "mod.io", true, new CommandHandler(OpenModIoPage)); } } } @@ -95,7 +93,7 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public record ButtonData(PackIconKind IconKind, string Text, bool IsEnabled, ICommand Command); + public record ButtonData(string Icon, string Text, bool IsEnabled, ICommand Command); public enum Mode { diff --git a/src/DesktopMagic/DesktopMagic.csproj b/src/DesktopMagic/DesktopMagic.csproj index 7f3b383..038bc52 100644 --- a/src/DesktopMagic/DesktopMagic.csproj +++ b/src/DesktopMagic/DesktopMagic.csproj @@ -50,6 +50,8 @@ + + diff --git a/src/DesktopMagic/Helpers/SettingElementGenerator.cs b/src/DesktopMagic/Helpers/SettingElementGenerator.cs index c4040f0..f53b602 100644 --- a/src/DesktopMagic/Helpers/SettingElementGenerator.cs +++ b/src/DesktopMagic/Helpers/SettingElementGenerator.cs @@ -6,14 +6,10 @@ using System.Windows.Controls; namespace DesktopMagic.Helpers; -internal class SettingElementGenerator(ComboBox optionsComboBox) +internal class SettingElementGenerator(uint pluginId) { - private readonly ComboBox optionsComboBox = optionsComboBox; - - public void Generate(SettingElement settingElement, DockPanel dockPanel, TextBlock textBlock) + public Control? Generate(SettingElement settingElement, System.Windows.Controls.TextBlock textBlock) { - dockPanel.UpdateLayout(); - textBlock.UpdateLayout(); if (settingElement.Input is DesktopMagic.Api.Settings.Label eLabel) { textBlock.Text = eLabel.Value; @@ -32,16 +28,13 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) textBlock.Text = eLabel.Value; }); }; + return null; } else if (settingElement.Input is DesktopMagic.Api.Settings.Button eButton) { - Button button = new() + Wpf.Ui.Controls.Button button = new() { Content = eButton.Value, - FontSize = 10, - Height = 20, - Margin = new Thickness(0, 10, 0, 10), - Padding = new Thickness(0), VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Stretch }; @@ -64,16 +57,18 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) }); }; - _ = dockPanel.Children.Add(button); + return button; } else if (settingElement.Input is DesktopMagic.Api.Settings.CheckBox eCheckBox) { - CheckBox checkBox = new() + Wpf.Ui.Controls.ToggleSwitch checkBox = new() { IsChecked = eCheckBox.Value, - Style = (Style)dockPanel.FindResource("MaterialDesignDarkCheckBox"), VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Stretch + HorizontalAlignment = HorizontalAlignment.Stretch, + HorizontalContentAlignment = HorizontalAlignment.Right, + Height = 30, + Style = Application.Current.FindResource("ToggleSwitchContentLeftStyle") as Style }; checkBox.Click += (_s, _e) => { @@ -94,11 +89,11 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) }); }; - _ = dockPanel.Children.Add(checkBox); + return checkBox; } else if (settingElement.Input is DesktopMagic.Api.Settings.TextBox eTextBox) { - TextBox textBox = new() + Wpf.Ui.Controls.TextBox textBox = new() { Text = eTextBox.Value, TextWrapping = TextWrapping.Wrap, @@ -123,26 +118,23 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) textBox.Text = eTextBox.Value; }); }; - _ = dockPanel.Children.Add(textBox); + return textBox; } else if (settingElement.Input is DesktopMagic.Api.Settings.IntegerUpDown eIntegerUpDown) { - MaterialDesignThemes.Wpf.NumericUpDown numericUpDown = new() + Wpf.Ui.Controls.NumberBox numberBox = new() { Value = eIntegerUpDown.Value, Minimum = eIntegerUpDown.Minimum, Maximum = eIntegerUpDown.Maximum, - IncreaseContent = new Label() { Content = "+" }, - DecreaseContent = new Label() { Content = "–" }, VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Stretch, - AllowChangeOnScroll = true + HorizontalAlignment = HorizontalAlignment.Stretch }; - numericUpDown.ValueChanged += (_s, _e) => + numberBox.ValueChanged += (_s, _e) => { try { - eIntegerUpDown.Value = numericUpDown.Value; + eIntegerUpDown.Value = (int)numberBox.Value; } catch (Exception ex) { @@ -151,12 +143,12 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) }; eIntegerUpDown.OnValueChanged += () => { - numericUpDown.Dispatcher.Invoke(() => + numberBox.Dispatcher.Invoke(() => { - numericUpDown.Value = eIntegerUpDown.Value; + numberBox.Value = eIntegerUpDown.Value; }); }; - _ = dockPanel.Children.Add(numericUpDown); + return numberBox; } else if (settingElement.Input is DesktopMagic.Api.Settings.Slider eSlider) { @@ -168,7 +160,8 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) TickFrequency = 1, IsSnapToTickEnabled = true, VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Stretch + HorizontalAlignment = HorizontalAlignment.Stretch, + Margin = new Thickness(0, 5, 0, 5) }; slider.ValueChanged += (_s, _e) => { @@ -189,7 +182,7 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) }); }; - _ = dockPanel.Children.Add(slider); + return slider; } else if (settingElement.Input is DesktopMagic.Api.Settings.ComboBox eComboBox) { @@ -220,19 +213,21 @@ internal class SettingElementGenerator(ComboBox optionsComboBox) comboBox.SelectedItem = eComboBox.Value; }; - _ = dockPanel.Children.Add(comboBox); + return comboBox; } + + return null; } private void DisplayException(string message) { App.Logger.LogInfo(message, source: "PluginInput"); - _ = MessageBox.Show("File execution error:\n" + message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); - int index = MainWindow.WindowNames.IndexOf(optionsComboBox.SelectedItem.ToString() ?? string.Empty); + _ = System.Windows.MessageBox.Show("File execution error:\n" + message, "Error", System.Windows.MessageBoxButton.OK, MessageBoxImage.Error); + int index = Manager.Instance.PluginWindows.FindIndex(p => p.PluginMetadata.Id == pluginId); - if (index >= 0 && index < MainWindow.Windows.Count) + if (index >= 0 && index < Manager.Instance.PluginWindows.Count) { - IPluginWindow window = MainWindow.Windows[index]; + IPluginWindow window = Manager.Instance.PluginWindows[index]; window?.Exit(); } } diff --git a/src/DesktopMagic/Helpers/UnderscoreEscapingConverter.cs b/src/DesktopMagic/Helpers/UnderscoreEscapingConverter.cs new file mode 100644 index 0000000..7b057ed --- /dev/null +++ b/src/DesktopMagic/Helpers/UnderscoreEscapingConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace DesktopMagic.Helpers; + +public class UnderscoreEscapingConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is string text) + { + // Replaces single underscores with double underscores to escape them in labels + return text.Replace("_", "__"); + } + return value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/DesktopMagic/MainWindow.xaml b/src/DesktopMagic/MainWindow.xaml index 1c1b7be..1dfe583 100644 --- a/src/DesktopMagic/MainWindow.xaml +++ b/src/DesktopMagic/MainWindow.xaml @@ -1,156 +1,106 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - + - + - - \ No newline at end of file + + \ No newline at end of file diff --git a/src/DesktopMagic/MainWindow.xaml.cs b/src/DesktopMagic/MainWindow.xaml.cs index 2be98b0..614e1b6 100644 --- a/src/DesktopMagic/MainWindow.xaml.cs +++ b/src/DesktopMagic/MainWindow.xaml.cs @@ -1,663 +1,171 @@ -using DesktopMagic.BuiltInPlugins; -using DesktopMagic.DataContexts; -using DesktopMagic.Dialogs; -using DesktopMagic.Helpers; -using DesktopMagic.Plugins; -using DesktopMagic.Settings; +using DesktopMagic.DataContexts; using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.Json; using System.Windows; -using System.Windows.Controls; -namespace DesktopMagic +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; + +namespace DesktopMagic; + +public partial class MainWindow : FluentWindow { - public partial class MainWindow : Window + private readonly System.Windows.Forms.NotifyIcon _notifyIcon = new(); + private readonly Manager _manager = Manager.Instance; + private readonly MainWindowDataContext _mainWindowDataContext = new(); + + public MainWindow() { - private readonly System.Windows.Forms.NotifyIcon notifyIcon = new(); + SystemThemeWatcher.Watch(this); - private readonly MainWindowDataContext mainWindowDataContext = new(); + InitializeComponent(); - private readonly Dictionary builtInPlugins = new() + DataContext = _mainWindowDataContext; + _mainWindowDataContext.Settings = _manager.Settings; + + Resources.MergedDictionaries.Add(App.LanguageDictionary); + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + try { - {new((string)App.LanguageDictionary["musicVisualizer"], 1), typeof(MusicVisualizerPlugin)}, - {new((string)App.LanguageDictionary["time"],2), typeof(TimePlugin)}, - {new((string)App.LanguageDictionary["date"],3), typeof(DatePlugin)}, - {new((string)App.LanguageDictionary["cpuUsage"], 4), typeof(CpuMonitorPlugin)}, - {new((string)App.LanguageDictionary["weather"], 5), typeof(WeatherPlugin)}, - }; + App.Logger.LogInfo("Loading application", source: "MainWindow"); - private bool loaded = false; - private bool blockWindowsClosing = true; - public static List Windows { get; } = []; - public static List WindowNames { get; } = []; + _mainWindowDataContext.IsLoading = true; - private DesktopMagicSettings Settings - { - get => mainWindowDataContext.Settings; - set => mainWindowDataContext.Settings = value; + // Load plugins and settings through manager + _manager.LoadPlugins(); + _manager.LoadSettings(); + _manager.LoadLayout(); + + _manager.IsLoaded = true; + _mainWindowDataContext.IsLoading = false; + + App.Logger.LogInfo("Application loaded", source: "MainWindow"); } - - public MainWindow() + catch (Exception ex) { - DataContext = mainWindowDataContext; - - try - { - Stream iconStream = Application.GetResourceStream(new Uri("pack://application:,,,/DesktopMagic;component/icon.ico")).Stream; - notifyIcon.MouseClick += NotifyIcon_MouseClick; - notifyIcon.Visible = true; - notifyIcon.Text = App.AppName; - notifyIcon.Icon = new System.Drawing.Icon(iconStream); - notifyIcon.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip() - { - Items = - { - new System.Windows.Forms.ToolStripMenuItem((string)App.LanguageDictionary["open"], null, (s, e) => RestoreWindow()), - new System.Windows.Forms.ToolStripMenuItem((string)App.LanguageDictionary["toggleEditMode"], null, (s, e) => { editCheckBox.IsChecked = !editCheckBox.IsChecked; EditCheckBox_Click(null, null); }), - new System.Windows.Forms.ToolStripMenuItem((string)App.LanguageDictionary["pluginManager"], null, (s, e) => PluginManagerButton_Click(null!, null!)), - new System.Windows.Forms.ToolStripMenuItem("GitHub", null, (s, e) => GitHubButton_Click(null!, null!)), - new System.Windows.Forms.ToolStripMenuItem((string)App.LanguageDictionary["quit"], null, (s, e) => Quit()), - } - }; - - InitializeComponent(); - - Resources.MergedDictionaries.Add(App.LanguageDictionary); - -#if DEBUG - Title = $"{App.AppName} - Dev {System.Windows.Forms.Application.ProductVersion}"; -#else - Title = $"{App.AppName} - {System.Windows.Forms.Application.ProductVersion}"; -#endif - } - catch (Exception ex) - { - _ = MessageBox.Show(ex.ToString(), App.AppName, MessageBoxButton.OK, MessageBoxImage.Error); - } + App.Logger.LogError(ex.Message, source: "MainWindow"); + _ = System.Windows.MessageBox.Show(ex.ToString(), App.AppName, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); } + } - #region Load + private async void NavigationView_Loaded(object sender, RoutedEventArgs e) + { + _ = NavigationView.Navigate(typeof(Pages.MainPage)); + } - private readonly Dictionary plugins = []; + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + _manager.SaveSettings(); - private void Window_Loaded(object sender, RoutedEventArgs e) - { - try - { - //Write To Log File and Load Elements - - App.Logger.LogInfo("Loading Plugin names", source: "Main"); - LoadPlugins(); - App.Logger.LogInfo("Loading Layout names", source: "Main"); - LoadSettings(); - App.Logger.LogInfo("Loading Layout", source: "Main"); - LoadLayout(); - - loaded = true; - App.Logger.LogInfo("Window Loaded", source: "Main"); - } - catch (Exception ex) - { - App.Logger.LogError(ex.Message, source: "Main"); - _ = MessageBox.Show(ex.ToString(), App.AppName, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void LoadPlugins() - { - mainWindowDataContext.IsLoading = true; - plugins.Clear(); - - foreach (var buildInPlugin in builtInPlugins.Keys) - { - plugins.Add(buildInPlugin.Id, new(buildInPlugin, PluginType.DotNet, string.Empty)); - } - - foreach (string directory in Directory.GetDirectories(App.PluginsPath)) - { - string? pluginDllPath = Directory.GetFiles(directory, "main.dll").FirstOrDefault(); - string? pluginHtmlPath = Directory.GetFiles(directory, "main.html").FirstOrDefault(); - string? pluginMetadataPath = Directory.GetFiles(directory, "metadata.json").FirstOrDefault(); - - if (pluginDllPath is null && pluginHtmlPath is null) - { - App.Logger.LogError($"Plugin \"{directory}\" has no \"main.dll\" or \"main.html\"", source: "Main"); - continue; - } - - if (pluginMetadataPath is null) - { - App.Logger.LogWarn($"Plugin \"{directory}\" has no \"metadata.json\"", source: "Main"); - continue; - } - - PluginMetadata? pluginMetadata = JsonSerializer.Deserialize(File.ReadAllText(pluginMetadataPath)); - - if (pluginMetadata is null) - { - App.Logger.LogError($"Plugin \"{directory}\" has no valid \"metadata.json\"", source: "Main"); - continue; - } - - if (plugins.ContainsKey(pluginMetadata.Id)) - { - App.Logger.LogError($"Plugin \"{directory}\" has the same id as another plugin", source: "Main"); - continue; - } - - PluginType pluginType = PluginType.DotNet; - - if (pluginHtmlPath is not null) - { - pluginType = PluginType.Web; - } - - plugins.Add(pluginMetadata.Id, new(pluginMetadata, pluginType, directory)); - } - mainWindowDataContext.IsLoading = false; - } - - #endregion Load - - #region Windows - - private void EditCheckBox_Click(object? sender, RoutedEventArgs? e) - { - foreach (IPluginWindow window in Windows) - { - window.SetEditMode(editCheckBox.IsChecked == true); - } - - SaveSettings(); - } - - private void PluginCheckBox_Click(object sender, RoutedEventArgs? e) - { - if (sender is not CheckBox checkBox) - { - return; - } - - LoadPlugin(uint.Parse(checkBox.Tag.ToString()!)); - } - - private void LoadPlugin(uint pluginId) - { - if (!plugins.TryGetValue(pluginId, out InternalPluginData? internalPluginData)) - { - return; - } - - if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings)) - { - pluginSettings = new PluginSettings(); - Settings.CurrentLayout.Plugins.Add(pluginId, pluginSettings); - } - - if (WindowNames.Contains(internalPluginData.Metadata.Id.ToString()) || !pluginSettings.Enabled) - { - int index = WindowNames.IndexOf(internalPluginData.Metadata.Id.ToString()); - - if (index >= 0) - { - try - { - blockWindowsClosing = false; - Windows[index].Close(); - blockWindowsClosing = true; - Windows.RemoveAt(index); - WindowNames.RemoveAt(index); - } - catch (Exception ex) - { - App.Logger.LogError(ex.Message, source: "Main"); - } - } - return; - } - - IPluginWindow window; - - if (builtInPlugins.TryGetValue(internalPluginData.Metadata, out Type? pluginType)) - { - window = new PluginWindow((Api.Plugin)Activator.CreateInstance(pluginType)!, internalPluginData.Metadata, pluginSettings) - { - Title = internalPluginData.Metadata.Id.ToString() - }; - } - else if (internalPluginData.Type == PluginType.Web) - { - window = new WebPluginWindow(internalPluginData.Metadata, pluginSettings, internalPluginData.DirectoryPath) - { - Title = internalPluginData.Metadata.Id.ToString() - }; - } - else - { - window = new PluginWindow(internalPluginData.Metadata, pluginSettings, internalPluginData.DirectoryPath) - { - Title = internalPluginData.Metadata.Id.ToString() - }; - } - - Action? onPluginLoaded = null; - onPluginLoaded = () => - { - Dispatcher.Invoke(() => - { - if (!optionsComboBox.Items.Contains(internalPluginData.Metadata)) - { - _ = optionsComboBox.Items.Add(internalPluginData.Metadata); - } - optionsComboBox.SelectedIndex = -1; - optionsComboBox.SelectedIndex = optionsComboBox.Items.IndexOf(internalPluginData.Metadata); - - window.PluginLoaded -= onPluginLoaded; - }); - }; - - Action onExit = () => - { - Windows.Remove(window); - WindowNames.Remove(window.Title); - blockWindowsClosing = false; - window.Close(); - blockWindowsClosing = true; - pluginSettings.Enabled = false; - }; - - window.PluginLoaded += onPluginLoaded; - window.OnExit += onExit; - - window.Show(); - window.SetEditMode(editCheckBox.IsChecked == true); - - window.Closing += DisplayWindow_Closing; - Windows.Add(window); - WindowNames.Add(window.Title); - } - - private void DisplayWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) - { - e.Cancel = blockWindowsClosing; - } - - private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) - { - SaveSettings(); - - if (blockWindowsClosing) - { - e.Cancel = true; - - editCheckBox.IsChecked = false; - EditCheckBox_Click(null, null); - ShowInTaskbar = false; - WindowState = WindowState.Minimized; - Visibility = Visibility.Collapsed; - } - } - - private void Window_Closed(object sender, EventArgs e) + if (_manager.BlockWindowsClosing) { + e.Cancel = true; + _manager.SetEditMode(false); + ShowInTaskbar = false; + WindowState = WindowState.Minimized; Visibility = Visibility.Collapsed; - UpdateLayout(); - foreach (IPluginWindow window in Windows) - { - window.Hide(); - } - Environment.Exit(0); } + } - #endregion Windows + private void Window_Closed(object sender, EventArgs e) + { + Visibility = Visibility.Collapsed; + UpdateLayout(); + _manager.CloseAllPluginWindows(); + _notifyIcon.Dispose(); + Environment.Exit(0); + } - #region Options - - private void AddThemeButton_Click(object sender, RoutedEventArgs e) + private void NotifyIcon_MouseClick(object? sender, System.Windows.Forms.MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Left) { - InputDialog inputDialog = new((string)FindResource("enterThemeName")) - { - Owner = this - }; - - if (inputDialog.ShowDialog() == true) - { - if (Settings.Themes.Any(l => l.Name.Trim() == inputDialog.ResponseText.Trim())) - { - _ = MessageBox.Show((string)FindResource("themeAlreadyExists"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - Settings.Themes.Add(new Theme(inputDialog.ResponseText.Trim())); - Settings.CurrentLayout.CurrentThemeName = inputDialog.ResponseText.Trim(); - SaveSettings(); - } + RestoreWindow(); } + } - private void DeleteThemeButton_Click(object sender, RoutedEventArgs e) + internal void RestoreWindow() + { + for (int i = 0; i < 10; i++) { - if (Settings.Themes.Count <= 1) - { - _ = MessageBox.Show((string)FindResource("cannotDeleteLastTheme"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - MessageBoxResult result = MessageBox.Show((string)FindResource("confirmDeleteTheme"), App.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question); - if (result != MessageBoxResult.Yes) - { - return; - } - - Settings.Themes.Remove((Theme)themesListBox.SelectedItem); - SaveSettings(); + ShowInTaskbar = true; + Visibility = Visibility.Visible; + SystemCommands.RestoreWindow(this); + Topmost = true; + _ = Activate(); + Topmost = false; } + } - private void ChangeThemeButton_Click(object sender, RoutedEventArgs e) + private void Quit() + { + _manager.BlockWindowsClosing = false; + Close(); + } + + private void ToggleEditMode() + { + _manager.SetEditMode(!_manager.IsEditMode); + } + + private void OpenPluginManager() + { + RestoreWindow(); + _ = NavigationView.Navigate(typeof(Plugins.PluginManager)); + } + + private void UpdatePlugins() + { + _mainWindowDataContext.IsLoading = true; + _manager.LoadPlugins(); + _manager.LoadLayout(false); + _mainWindowDataContext.IsLoading = false; + } + + private void ReportBugNavigationViewItem_Click(object sender, RoutedEventArgs e) + { + string uri = "https://github.com/Stone-Red-Code/DesktopMagic/issues/new?template=bug_report.md"; + ProcessStartInfo psi = new() { - Theme theme = themesListBox.SelectedItem as Theme ?? Settings.CurrentLayout.Theme; - - ThemeDialog themeDialog = new(theme.Name, theme, App.AppName) - { - Owner = this - }; - - themeDialog.ShowDialog(); - } - - private void OptionsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - optionsPanel.Visibility = Visibility.Visible; - optionsPanel.Children.Clear(); - optionsPanel.UpdateLayout(); - - if (optionsComboBox.SelectedItem is null) - { - return; - } - - bool success = Settings.CurrentLayout.Plugins.TryGetValue(((PluginMetadata)optionsComboBox.SelectedItem).Id, out Settings.PluginSettings? pluginSettings); - if (!success || pluginSettings is null || pluginSettings.Settings.Count == 0) - { - _ = optionsPanel.Children.Add(new TextBlock() { Text = (string)FindResource("noOptions") }); - return; - } - - SettingElementGenerator settingElementGenerator = new SettingElementGenerator(optionsComboBox); - - foreach (SettingElement settingElement in pluginSettings.Settings) - { - DockPanel dockPanel = new() - { - LastChildFill = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - Margin = new Thickness(0, 0, 0, 5) - }; - _ = optionsPanel.Children.Add(dockPanel); - - TextBlock textBlock = new() - { - Text = string.IsNullOrWhiteSpace(settingElement.Name) ? string.Empty : $"{settingElement.Name}:", - Padding = new Thickness(0, 0, 3, 0), - VerticalAlignment = VerticalAlignment.Center - }; - - _ = dockPanel.Children.Add(textBlock); - settingElementGenerator.Generate(settingElement, dockPanel, textBlock); - } - } - - #endregion Options - - internal void RestoreWindow() - { - for (int i = 0; i < 10; i++) - { - ShowInTaskbar = true; - Visibility = Visibility.Visible; - SystemCommands.RestoreWindow(this); - Topmost = true; - _ = Activate(); - Topmost = false; - } - } - - private void Quit() - { - blockWindowsClosing = false; - Close(); - } - - private void OpenPluginsFolderButton_Click(object sender, RoutedEventArgs e) - { - _ = Process.Start("explorer.exe", App.PluginsPath); - } - - private void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) - { - ScrollViewer scv = (ScrollViewer)sender; - scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta); - e.Handled = true; - } - - #region Layout - - private readonly JsonSerializerOptions jsonSettingsOptions = new() - { - Converters = - { - new ColorJsonConverter() - } + UseShellExecute = true, + FileName = uri }; - - private void LayoutsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (loaded) - { - SaveSettings(); - LoadLayout(false); - } - } - - private void NewLayoutButton_Click(object sender, RoutedEventArgs e) - { - InputDialog inputDialog = new((string)FindResource("enterLayoutName")) - { - Owner = this - }; - - if (inputDialog.ShowDialog() == true) - { - if (Settings.Layouts.Any(l => l.Name.Trim() == inputDialog.ResponseText.Trim())) - { - _ = MessageBox.Show((string)FindResource("layoutAlreadyExists"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - Settings.Layouts.Add(new Layout(inputDialog.ResponseText.Trim())); - Settings.CurrentLayoutName = inputDialog.ResponseText.Trim(); - SaveSettings(); - } - } - - private void RemoveLayoutButton_Click(object sender, RoutedEventArgs e) - { - if (Settings.Layouts.Count <= 1) - { - _ = MessageBox.Show((string)FindResource("cannotDeleteLastLayout"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - MessageBoxResult result = MessageBox.Show((string)FindResource("confirmDeleteLayout"), App.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question); - if (result != MessageBoxResult.Yes) - { - return; - } - - Settings.Layouts.Remove(Settings.CurrentLayout); - SaveSettings(); - } - - private void SaveSettings() - { - if (!loaded) - { - return; - } - - string json = JsonSerializer.Serialize(Settings, jsonSettingsOptions); - File.WriteAllText(Path.Combine(App.ApplicationDataPath, "settings.json"), json); - } - - private void LoadSettings() - { - if (!File.Exists(Path.Combine(App.ApplicationDataPath, "settings.json"))) - { - Settings = new DesktopMagicSettings(); - - Settings.Layouts.Add(new Layout((string)FindResource("default"))); - Settings.Themes.Add(new Theme((string)FindResource("default"))); - - return; - } - - string json = File.ReadAllText(Path.Combine(App.ApplicationDataPath, "settings.json")); - - Settings = JsonSerializer.Deserialize(json, jsonSettingsOptions) ?? new DesktopMagicSettings(); - - if (Settings.Layouts.Count == 0) - { - Settings.Layouts.Add(new Layout((string)FindResource("default"))); - } - - if (Settings.Themes.Count == 0) - { - Settings.Themes.Add(new Theme((string)FindResource("default"))); - } - } - - private void LoadLayout(bool minimize = true) - { - mainWindowDataContext.IsLoading = true; - blockWindowsClosing = false; - - foreach (IPluginWindow window in Windows) - { - window.Close(); - } - - editCheckBox.IsChecked = false; - EditCheckBox_Click(null, null); - blockWindowsClosing = true; - Windows.Clear(); - WindowNames.Clear(); - optionsComboBox.Items.Clear(); - - bool showWindow = true; - - // Load plugins - foreach (uint pluginId in plugins.Keys) - { - InternalPluginData internalPluginData = plugins[pluginId]; - - // Add plugin to layout if it doesn't exist - if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings)) - { - Settings.CurrentLayout.Plugins.Add(pluginId, new PluginSettings() { Name = internalPluginData.Metadata.Name }); - - continue; - } - - pluginSettings.Name = internalPluginData.Metadata.Name; - - if (pluginSettings.Enabled) - { - LoadPlugin(pluginId); - } - - if (showWindow && pluginSettings.Enabled) - { - showWindow = false; - } - } - - // Remove plugins that are not loaded anymore - foreach (uint pluginId in Settings.CurrentLayout.Plugins.Keys) - { - if (!plugins.ContainsKey(pluginId)) - { - Settings.CurrentLayout.Plugins.Remove(pluginId); - } - } - - Settings.CurrentLayout.UpdatePlugins(); - - if (!showWindow && minimize) - { - Close(); - } - else - { - RestoreWindow(); - } - - mainWindowDataContext.IsLoading = false; - } - - #endregion Layout - - private void UpdatePluginsButton_Click(object sender, RoutedEventArgs e) - { - LoadPlugins(); - LoadLayout(false); - } - - private void NotifyIcon_MouseClick(object? sender, System.Windows.Forms.MouseEventArgs e) - { - if (e.Button == System.Windows.Forms.MouseButtons.Left) - { - RestoreWindow(); - } - } - - private void GitHubButton_Click(object sender, RoutedEventArgs e) - { - string uri = "https://github.com/Stone-Red-Code/DesktopMagic"; - ProcessStartInfo psi = new ProcessStartInfo - { - UseShellExecute = true, - FileName = uri - }; - _ = Process.Start(psi); - } - - private void PluginManagerButton_Click(object sender, RoutedEventArgs e) - { - PluginManager pluginManager = new PluginManager - { - Owner = this - }; - - pluginManager.ShowDialog(); - LoadPlugins(); - LoadLayout(Visibility != Visibility.Visible); - } + _ = Process.Start(psi); } - internal class InternalPluginData(PluginMetadata pluginMetadata, PluginType pluginType, string directoryPath) + private void RequestFeatureNavigationViewItem_Click(object sender, RoutedEventArgs e) { - public PluginMetadata Metadata { get; set; } = pluginMetadata; - public PluginType Type { get; set; } = pluginType; - public string DirectoryPath { get; set; } = directoryPath; - + string uri = "https://github.com/Stone-Red-Code/DesktopMagic/issues/new?template=feature_request.md"; + ProcessStartInfo psi = new() + { + UseShellExecute = true, + FileName = uri + }; + _ = Process.Start(psi); } - internal enum PluginType + + private void NotifyIcon_LeftClick(Wpf.Ui.Tray.Controls.NotifyIcon sender, RoutedEventArgs e) { - DotNet, - Web + RestoreWindow(); + } + + private void SettingsMenuItem_Click(object sender, RoutedEventArgs e) + { + RestoreWindow(); + _ = NavigationView.Navigate(typeof(Pages.MainPage)); + } + + private void EditLayoutMenuItem_Click(object sender, RoutedEventArgs e) + { + _manager.SetEditMode(!_manager.IsEditMode); + } + + private void QuitMenuItem_Click(object sender, RoutedEventArgs e) + { + Quit(); } } \ No newline at end of file diff --git a/src/DesktopMagic/Manager.cs b/src/DesktopMagic/Manager.cs new file mode 100644 index 0000000..ccbd0a3 --- /dev/null +++ b/src/DesktopMagic/Manager.cs @@ -0,0 +1,365 @@ +using DesktopMagic.BuiltInPlugins; +using DesktopMagic.Helpers; +using DesktopMagic.Plugins; +using DesktopMagic.Settings; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Windows; + +namespace DesktopMagic; + +/// +/// Singleton Manager class that handles global state, plugin management, and shared operations +/// +public sealed class Manager +{ + private static Manager? _instance; + private static readonly object _lock = new(); + + public static Manager Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + _instance ??= new Manager(); + } + } + return _instance; + } + } + + // Plugin management + private readonly Dictionary _plugins = []; + private readonly Dictionary _builtInPlugins = new() + { + {new((string)App.LanguageDictionary["musicVisualizer"], 1) { Author = "Stone_Red" }, typeof(MusicVisualizerPlugin)}, + {new((string)App.LanguageDictionary["time"],2) { Author = "Stone_Red" }, typeof(TimePlugin)}, + {new((string)App.LanguageDictionary["date"],3) { Author = "Stone_Red" }, typeof(DatePlugin)}, + {new((string)App.LanguageDictionary["cpuUsage"], 4) { Author = "Stone_Red" }, typeof(CpuMonitorPlugin)}, + {new((string)App.LanguageDictionary["weather"], 5) { Author = "Stone_Red" }, typeof(WeatherPlugin)}, + }; + + // Window management + public List PluginWindows { get; } = []; + + public IReadOnlyDictionary Plugins => _plugins; + + public bool BlockWindowsClosing { get; set; } = true; + + // Edit mode tracking + private bool _isEditMode = false; + public bool IsEditMode => _isEditMode; + + // Settings + public DesktopMagicSettings Settings { get; set; } = new(); + public bool IsLoaded { get; set; } = false; + + private readonly JsonSerializerOptions _jsonSettingsOptions = new() + { + Converters = { new ColorJsonConverter() } + }; + + // Events + public event Action? PluginsChanged; + public event Action? SettingsChanged; + public event Action? EditModeChanged; + + private Manager() + { + // Private constructor for singleton + } + + #region Plugin Management + + public Dictionary GetPlugins() => new(_plugins); + + public void LoadPlugins() + { + App.Logger.LogInfo("Loading plugins", source: "Manager"); + _plugins.Clear(); + + // Load built-in plugins + foreach (var builtInPlugin in _builtInPlugins.Keys) + { + _plugins.Add(builtInPlugin.Id, new(builtInPlugin, PluginType.DotNet, string.Empty)); + } + + // Load external plugins + foreach (string directory in Directory.GetDirectories(App.PluginsPath)) + { + string? pluginDllPath = Directory.GetFiles(directory, "main.dll").FirstOrDefault(); + string? pluginHtmlPath = Directory.GetFiles(directory, "main.html").FirstOrDefault(); + string? pluginMetadataPath = Directory.GetFiles(directory, "metadata.json").FirstOrDefault(); + + if (pluginDllPath is null && pluginHtmlPath is null) + { + App.Logger.LogError($"Plugin \"{directory}\" has no \"main.dll\" or \"main.html\"", source: "Manager"); + continue; + } + + if (pluginMetadataPath is null) + { + App.Logger.LogWarn($"Plugin \"{directory}\" has no \"metadata.json\"", source: "Manager"); + continue; + } + + PluginMetadata? pluginMetadata = JsonSerializer.Deserialize(File.ReadAllText(pluginMetadataPath)); + + if (pluginMetadata is null) + { + App.Logger.LogError($"Plugin \"{directory}\" has no valid \"metadata.json\"", source: "Manager"); + continue; + } + + if (_plugins.ContainsKey(pluginMetadata.Id)) + { + App.Logger.LogError($"Plugin \"{directory}\" has the same id as another plugin", source: "Manager"); + continue; + } + + PluginType pluginType = pluginHtmlPath is not null ? PluginType.Web : PluginType.DotNet; + + _plugins.Add(pluginMetadata.Id, new(pluginMetadata, pluginType, directory)); + } + + PluginsChanged?.Invoke(); + App.Logger.LogInfo($"Loaded {_plugins.Count} plugins", source: "Manager"); + } + + public void LoadPlugin(uint pluginId, Action? onPluginLoaded = null) + { + if (!_plugins.TryGetValue(pluginId, out InternalPluginData? internalPluginData)) + { + return; + } + + if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings)) + { + pluginSettings = new PluginSettings(); + Settings.CurrentLayout.Plugins.Add(pluginId, pluginSettings); + } + + IPluginWindow? existingWindow = PluginWindows.FirstOrDefault(w => w.PluginMetadata.Id == internalPluginData.Metadata.Id); + + if (existingWindow is not null || !pluginSettings.Enabled) + { + // Close the window if it's already open or disabled + if (existingWindow is not null) + { + try + { + BlockWindowsClosing = false; + existingWindow.Close(); + BlockWindowsClosing = true; + PluginWindows.Remove(existingWindow); + } + catch (Exception ex) + { + App.Logger.LogError(ex.Message, source: "Manager"); + } + } + return; + } + + IPluginWindow window; + + if (_builtInPlugins.TryGetValue(internalPluginData.Metadata, out Type? pluginType)) + { + window = new PluginWindow((Api.Plugin)Activator.CreateInstance(pluginType)!, internalPluginData.Metadata, pluginSettings) + { + Title = internalPluginData.Metadata.Id.ToString() + }; + } + else if (internalPluginData.Type == PluginType.Web) + { + window = new WebPluginWindow(internalPluginData.Metadata, pluginSettings, internalPluginData.DirectoryPath) + { + Title = internalPluginData.Metadata.Id.ToString() + }; + } + else + { + window = new PluginWindow(internalPluginData.Metadata, pluginSettings, internalPluginData.DirectoryPath) + { + Title = internalPluginData.Metadata.Id.ToString() + }; + } + + Action? pluginLoadedHandler = null; + pluginLoadedHandler = () => + { + onPluginLoaded?.Invoke(internalPluginData); + window.PluginLoaded -= pluginLoadedHandler; + }; + + Action exitHandler = () => + { + PluginWindows.Remove(window); + BlockWindowsClosing = false; + window.Close(); + BlockWindowsClosing = true; + pluginSettings.Enabled = false; + }; + + window.PluginLoaded += pluginLoadedHandler; + window.OnExit += exitHandler; + + window.Show(); + window.SetEditMode(_isEditMode); + + PluginWindows.Add(window); + } + + public void SetEditMode(bool editMode) + { + _isEditMode = editMode; + foreach (IPluginWindow window in PluginWindows) + { + window.SetEditMode(editMode); + } + EditModeChanged?.Invoke(editMode); + SaveSettings(); + } + + #endregion + + #region Settings Management + + public void LoadSettings() + { + App.Logger.LogInfo("Loading settings", source: "Manager"); + + if (!File.Exists(Path.Combine(App.ApplicationDataPath, "settings.json"))) + { + Settings = new DesktopMagicSettings(); + Settings.Layouts.Add(new Layout("Default")); + Settings.Themes.Add(new Theme("Default")); + return; + } + + string json = File.ReadAllText(Path.Combine(App.ApplicationDataPath, "settings.json")); + Settings = JsonSerializer.Deserialize(json, _jsonSettingsOptions) ?? new DesktopMagicSettings(); + + if (Settings.Layouts.Count == 0) + { + Settings.Layouts.Add(new Layout("Default")); + } + + if (Settings.Themes.Count == 0) + { + Settings.Themes.Add(new Theme("Default")); + } + + SettingsChanged?.Invoke(); + } + + public void SaveSettings() + { + if (!IsLoaded) + { + return; + } + + string json = JsonSerializer.Serialize(Settings, _jsonSettingsOptions); + File.WriteAllText(Path.Combine(App.ApplicationDataPath, "settings.json"), json); + App.Logger.LogInfo("Settings saved", source: "Manager"); + SettingsChanged?.Invoke(); + } + + #endregion + + #region Layout Management + + public void LoadLayout(bool minimize = true, Action? onComplete = null) + { + App.Logger.LogInfo("Loading layout", source: "Manager"); + BlockWindowsClosing = false; + + foreach (IPluginWindow window in PluginWindows) + { + window.Close(); + } + + BlockWindowsClosing = true; + PluginWindows.Clear(); + + bool showWindow = true; + + // Load plugins + foreach (uint pluginId in _plugins.Keys) + { + InternalPluginData internalPluginData = _plugins[pluginId]; + + // Add plugin to layout if it doesn't exist + if (!Settings.CurrentLayout.Plugins.TryGetValue(pluginId, out PluginSettings? pluginSettings)) + { + Settings.CurrentLayout.Plugins.Add(pluginId, new PluginSettings() { Metadata = internalPluginData.Metadata }); + continue; + } + + pluginSettings.Metadata = internalPluginData.Metadata; + + if (pluginSettings.Enabled) + { + LoadPlugin(pluginId); + } + + if (showWindow && pluginSettings.Enabled) + { + showWindow = false; + } + } + + // Remove plugins that are not loaded anymore + var pluginIdsToRemove = Settings.CurrentLayout.Plugins.Keys.Where(id => !_plugins.ContainsKey(id)).ToList(); + foreach (uint pluginId in pluginIdsToRemove) + { + Settings.CurrentLayout.Plugins.Remove(pluginId); + } + + Settings.CurrentLayout.UpdatePlugins(); + + if (minimize && !showWindow) + { + Application.Current.MainWindow.WindowState = WindowState.Minimized; + Application.Current.MainWindow.ShowInTaskbar = false; + } + + onComplete?.Invoke(); + App.Logger.LogInfo("Layout loaded", source: "Manager"); + } + + #endregion + + #region Cleanup + + public void CloseAllPluginWindows() + { + foreach (IPluginWindow window in PluginWindows) + { + window.Hide(); + } + } + + #endregion +} + +public class InternalPluginData(PluginMetadata pluginMetadata, PluginType pluginType, string directoryPath) +{ + public PluginMetadata Metadata { get; set; } = pluginMetadata; + public PluginType Type { get; set; } = pluginType; + public string DirectoryPath { get; set; } = directoryPath; +} + +public enum PluginType +{ + DotNet, + Web +} \ No newline at end of file diff --git a/src/DesktopMagic/Pages/MainPage.xaml b/src/DesktopMagic/Pages/MainPage.xaml new file mode 100644 index 0000000..752027b --- /dev/null +++ b/src/DesktopMagic/Pages/MainPage.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DesktopMagic/Pages/MainPage.xaml.cs b/src/DesktopMagic/Pages/MainPage.xaml.cs new file mode 100644 index 0000000..3a6dc29 --- /dev/null +++ b/src/DesktopMagic/Pages/MainPage.xaml.cs @@ -0,0 +1,261 @@ +using DesktopMagic.DataContexts; +using DesktopMagic.Dialogs; +using DesktopMagic.Helpers; +using DesktopMagic.Plugins; +using DesktopMagic.Settings; + +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace DesktopMagic.Pages; + +/// +/// Interaction logic for MainPage.xaml +/// +public partial class MainPage : Page +{ + private readonly Manager _manager = Manager.Instance; + private readonly MainWindowDataContext _dataContext; + private bool _isLoadingLayout = false; + + public MainPage() + { + InitializeComponent(); + + _dataContext = new MainWindowDataContext + { + Settings = _manager.Settings + }; + + DataContext = _dataContext; + + // Subscribe to manager events + _manager.PluginsChanged += OnPluginsChanged; + _manager.EditModeChanged += OnEditModeChanged; + + Loaded += MainPage_Loaded; + Unloaded += MainPage_Unloaded; + } + + private void MainPage_Loaded(object sender, RoutedEventArgs e) + { + // Initialize edit checkbox state + editCheckBox.IsChecked = _manager.IsEditMode; + } + + private void MainPage_Unloaded(object sender, RoutedEventArgs e) + { + // Unsubscribe from events + _manager.PluginsChanged -= OnPluginsChanged; + _manager.EditModeChanged -= OnEditModeChanged; + } + + private void OnPluginsChanged() + { + Dispatcher.Invoke(() => + { + // Refresh the UI if needed + _dataContext.Settings = _manager.Settings; + }); + } + + private void OnEditModeChanged(bool editMode) + { + Dispatcher.Invoke(() => + { + editCheckBox.IsChecked = editMode; + }); + } + + private void EditCheckBox_Click(object sender, RoutedEventArgs e) + { + _manager.SetEditMode(editCheckBox.IsChecked == true); + } + + private void PluginCheckBox_Click(object sender, RoutedEventArgs e) + { + if (sender is not Control checkBox) + { + return; + } + + uint pluginId = uint.Parse(checkBox.Tag.ToString()!); + + _manager.LoadPlugin(pluginId, (internalPluginData) => + { + Dispatcher.Invoke(() => + { + // Update the card expander to show options when enabling a plugin + Wpf.Ui.Controls.CardExpander? cardExpander = ((checkBox.Parent as FrameworkElement)?.Parent) as Wpf.Ui.Controls.CardExpander; + + if (cardExpander is not null) + { + OptionsCardExpander_Expanded(cardExpander, new RoutedEventArgs()); + } + }); + }); + } + + private void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) + { + if (sender is ScrollViewer scv) + { + scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta); + e.Handled = true; + } + } + + #region Layout Management + + private void LayoutsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + // Prevent recursive calls and only process if fully loaded + if (_isLoadingLayout || !_manager.IsLoaded || !IsLoaded) + { + return; + } + + // Check if this is actually a user-initiated change + // by verifying that the removed and added items are different + if (e.RemovedItems.Count > 0 && e.AddedItems.Count > 0) + { + if (e.RemovedItems[0] == e.AddedItems[0]) + { + return; + } + } + + try + { + _isLoadingLayout = true; + _manager.SaveSettings(); + _manager.LoadLayout(false); + } + finally + { + _isLoadingLayout = false; + } + } + + private void NewLayoutButton_Click(object sender, RoutedEventArgs e) + { + InputDialog inputDialog = new((string)FindResource("enterLayoutName")) + { + Owner = Window.GetWindow(this) + }; + + if (inputDialog.ShowDialog() == true) + { + if (_manager.Settings.Layouts.Any(l => l.Name.Trim() == inputDialog.ResponseText.Trim())) + { + _ = System.Windows.MessageBox.Show((string)FindResource("layoutAlreadyExists"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + try + { + _isLoadingLayout = true; + _manager.Settings.Layouts.Add(new Layout(inputDialog.ResponseText.Trim())); + _manager.Settings.CurrentLayoutName = inputDialog.ResponseText.Trim(); + _manager.SaveSettings(); + } + finally + { + _isLoadingLayout = false; + } + } + } + + private void RemoveLayoutButton_Click(object sender, RoutedEventArgs e) + { + if (_manager.Settings.Layouts.Count <= 1) + { + _ = System.Windows.MessageBox.Show((string)FindResource("cannotDeleteLastLayout"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + MessageBoxResult result = System.Windows.MessageBox.Show((string)FindResource("confirmDeleteLayout"), App.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + { + return; + } + + try + { + _isLoadingLayout = true; + _ = _manager.Settings.Layouts.Remove(_manager.Settings.CurrentLayout); + _manager.SaveSettings(); + } + finally + { + _isLoadingLayout = false; + } + } + + #endregion + + private void OptionsCardExpander_Expanded(object sender, RoutedEventArgs e) + { + if (sender is not Wpf.Ui.Controls.CardExpander expander || expander.Tag is not KeyValuePair keyValuePair) + { + return; + } + + PluginSettings? pluginSettings = keyValuePair.Value; + uint pluginId = keyValuePair.Key; + + StackPanel optionsPanel = new StackPanel + { + Visibility = Visibility.Visible + }; + + expander.Content = optionsPanel; + optionsPanel.UpdateLayout(); + + // s.Input being null means the plugins has not been loaded yet but the settings are present in the saved configuration. + if (pluginSettings is null || pluginSettings.Settings.Count == 0 || pluginSettings.Settings.All(s => s.Input is null)) + { + _ = optionsPanel.Children.Add(new TextBlock() + { + Text = (string)FindResource(pluginSettings?.Enabled == true ? "noOptions" : "enablePluginToConfigure") + }); + + return; + } + + SettingElementGenerator settingElementGenerator = new SettingElementGenerator(pluginId); + + foreach (SettingElement settingElement in pluginSettings.Settings) + { + Wpf.Ui.Controls.CardControl card = new() + { + Margin = new Thickness(0, 0, 0, 5), + Padding = new Thickness(5) + }; + + TextBlock textBlock = new() + { + Text = string.IsNullOrWhiteSpace(settingElement.Name) ? string.Empty : settingElement.Name, + Padding = new Thickness(0, 0, 3, 0), + VerticalAlignment = VerticalAlignment.Center, + FontWeight = FontWeights.SemiBold + }; + + card.Header = textBlock; + + _ = optionsPanel.Children.Add(card); + + Control? control = settingElementGenerator.Generate(settingElement, textBlock); + + if (control is not null) + { + control.MinWidth = 200; + card.Content = control; + } + } + + optionsPanel.UpdateLayout(); + } +} diff --git a/src/DesktopMagic/Pages/ThemePage.xaml b/src/DesktopMagic/Pages/ThemePage.xaml new file mode 100644 index 0000000..0b3ad60 --- /dev/null +++ b/src/DesktopMagic/Pages/ThemePage.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DesktopMagic/Pages/ThemePage.xaml.cs b/src/DesktopMagic/Pages/ThemePage.xaml.cs new file mode 100644 index 0000000..e7e3baf --- /dev/null +++ b/src/DesktopMagic/Pages/ThemePage.xaml.cs @@ -0,0 +1,103 @@ +using DesktopMagic.DataContexts; +using DesktopMagic.Dialogs; +using DesktopMagic.Plugins; + +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace DesktopMagic.Pages; + +/// +/// Interaction logic for ThemePage.xaml +/// +public partial class ThemePage : Page +{ + private readonly Manager _manager = Manager.Instance; + private readonly MainWindowDataContext _dataContext; + + public ThemePage() + { + InitializeComponent(); + + _dataContext = new MainWindowDataContext + { + Settings = _manager.Settings + }; + + DataContext = _dataContext; + + // Subscribe to manager events + _manager.SettingsChanged += OnSettingsChanged; + + Unloaded += ThemePage_Unloaded; + } + + private void ThemePage_Unloaded(object sender, RoutedEventArgs e) + { + // Unsubscribe from events + _manager.SettingsChanged -= OnSettingsChanged; + } + + private void OnSettingsChanged() + { + Dispatcher.Invoke(() => + { + _dataContext.Settings = _manager.Settings; + }); + } + + private void AddThemeButton_Click(object sender, RoutedEventArgs e) + { + InputDialog inputDialog = new((string)FindResource("enterThemeName")) + { + Owner = Window.GetWindow(this) + }; + + if (inputDialog.ShowDialog() == true) + { + if (_manager.Settings.Themes.Any(l => l.Name.Trim() == inputDialog.ResponseText.Trim())) + { + _ = System.Windows.MessageBox.Show((string)FindResource("themeAlreadyExists"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + _manager.Settings.Themes.Add(new Theme(inputDialog.ResponseText.Trim())); + _manager.Settings.CurrentLayout.CurrentThemeName = inputDialog.ResponseText.Trim(); + _manager.SaveSettings(); + } + } + + private void DeleteThemeButton_Click(object sender, RoutedEventArgs e) + { + if (_manager.Settings.Themes.Count <= 1) + { + _ = System.Windows.MessageBox.Show((string)FindResource("cannotDeleteLastTheme"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + MessageBoxResult result = System.Windows.MessageBox.Show((string)FindResource("confirmDeleteTheme"), App.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + { + return; + } + + if (themesListBox.SelectedItem is Theme theme) + { + _ = _manager.Settings.Themes.Remove(theme); + _manager.SaveSettings(); + } + } + + private void ChangeThemeButton_Click(object sender, RoutedEventArgs e) + { + Theme theme = themesListBox.SelectedItem as Theme ?? _manager.Settings.CurrentLayout.Theme; + + ThemeDialog themeDialog = new(theme.Name, theme, App.AppName) + { + Owner = Window.GetWindow(this) + }; + + _ = themeDialog.ShowDialog(); + } +} diff --git a/src/DesktopMagic/Plugins/PluginEntry.xaml b/src/DesktopMagic/Plugins/PluginEntry.xaml index d29b84a..f56e9e5 100644 --- a/src/DesktopMagic/Plugins/PluginEntry.xaml +++ b/src/DesktopMagic/Plugins/PluginEntry.xaml @@ -2,8 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:local="clr-namespace:DesktopMagic.Plugins" xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts" d:DataContext="{d:DesignInstance Type=dataContext:PluginEntryDataContext}" @@ -12,52 +12,54 @@ Background="{DynamicResource MaterialDesignPaper}" FontFamily="{DynamicResource MaterialDesignFont}" Visibility="{Binding Visibility}"> - - - - - - + + + + + + + - - - - - - - - - - - - - - + - - - + + + - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml b/src/DesktopMagic/Plugins/PluginManager.xaml index 8031cac..1003f0f 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml +++ b/src/DesktopMagic/Plugins/PluginManager.xaml @@ -1,24 +1,20 @@ - + d:DesignHeight="450" + d:DesignWidth="800" + ScrollViewer.CanContentScroll="False" + Title="{DynamicResource pluginManager}"> - + @@ -28,21 +24,58 @@ + - - \ No newline at end of file + \ No newline at end of file diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml.cs b/src/DesktopMagic/Plugins/PluginManager.xaml.cs index 3f3d9b3..2047371 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginManager.xaml.cs @@ -8,7 +8,6 @@ using Modio.Models; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; @@ -19,6 +18,7 @@ using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; using System.Windows.Threading; using System.Xml.Linq; @@ -30,7 +30,7 @@ namespace DesktopMagic.Plugins; /// /// Interaction logic for PluginManager.xaml /// -public partial class PluginManager : Window +public partial class PluginManager : Page { private const int ModIoGameId = 5665; private const string ModIoApiKey = "88e6ea774c3a502b06114e7fee0829ac"; @@ -38,6 +38,9 @@ public partial class PluginManager : Window private readonly PluginManagerDataContext pluginManagerDataContext = new(); private readonly string pluginsPath = Path.Combine(App.ApplicationDataPath, "Plugins"); private readonly string pluginDevelopmentPath = Path.Combine(App.ApplicationDataPath, "PluginDevelopment"); + private readonly Manager _manager = Manager.Instance; + + private bool changed = false; private readonly DispatcherTimer searchTimer = new() { @@ -72,55 +75,29 @@ public partial class PluginManager : Window searchTimer.Stop(); await SearchAllPlugins(pluginManagerDataContext.AllPluginsSearchText); }; + + Loaded += PluginManager_Loaded; + Unloaded += PluginManager_Unloaded; } - public async Task Remove(string pluginPath, uint id) + private async void PluginManager_Loaded(object sender, RoutedEventArgs e) { - App.Logger.LogInfo($"Removing plugin with ID {id} from path: {pluginPath}", source: "PluginManager"); - pluginManagerDataContext.IsLoading = true; - - PluginEntryDataContext? pluginEntryDataContext = pluginManagerDataContext.InstalledPlugins.FirstOrDefault(p => p.Id == id); - - if (Directory.Exists(pluginPath)) - { - try - { - Directory.Delete(pluginPath, true); - App.Logger.LogInfo($"Successfully deleted plugin directory: {pluginPath}", source: "PluginManager"); - } - catch (Exception ex) - { - _ = MessageBox.Show(ex.Message, "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error); - App.Logger.LogError(ex.Message, source: "PluginManager"); - } - } - - if (pluginEntryDataContext is not null) - { - _ = pluginManagerDataContext.InstalledPlugins.Remove(pluginEntryDataContext); - App.Logger.LogInfo($"Removed plugin {id} from installed plugins list", source: "PluginManager"); - } - - if (pluginManagerDataContext.IsAuthenticated) - { - try - { - await modIoClient.Games[ModIoGameId].Mods.Unsubscribe(id); - App.Logger.LogInfo($"Unsubscribed from plugin {id} on mod.io", source: "PluginManager"); - } - catch (Exception ex) - { - App.Logger.LogError($"Failed to unsubscribe from plugin {id}: {ex.Message}", source: "PluginManager"); - } - } - - pluginManagerDataContext.IsLoading = false; + await InitializePluginManager(); } - protected override async void OnInitialized(EventArgs e) + private void PluginManager_Unloaded(object sender, RoutedEventArgs e) { - base.OnInitialized(e); + searchTimer.Stop(); + if (changed) + { + _manager.LoadPlugins(); + _manager.LoadLayout(Application.Current.MainWindow.Visibility != System.Windows.Visibility.Visible); + } + } + + private async Task InitializePluginManager() + { App.Logger.LogInfo("Initializing Plugin Manager", source: "PluginManager"); HashSet pluginIds = []; @@ -197,9 +174,48 @@ public partial class PluginManager : Window App.Logger.LogInfo("Plugin Manager initialization complete", source: "PluginManager"); } - protected override void OnClosing(CancelEventArgs e) + public async Task Remove(string pluginPath, uint id) { - e.Cancel = pluginManagerDataContext.IsLoading; + App.Logger.LogInfo($"Removing plugin with ID {id} from path: {pluginPath}", source: "PluginManager"); + pluginManagerDataContext.IsLoading = true; + changed = true; + + PluginEntryDataContext? pluginEntryDataContext = pluginManagerDataContext.InstalledPlugins.FirstOrDefault(p => p.Id == id); + + if (Directory.Exists(pluginPath)) + { + try + { + Directory.Delete(pluginPath, true); + App.Logger.LogInfo($"Successfully deleted plugin directory: {pluginPath}", source: "PluginManager"); + } + catch (Exception ex) + { + _ = MessageBox.Show(ex.Message, "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error); + App.Logger.LogError(ex.Message, source: "PluginManager"); + } + } + + if (pluginEntryDataContext is not null) + { + _ = pluginManagerDataContext.InstalledPlugins.Remove(pluginEntryDataContext); + App.Logger.LogInfo($"Removed plugin {id} from installed plugins list", source: "PluginManager"); + } + + if (pluginManagerDataContext.IsAuthenticated) + { + try + { + await modIoClient.Games[ModIoGameId].Mods.Unsubscribe(id); + App.Logger.LogInfo($"Unsubscribed from plugin {id} on mod.io", source: "PluginManager"); + } + catch (Exception ex) + { + App.Logger.LogError($"Failed to unsubscribe from plugin {id}: {ex.Message}", source: "PluginManager"); + } + } + + pluginManagerDataContext.IsLoading = false; } [GeneratedRegex(@"[^a-zA-Z0-9]")] @@ -209,6 +225,7 @@ public partial class PluginManager : Window { App.Logger.LogInfo($"Installing plugin: {mod.Name} (ID: {mod.Id})", source: "PluginManager"); pluginManagerDataContext.IsLoading = true; + changed = true; if (mod.Modfile?.Download?.BinaryUrl is null) { @@ -383,7 +400,7 @@ public partial class PluginManager : Window InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager") { - Owner = this, + Owner = Window.GetWindow(this), }; if (inputDialog.ShowDialog() != true) @@ -547,7 +564,7 @@ public class {pluginSafeName}Plugin : Plugin { InputDialog inputDialog = new((string)FindResource("enterModIoEmail"), "Plugin Manager") { - Owner = this, + Owner = Window.GetWindow(this), }; if (inputDialog.ShowDialog() != true) @@ -561,7 +578,7 @@ public class {pluginSafeName}Plugin : Plugin inputDialog = new((string)FindResource("enterModIoAccessToken"), "Plugin Manager") { - Owner = this, + Owner = Window.GetWindow(this), }; if (inputDialog.ShowDialog() != true) diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml b/src/DesktopMagic/Plugins/PluginWindow.xaml index 7372412..9d0533c 100644 --- a/src/DesktopMagic/Plugins/PluginWindow.xaml +++ b/src/DesktopMagic/Plugins/PluginWindow.xaml @@ -1,6 +1,7 @@  - - + + + - - - - - - - - - - + + + + + + + + + + + diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs index 44663a5..852b255 100644 --- a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs @@ -210,6 +210,8 @@ public partial class PluginWindow : Window, IPluginWindow return; } PluginLoaded?.Invoke(); + + _ = Dispatcher.Invoke(() => busyMask.IsBusy = false); } private void ThemeChanged() diff --git a/src/DesktopMagic/Plugins/WebPluginWindow.xaml b/src/DesktopMagic/Plugins/WebPluginWindow.xaml index 00c9d6f..deb7d11 100644 --- a/src/DesktopMagic/Plugins/WebPluginWindow.xaml +++ b/src/DesktopMagic/Plugins/WebPluginWindow.xaml @@ -1,6 +1,7 @@ - - - - - - - - - - + + + + + diff --git a/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs b/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs index c491cbe..9c9f27b 100644 --- a/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs +++ b/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs @@ -192,6 +192,8 @@ public partial class WebPluginWindow : Window, IPluginWindow App.Logger.LogInfo($"\"{PluginMetadata.Name}\" - WebView2 initialized successfully", source: "WebPlugin"); PluginLoaded?.Invoke(); + + busyMask.IsBusy = false; } catch (Exception ex) { diff --git a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml index 08847d5..96ca827 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml @@ -3,6 +3,9 @@ xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:system="clr-namespace:System;assembly=mscorlib"> + Entwickelt von Stone_Red + Bug Melden + Feature Anfragen Linie Spiegeln Ordner @@ -15,13 +18,15 @@ Mod.io Account E-Mail eingeben Geben Sie Ihren mod.io Sicherheitscode ein Bearbeitungsmodus umschalten - Alle Plugins - Installierte Plugins - Neu Laden + Alle Plugins Suchen... + Installierte Plugins Suchen... + Plugins Neu Laden Plugins Ordner Plugins Manager Neues Plugin erstellen Pluginnamen eingeben + Plugins + Aktivieren Sie das Plugin, um seine Einstellungen zu konfigurieren. Farbe: Standard Wollen sie das Programm wirklich schließen? @@ -38,6 +43,7 @@ Neues Theme Theme Löschen Theme Ändern + Themes Ok Abbrechen Möchten Sie dieses Layout wirklich löschen? diff --git a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml index dc7ff01..703a1ad 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml @@ -3,6 +3,9 @@ xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:system="clr-namespace:System;assembly=mscorlib"> + Developed by Stone_Red + Report Bug + Request Feature Line Mirror Folder @@ -15,13 +18,15 @@ Enter your mod.io account E-mail Enter your mod.io security code Toggle Edit Mode - All Plugins - Installed Plugins - Reload + Search All Plugins... + Search Installed Plugins... + Reload Plugins Plugins Folder Plugins Manager Create New Plugin Enter plugin name + Plugins + Enable the plugin to configure its settings. Color: Default Do you really want to close the program? @@ -38,6 +43,7 @@ New Theme Delete Theme Change Theme + Themes Ok Cancel Do you really want to delete this layout? diff --git a/src/DesktopMagic/Resources/Styles/ToggleSwitchContentLeftStyle.xaml b/src/DesktopMagic/Resources/Styles/ToggleSwitchContentLeftStyle.xaml new file mode 100644 index 0000000..3420ca0 --- /dev/null +++ b/src/DesktopMagic/Resources/Styles/ToggleSwitchContentLeftStyle.xaml @@ -0,0 +1,131 @@ + + + 40 + 20 + 1 + 0,0,8,0 + + + \ No newline at end of file diff --git a/src/DesktopMagic/Settings/DesktopMagicSettings.cs b/src/DesktopMagic/Settings/DesktopMagicSettings.cs index 62409e8..42fb981 100644 --- a/src/DesktopMagic/Settings/DesktopMagicSettings.cs +++ b/src/DesktopMagic/Settings/DesktopMagicSettings.cs @@ -8,7 +8,7 @@ using System.Text.Json.Serialization; namespace DesktopMagic.Settings; -internal class DesktopMagicSettings : INotifyPropertyChanged +public class DesktopMagicSettings : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; diff --git a/src/DesktopMagic/Settings/Layout.cs b/src/DesktopMagic/Settings/Layout.cs index 420f746..a92dff7 100644 --- a/src/DesktopMagic/Settings/Layout.cs +++ b/src/DesktopMagic/Settings/Layout.cs @@ -9,7 +9,7 @@ using System.Text.Json.Serialization; namespace DesktopMagic.Settings; -internal class Layout(string name) : INotifyPropertyChanged +public class Layout(string name) : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; diff --git a/src/DesktopMagic/Settings/PluginSettings.cs b/src/DesktopMagic/Settings/PluginSettings.cs index 0e67497..5655032 100644 --- a/src/DesktopMagic/Settings/PluginSettings.cs +++ b/src/DesktopMagic/Settings/PluginSettings.cs @@ -24,7 +24,7 @@ public class PluginSettings : INotifyPropertyChanged // Only for internal use to show the name of the plugin in the main window [JsonIgnore] - public string Name { get; set; } = string.Empty; + public PluginMetadata Metadata { get; set; } = new(); [JsonIgnore] public Theme Theme