diff --git a/installer/DesktopMagic-Installer.exe b/installer/DesktopMagic-Installer.exe index ef85580..8d439d7 100644 Binary files a/installer/DesktopMagic-Installer.exe and b/installer/DesktopMagic-Installer.exe differ diff --git a/src/DesktopMagic/BuiltInPlugins/DatePlugin.cs b/src/DesktopMagic/BuiltInPlugins/DatePlugin.cs index cefe11e..08d22e0 100644 --- a/src/DesktopMagic/BuiltInPlugins/DatePlugin.cs +++ b/src/DesktopMagic/BuiltInPlugins/DatePlugin.cs @@ -4,13 +4,34 @@ using DesktopMagic.Api.Settings; using System; using System.Drawing; using System.Drawing.Text; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; namespace DesktopMagic.BuiltInPlugins; -internal class DatePlugin : Plugin +internal partial class DatePlugin : Plugin { - [Setting("short-date", "Short date")] - private readonly CheckBox shortDateCheckBox = new CheckBox(true); + [Setting("week-day-style", "Week Day Style")] + public ComboBox weekDayStyleComboBox = new ComboBox([.. Enum.GetValues().Select(e => e.ToString())]); + + [Setting("day-style", "Day Style")] + public ComboBox dayStyleComboBox = new ComboBox([.. Enum.GetValues().Select(e => e.ToString())]); + + [Setting("month-style", "Month Style")] + public ComboBox monthStyleComboBox = new ComboBox([.. Enum.GetValues().Select(e => e.ToString())]); + + [Setting("year-style", "Year Style")] + public ComboBox yearStyleComboBox = new ComboBox([.. Enum.GetValues().Select(e => e.ToString())]); + + [Setting("use-date-separators", "Use Date Separators")] + public CheckBox useDateSeparators = new CheckBox(true); + + [Setting("use-color-override-for-weekend", "Use Color Override For Weekend")] + public CheckBox useColorOverrideForWeekend = new CheckBox(false); + + [Setting("weekend-color-override", "Weekend Color Override")] + public ColorPicker weekendColorOverride = new ColorPicker(Color.Red); private DateTime oldDateTime = DateTime.MinValue; private bool themeChanged = false; @@ -27,7 +48,68 @@ internal class DatePlugin : Plugin oldDateTime = DateTime.Now; themeChanged = false; - string date = shortDateCheckBox.Value ? DateTime.Now.ToShortDateString() : DateTime.Now.ToLongDateString(); + CultureInfo culture = CultureInfo.CurrentCulture; + DateTime dateTime = DateTime.Now; + + DateTimeFormatInfo dtf = culture.DateTimeFormat; + + string pattern = dtf.LongDatePattern; + + WeekdayStyle weekdayStyle = Enum.Parse(weekDayStyleComboBox.Value); + DayStyle dayStyle = Enum.Parse(dayStyleComboBox.Value); + MonthStyle monthStyle = Enum.Parse(monthStyleComboBox.Value); + YearStyle yearStyle = Enum.Parse(yearStyleComboBox.Value); + + // Weekday: ddd, dddd + pattern = WeekdayRegex().Replace(pattern, weekdayStyle switch + { + WeekdayStyle.Long => "dddd", + WeekdayStyle.Short => "ddd", + _ => "" + }); + + // Day: d, dd + pattern = DayRegex().Replace(pattern, dayStyle switch + { + DayStyle.Numeric => "dd", + _ => "" + }); + + // Month: M, MM, MMM, MMMM + pattern = MonthRegex().Replace(pattern, monthStyle switch + { + MonthStyle.Long => "MMMM", + MonthStyle.Short => "MMM", + MonthStyle.Numeric => "MM", + _ => "" + }); + + // Year: yy, yyyy + pattern = YearRegex().Replace(pattern, yearStyle switch + { + YearStyle.FourDigit => "yyyy", + YearStyle.TwoDigit => "yy", + _ => "" + }); + + // Clean up leftover punctuation/spacing + pattern = CleanupRegex().Replace(pattern, " ").Trim(); + + if (string.IsNullOrWhiteSpace(pattern)) + { + pattern = dtf.LongDatePattern; + } + + string date = dateTime.ToString(pattern, culture); + + if (useDateSeparators.Value) + { + date = DateSeparatorsRegex().Replace(date, dtf.DateSeparator); + } + + Color color = useColorOverrideForWeekend.Value && (dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday) + ? weekendColorOverride.Value + : Application.Theme.PrimaryColor; using Font font = new Font(Application.Theme.Font, 200); @@ -42,7 +124,7 @@ internal class DatePlugin : Plugin bmp.SetResolution(100, 100); using Graphics gr = Graphics.FromImage(bmp); - using SolidBrush brush = new SolidBrush(Application.Theme.PrimaryColor); + using SolidBrush brush = new SolidBrush(color); gr.TextRenderingHint = TextRenderingHint.AntiAlias; gr.DrawString(date, font, brush, 0, 0); @@ -54,4 +136,32 @@ internal class DatePlugin : Plugin { themeChanged = true; } -} \ No newline at end of file + + public override void OnSettingsChanged() + { + themeChanged = true; + } + + [GeneratedRegex(@"d{3,4}")] + private static partial Regex WeekdayRegex(); + + [GeneratedRegex(@"\b(? command; - public ButtonData InstallUninstallButtonData => new(mode == Mode.Install ? "Download24" : "Delete24", GetInstallUninstallButtonText(), true, Command); + public ButtonData InstallUninstallButtonData + { + get + { + if (mode == Mode.Install) + { + return new(new SymbolIcon(SymbolRegular.ArrowDownload24), GetInstallUninstallButtonText(), true, Command); + } + else + { + return new(new SymbolIcon(SymbolRegular.Delete24), GetInstallUninstallButtonText(), true, Command); + } + } + } public ButtonData OpenButtonData { @@ -46,15 +61,15 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co { if (File.Exists(csprojPath)) { - return new("Code24", "IDE", true, new CommandHandler(OpenCsprojInIDE)); + return new(new SymbolIcon(SymbolRegular.Code24), "IDE", true, new CommandHandler(OpenCsprojInIDE)); } else if (string.IsNullOrWhiteSpace(pluginMetadata.ProfileUri?.ToString())) { - return new("Folder24", (string)App.LanguageDictionary["folder"], path is not null, new CommandHandler(() => Process.Start("explorer.exe", path!))); + return new(new SymbolIcon(SymbolRegular.Folder24), (string)App.LanguageDictionary["folder"], path is not null, new CommandHandler(() => Process.Start("explorer.exe", path!))); } else { - return new("Open24", "mod.io", true, new CommandHandler(OpenModIoPage)); + return new(new SymbolIcon(SymbolRegular.Open24), "mod.io", true, new CommandHandler(OpenModIoPage)); } } } @@ -126,7 +141,7 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public record ButtonData(string Icon, string Text, bool IsEnabled, ICommand Command); + public record ButtonData(SymbolIcon Icon, string Text, bool IsEnabled, ICommand Command); public enum Mode { diff --git a/src/DesktopMagic/DesktopMagic.csproj b/src/DesktopMagic/DesktopMagic.csproj index 6348d60..60c67c3 100644 --- a/src/DesktopMagic/DesktopMagic.csproj +++ b/src/DesktopMagic/DesktopMagic.csproj @@ -7,14 +7,15 @@ icon.ico OnBuildSuccess Stone_Red - 1.3.0.0 + 1.3.1.0 https://github.com/Stone-Red-Code/DesktopMagic https://github.com/Stone-Red-Code/DesktopMagic - 1.3.0.0 - 1.3.0.0 + 1.3.1.0 + 1.3.1.0 net8.0-windows7.0 win-x64 enable + true false AnyCPU;x64 @@ -43,16 +44,15 @@ - + - - + - - + + diff --git a/src/DesktopMagic/Dialogs/ColorDialog.xaml b/src/DesktopMagic/Dialogs/ColorDialog.xaml index 16bf62b..bf61033 100644 --- a/src/DesktopMagic/Dialogs/ColorDialog.xaml +++ b/src/DesktopMagic/Dialogs/ColorDialog.xaml @@ -28,7 +28,7 @@ - + diff --git a/src/DesktopMagic/Dialogs/ColorDialog.xaml.cs b/src/DesktopMagic/Dialogs/ColorDialog.xaml.cs index 0b860a8..2e3d8fd 100644 --- a/src/DesktopMagic/Dialogs/ColorDialog.xaml.cs +++ b/src/DesktopMagic/Dialogs/ColorDialog.xaml.cs @@ -24,6 +24,7 @@ public partial class ColorDialog : FluentWindow Resources.MergedDictionaries.Add(App.LanguageDictionary); label.Content = content; + titleBar.Title = title; Title = title; alphaSlider.Value = defaultColor.A; diff --git a/src/DesktopMagic/Dialogs/CreatePluginDialog.xaml b/src/DesktopMagic/Dialogs/CreatePluginDialog.xaml new file mode 100644 index 0000000..c5000bc --- /dev/null +++ b/src/DesktopMagic/Dialogs/CreatePluginDialog.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + Please check out the + + + + + + + + + - + + diff --git a/src/DesktopMagic/Pages/ThemePage.xaml b/src/DesktopMagic/Pages/ThemePage.xaml index 653b9eb..2efdfba 100644 --- a/src/DesktopMagic/Pages/ThemePage.xaml +++ b/src/DesktopMagic/Pages/ThemePage.xaml @@ -25,12 +25,9 @@ - + + + @@ -47,18 +44,8 @@ - - + + diff --git a/src/DesktopMagic/Plugins/PluginEntry.xaml b/src/DesktopMagic/Plugins/PluginEntry.xaml index 3710e77..6fbe473 100644 --- a/src/DesktopMagic/Plugins/PluginEntry.xaml +++ b/src/DesktopMagic/Plugins/PluginEntry.xaml @@ -40,18 +40,8 @@ - - + + diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml b/src/DesktopMagic/Plugins/PluginManager.xaml index b994a6a..d27c0fb 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml +++ b/src/DesktopMagic/Plugins/PluginManager.xaml @@ -14,7 +14,7 @@ ScrollViewer.CanContentScroll="False" Title="{DynamicResource pluginManager}"> - + @@ -36,12 +36,12 @@ - - + + - + @@ -106,24 +106,9 @@ - - - + + + diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml.cs b/src/DesktopMagic/Plugins/PluginManager.xaml.cs index a37a331..bb8f83f 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginManager.xaml.cs @@ -440,7 +440,7 @@ public partial class PluginManager : Page string pluginPath = Path.Combine(pluginsPath, pluginGuid); uint pluginId = (uint)Random.Shared.Next(1000, 9999); - InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager") + CreatePluginDialog inputDialog = new() { Owner = Window.GetWindow(this), }; diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml b/src/DesktopMagic/Plugins/PluginWindow.xaml index 9d0533c..b68b1ee 100644 --- a/src/DesktopMagic/Plugins/PluginWindow.xaml +++ b/src/DesktopMagic/Plugins/PluginWindow.xaml @@ -16,7 +16,7 @@ Closing="Window_Closing" ContentRendered="Window_ContentRendered" x:Name="window"> - + diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs index f45913b..f6cc550 100644 --- a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs @@ -42,6 +42,9 @@ public partial class PluginWindow : Window, IPluginWindow private System.Timers.Timer? reloadDebounceTimer; private bool isReloading = false; + private WriteableBitmap? writeableBitmap; + private BitmapScalingMode lastBitmapScalingMode = BitmapScalingMode.Unspecified; + public bool IsRunning { get; private set; } = true; public PluginMetadata PluginMetadata { get; private set; } public string PluginFolderPath { get; private set; } @@ -241,21 +244,21 @@ public partial class PluginWindow : Window, IPluginWindow assemblyLoadContext = CreateAssemblyLoadContext(); // Show busy indicator - await Dispatcher.InvokeAsync(() => busyMask.IsBusy = true); + _ = await Dispatcher.InvokeAsync(() => busyMask.IsBusy = true); // Reload the plugin await ExecuteSource(); // Hide busy indicator - await Dispatcher.InvokeAsync(() => busyMask.IsBusy = false); + _ = await Dispatcher.InvokeAsync(() => busyMask.IsBusy = false); App.Logger.LogInfo($"\"{PluginMetadata.Name}\" - Plugin reloaded successfully", source: "Plugin"); } catch (Exception ex) { App.Logger.LogError($"\"{PluginMetadata.Name}\" - Failed to reload plugin: {ex}", source: "Plugin"); - - await Dispatcher.InvokeAsync(async () => + + _ = await Dispatcher.InvokeAsync(async () => { Wpf.Ui.Controls.MessageBox messageBox = new Wpf.Ui.Controls.MessageBox { @@ -337,22 +340,52 @@ public partial class PluginWindow : Window, IPluginWindow WindowPos.GetWindowLong(helper.Handle, WindowPos.GWL_EXSTYLE) | WindowPos.WS_EX_NOACTIVATE); } - private static BitmapSource BitmapToImageSource(Bitmap bitmap) + private void UpdateImageFromBitmap(Bitmap bitmap, BitmapScalingMode scalingMode) { - BitmapData bitmapData = bitmap.LockBits( - new Rectangle(0, 0, bitmap.Width, bitmap.Height), - ImageLockMode.ReadOnly, bitmap.PixelFormat); + BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - BitmapSource bitmapSource = BitmapSource.Create( - bitmapData.Width, bitmapData.Height, - bitmap.HorizontalResolution, bitmap.VerticalResolution, - PixelFormats.Bgra32, null, - bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); + try + { + Dispatcher.Invoke(() => + { + if (lastBitmapScalingMode != scalingMode) + { + RenderOptions.SetBitmapScalingMode(image, scalingMode); + lastBitmapScalingMode = scalingMode; + } - bitmap.UnlockBits(bitmapData); + if (writeableBitmap == null || writeableBitmap.PixelWidth != bitmapData.Width || writeableBitmap.PixelHeight != bitmapData.Height) + { + writeableBitmap = new WriteableBitmap( + bitmapData.Width, bitmapData.Height, + bitmap.HorizontalResolution, bitmap.VerticalResolution, + PixelFormats.Bgra32, null); + image.Source = writeableBitmap; + } - bitmapSource.Freeze(); - return bitmapSource; + writeableBitmap.Lock(); + try + { + unsafe + { + Buffer.MemoryCopy( + bitmapData.Scan0.ToPointer(), + writeableBitmap.BackBuffer.ToPointer(), + writeableBitmap.BackBufferStride * writeableBitmap.PixelHeight, + bitmapData.Stride * bitmapData.Height); + } + writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, bitmapData.Width, bitmapData.Height)); + } + finally + { + writeableBitmap.Unlock(); + } + }); + } + finally + { + bitmap.UnlockBits(bitmapData); + } } private void Window_ContentRendered(object? sender, EventArgs e) @@ -525,11 +558,13 @@ public partial class PluginWindow : Window, IPluginWindow { SetHorizontalAlignment(); SetVerticalAlignment(); + SetRotation(); SetThemeOverride(); SetThemeOverrideItems(); pluginClassInstance.horizontalAlignment.OnValueChanged += SetHorizontalAlignment; pluginClassInstance.verticalAlignment.OnValueChanged += SetVerticalAlignment; + pluginClassInstance.rotation.OnValueChanged += SetRotation; pluginClassInstance.themeOverride.OnValueChanged += SetThemeOverride; DesktopMagicSettings desktopMagicSettings = MainWindowDataContext.GetSettings(); @@ -564,6 +599,11 @@ public partial class PluginWindow : Window, IPluginWindow border.HorizontalAlignment = horizontalAlignment; } + void SetRotation() + { + image.LayoutTransform = new RotateTransform(pluginClassInstance.rotation.Value); + } + void SetThemeOverride() { if (pluginClassInstance.themeOverride.Value == "") @@ -819,14 +859,7 @@ public partial class PluginWindow : Window, IPluginWindow _ => BitmapScalingMode.Unspecified }; - // Update Image - BitmapSource frozenSource = BitmapToImageSource(result); - - _ = Dispatcher.BeginInvoke(() => - { - RenderOptions.SetBitmapScalingMode(image, renderOptions); - image.Source = frozenSource; - }); + UpdateImageFromBitmap(result, renderOptions); } } } diff --git a/src/DesktopMagic/Plugins/WebPluginWindow.xaml b/src/DesktopMagic/Plugins/WebPluginWindow.xaml index deb7d11..e4de409 100644 --- a/src/DesktopMagic/Plugins/WebPluginWindow.xaml +++ b/src/DesktopMagic/Plugins/WebPluginWindow.xaml @@ -18,7 +18,7 @@ ContentRendered="Window_ContentRendered" x:Name="window"> - + diff --git a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml index 9218420..06be6cd 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml @@ -3,9 +3,10 @@ xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:system="clr-namespace:System;assembly=mscorlib"> - Entwickelt von Stone_Red + Entwickelt von Stone__Red Bug Melden Feature Anfragen + E-Mail Linie Spiegeln Ordner diff --git a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml index c524080..f1e0508 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml @@ -3,9 +3,10 @@ xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:system="clr-namespace:System;assembly=mscorlib"> - Developed by Stone_Red + Developed by Stone__Red Report Bug Request Feature + Email Line Mirror Folder diff --git a/src/DesktopMagicPlugin.Test/PluginScript.cs b/src/DesktopMagicPlugin.Test/PluginScript.cs index 454f5fb..4ce9809 100644 --- a/src/DesktopMagicPlugin.Test/PluginScript.cs +++ b/src/DesktopMagicPlugin.Test/PluginScript.cs @@ -14,10 +14,10 @@ namespace DesktopMagic.PluginTest; public class GifPlugin : Plugin { [Setting("gif-path", "GIF path")] - private readonly TextBox input = new TextBox(""); + private readonly FileSelector input = new FileSelector(); [Setting("info")] - private readonly Label info = new Label(""); + private readonly Label info = new Label("Select a GIF file to load."); private readonly List bitmaps = []; @@ -92,7 +92,7 @@ public class GifPlugin : Plugin info.Value = $"Loading frame {i + 1} of {gif.GetFrameCount(FrameDimension.Time)}"; } - info.Value = string.Empty; + info.Value = $"Loaded {bitmaps.Count} frames."; } else { diff --git a/src/DesktopMagicPluginAPI/Plugin.cs b/src/DesktopMagicPluginAPI/Plugin.cs index 5677365..b6fbc01 100644 --- a/src/DesktopMagicPluginAPI/Plugin.cs +++ b/src/DesktopMagicPluginAPI/Plugin.cs @@ -23,7 +23,10 @@ public abstract class Plugin [Setting("desktopmagic-vertical-alignment", "Vertical Alignment", -998)] internal ComboBox verticalAlignment = new ComboBox("Center", "Top", "Bottom"); - [Setting("theme-override", "Theme Override", -997)] + [Setting("rotation", "Rotation", -997)] + internal IntegerUpDown rotation = new IntegerUpDown(-360, 360, 0); + + [Setting("theme-override", "Theme Override", -996)] internal ComboBox themeOverride = new ComboBox(""); private IPluginData application = null!; diff --git a/src/MsixPackaging/Package.appxmanifest b/src/MsixPackaging/Package.appxmanifest index 9c328fd..6b84a76 100644 --- a/src/MsixPackaging/Package.appxmanifest +++ b/src/MsixPackaging/Package.appxmanifest @@ -9,7 +9,7 @@ + Version="1.3.1.0" /> Desktop Magic