mirror of
https://github.com/Stone-Red-Code/DesktopMagic.git
synced 2026-09-04 08:56:15 +02:00
UI overhaul
This commit is contained in:
@@ -1,20 +1,25 @@
|
|||||||
<Application x:Class="DesktopMagic.App"
|
<Application x:Class="DesktopMagic.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
|
xmlns:helpers="clr-namespace:DesktopMagic.Helpers"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
StartupUri="MainWindow.xaml">
|
StartupUri="MainWindow.xaml">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="Resources\Strings\StringResources.en.xaml" />
|
<ResourceDictionary Source="Resources\Strings\StringResources.en.xaml" />
|
||||||
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="Grey" SecondaryColor="LightBlue" />
|
<ui:ThemesDictionary Theme="Light" />
|
||||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
|
<ui:ControlsDictionary />
|
||||||
<ResourceDictionary Source="pack://application:,,,/BusyIndicator;component/Theme/Default.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/BusyIndicator;component/Theme/Default.xaml" />
|
||||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.ObsoleteBrushes.xaml" />
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.ObsoleteStyles.xaml" />
|
|
||||||
<ResourceDictionary Source="Resources\Images\ImageResources.xaml" />
|
<ResourceDictionary Source="Resources\Images\ImageResources.xaml" />
|
||||||
|
<ResourceDictionary Source="Resources\Styles\ToggleSwitchContentLeftStyle.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<Thickness x:Key="DefaultMargin" Top="6" Left="6" Right="6" Bottom ="6"></Thickness>
|
|
||||||
|
<!-- Converters -->
|
||||||
|
<helpers:UnderscoreEscapingConverter x:Key="UnderscoreEscapingConverter" />
|
||||||
|
|
||||||
|
<!-- Margins -->
|
||||||
|
<Thickness x:Key="DefaultMargin" Top="5" Left="5" Right="5" Bottom ="5" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
@@ -16,7 +16,7 @@ public class WeatherPlugin : AsyncPlugin
|
|||||||
[Setting("city-name", "City Name")]
|
[Setting("city-name", "City Name")]
|
||||||
private readonly TextBox cityInput = new TextBox("Tokyo");
|
private readonly TextBox cityInput = new TextBox("Tokyo");
|
||||||
|
|
||||||
[Setting("search-btn")]
|
[Setting("search-btn", "Update Location")]
|
||||||
private readonly Button searchButton = new Button("Update Location");
|
private readonly Button searchButton = new Button("Update Location");
|
||||||
|
|
||||||
[Setting("show-city", "Show City Name")]
|
[Setting("show-city", "Show City Name")]
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ internal class MainWindowDataContext : INotifyPropertyChanged
|
|||||||
private static DesktopMagicSettings settings = new();
|
private static DesktopMagicSettings settings = new();
|
||||||
private bool isLoading = true;
|
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
|
public DesktopMagicSettings Settings
|
||||||
{
|
{
|
||||||
get => settings;
|
get => settings;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using DesktopMagic.Helpers;
|
using DesktopMagic.Helpers;
|
||||||
using DesktopMagic.Plugins;
|
using DesktopMagic.Plugins;
|
||||||
|
|
||||||
using MaterialDesignThemes.Wpf;
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -39,7 +37,7 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co
|
|||||||
|
|
||||||
public ICommand Command => command;
|
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
|
public ButtonData OpenButtonData
|
||||||
{
|
{
|
||||||
@@ -47,11 +45,11 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(pluginMetadata.ProfileUri?.ToString()))
|
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
|
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));
|
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
|
public enum Mode
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
<PackageReference Include="Modio" Version="1.0.0" />
|
<PackageReference Include="Modio" Version="1.0.0" />
|
||||||
<PackageReference Include="NAudio" Version="2.2.1" />
|
<PackageReference Include="NAudio" Version="2.2.1" />
|
||||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||||
|
<PackageReference Include="WPF-UI" Version="4.1.0" />
|
||||||
|
<PackageReference Include="WPF-UI.Tray" Version="4.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -6,14 +6,10 @@ using System.Windows.Controls;
|
|||||||
|
|
||||||
namespace DesktopMagic.Helpers;
|
namespace DesktopMagic.Helpers;
|
||||||
|
|
||||||
internal class SettingElementGenerator(ComboBox optionsComboBox)
|
internal class SettingElementGenerator(uint pluginId)
|
||||||
{
|
{
|
||||||
private readonly ComboBox optionsComboBox = optionsComboBox;
|
public Control? Generate(SettingElement settingElement, System.Windows.Controls.TextBlock textBlock)
|
||||||
|
|
||||||
public void Generate(SettingElement settingElement, DockPanel dockPanel, TextBlock textBlock)
|
|
||||||
{
|
{
|
||||||
dockPanel.UpdateLayout();
|
|
||||||
textBlock.UpdateLayout();
|
|
||||||
if (settingElement.Input is DesktopMagic.Api.Settings.Label eLabel)
|
if (settingElement.Input is DesktopMagic.Api.Settings.Label eLabel)
|
||||||
{
|
{
|
||||||
textBlock.Text = eLabel.Value;
|
textBlock.Text = eLabel.Value;
|
||||||
@@ -32,16 +28,13 @@ internal class SettingElementGenerator(ComboBox optionsComboBox)
|
|||||||
textBlock.Text = eLabel.Value;
|
textBlock.Text = eLabel.Value;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
else if (settingElement.Input is DesktopMagic.Api.Settings.Button eButton)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.Button eButton)
|
||||||
{
|
{
|
||||||
Button button = new()
|
Wpf.Ui.Controls.Button button = new()
|
||||||
{
|
{
|
||||||
Content = eButton.Value,
|
Content = eButton.Value,
|
||||||
FontSize = 10,
|
|
||||||
Height = 20,
|
|
||||||
Margin = new Thickness(0, 10, 0, 10),
|
|
||||||
Padding = new Thickness(0),
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
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)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.CheckBox eCheckBox)
|
||||||
{
|
{
|
||||||
CheckBox checkBox = new()
|
Wpf.Ui.Controls.ToggleSwitch checkBox = new()
|
||||||
{
|
{
|
||||||
IsChecked = eCheckBox.Value,
|
IsChecked = eCheckBox.Value,
|
||||||
Style = (Style)dockPanel.FindResource("MaterialDesignDarkCheckBox"),
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
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) =>
|
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)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.TextBox eTextBox)
|
||||||
{
|
{
|
||||||
TextBox textBox = new()
|
Wpf.Ui.Controls.TextBox textBox = new()
|
||||||
{
|
{
|
||||||
Text = eTextBox.Value,
|
Text = eTextBox.Value,
|
||||||
TextWrapping = TextWrapping.Wrap,
|
TextWrapping = TextWrapping.Wrap,
|
||||||
@@ -123,26 +118,23 @@ internal class SettingElementGenerator(ComboBox optionsComboBox)
|
|||||||
textBox.Text = eTextBox.Value;
|
textBox.Text = eTextBox.Value;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
_ = dockPanel.Children.Add(textBox);
|
return textBox;
|
||||||
}
|
}
|
||||||
else if (settingElement.Input is DesktopMagic.Api.Settings.IntegerUpDown eIntegerUpDown)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.IntegerUpDown eIntegerUpDown)
|
||||||
{
|
{
|
||||||
MaterialDesignThemes.Wpf.NumericUpDown numericUpDown = new()
|
Wpf.Ui.Controls.NumberBox numberBox = new()
|
||||||
{
|
{
|
||||||
Value = eIntegerUpDown.Value,
|
Value = eIntegerUpDown.Value,
|
||||||
Minimum = eIntegerUpDown.Minimum,
|
Minimum = eIntegerUpDown.Minimum,
|
||||||
Maximum = eIntegerUpDown.Maximum,
|
Maximum = eIntegerUpDown.Maximum,
|
||||||
IncreaseContent = new Label() { Content = "+" },
|
|
||||||
DecreaseContent = new Label() { Content = "–" },
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
AllowChangeOnScroll = true
|
|
||||||
};
|
};
|
||||||
numericUpDown.ValueChanged += (_s, _e) =>
|
numberBox.ValueChanged += (_s, _e) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
eIntegerUpDown.Value = numericUpDown.Value;
|
eIntegerUpDown.Value = (int)numberBox.Value;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -151,12 +143,12 @@ internal class SettingElementGenerator(ComboBox optionsComboBox)
|
|||||||
};
|
};
|
||||||
eIntegerUpDown.OnValueChanged += () =>
|
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)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.Slider eSlider)
|
||||||
{
|
{
|
||||||
@@ -168,7 +160,8 @@ internal class SettingElementGenerator(ComboBox optionsComboBox)
|
|||||||
TickFrequency = 1,
|
TickFrequency = 1,
|
||||||
IsSnapToTickEnabled = true,
|
IsSnapToTickEnabled = true,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
|
Margin = new Thickness(0, 5, 0, 5)
|
||||||
};
|
};
|
||||||
slider.ValueChanged += (_s, _e) =>
|
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)
|
else if (settingElement.Input is DesktopMagic.Api.Settings.ComboBox eComboBox)
|
||||||
{
|
{
|
||||||
@@ -220,19 +213,21 @@ internal class SettingElementGenerator(ComboBox optionsComboBox)
|
|||||||
comboBox.SelectedItem = eComboBox.Value;
|
comboBox.SelectedItem = eComboBox.Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = dockPanel.Children.Add(comboBox);
|
return comboBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisplayException(string message)
|
private void DisplayException(string message)
|
||||||
{
|
{
|
||||||
App.Logger.LogInfo(message, source: "PluginInput");
|
App.Logger.LogInfo(message, source: "PluginInput");
|
||||||
_ = MessageBox.Show("File execution error:\n" + message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
_ = System.Windows.MessageBox.Show("File execution error:\n" + message, "Error", System.Windows.MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
int index = MainWindow.WindowNames.IndexOf(optionsComboBox.SelectedItem.ToString() ?? string.Empty);
|
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();
|
window?.Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,156 +1,106 @@
|
|||||||
<Window x:Class="DesktopMagic.MainWindow"
|
<ui:FluentWindow x:Class="DesktopMagic.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
|
xmlns:tray="http://schemas.lepo.co/wpfui/2022/xaml/tray"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:pages="clr-namespace:DesktopMagic.Pages"
|
||||||
|
xmlns:plugins="clr-namespace:DesktopMagic.Plugins"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:DesktopMagic"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
||||||
d:DataContext="{d:DesignInstance Type=dataContext:MainWindowDataContext}"
|
d:DataContext="{d:DesignInstance Type=dataContext:MainWindowDataContext}"
|
||||||
|
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||||
|
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
WindowCornerPreference="Round"
|
||||||
|
WindowBackdropType="Tabbed"
|
||||||
Closing="Window_Closing"
|
Closing="Window_Closing"
|
||||||
Closed="Window_Closed"
|
Closed="Window_Closed"
|
||||||
ShowInTaskbar="True"
|
Height="560"
|
||||||
Icon="{StaticResource Icon}"
|
Width="900"
|
||||||
Background="{DynamicResource MaterialDesignPaper}"
|
MinHeight="600"
|
||||||
FontFamily="{DynamicResource MaterialDesignFont}"
|
MinWidth="940"
|
||||||
Height="520"
|
ExtendsContentIntoTitleBar="True"
|
||||||
Width="570"
|
|
||||||
MinHeight="520"
|
|
||||||
MinWidth="570"
|
|
||||||
WindowState="Minimized"
|
WindowState="Minimized"
|
||||||
Loaded="Window_Loaded">
|
Loaded="Window_Loaded">
|
||||||
|
|
||||||
<busyIndicator:BusyMask x:Name="BusyIndicator" IsBusy="{Binding IsLoading}" IndicatorType="Cogs" BusyContent="Please wait..." BusyContentMargin="0,20,0,0" IsBusyAtStartup="False">
|
<Grid>
|
||||||
<Grid>
|
<Grid.RowDefinitions>
|
||||||
<TextBlock TextWrapping="Wrap" Text="0" VerticalAlignment="Top" FontFamily="Comic Sans MS" Foreground="Transparent" />
|
<RowDefinition Height="Auto" />
|
||||||
<Grid>
|
<RowDefinition Height="*" />
|
||||||
<Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="1*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid Margin="{StaticResource DefaultMargin}">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="1*" />
|
|
||||||
<ColumnDefinition Width="1*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<DockPanel Margin="0,0,2.5,0">
|
|
||||||
<Grid DockPanel.Dock="Top">
|
|
||||||
<Rectangle Grid.Row="1" Fill="#FFECECEC" Height="24" />
|
|
||||||
<CheckBox x:Name="editCheckBox" VerticalAlignment="Center" Grid.Row="1" Content="{DynamicResource editLayout}" Click="EditCheckBox_Click" Foreground="Black" BorderBrush="#FF323232" Style="{StaticResource MaterialDesignDarkCheckBox}" IsChecked="True" />
|
|
||||||
</Grid>
|
|
||||||
<ScrollViewer Background="#FFBBBBBB" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
|
||||||
<ItemsControl ItemsSource="{Binding Settings.CurrentLayout.Plugins}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<StackPanel />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<CheckBox Click="PluginCheckBox_Click" Content="{Binding Value.Name}" Tag="{Binding Key}" IsChecked="{Binding Value.Enabled}" Style="{StaticResource MaterialDesignDarkCheckBox}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<DockPanel Grid.Column="1" Margin="2.5,0,0,0">
|
<ui:TitleBar x:Name="TitleBar" Title="{Binding Title}">
|
||||||
<ComboBox x:Name="optionsComboBox" DisplayMemberPath="Name" DockPanel.Dock="Top" Height="24" SelectedIndex="0" SelectionChanged="OptionsComboBox_SelectionChanged" Background="#FFECECEC" Padding="4" VerticalAlignment="Center" />
|
<ui:TitleBar.Icon>
|
||||||
<ScrollViewer Background="#FFBBBBBB">
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
<Grid>
|
</ui:TitleBar.Icon>
|
||||||
<StackPanel x:Name="optionsPanel" Margin="3,3,3,0" HorizontalAlignment="Stretch" Visibility="Collapsed">
|
</ui:TitleBar>
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
<tray:NotifyIcon Grid.Row="0" FocusOnLeftClick="True" MenuOnRightClick="True" TooltipText="{Binding AppName}" Icon="{StaticResource Icon}" LeftClick="NotifyIcon_LeftClick">
|
||||||
</ScrollViewer>
|
<tray:NotifyIcon.Menu>
|
||||||
</DockPanel>
|
<ContextMenu>
|
||||||
</Grid>
|
<MenuItem Header="Settings" Click="SettingsMenuItem_Click" />
|
||||||
<Grid Grid.Row="1">
|
<MenuItem Header="Edit Layout" Click="EditLayoutMenuItem_Click" />
|
||||||
<Grid.RowDefinitions>
|
<MenuItem Header="Quit" Click="QuitMenuItem_Click" />
|
||||||
<RowDefinition Height="*" />
|
</ContextMenu>
|
||||||
<RowDefinition Height="110" />
|
</tray:NotifyIcon.Menu>
|
||||||
</Grid.RowDefinitions>
|
</tray:NotifyIcon>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ui:NavigationView x:Name="NavigationView" Grid.Row="0" IsBackButtonVisible="Collapsed" IsPaneToggleVisible="True" OpenPaneLength="200" Loaded="NavigationView_Loaded">
|
||||||
|
<ui:NavigationView.MenuItems>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource plugins}" TargetPageType="{x:Type pages:MainPage}">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Apps24" />
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource themes}" TargetPageType="{x:Type pages:ThemePage}">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Color24" />
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource pluginManager}" TargetPageType="{x:Type plugins:PluginManager}">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="AppsAddIn24" />
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
</ui:NavigationView.MenuItems>
|
||||||
|
|
||||||
|
<ui:NavigationView.FooterMenuItems>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource requestFeature}" Click="ReportBugNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="ChatHelp24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource reportBug}" Click="ReportBugNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Bug24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
</ui:NavigationView.FooterMenuItems>
|
||||||
|
</ui:NavigationView>
|
||||||
|
|
||||||
|
<Border Grid.Row="1" Background="{DynamicResource ControlFillColorDefaultBrush}" BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}" BorderThickness="0,1,0,0" Padding="5 2 5 5">
|
||||||
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<ListBox x:Name="themesListBox" Grid.Row="0" Grid.ColumnSpan="1" Margin="{StaticResource DefaultMargin}" ItemsSource="{Binding Settings.Themes}" DisplayMemberPath="Name" SelectedValue="{Binding Settings.CurrentLayout.CurrentThemeName}" SelectedValuePath="Name" />
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="{StaticResource DefaultMargin}">
|
<StackPanel Orientation="Horizontal" Grid.Column="0">
|
||||||
<Button x:Name="addThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="AddThemeButton_Click" FontWeight="Regular">
|
<ui:HyperlinkButton FontWeight="Bold" Content="{DynamicResource developedBy}" NavigateUri="https://me.stone-red.net"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="Add" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource newTheme}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="deleteThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="DeleteThemeButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="DeleteOutline" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource deleteTheme}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="changeThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="ChangeThemeButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="PaletteOutline" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource changeTheme}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid Grid.Row="1" Grid.ColumnSpan="2" VerticalAlignment="Bottom">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Rectangle Fill="#FFC5C5C5" Stroke="#FFC5C5C5" Grid.ColumnSpan="3" />
|
<ui:ToggleSwitch x:Name="autoStartCheckBox" Style="{StaticResource ToggleSwitchContentLeftStyle}" Grid.Column="2" Margin="0 0 5 0" Content="{DynamicResource autoStart}" IsChecked="{Binding IsAutoStartEnabled}" VerticalAlignment="Center"/>
|
||||||
<StackPanel Margin="{StaticResource DefaultMargin}" VerticalAlignment="Bottom">
|
|
||||||
<ComboBox x:Name="layoutsComboBox" ItemsSource="{Binding Settings.Layouts}" DisplayMemberPath="Name" SelectedValue="{Binding Settings.CurrentLayoutName}" SelectedValuePath="Name" HorizontalAlignment="Left" Margin="0,0,0,5" Width="170" Height="20" SelectionChanged="LayoutsComboBox_SelectionChanged" />
|
|
||||||
<Button x:Name="newLayoutButton" Margin="0,0,0,5" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="170" Click="NewLayoutButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="Add" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource newLayout}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="removeLayoutButton" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="170" Click="RemoveLayoutButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="DeleteOutline" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource deleteLayout}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Column="1" Margin="{StaticResource DefaultMargin}" VerticalAlignment="Bottom">
|
|
||||||
<Button x:Name="updatePluginsButton" Margin="0,0,0,5" HorizontalAlignment="Center" Width="170" Click="UpdatePluginsButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource updatePlugins}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="githubButton" HorizontalAlignment="Center" Width="170" Click="GitHubButton_Click" FontWeight="Regular" Cursor="Hand">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="Github" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="GitHub" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Grid.Column="2" Margin="{StaticResource DefaultMargin}" VerticalAlignment="Bottom">
|
|
||||||
<CheckBox x:Name="autoStartCheckBox" Content="{DynamicResource autoStart}" Foreground="Black" BorderBrush="#FF323232" Margin="0,0,0,5" Width="170" Height="20" Style="{StaticResource MaterialDesignDarkCheckBox}" IsChecked="{Binding IsAutoStartEnabled}" />
|
|
||||||
<Button x:Name="downloadPluginsButton" Margin="0,0,0,5" HorizontalAlignment="Right" Width="170" Click="PluginManagerButton_Click" FontWeight="Regular" Cursor="Hand">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="ExtensionOutline" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource pluginManager}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="openPluginsFolderButton" HorizontalAlignment="Right" Width="170" Click="OpenPluginsFolderButton_Click" FontWeight="Regular">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="FolderOutline" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource pluginsFolder}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</busyIndicator:BusyMask>
|
</Grid>
|
||||||
</Window>
|
</ui:FluentWindow>
|
||||||
+133
-625
@@ -1,663 +1,171 @@
|
|||||||
using DesktopMagic.BuiltInPlugins;
|
using DesktopMagic.DataContexts;
|
||||||
using DesktopMagic.DataContexts;
|
|
||||||
using DesktopMagic.Dialogs;
|
|
||||||
using DesktopMagic.Helpers;
|
|
||||||
using DesktopMagic.Plugins;
|
|
||||||
using DesktopMagic.Settings;
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Windows;
|
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<PluginMetadata, Type> 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)},
|
App.Logger.LogInfo("Loading application", source: "MainWindow");
|
||||||
{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)},
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool loaded = false;
|
_mainWindowDataContext.IsLoading = true;
|
||||||
private bool blockWindowsClosing = true;
|
|
||||||
public static List<IPluginWindow> Windows { get; } = [];
|
|
||||||
public static List<string> WindowNames { get; } = [];
|
|
||||||
|
|
||||||
private DesktopMagicSettings Settings
|
// Load plugins and settings through manager
|
||||||
{
|
_manager.LoadPlugins();
|
||||||
get => mainWindowDataContext.Settings;
|
_manager.LoadSettings();
|
||||||
set => mainWindowDataContext.Settings = value;
|
_manager.LoadLayout();
|
||||||
|
|
||||||
|
_manager.IsLoaded = true;
|
||||||
|
_mainWindowDataContext.IsLoading = false;
|
||||||
|
|
||||||
|
App.Logger.LogInfo("Application loaded", source: "MainWindow");
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public MainWindow()
|
|
||||||
{
|
{
|
||||||
DataContext = mainWindowDataContext;
|
App.Logger.LogError(ex.Message, source: "MainWindow");
|
||||||
|
_ = System.Windows.MessageBox.Show(ex.ToString(), App.AppName, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Load
|
private async void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_ = NavigationView.Navigate(typeof(Pages.MainPage));
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Dictionary<uint, InternalPluginData> plugins = [];
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
_manager.SaveSettings();
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
if (_manager.BlockWindowsClosing)
|
||||||
{
|
|
||||||
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<PluginMetadata>(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)
|
|
||||||
{
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
_manager.SetEditMode(false);
|
||||||
|
ShowInTaskbar = false;
|
||||||
|
WindowState = WindowState.Minimized;
|
||||||
Visibility = Visibility.Collapsed;
|
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 NotifyIcon_MouseClick(object? sender, System.Windows.Forms.MouseEventArgs e)
|
||||||
|
{
|
||||||
private void AddThemeButton_Click(object sender, RoutedEventArgs e)
|
if (e.Button == System.Windows.Forms.MouseButtons.Left)
|
||||||
{
|
{
|
||||||
InputDialog inputDialog = new((string)FindResource("enterThemeName"))
|
RestoreWindow();
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DeleteThemeButton_Click(object sender, RoutedEventArgs e)
|
internal void RestoreWindow()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
if (Settings.Themes.Count <= 1)
|
ShowInTaskbar = true;
|
||||||
{
|
Visibility = Visibility.Visible;
|
||||||
_ = MessageBox.Show((string)FindResource("cannotDeleteLastTheme"), App.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
|
SystemCommands.RestoreWindow(this);
|
||||||
return;
|
Topmost = true;
|
||||||
}
|
_ = Activate();
|
||||||
|
Topmost = false;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
UseShellExecute = true,
|
||||||
|
FileName = uri
|
||||||
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()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
_ = Process.Start(psi);
|
||||||
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<DesktopMagicSettings>(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class InternalPluginData(PluginMetadata pluginMetadata, PluginType pluginType, string directoryPath)
|
private void RequestFeatureNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
public PluginMetadata Metadata { get; set; } = pluginMetadata;
|
string uri = "https://github.com/Stone-Red-Code/DesktopMagic/issues/new?template=feature_request.md";
|
||||||
public PluginType Type { get; set; } = pluginType;
|
ProcessStartInfo psi = new()
|
||||||
public string DirectoryPath { get; set; } = directoryPath;
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = uri
|
||||||
|
};
|
||||||
|
_ = Process.Start(psi);
|
||||||
}
|
}
|
||||||
internal enum PluginType
|
|
||||||
|
private void NotifyIcon_LeftClick(Wpf.Ui.Tray.Controls.NotifyIcon sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
DotNet,
|
RestoreWindow();
|
||||||
Web
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton Manager class that handles global state, plugin management, and shared operations
|
||||||
|
/// </summary>
|
||||||
|
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<uint, InternalPluginData> _plugins = [];
|
||||||
|
private readonly Dictionary<PluginMetadata, Type> _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<IPluginWindow> PluginWindows { get; } = [];
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<uint, InternalPluginData> 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<bool>? EditModeChanged;
|
||||||
|
|
||||||
|
private Manager()
|
||||||
|
{
|
||||||
|
// Private constructor for singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Plugin Management
|
||||||
|
|
||||||
|
public Dictionary<uint, InternalPluginData> 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<PluginMetadata>(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<InternalPluginData>? 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<DesktopMagicSettings>(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
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<Page x:Class="DesktopMagic.Pages.MainPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:DesktopMagic.Pages"
|
||||||
|
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DataContext="{d:DesignInstance Type=dataContext:MainWindowDataContext}"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
|
ScrollViewer.CanContentScroll="False"
|
||||||
|
Title="MainPage">
|
||||||
|
|
||||||
|
<Grid Margin="{StaticResource DefaultMargin}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ui:Card Padding="5" Margin="0 0 0 5">
|
||||||
|
<ui:ToggleSwitch x:Name="editCheckBox" Height="30" VerticalAlignment="Center" Margin="5 0 0 0" Content="{DynamicResource editLayout}" Click="EditCheckBox_Click" />
|
||||||
|
</ui:Card>
|
||||||
|
|
||||||
|
<ui:Card Grid.Row="1" Margin="0,0,2.5,0" Padding="0 5" VerticalAlignment="Stretch" VerticalContentAlignment="Top">
|
||||||
|
<ScrollViewer Background="#FFBBBBBB" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
||||||
|
<ItemsControl ItemsSource="{Binding Settings.CurrentLayout.Plugins}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ui:CardExpander Tag="{Binding}" Margin="5 0 5 5" Expanded="OptionsCardExpander_Expanded">
|
||||||
|
<ui:CardExpander.Header>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ui:TextBlock Grid.Row="0" Grid.Column="0" FontTypography="Body" Text="{Binding Value.Metadata.Name}" />
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}" FontSize="12" ContentStringFormat="{DynamicResource authorFormat}">
|
||||||
|
<Label.Style>
|
||||||
|
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
|
||||||
|
<Setter Property="Content" Value="{Binding Value.Metadata.Author, Converter={StaticResource UnderscoreEscapingConverter}}" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Value.Metadata.Author}" Value="{x:Null}">
|
||||||
|
<Setter Property="Content" Value="{DynamicResource unknown}" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Label.Style>
|
||||||
|
</Label>
|
||||||
|
<ui:ToggleSwitch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Click="PluginCheckBox_Click" IsChecked="{Binding Value.Enabled}" Tag="{Binding Key}" Margin="0,0,16,0" VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</ui:CardExpander.Header>
|
||||||
|
</ui:CardExpander>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</ui:Card>
|
||||||
|
|
||||||
|
<ui:Card Grid.Row="2" Grid.ColumnSpan="2" Margin="0 5 0 0" Padding="5">
|
||||||
|
<Grid >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="1*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="0.5*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="0.5*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
|
||||||
|
<ComboBox x:Name="layoutsComboBox" ItemsSource="{Binding Settings.Layouts}" DisplayMemberPath="Name" SelectedValue="{Binding Settings.CurrentLayoutName}" SelectedValuePath="Name" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Padding="4" SelectionChanged="LayoutsComboBox_SelectionChanged" />
|
||||||
|
<Button x:Name="newLayoutButton" Grid.Column="2" HorizontalAlignment="Stretch" Click="NewLayoutButton_Click" FontWeight="Regular">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Add24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{DynamicResource newLayout}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="removeLayoutButton" Grid.Column="4" HorizontalAlignment="Stretch" Click="RemoveLayoutButton_Click" FontWeight="Regular">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Delete24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{DynamicResource deleteLayout}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</ui:Card>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainPage.xaml
|
||||||
|
/// </summary>
|
||||||
|
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<uint, PluginSettings> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<Page x:Class="DesktopMagic.Pages.ThemePage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:DesktopMagic.Pages"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
|
Title="ThemePage">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<ui:ListView x:Name="themesListBox" Grid.Row="0" Grid.ColumnSpan="1" Margin="{StaticResource DefaultMargin}" ItemsSource="{Binding Settings.Themes}" SelectedValue="{Binding Settings.CurrentLayout.CurrentThemeName}" SelectedValuePath="Name" SelectedIndex="2" SelectionMode="Single">
|
||||||
|
<ui:ListView.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Margin="8,4" Text="{Binding Name, Mode=OneWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ui:ListView.ItemTemplate>
|
||||||
|
</ui:ListView>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="{StaticResource DefaultMargin}">
|
||||||
|
<Button x:Name="addThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="AddThemeButton_Click" FontWeight="Regular">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Add24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{DynamicResource newTheme}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="deleteThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="DeleteThemeButton_Click" FontWeight="Regular">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Delete24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{DynamicResource deleteTheme}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="changeThemeButton" Margin="0,0,0,5" HorizontalAlignment="Stretch" Click="ChangeThemeButton_Click" FontWeight="Regular">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Color24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{DynamicResource changeTheme}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for ThemePage.xaml
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
|
||||||
xmlns:local="clr-namespace:DesktopMagic.Plugins"
|
xmlns:local="clr-namespace:DesktopMagic.Plugins"
|
||||||
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
||||||
d:DataContext="{d:DesignInstance Type=dataContext:PluginEntryDataContext}"
|
d:DataContext="{d:DesignInstance Type=dataContext:PluginEntryDataContext}"
|
||||||
@@ -12,52 +12,54 @@
|
|||||||
Background="{DynamicResource MaterialDesignPaper}"
|
Background="{DynamicResource MaterialDesignPaper}"
|
||||||
FontFamily="{DynamicResource MaterialDesignFont}"
|
FontFamily="{DynamicResource MaterialDesignFont}"
|
||||||
Visibility="{Binding Visibility}">
|
Visibility="{Binding Visibility}">
|
||||||
<Grid Background="White" Margin="10">
|
<ui:Card Padding="5">
|
||||||
<Grid.RowDefinitions>
|
<Grid>
|
||||||
<RowDefinition Height="2*" />
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto" />
|
<RowDefinition Height="2*" />
|
||||||
<RowDefinition Height="3*" />
|
<RowDefinition Height="auto" />
|
||||||
</Grid.RowDefinitions>
|
<RowDefinition Height="3*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto" />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Border HorizontalAlignment="Center" Margin="10" Width="100" Height="100" BorderThickness="1" BorderBrush="LightGray">
|
|
||||||
<Image Source="{Binding Logo, FallbackValue={StaticResource ModioCogBlue}, TargetNullValue={StaticResource ModioCogBlue}}" Width="100" Height="100" />
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
|
||||||
<Label Content="{Binding Author}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource authorFormat}" />
|
|
||||||
<Label Content="{Binding DateAdded}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource addedFormat}" />
|
|
||||||
<Label Content="{Binding DateUpdated}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource updatedFormat}" />
|
|
||||||
<Label Content="{Binding Version}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource versionFormat}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Grid.ColumnSpan="2">
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition Width="auto" />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Button Grid.Column="0" Command="{Binding InstallUninstallButtonData.Command}" IsEnabled="{Binding InstallUninstallButtonData.IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Margin="10 5 5 0">
|
<ui:Card HorizontalAlignment="Center" Width="100" Padding="5" Height="100" BorderThickness="1" BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||||
<StackPanel Orientation="Horizontal">
|
<Image Source="{Binding Logo, FallbackValue={StaticResource ModioCogBlue}, TargetNullValue={StaticResource ModioCogBlue}}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||||
<materialDesign:PackIcon Kind="{Binding InstallUninstallButtonData.IconKind}" Margin="0 0 10 0" />
|
</ui:Card>
|
||||||
<TextBlock Text="{Binding InstallUninstallButtonData.Text}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Grid.Column="1" Command="{Binding OpenButtonData.Command}" IsEnabled="{Binding OpenButtonData.IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Margin="5 5 10 0">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="{Binding OpenButtonData.IconKind}" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{Binding OpenButtonData.Text}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="10">
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Label Content="{Binding Name}" FontSize="20" Padding="0" />
|
<Label Content="{Binding Author, Converter={StaticResource UnderscoreEscapingConverter}}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource authorFormat}" />
|
||||||
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" />
|
<Label Content="{Binding DateAdded}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource addedFormat}" />
|
||||||
</StackPanel>
|
<Label Content="{Binding DateUpdated}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource updatedFormat}" />
|
||||||
</Grid>
|
<Label Content="{Binding Version}" HorizontalAlignment="Center" ContentStringFormat="{DynamicResource versionFormat}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Grid.ColumnSpan="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Button Grid.Column="0" Command="{Binding InstallUninstallButtonData.Command}" IsEnabled="{Binding InstallUninstallButtonData.IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="Delete24" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{Binding InstallUninstallButtonData.Text}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Grid.Column="1" Command="{Binding OpenButtonData.Command}" IsEnabled="{Binding OpenButtonData.IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Margin="5 5 10 0">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="{Binding OpenButtonData.Icon}" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{Binding OpenButtonData.Text}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="0 5">
|
||||||
|
<Label Content="{Binding Name, Converter={StaticResource UnderscoreEscapingConverter}}" FontSize="20" Padding="0" />
|
||||||
|
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</ui:Card>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -1,24 +1,20 @@
|
|||||||
<Window x:Class="DesktopMagic.Plugins.PluginManager"
|
<Page x:Class="DesktopMagic.Plugins.PluginManager"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
|
xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
|
||||||
xmlns:local="clr-namespace:DesktopMagic.Plugins"
|
xmlns:local="clr-namespace:DesktopMagic.Plugins"
|
||||||
xmlns:dataContexts="clr-namespace:DesktopMagic.DataContexts"
|
xmlns:dataContexts="clr-namespace:DesktopMagic.DataContexts"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DataContext="{d:DesignInstance Type=dataContexts:PluginManagerDataContext}"
|
d:DataContext="{d:DesignInstance Type=dataContexts:PluginManagerDataContext}"
|
||||||
Title="{DynamicResource pluginManager}"
|
d:DesignHeight="450"
|
||||||
Height="450"
|
d:DesignWidth="800"
|
||||||
Width="800"
|
ScrollViewer.CanContentScroll="False"
|
||||||
MinHeight="520"
|
Title="{DynamicResource pluginManager}">
|
||||||
MinWidth="700"
|
|
||||||
WindowStyle="ToolWindow"
|
|
||||||
Background="{DynamicResource MaterialDesignPaper}"
|
|
||||||
FontFamily="{DynamicResource MaterialDesignFont}">
|
|
||||||
|
|
||||||
<busyIndicator:BusyMask x:Name="BusyIndicator" IsBusy="{Binding IsLoading}" IndicatorType="Cogs" BusyContent="Please wait..." BusyContentMargin="0,20,0,0" IsBusyAtStartup="False">
|
<busyIndicator:BusyMask x:Name="BusyIndicator" IsBusy="{Binding IsLoading}" IndicatorType="Cogs" BusyContent="Please wait..." BusyContentMargin="0,20,0,0" IsBusyAtStartup="False" Foreground="{DynamicResource TextFillColorPrimaryBrush}" Background="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||||
<Grid Margin="5">
|
<Grid Margin="5">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto" />
|
<RowDefinition Height="auto" />
|
||||||
@@ -28,21 +24,58 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Label Content="{DynamicResource allPlugins}" FontSize="20" />
|
<ui:Card Grid.ColumnSpan="3" Padding="5" Margin="0 0 0 5">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<TextBox Grid.Row="1" Text="{Binding AllPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="AllPluginsSearchTextBox_TextChanged" materialDesign:HintAssist.Hint="Search..." materialDesign:TextFieldAssist.HasLeadingIcon="True" materialDesign:TextFieldAssist.LeadingIcon="Search" />
|
<ui:TextBox Grid.Column="0" Text="{Binding AllPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="AllPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchAllPlugins}" />
|
||||||
|
<ui:TextBox Grid.Column="2" Text="{Binding InstalledPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="InstalledPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchInstalledPlugins}" />
|
||||||
|
</Grid>
|
||||||
|
</ui:Card>
|
||||||
|
|
||||||
<busyIndicator:BusyMask Grid.Row="2" IsBusy="{Binding IsSearching}" IndicatorType="Ring" BusyContent="Searching..." BusyContentMargin="0,50,0,0" IsBusyAtStartup="False">
|
<busyIndicator:BusyMask Grid.Row="2" IsBusy="{Binding IsSearching}" IndicatorType="Ring" BusyContent="Searching..." BusyContentMargin="0,50,0,0" IsBusyAtStartup="False" Foreground="{DynamicResource TextFillColorPrimaryBrush}" Background="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||||
|
<ui:Card VerticalAlignment="Stretch" Padding="0" VerticalContentAlignment="Top">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
|
<ItemsControl ItemsSource="{Binding AllPlugins}" Margin="5">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<local:ModEntry Margin="0 0 0 5" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
<ItemsControl.Style>
|
||||||
|
<Style TargetType="ItemsControl">
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="HasItems" Value="false">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate>
|
||||||
|
<TextBlock Text="No plugins found!" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</ItemsControl.Style>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</ui:Card>
|
||||||
|
</busyIndicator:BusyMask>
|
||||||
|
|
||||||
|
<ui:Card Grid.Row="2" Grid.Column="2" VerticalAlignment="Stretch" Padding="0" VerticalContentAlignment="Top">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<ItemsControl ItemsSource="{Binding AllPlugins}">
|
<ItemsControl ItemsSource="{Binding InstalledPlugins}" Margin="5">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Border BorderBrush="LightGray" BorderThickness="1">
|
<local:ModEntry Margin="0 0 0 5"/>
|
||||||
<local:ModEntry />
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
<ItemsControl.Style>
|
<ItemsControl.Style>
|
||||||
@@ -62,56 +95,33 @@
|
|||||||
</ItemsControl.Style>
|
</ItemsControl.Style>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</busyIndicator:BusyMask>
|
</ui:Card>
|
||||||
|
|
||||||
<Label Grid.Column="1" Content="{DynamicResource installedPlugins}" FontSize="20" />
|
<ui:Card Grid.Row="3" Grid.ColumnSpan="3" Padding="5" Margin="0 5 0 0">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="10" />
|
||||||
|
<ColumnDefinition Width="0.5*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="0.5*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding InstalledPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="InstalledPluginsSearchTextBox_TextChanged" materialDesign:HintAssist.Hint="Search..." materialDesign:TextFieldAssist.HasLeadingIcon="True" materialDesign:TextFieldAssist.LeadingIcon="Search" />
|
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="2" Grid.Column="1" VerticalScrollBarVisibility="Auto">
|
|
||||||
<ItemsControl ItemsSource="{Binding InstalledPlugins}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Border BorderBrush="LightGray" BorderThickness="1">
|
|
||||||
<local:ModEntry />
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
<ItemsControl.Style>
|
|
||||||
<Style TargetType="ItemsControl">
|
|
||||||
<Style.Triggers>
|
|
||||||
<Trigger Property="HasItems" Value="false">
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate>
|
|
||||||
<TextBlock Text="No plugins found!" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</ItemsControl.Style>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<Border Grid.Row="3" Grid.ColumnSpan="2" Padding="0 5 0 0" BorderThickness="0 2 0 0" BorderBrush="DarkGray">
|
|
||||||
<DockPanel>
|
|
||||||
<Image Source="{StaticResource ModioLogoBlueDark}" Cursor="Hand" Height="30" MouseUp="Image_MouseUp" HorizontalAlignment="Left" />
|
<Image Source="{StaticResource ModioLogoBlueDark}" Cursor="Hand" Height="30" MouseUp="Image_MouseUp" HorizontalAlignment="Left" />
|
||||||
<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Click="LogInButton_Click">
|
<Button Grid.Column="2" HorizontalAlignment="Stretch" Click="CreatePluginButton_Click">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<materialDesign:PackIcon Kind="Login" Margin="0 0 10 0" />
|
<ui:SymbolIcon Symbol="Add24" Margin="0 0 10 0" />
|
||||||
<TextBlock Text="{Binding LoginButtonText}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="0 0 10 0" Click="CreatePluginButton_Click">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="Add" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource createNewPlugin}" />
|
<TextBlock Text="{DynamicResource createNewPlugin}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
</DockPanel>
|
<Button Grid.Column="4" HorizontalAlignment="Stretch" Click="LogInButton_Click">
|
||||||
</Border>
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ui:SymbolIcon Symbol="PersonKey20" Margin="0 0 10 0" />
|
||||||
|
<TextBlock Text="{Binding LoginButtonText}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</ui:Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
</busyIndicator:BusyMask>
|
</busyIndicator:BusyMask>
|
||||||
</Window>
|
</Page>
|
||||||
@@ -8,7 +8,6 @@ using Modio.Models;
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -19,6 +18,7 @@ using System.Text.Json;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ namespace DesktopMagic.Plugins;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for PluginManager.xaml
|
/// Interaction logic for PluginManager.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PluginManager : Window
|
public partial class PluginManager : Page
|
||||||
{
|
{
|
||||||
private const int ModIoGameId = 5665;
|
private const int ModIoGameId = 5665;
|
||||||
private const string ModIoApiKey = "88e6ea774c3a502b06114e7fee0829ac";
|
private const string ModIoApiKey = "88e6ea774c3a502b06114e7fee0829ac";
|
||||||
@@ -38,6 +38,9 @@ public partial class PluginManager : Window
|
|||||||
private readonly PluginManagerDataContext pluginManagerDataContext = new();
|
private readonly PluginManagerDataContext pluginManagerDataContext = new();
|
||||||
private readonly string pluginsPath = Path.Combine(App.ApplicationDataPath, "Plugins");
|
private readonly string pluginsPath = Path.Combine(App.ApplicationDataPath, "Plugins");
|
||||||
private readonly string pluginDevelopmentPath = Path.Combine(App.ApplicationDataPath, "PluginDevelopment");
|
private readonly string pluginDevelopmentPath = Path.Combine(App.ApplicationDataPath, "PluginDevelopment");
|
||||||
|
private readonly Manager _manager = Manager.Instance;
|
||||||
|
|
||||||
|
private bool changed = false;
|
||||||
|
|
||||||
private readonly DispatcherTimer searchTimer = new()
|
private readonly DispatcherTimer searchTimer = new()
|
||||||
{
|
{
|
||||||
@@ -72,55 +75,29 @@ public partial class PluginManager : Window
|
|||||||
searchTimer.Stop();
|
searchTimer.Stop();
|
||||||
await SearchAllPlugins(pluginManagerDataContext.AllPluginsSearchText);
|
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");
|
await InitializePluginManager();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
App.Logger.LogInfo("Initializing Plugin Manager", source: "PluginManager");
|
||||||
HashSet<uint> pluginIds = [];
|
HashSet<uint> pluginIds = [];
|
||||||
|
|
||||||
@@ -197,9 +174,48 @@ public partial class PluginManager : Window
|
|||||||
App.Logger.LogInfo("Plugin Manager initialization complete", source: "PluginManager");
|
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]")]
|
[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");
|
App.Logger.LogInfo($"Installing plugin: {mod.Name} (ID: {mod.Id})", source: "PluginManager");
|
||||||
pluginManagerDataContext.IsLoading = true;
|
pluginManagerDataContext.IsLoading = true;
|
||||||
|
changed = true;
|
||||||
|
|
||||||
if (mod.Modfile?.Download?.BinaryUrl is null)
|
if (mod.Modfile?.Download?.BinaryUrl is null)
|
||||||
{
|
{
|
||||||
@@ -383,7 +400,7 @@ public partial class PluginManager : Window
|
|||||||
|
|
||||||
InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager")
|
InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager")
|
||||||
{
|
{
|
||||||
Owner = this,
|
Owner = Window.GetWindow(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inputDialog.ShowDialog() != true)
|
if (inputDialog.ShowDialog() != true)
|
||||||
@@ -547,7 +564,7 @@ public class {pluginSafeName}Plugin : Plugin
|
|||||||
{
|
{
|
||||||
InputDialog inputDialog = new((string)FindResource("enterModIoEmail"), "Plugin Manager")
|
InputDialog inputDialog = new((string)FindResource("enterModIoEmail"), "Plugin Manager")
|
||||||
{
|
{
|
||||||
Owner = this,
|
Owner = Window.GetWindow(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inputDialog.ShowDialog() != true)
|
if (inputDialog.ShowDialog() != true)
|
||||||
@@ -561,7 +578,7 @@ public class {pluginSafeName}Plugin : Plugin
|
|||||||
|
|
||||||
inputDialog = new((string)FindResource("enterModIoAccessToken"), "Plugin Manager")
|
inputDialog = new((string)FindResource("enterModIoAccessToken"), "Plugin Manager")
|
||||||
{
|
{
|
||||||
Owner = this,
|
Owner = Window.GetWindow(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inputDialog.ShowDialog() != true)
|
if (inputDialog.ShowDialog() != true)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<Window x:Class="DesktopMagic.PluginWindow"
|
<Window x:Class="DesktopMagic.PluginWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:DesktopMagic"
|
xmlns:local="clr-namespace:DesktopMagic"
|
||||||
@@ -15,19 +16,21 @@
|
|||||||
Closing="Window_Closing"
|
Closing="Window_Closing"
|
||||||
ContentRendered="Window_ContentRendered"
|
ContentRendered="Window_ContentRendered"
|
||||||
x:Name="window">
|
x:Name="window">
|
||||||
<Grid>
|
<busyIndicator:BusyMask x:Name="busyMask" IsBusyAtStartup="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
||||||
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
<Grid>
|
||||||
|
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
||||||
|
|
||||||
<Border SizeChanged="Border_SizeChanged" x:Name="border" />
|
<Border SizeChanged="Border_SizeChanged" x:Name="border" />
|
||||||
<Viewbox SizeChanged="ViewBox_SizeChanged" x:Name="viewBox" StretchDirection="Both" Stretch="Uniform">
|
<Viewbox SizeChanged="ViewBox_SizeChanged" x:Name="viewBox" StretchDirection="Both" Stretch="Uniform">
|
||||||
<Viewbox.Clip>
|
<Viewbox.Clip>
|
||||||
<RectangleGeometry x:Name="rectangleGeometry" RadiusX="{Binding ElementName=border, Path=CornerRadius.TopLeft}" RadiusY="{Binding ElementName=border, Path=CornerRadius.TopLeft}" />
|
<RectangleGeometry x:Name="rectangleGeometry" RadiusX="{Binding ElementName=border, Path=CornerRadius.TopLeft}" RadiusY="{Binding ElementName=border, Path=CornerRadius.TopLeft}" />
|
||||||
</Viewbox.Clip>
|
</Viewbox.Clip>
|
||||||
<Border x:Name="imageBorder" BorderBrush="#BAFFEF">
|
<Border x:Name="imageBorder" BorderBrush="#BAFFEF">
|
||||||
<Image x:Name="image" RenderOptions.EdgeMode="Aliased" Margin="0,0,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Stretch="Uniform" MouseDown="Window_MouseDown" MouseMove="Window_MouseMove" MouseWheel="Window_MouseWheel" />
|
<Image x:Name="image" RenderOptions.EdgeMode="Aliased" Margin="0,0,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Stretch="Uniform" MouseDown="Window_MouseDown" MouseMove="Window_MouseMove" MouseWheel="Window_MouseWheel" />
|
||||||
</Border>
|
</Border>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</busyIndicator:BusyMask>
|
||||||
<WindowChrome.WindowChrome>
|
<WindowChrome.WindowChrome>
|
||||||
<WindowChrome x:Name="tileBar" CaptionHeight="30" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
<WindowChrome x:Name="tileBar" CaptionHeight="30" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||||
</WindowChrome.WindowChrome>
|
</WindowChrome.WindowChrome>
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PluginLoaded?.Invoke();
|
PluginLoaded?.Invoke();
|
||||||
|
|
||||||
|
_ = Dispatcher.Invoke(() => busyMask.IsBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged()
|
private void ThemeChanged()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<Window x:Class="DesktopMagic.WebPluginWindow"
|
<Window x:Class="DesktopMagic.WebPluginWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:busyIndicator="https://github.com/moh3ngolshani/BusyIndicator"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:DesktopMagic"
|
xmlns:local="clr-namespace:DesktopMagic"
|
||||||
@@ -16,20 +17,23 @@
|
|||||||
Closing="Window_Closing"
|
Closing="Window_Closing"
|
||||||
ContentRendered="Window_ContentRendered"
|
ContentRendered="Window_ContentRendered"
|
||||||
x:Name="window">
|
x:Name="window">
|
||||||
<Grid>
|
|
||||||
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
|
||||||
|
|
||||||
<Border x:Name="border" Background="Transparent" CornerRadius="10">
|
<busyIndicator:BusyMask x:Name="busyMask" IsBusyAtStartup="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
||||||
<Grid>
|
<Grid>
|
||||||
<wv2:WebView2 x:Name="webView" DefaultBackgroundColor="Transparent" CoreWebView2InitializationCompleted="WebView_CoreWebView2InitializationCompleted" />
|
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
||||||
<!--
|
|
||||||
|
<Border x:Name="border" Background="Transparent" CornerRadius="10">
|
||||||
|
<Grid>
|
||||||
|
<wv2:WebView2 x:Name="webView" DefaultBackgroundColor="Transparent" CoreWebView2InitializationCompleted="WebView_CoreWebView2InitializationCompleted" />
|
||||||
|
<!--
|
||||||
WORKAROUND: This rectangle exists to allow interaction with the WebView2 when the window is transparent
|
WORKAROUND: This rectangle exists to allow interaction with the WebView2 when the window is transparent
|
||||||
https://github.com/MicrosoftEdge/WebView2Feedback/issues/997
|
https://github.com/MicrosoftEdge/WebView2Feedback/issues/997
|
||||||
-->
|
-->
|
||||||
<Rectangle Fill="#01000000" />
|
<Rectangle Fill="#01000000" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</busyIndicator:BusyMask>
|
||||||
<WindowChrome.WindowChrome>
|
<WindowChrome.WindowChrome>
|
||||||
<WindowChrome x:Name="tileBar" CaptionHeight="30" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
<WindowChrome x:Name="tileBar" CaptionHeight="30" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||||
</WindowChrome.WindowChrome>
|
</WindowChrome.WindowChrome>
|
||||||
|
|||||||
@@ -192,6 +192,8 @@ public partial class WebPluginWindow : Window, IPluginWindow
|
|||||||
|
|
||||||
App.Logger.LogInfo($"\"{PluginMetadata.Name}\" - WebView2 initialized successfully", source: "WebPlugin");
|
App.Logger.LogInfo($"\"{PluginMetadata.Name}\" - WebView2 initialized successfully", source: "WebPlugin");
|
||||||
PluginLoaded?.Invoke();
|
PluginLoaded?.Invoke();
|
||||||
|
|
||||||
|
busyMask.IsBusy = false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
|
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
|
<system:String x:Key="developedBy">Entwickelt von Stone_Red</system:String>
|
||||||
|
<system:String x:Key="reportBug">Bug Melden</system:String>
|
||||||
|
<system:String x:Key="requestFeature">Feature Anfragen</system:String>
|
||||||
<system:String x:Key="lineMode">Linie</system:String>
|
<system:String x:Key="lineMode">Linie</system:String>
|
||||||
<system:String x:Key="mirrorMode">Spiegeln</system:String>
|
<system:String x:Key="mirrorMode">Spiegeln</system:String>
|
||||||
<system:String x:Key="folder">Ordner</system:String>
|
<system:String x:Key="folder">Ordner</system:String>
|
||||||
@@ -15,13 +18,15 @@
|
|||||||
<system:String x:Key="enterModIoEmail">Mod.io Account E-Mail eingeben</system:String>
|
<system:String x:Key="enterModIoEmail">Mod.io Account E-Mail eingeben</system:String>
|
||||||
<system:String x:Key="enterModIoAccessToken">Geben Sie Ihren mod.io Sicherheitscode ein</system:String>
|
<system:String x:Key="enterModIoAccessToken">Geben Sie Ihren mod.io Sicherheitscode ein</system:String>
|
||||||
<system:String x:Key="toggleEditMode">Bearbeitungsmodus umschalten</system:String>
|
<system:String x:Key="toggleEditMode">Bearbeitungsmodus umschalten</system:String>
|
||||||
<system:String x:Key="allPlugins">Alle Plugins</system:String>
|
<system:String x:Key="searchAllPlugins">Alle Plugins Suchen...</system:String>
|
||||||
<system:String x:Key="installedPlugins">Installierte Plugins</system:String>
|
<system:String x:Key="searchInstalledPlugins">Installierte Plugins Suchen...</system:String>
|
||||||
<system:String x:Key="updatePlugins">Neu Laden</system:String>
|
<system:String x:Key="reloadPlugins">Plugins Neu Laden</system:String>
|
||||||
<system:String x:Key="pluginsFolder">Plugins Ordner</system:String>
|
<system:String x:Key="pluginsFolder">Plugins Ordner</system:String>
|
||||||
<system:String x:Key="pluginManager">Plugins Manager</system:String>
|
<system:String x:Key="pluginManager">Plugins Manager</system:String>
|
||||||
<system:String x:Key="createNewPlugin">Neues Plugin erstellen</system:String>
|
<system:String x:Key="createNewPlugin">Neues Plugin erstellen</system:String>
|
||||||
<system:String x:Key="enterPluginName">Pluginnamen eingeben</system:String>
|
<system:String x:Key="enterPluginName">Pluginnamen eingeben</system:String>
|
||||||
|
<system:String x:Key="plugins">Plugins</system:String>
|
||||||
|
<system:String x:Key="enablePluginToConfigure">Aktivieren Sie das Plugin, um seine Einstellungen zu konfigurieren.</system:String>
|
||||||
<system:String x:Key="color">Farbe:</system:String>
|
<system:String x:Key="color">Farbe:</system:String>
|
||||||
<system:String x:Key="default">Standard</system:String>
|
<system:String x:Key="default">Standard</system:String>
|
||||||
<system:String x:Key="wantToCloseProgram">Wollen sie das Programm wirklich schließen?</system:String>
|
<system:String x:Key="wantToCloseProgram">Wollen sie das Programm wirklich schließen?</system:String>
|
||||||
@@ -38,6 +43,7 @@
|
|||||||
<system:String x:Key="newTheme">Neues Theme</system:String>
|
<system:String x:Key="newTheme">Neues Theme</system:String>
|
||||||
<system:String x:Key="deleteTheme">Theme Löschen</system:String>
|
<system:String x:Key="deleteTheme">Theme Löschen</system:String>
|
||||||
<system:String x:Key="changeTheme">Theme Ändern</system:String>
|
<system:String x:Key="changeTheme">Theme Ändern</system:String>
|
||||||
|
<system:String x:Key="themes">Themes</system:String>
|
||||||
<system:String x:Key="ok">Ok</system:String>
|
<system:String x:Key="ok">Ok</system:String>
|
||||||
<system:String x:Key="cancel">Abbrechen</system:String>
|
<system:String x:Key="cancel">Abbrechen</system:String>
|
||||||
<system:String x:Key="confirmDeleteLayout">Möchten Sie dieses Layout wirklich löschen?</system:String>
|
<system:String x:Key="confirmDeleteLayout">Möchten Sie dieses Layout wirklich löschen?</system:String>
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
|
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
|
<system:String x:Key="developedBy">Developed by Stone_Red</system:String>
|
||||||
|
<system:String x:Key="reportBug">Report Bug</system:String>
|
||||||
|
<system:String x:Key="requestFeature">Request Feature</system:String>
|
||||||
<system:String x:Key="lineMode">Line</system:String>
|
<system:String x:Key="lineMode">Line</system:String>
|
||||||
<system:String x:Key="mirrorMode">Mirror</system:String>
|
<system:String x:Key="mirrorMode">Mirror</system:String>
|
||||||
<system:String x:Key="folder">Folder</system:String>
|
<system:String x:Key="folder">Folder</system:String>
|
||||||
@@ -15,13 +18,15 @@
|
|||||||
<system:String x:Key="enterModIoEmail">Enter your mod.io account E-mail</system:String>
|
<system:String x:Key="enterModIoEmail">Enter your mod.io account E-mail</system:String>
|
||||||
<system:String x:Key="enterModIoAccessToken">Enter your mod.io security code</system:String>
|
<system:String x:Key="enterModIoAccessToken">Enter your mod.io security code</system:String>
|
||||||
<system:String x:Key="toggleEditMode">Toggle Edit Mode</system:String>
|
<system:String x:Key="toggleEditMode">Toggle Edit Mode</system:String>
|
||||||
<system:String x:Key="allPlugins">All Plugins</system:String>
|
<system:String x:Key="searchAllPlugins">Search All Plugins...</system:String>
|
||||||
<system:String x:Key="installedPlugins">Installed Plugins</system:String>
|
<system:String x:Key="searchInstalledPlugins">Search Installed Plugins...</system:String>
|
||||||
<system:String x:Key="updatePlugins">Reload</system:String>
|
<system:String x:Key="reloadPlugins">Reload Plugins</system:String>
|
||||||
<system:String x:Key="pluginsFolder">Plugins Folder</system:String>
|
<system:String x:Key="pluginsFolder">Plugins Folder</system:String>
|
||||||
<system:String x:Key="pluginManager">Plugins Manager</system:String>
|
<system:String x:Key="pluginManager">Plugins Manager</system:String>
|
||||||
<system:String x:Key="createNewPlugin">Create New Plugin</system:String>
|
<system:String x:Key="createNewPlugin">Create New Plugin</system:String>
|
||||||
<system:String x:Key="enterPluginName">Enter plugin name</system:String>
|
<system:String x:Key="enterPluginName">Enter plugin name</system:String>
|
||||||
|
<system:String x:Key="plugins">Plugins</system:String>
|
||||||
|
<system:String x:Key="enablePluginToConfigure">Enable the plugin to configure its settings.</system:String>
|
||||||
<system:String x:Key="color">Color:</system:String>
|
<system:String x:Key="color">Color:</system:String>
|
||||||
<system:String x:Key="default">Default</system:String>
|
<system:String x:Key="default">Default</system:String>
|
||||||
<system:String x:Key="wantToCloseProgram">Do you really want to close the program?</system:String>
|
<system:String x:Key="wantToCloseProgram">Do you really want to close the program?</system:String>
|
||||||
@@ -38,6 +43,7 @@
|
|||||||
<system:String x:Key="newTheme">New Theme</system:String>
|
<system:String x:Key="newTheme">New Theme</system:String>
|
||||||
<system:String x:Key="deleteTheme">Delete Theme</system:String>
|
<system:String x:Key="deleteTheme">Delete Theme</system:String>
|
||||||
<system:String x:Key="changeTheme">Change Theme</system:String>
|
<system:String x:Key="changeTheme">Change Theme</system:String>
|
||||||
|
<system:String x:Key="themes">Themes</system:String>
|
||||||
<system:String x:Key="ok">Ok</system:String>
|
<system:String x:Key="ok">Ok</system:String>
|
||||||
<system:String x:Key="cancel">Cancel</system:String>
|
<system:String x:Key="cancel">Cancel</system:String>
|
||||||
<system:String x:Key="confirmDeleteLayout">Do you really want to delete this layout?</system:String>
|
<system:String x:Key="confirmDeleteLayout">Do you really want to delete this layout?</system:String>
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
|
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||||
|
|
||||||
|
<system:Double x:Key="ToggleButtonWidth">40</system:Double>
|
||||||
|
<system:Double x:Key="ToggleButtonHeight">20</system:Double>
|
||||||
|
<Thickness x:Key="ToggleSwitchBorderThemeThickness">1</Thickness>
|
||||||
|
<Thickness x:Key="ToggleSwitchContentLeftMargin">0,0,8,0</Thickness>
|
||||||
|
|
||||||
|
<Style x:Key="ToggleSwitchContentLeftStyle" TargetType="{x:Type ui:ToggleSwitch}">
|
||||||
|
<Setter Property="FocusVisualStyle" Value="{DynamicResource DefaultControlFocusVisualStyle}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOn}" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchContentForeground}" />
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
|
<Setter Property="BorderThickness" Value="{StaticResource ToggleSwitchBorderThemeThickness}" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
|
||||||
|
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type ui:ToggleSwitch}">
|
||||||
|
<Grid Margin="{TemplateBinding Padding}" Background="Transparent">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="ContentPresenter"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource ToggleSwitchContentLeftMargin}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
RecognizesAccessKey="True"
|
||||||
|
TextElement.Foreground="{TemplateBinding Foreground}" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="{StaticResource ToggleButtonWidth}"
|
||||||
|
Height="{StaticResource ToggleButtonHeight}">
|
||||||
|
<Rectangle
|
||||||
|
x:Name="ToggleRectangle"
|
||||||
|
Width="{StaticResource ToggleButtonWidth}"
|
||||||
|
Height="{StaticResource ToggleButtonHeight}"
|
||||||
|
Fill="{DynamicResource ToggleSwitchFillOff}"
|
||||||
|
RadiusX="10" RadiusY="10"
|
||||||
|
Stroke="{DynamicResource ToggleSwitchStrokeOff}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
<Rectangle
|
||||||
|
x:Name="ActiveToggleRectangle"
|
||||||
|
Width="{StaticResource ToggleButtonWidth}"
|
||||||
|
Height="{StaticResource ToggleButtonHeight}"
|
||||||
|
Fill="{TemplateBinding Background}"
|
||||||
|
Opacity="0.0"
|
||||||
|
RadiusX="10" RadiusY="10" />
|
||||||
|
<Rectangle
|
||||||
|
x:Name="ToggleEllipse"
|
||||||
|
Width="12" Height="12"
|
||||||
|
Fill="{DynamicResource ToggleSwitchKnobFillOff}"
|
||||||
|
RadiusX="7" RadiusY="7">
|
||||||
|
<Rectangle.RenderTransform>
|
||||||
|
<TranslateTransform X="-9" />
|
||||||
|
</Rectangle.RenderTransform>
|
||||||
|
</Rectangle>
|
||||||
|
<Rectangle
|
||||||
|
x:Name="ActiveToggleEllipse"
|
||||||
|
Width="12" Height="12"
|
||||||
|
Fill="{DynamicResource ToggleSwitchKnobFillOn}"
|
||||||
|
Opacity="0.0"
|
||||||
|
RadiusX="7" RadiusY="7">
|
||||||
|
<Rectangle.RenderTransform>
|
||||||
|
<TranslateTransform X="-9" />
|
||||||
|
</Rectangle.RenderTransform>
|
||||||
|
</Rectangle>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Trigger.EnterActions>
|
||||||
|
<BeginStoryboard Name="SlideRight">
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ToggleRectangle" Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleRectangle" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ToggleEllipse" Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleEllipse" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleEllipse" Storyboard.TargetProperty="(Rectangle.RenderTransform).(TranslateTransform.X)" To="9" Duration="0:0:0.167" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</Trigger.EnterActions>
|
||||||
|
<Trigger.ExitActions>
|
||||||
|
<BeginStoryboard>
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ToggleRectangle" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleRectangle" Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ToggleEllipse" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleEllipse" Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.16" />
|
||||||
|
<DoubleAnimation Storyboard.TargetName="ActiveToggleEllipse" Storyboard.TargetProperty="(Rectangle.RenderTransform).(TranslateTransform.X)" To="-9" Duration="0:0:0.167" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</Trigger.ExitActions>
|
||||||
|
</Trigger>
|
||||||
|
|
||||||
|
<MultiTrigger>
|
||||||
|
<MultiTrigger.Conditions>
|
||||||
|
<Condition Property="Content" Value="{x:Null}" />
|
||||||
|
<Condition Property="IsChecked" Value="True" />
|
||||||
|
</MultiTrigger.Conditions>
|
||||||
|
<Setter TargetName="ContentPresenter" Property="Content" Value="{Binding OnContent, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||||
|
</MultiTrigger>
|
||||||
|
<MultiTrigger>
|
||||||
|
<MultiTrigger.Conditions>
|
||||||
|
<Condition Property="Content" Value="{x:Null}" />
|
||||||
|
<Condition Property="IsChecked" Value="False" />
|
||||||
|
</MultiTrigger.Conditions>
|
||||||
|
<Setter TargetName="ContentPresenter" Property="Content" Value="{Binding OffContent, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||||
|
</MultiTrigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace DesktopMagic.Settings;
|
namespace DesktopMagic.Settings;
|
||||||
|
|
||||||
internal class DesktopMagicSettings : INotifyPropertyChanged
|
public class DesktopMagicSettings : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace DesktopMagic.Settings;
|
namespace DesktopMagic.Settings;
|
||||||
|
|
||||||
internal class Layout(string name) : INotifyPropertyChanged
|
public class Layout(string name) : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class PluginSettings : INotifyPropertyChanged
|
|||||||
|
|
||||||
// Only for internal use to show the name of the plugin in the main window
|
// Only for internal use to show the name of the plugin in the main window
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string Name { get; set; } = string.Empty;
|
public PluginMetadata Metadata { get; set; } = new();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Theme Theme
|
public Theme Theme
|
||||||
|
|||||||
Reference in New Issue
Block a user