mirror of
https://github.com/Stone-Red-Code/DesktopMagic.git
synced 2026-09-04 08:56:15 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b3b7aa4b6 | ||
|
|
2c82c9373f | ||
|
|
51b4f15cf1 | ||
|
|
2916f0983a | ||
|
|
3683e75a17 | ||
|
|
3d98c8b196 | ||
|
|
7604acf115 | ||
|
|
8c97f6b56a | ||
|
|
01a1d109ff | ||
|
|
d2f123e0f5 | ||
|
|
f217574022 | ||
|
|
1e9c74af52 | ||
|
|
2853d49dd9 | ||
|
|
4133f61a5c | ||
|
|
e3056a398a | ||
|
|
cd0bcad181 | ||
|
|
fec26069be | ||
|
|
f84dd20eb9 | ||
|
|
9581cc185c | ||
|
|
a438ab5475 | ||
|
|
1b496db094 | ||
|
|
3dce9ab59a | ||
|
|
a85e57ad58 | ||
|
|
1cafc01cd7 | ||
|
|
d319b32263 | ||
|
|
ca83e5585f |
@@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Third party licenses
|
## Third party licenses
|
||||||
|
|
||||||
- [BusyIndicator](https://github.com/moh3ngolshani/BusyIndicator) - [MIT](https://github.com/Moh3nGolshani/BusyIndicator/blob/master/LICENSE)
|
- [BusyIndicator](https://github.com/moh3ngolshani/BusyIndicator) - [MIT](https://github.com/Moh3nGolshani/BusyIndicator/blob/master/LICENSE)
|
||||||
- [Material Design In XAML Toolkit](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit) - [MIT](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/master/LICENSE)
|
- [WPF UI](https://github.com/lepoco/wpfui) - [MIT](https://github.com/lepoco/wpfui/blob/main/LICENSE.md)
|
||||||
- [Modio.NET](https://github.com/nickelc/modio.net) - [Apache-2.0](https://github.com/nickelc/modio.net/blob/master/LICENSE-APACHE), [MIT](https://github.com/nickelc/modio.net/blob/master/LICENSE-MIT)
|
- [Modio.NET](https://github.com/nickelc/modio.net) - [Apache-2.0](https://github.com/nickelc/modio.net/blob/master/LICENSE-APACHE), [MIT](https://github.com/nickelc/modio.net/blob/master/LICENSE-MIT)
|
||||||
- [NAudio](https://github.com/naudio/NAudio) - [MIT](https://github.com/naudio/NAudio/blob/master/license.txt)
|
- [NAudio](https://github.com/naudio/NAudio) - [MIT](https://github.com/naudio/NAudio/blob/master/license.txt)
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
<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:md="http://schemas.lepo.co/wpfui/2022/xaml/markdown"
|
||||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
xmlns:helpers="clr-namespace:DesktopMagic.Helpers"
|
xmlns:helpers="clr-namespace:DesktopMagic.Helpers"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -10,6 +11,8 @@
|
|||||||
<ResourceDictionary Source="Resources\Strings\StringResources.en.xaml" />
|
<ResourceDictionary Source="Resources\Strings\StringResources.en.xaml" />
|
||||||
<ui:ThemesDictionary Theme="Light" />
|
<ui:ThemesDictionary Theme="Light" />
|
||||||
<ui:ControlsDictionary />
|
<ui:ControlsDictionary />
|
||||||
|
<md:ThemesDictionary Theme="Light" />
|
||||||
|
<md:ControlsDictionary />
|
||||||
<ResourceDictionary Source="pack://application:,,,/BusyIndicator;component/Theme/Default.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/BusyIndicator;component/Theme/Default.xaml" />
|
||||||
<ResourceDictionary Source="Resources\Images\ImageResources.xaml" />
|
<ResourceDictionary Source="Resources\Images\ImageResources.xaml" />
|
||||||
<ResourceDictionary Source="Resources\Styles\ToggleSwitchContentLeftStyle.xaml" />
|
<ResourceDictionary Source="Resources\Styles\ToggleSwitchContentLeftStyle.xaml" />
|
||||||
@@ -22,4 +25,4 @@
|
|||||||
<Thickness x:Key="DefaultMargin" Top="5" Left="5" Right="5" Bottom ="5" />
|
<Thickness x:Key="DefaultMargin" Top="5" Left="5" Right="5" Bottom ="5" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
using DesktopMagic.Api;
|
||||||
|
using DesktopMagic.Api.Settings;
|
||||||
|
using DesktopMagic.Helpers;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DesktopMagic.BuiltInPlugins;
|
||||||
|
|
||||||
|
internal class AgendaPlugin : AsyncPlugin
|
||||||
|
{
|
||||||
|
[Setting("agenda-calendar-url", "iCal URL")]
|
||||||
|
private readonly TextBox calendarUrl = new TextBox(string.Empty);
|
||||||
|
|
||||||
|
[Setting("agenda-refresh-button", "Refresh Calendar")]
|
||||||
|
private readonly Button refreshButton = new Button("Refresh");
|
||||||
|
|
||||||
|
[Setting("agenda-refresh-interval", "Refresh Every (minutes)")]
|
||||||
|
private readonly IntegerUpDown refreshMinutes = new IntegerUpDown(1, 120, 10);
|
||||||
|
|
||||||
|
[Setting("agenda-lookahead-hours", "Look Ahead (hours)")]
|
||||||
|
private readonly IntegerUpDown lookAheadHours = new IntegerUpDown(1, 336, 24);
|
||||||
|
|
||||||
|
[Setting("agenda-max-events", "Maximum Events")]
|
||||||
|
private readonly IntegerUpDown maxEvents = new IntegerUpDown(1, 15, 5);
|
||||||
|
|
||||||
|
[Setting("agenda-include-all-day", "Include All-Day Events")]
|
||||||
|
private readonly CheckBox includeAllDayEvents = new CheckBox(true);
|
||||||
|
|
||||||
|
[Setting("agenda-show-date", "Show Date")]
|
||||||
|
private readonly CheckBox showDate = new CheckBox(true);
|
||||||
|
|
||||||
|
public override int UpdateInterval { get; set; } = 30000;
|
||||||
|
|
||||||
|
private List<CalendarEventEntry> upcomingEvents = [];
|
||||||
|
private DateTime nextRefreshTime = DateTime.MinValue;
|
||||||
|
private bool forceRefresh = true;
|
||||||
|
private bool themeChanged = true;
|
||||||
|
private string? errorText;
|
||||||
|
|
||||||
|
public override Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
refreshButton.OnClick += () =>
|
||||||
|
{
|
||||||
|
forceRefresh = true;
|
||||||
|
Application.UpdateWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
calendarUrl.OnValueChanged += MarkForRefresh;
|
||||||
|
refreshMinutes.OnValueChanged += MarkForRefresh;
|
||||||
|
lookAheadHours.OnValueChanged += MarkForRefresh;
|
||||||
|
maxEvents.OnValueChanged += MarkForRefresh;
|
||||||
|
includeAllDayEvents.OnValueChanged += MarkForRefresh;
|
||||||
|
showDate.OnValueChanged += Application.UpdateWindow;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Bitmap?> MainAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (themeChanged || forceRefresh || DateTime.Now >= nextRefreshTime)
|
||||||
|
{
|
||||||
|
await RefreshEventsAsync(cancellationToken);
|
||||||
|
themeChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RenderAgendaBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThemeChanged()
|
||||||
|
{
|
||||||
|
themeChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSettingsChanged()
|
||||||
|
{
|
||||||
|
Application.UpdateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkForRefresh()
|
||||||
|
{
|
||||||
|
forceRefresh = true;
|
||||||
|
Application.UpdateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshEventsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
forceRefresh = false;
|
||||||
|
nextRefreshTime = DateTime.Now.AddMinutes(Math.Max(1, refreshMinutes.Value));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(calendarUrl.Value))
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = "Set iCal URL in settings";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
DateTime windowEnd = now.AddHours(Math.Max(1, lookAheadHours.Value));
|
||||||
|
|
||||||
|
upcomingEvents = await IcsCalendarHelper.GetUpcomingEventsAsync(
|
||||||
|
calendarUrl.Value.Trim(),
|
||||||
|
now,
|
||||||
|
windowEnd,
|
||||||
|
maxEvents.Value,
|
||||||
|
includeAllDayEvents.Value,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
errorText = null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap RenderAgendaBitmap()
|
||||||
|
{
|
||||||
|
List<(string Day, string Date, string Time, string Title, bool IsMessage)> rows = [];
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(errorText))
|
||||||
|
{
|
||||||
|
rows.Add((string.Empty, string.Empty, string.Empty, errorText, true));
|
||||||
|
}
|
||||||
|
else if (upcomingEvents.Count == 0)
|
||||||
|
{
|
||||||
|
rows.Add((string.Empty, string.Empty, string.Empty, "No events in selected window", true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (CalendarEventEntry eventEntry in upcomingEvents)
|
||||||
|
{
|
||||||
|
string dayPart = showDate.Value ? eventEntry.StartLocal.ToString("ddd") : string.Empty;
|
||||||
|
string datePart = showDate.Value ? eventEntry.StartLocal.ToString("dd MMM yyyy") : string.Empty;
|
||||||
|
string timePart = eventEntry.IsAllDay ? "All day" : eventEntry.StartLocal.ToShortTimeString();
|
||||||
|
|
||||||
|
rows.Add((dayPart, datePart, timePart, eventEntry.Title, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using Font font = new Font(Application.Theme.Font, 70);
|
||||||
|
using Bitmap measureBitmap = new Bitmap(1, 1);
|
||||||
|
using Graphics measureGraphics = Graphics.FromImage(measureBitmap);
|
||||||
|
measureGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
|
|
||||||
|
bool hasDayColumn = rows.Any(row => !row.IsMessage && !string.IsNullOrWhiteSpace(row.Day));
|
||||||
|
bool hasDateColumn = rows.Any(row => !row.IsMessage && !string.IsNullOrWhiteSpace(row.Date));
|
||||||
|
bool hasTimeColumn = rows.Any(row => !row.IsMessage && !string.IsNullOrWhiteSpace(row.Time));
|
||||||
|
float maxDayWidth = 0;
|
||||||
|
float maxDateWidth = 0;
|
||||||
|
float maxTimeWidth = 0;
|
||||||
|
float maxTitleWidth = 0;
|
||||||
|
float lineHeight = 0;
|
||||||
|
|
||||||
|
foreach ((string day, string date, string time, string title, bool isMessage) in rows)
|
||||||
|
{
|
||||||
|
if (isMessage)
|
||||||
|
{
|
||||||
|
SizeF messageSize = measureGraphics.MeasureString(title, font);
|
||||||
|
maxTitleWidth = Math.Max(maxTitleWidth, messageSize.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, messageSize.Height);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDayColumn)
|
||||||
|
{
|
||||||
|
SizeF daySize = measureGraphics.MeasureString(day, font);
|
||||||
|
maxDayWidth = Math.Max(maxDayWidth, daySize.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, daySize.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDateColumn)
|
||||||
|
{
|
||||||
|
SizeF dateSize = measureGraphics.MeasureString(date, font);
|
||||||
|
maxDateWidth = Math.Max(maxDateWidth, dateSize.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, dateSize.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTimeColumn)
|
||||||
|
{
|
||||||
|
SizeF timeSize = measureGraphics.MeasureString(time, font);
|
||||||
|
maxTimeWidth = Math.Max(maxTimeWidth, timeSize.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, timeSize.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeF titleSize = measureGraphics.MeasureString(title, font);
|
||||||
|
maxTitleWidth = Math.Max(maxTitleWidth, titleSize.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, titleSize.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
float columnSpacing = measureGraphics.MeasureString(" ", font).Width;
|
||||||
|
|
||||||
|
float prefixWidth = 0;
|
||||||
|
bool hasAnyPrefix = false;
|
||||||
|
if (hasDayColumn)
|
||||||
|
{
|
||||||
|
prefixWidth += maxDayWidth;
|
||||||
|
hasAnyPrefix = true;
|
||||||
|
}
|
||||||
|
if (hasDateColumn)
|
||||||
|
{
|
||||||
|
prefixWidth += (hasAnyPrefix ? columnSpacing : 0) + maxDateWidth;
|
||||||
|
hasAnyPrefix = true;
|
||||||
|
}
|
||||||
|
if (hasTimeColumn)
|
||||||
|
{
|
||||||
|
prefixWidth += (hasAnyPrefix ? columnSpacing : 0) + maxTimeWidth;
|
||||||
|
hasAnyPrefix = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float totalWidth = (hasAnyPrefix ? prefixWidth + columnSpacing : 0) + maxTitleWidth;
|
||||||
|
|
||||||
|
int width = Math.Max(1, (int)Math.Ceiling(totalWidth));
|
||||||
|
int height = Math.Max(1, (int)Math.Ceiling(lineHeight * rows.Count));
|
||||||
|
|
||||||
|
Bitmap bitmap = new Bitmap(width, height);
|
||||||
|
bitmap.SetResolution(100, 100);
|
||||||
|
|
||||||
|
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||||
|
using SolidBrush brush = new SolidBrush(Application.Theme.PrimaryColor);
|
||||||
|
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
|
graphics.Clear(Color.Transparent);
|
||||||
|
|
||||||
|
float dayColumnX = 0;
|
||||||
|
float dateColumnX = hasDayColumn ? dayColumnX + maxDayWidth + columnSpacing : dayColumnX;
|
||||||
|
float timeColumnX = hasDateColumn ? dateColumnX + maxDateWidth + columnSpacing : dateColumnX;
|
||||||
|
float titleColumnX = hasAnyPrefix ? timeColumnX + (hasTimeColumn ? maxTimeWidth : 0) + columnSpacing : 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < rows.Count; index++)
|
||||||
|
{
|
||||||
|
(string day, string date, string time, string title, bool isMessage) = rows[index];
|
||||||
|
float y = index * lineHeight;
|
||||||
|
|
||||||
|
if (isMessage)
|
||||||
|
{
|
||||||
|
graphics.DrawString(title, font, brush, 0, y);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDayColumn && !string.IsNullOrWhiteSpace(day))
|
||||||
|
{
|
||||||
|
graphics.DrawString(day, font, brush, dayColumnX, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDateColumn && !string.IsNullOrWhiteSpace(date))
|
||||||
|
{
|
||||||
|
graphics.DrawString(date, font, brush, dateColumnX, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTimeColumn && !string.IsNullOrWhiteSpace(time))
|
||||||
|
{
|
||||||
|
graphics.DrawString(time, font, brush, timeColumnX, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.DrawString(title, font, brush, titleColumnX, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,34 @@ using DesktopMagic.Api.Settings;
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Text;
|
using System.Drawing.Text;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace DesktopMagic.BuiltInPlugins;
|
namespace DesktopMagic.BuiltInPlugins;
|
||||||
|
|
||||||
internal class DatePlugin : Plugin
|
internal partial class DatePlugin : Plugin
|
||||||
{
|
{
|
||||||
[Setting("short-date", "Short date")]
|
[Setting("week-day-style", "Week Day Style")]
|
||||||
private readonly CheckBox shortDateCheckBox = new CheckBox(true);
|
public ComboBox weekDayStyleComboBox = new ComboBox([.. Enum.GetValues<WeekdayStyle>().Select(e => e.ToString())]);
|
||||||
|
|
||||||
|
[Setting("day-style", "Day Style")]
|
||||||
|
public ComboBox dayStyleComboBox = new ComboBox([.. Enum.GetValues<DayStyle>().Select(e => e.ToString())]);
|
||||||
|
|
||||||
|
[Setting("month-style", "Month Style")]
|
||||||
|
public ComboBox monthStyleComboBox = new ComboBox([.. Enum.GetValues<MonthStyle>().Select(e => e.ToString())]);
|
||||||
|
|
||||||
|
[Setting("year-style", "Year Style")]
|
||||||
|
public ComboBox yearStyleComboBox = new ComboBox([.. Enum.GetValues<YearStyle>().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 DateTime oldDateTime = DateTime.MinValue;
|
||||||
private bool themeChanged = false;
|
private bool themeChanged = false;
|
||||||
@@ -27,7 +48,68 @@ internal class DatePlugin : Plugin
|
|||||||
oldDateTime = DateTime.Now;
|
oldDateTime = DateTime.Now;
|
||||||
themeChanged = false;
|
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<WeekdayStyle>(weekDayStyleComboBox.Value);
|
||||||
|
DayStyle dayStyle = Enum.Parse<DayStyle>(dayStyleComboBox.Value);
|
||||||
|
MonthStyle monthStyle = Enum.Parse<MonthStyle>(monthStyleComboBox.Value);
|
||||||
|
YearStyle yearStyle = Enum.Parse<YearStyle>(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);
|
using Font font = new Font(Application.Theme.Font, 200);
|
||||||
|
|
||||||
@@ -42,7 +124,7 @@ internal class DatePlugin : Plugin
|
|||||||
bmp.SetResolution(100, 100);
|
bmp.SetResolution(100, 100);
|
||||||
|
|
||||||
using Graphics gr = Graphics.FromImage(bmp);
|
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.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
gr.DrawString(date, font, brush, 0, 0);
|
gr.DrawString(date, font, brush, 0, 0);
|
||||||
@@ -54,4 +136,32 @@ internal class DatePlugin : Plugin
|
|||||||
{
|
{
|
||||||
themeChanged = true;
|
themeChanged = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public override void OnSettingsChanged()
|
||||||
|
{
|
||||||
|
themeChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"d{3,4}")]
|
||||||
|
private static partial Regex WeekdayRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\b(?<!d)d{1,2}\b")]
|
||||||
|
private static partial Regex DayRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"M{1,4}")]
|
||||||
|
private static partial Regex MonthRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"y{2,4}")]
|
||||||
|
private static partial Regex YearRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[\s,./\-]+")]
|
||||||
|
private static partial Regex CleanupRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?<=\d)\s+(?=\d)")]
|
||||||
|
private static partial Regex DateSeparatorsRegex();
|
||||||
|
|
||||||
|
public enum WeekdayStyle { None, Short, Long }
|
||||||
|
public enum DayStyle { None, Numeric }
|
||||||
|
public enum MonthStyle { None, Numeric, Short, Long }
|
||||||
|
public enum YearStyle { None, TwoDigit, FourDigit }
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ internal class MusicVisualizerPlugin : Plugin
|
|||||||
|
|
||||||
private readonly Bitmap output = new Bitmap(880, 300);
|
private readonly Bitmap output = new Bitmap(880, 300);
|
||||||
|
|
||||||
|
private readonly System.Diagnostics.Stopwatch frameStopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
|
||||||
|
[Setting("fps-limit", "FPS Limit")]
|
||||||
|
private readonly IntegerUpDown fpsLimit = new IntegerUpDown(1, 144, 60);
|
||||||
|
|
||||||
[Setting("mirror", "Mirror")]
|
[Setting("mirror", "Mirror")]
|
||||||
private readonly CheckBox mirrorMode = new CheckBox(false);
|
private readonly CheckBox mirrorMode = new CheckBox(false);
|
||||||
|
|
||||||
@@ -51,8 +56,16 @@ internal class MusicVisualizerPlugin : Plugin
|
|||||||
waveIn.StartRecording();
|
waveIn.StartRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Bitmap Main()
|
public override Bitmap? Main()
|
||||||
{
|
{
|
||||||
|
double msPerFrame = 1000.0 / Math.Max(1, fpsLimit.Value);
|
||||||
|
|
||||||
|
if (frameStopwatch.Elapsed.TotalMilliseconds < msPerFrame)
|
||||||
|
{
|
||||||
|
return null; // Exit early if we haven't reached the time threshold
|
||||||
|
}
|
||||||
|
frameStopwatch.Restart(); // Reset timer for the next frame
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
using DesktopMagic.Api;
|
||||||
|
using DesktopMagic.Api.Settings;
|
||||||
|
using DesktopMagic.Helpers;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DesktopMagic.BuiltInPlugins;
|
||||||
|
|
||||||
|
internal class NextMeetingCountdownPlugin : AsyncPlugin
|
||||||
|
{
|
||||||
|
[Setting("meeting-calendar-url", "iCal URL")]
|
||||||
|
private readonly TextBox calendarUrl = new TextBox(string.Empty);
|
||||||
|
|
||||||
|
[Setting("meeting-refresh-button", "Refresh Calendar")]
|
||||||
|
private readonly Button refreshButton = new Button("Refresh");
|
||||||
|
|
||||||
|
[Setting("meeting-refresh-interval", "Refresh Every (minutes)")]
|
||||||
|
private readonly IntegerUpDown refreshMinutes = new IntegerUpDown(1, 120, 10);
|
||||||
|
|
||||||
|
[Setting("meeting-lookahead-hours", "Look Ahead (hours)")]
|
||||||
|
private readonly IntegerUpDown lookAheadHours = new IntegerUpDown(1, 336, 48);
|
||||||
|
|
||||||
|
[Setting("meeting-include-all-day", "Include All-Day Events")]
|
||||||
|
private readonly CheckBox includeAllDayEvents = new CheckBox(false);
|
||||||
|
|
||||||
|
[Setting("meeting-time-display", "Start Time Format")]
|
||||||
|
private readonly ComboBox startTimeDisplay = new ComboBox("None", "Date and time", "Time only");
|
||||||
|
|
||||||
|
[Setting("meeting-show-location", "Show Location")]
|
||||||
|
private readonly CheckBox showLocation = new CheckBox(false);
|
||||||
|
|
||||||
|
[Setting("meeting-center-lines", "Center Lines")]
|
||||||
|
private readonly CheckBox centerLines = new CheckBox(false);
|
||||||
|
|
||||||
|
public override int UpdateInterval { get; set; } = 1000;
|
||||||
|
|
||||||
|
private List<CalendarEventEntry> upcomingEvents = [];
|
||||||
|
private DateTime nextRefreshTime = DateTime.MinValue;
|
||||||
|
private bool forceRefresh = true;
|
||||||
|
private bool themeChanged = true;
|
||||||
|
private string? errorText;
|
||||||
|
|
||||||
|
public override Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
refreshButton.OnClick += () =>
|
||||||
|
{
|
||||||
|
forceRefresh = true;
|
||||||
|
Application.UpdateWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
calendarUrl.OnValueChanged += MarkForRefresh;
|
||||||
|
refreshMinutes.OnValueChanged += MarkForRefresh;
|
||||||
|
lookAheadHours.OnValueChanged += MarkForRefresh;
|
||||||
|
includeAllDayEvents.OnValueChanged += MarkForRefresh;
|
||||||
|
startTimeDisplay.OnValueChanged += Application.UpdateWindow;
|
||||||
|
showLocation.OnValueChanged += Application.UpdateWindow;
|
||||||
|
centerLines.OnValueChanged += Application.UpdateWindow;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Bitmap?> MainAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (themeChanged || forceRefresh || DateTime.Now >= nextRefreshTime)
|
||||||
|
{
|
||||||
|
await RefreshEventsAsync(cancellationToken);
|
||||||
|
themeChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RenderCountdownBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThemeChanged()
|
||||||
|
{
|
||||||
|
themeChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSettingsChanged()
|
||||||
|
{
|
||||||
|
Application.UpdateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkForRefresh()
|
||||||
|
{
|
||||||
|
forceRefresh = true;
|
||||||
|
Application.UpdateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshEventsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
forceRefresh = false;
|
||||||
|
nextRefreshTime = DateTime.Now.AddMinutes(Math.Max(1, refreshMinutes.Value));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(calendarUrl.Value))
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = "Set iCal URL in settings";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
DateTime windowEnd = now.AddHours(Math.Max(1, lookAheadHours.Value));
|
||||||
|
|
||||||
|
upcomingEvents = await IcsCalendarHelper.GetUpcomingEventsAsync(
|
||||||
|
calendarUrl.Value.Trim(),
|
||||||
|
now,
|
||||||
|
windowEnd,
|
||||||
|
20,
|
||||||
|
includeAllDayEvents.Value,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
errorText = null;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
upcomingEvents = [];
|
||||||
|
errorText = $"Calendar unavailable: {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap RenderCountdownBitmap()
|
||||||
|
{
|
||||||
|
List<string> lines = [];
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(errorText))
|
||||||
|
{
|
||||||
|
lines.Add(errorText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
CalendarEventEntry? nextEvent = upcomingEvents.FirstOrDefault(eventEntry => eventEntry.EndLocal > now);
|
||||||
|
|
||||||
|
if (nextEvent is null)
|
||||||
|
{
|
||||||
|
lines.Add("No upcoming meetings");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines.Add(nextEvent.Title);
|
||||||
|
|
||||||
|
if (startTimeDisplay.Value != "None")
|
||||||
|
{
|
||||||
|
string timeText = nextEvent.IsAllDay
|
||||||
|
? "All day"
|
||||||
|
: startTimeDisplay.Value == "Time only"
|
||||||
|
? nextEvent.StartLocal.ToString("t")
|
||||||
|
: $"{nextEvent.StartLocal:D} {nextEvent.StartLocal:t}";
|
||||||
|
lines.Add(timeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showLocation.Value && !string.IsNullOrWhiteSpace(nextEvent.Location))
|
||||||
|
{
|
||||||
|
lines.Add(nextEvent.Location!);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now < nextEvent.StartLocal)
|
||||||
|
{
|
||||||
|
lines.Add($"Starts in {FormatCountdown(nextEvent.StartLocal - now)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines.Add("In progress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using Font font = new Font(Application.Theme.Font, 90);
|
||||||
|
using Bitmap measureBitmap = new Bitmap(1, 1);
|
||||||
|
using Graphics measureGraphics = Graphics.FromImage(measureBitmap);
|
||||||
|
measureGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
|
|
||||||
|
float maxWidth = 0;
|
||||||
|
float lineHeight = 0;
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
SizeF size = measureGraphics.MeasureString(line, font);
|
||||||
|
maxWidth = Math.Max(maxWidth, size.Width);
|
||||||
|
lineHeight = Math.Max(lineHeight, size.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = Math.Max(1, (int)Math.Ceiling(maxWidth));
|
||||||
|
int height = Math.Max(1, (int)Math.Ceiling(lineHeight * lines.Count));
|
||||||
|
|
||||||
|
Bitmap bitmap = new Bitmap(width, height);
|
||||||
|
bitmap.SetResolution(100, 100);
|
||||||
|
|
||||||
|
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||||
|
using SolidBrush brush = new SolidBrush(Application.Theme.PrimaryColor);
|
||||||
|
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
|
graphics.Clear(Color.Transparent);
|
||||||
|
|
||||||
|
for (int index = 0; index < lines.Count; index++)
|
||||||
|
{
|
||||||
|
string line = lines[index];
|
||||||
|
float x = 0;
|
||||||
|
if (centerLines.Value)
|
||||||
|
{
|
||||||
|
SizeF lineSize = graphics.MeasureString(line, font);
|
||||||
|
x = Math.Max(0, (width - lineSize.Width) / 2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.DrawString(line, font, brush, x, index * lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatCountdown(TimeSpan timeSpan)
|
||||||
|
{
|
||||||
|
int totalHours = (int)timeSpan.TotalHours;
|
||||||
|
return $"{totalHours:00}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
using Wpf.Ui.Controls;
|
||||||
|
|
||||||
namespace DesktopMagic.DataContexts;
|
namespace DesktopMagic.DataContexts;
|
||||||
|
|
||||||
internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand command, PluginEntryDataContext.Mode mode, string? path = null, string? csprojPath = null) : INotifyPropertyChanged
|
internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand command, PluginEntryDataContext.Mode mode, string? path = null, string? csprojPath = null) : INotifyPropertyChanged
|
||||||
@@ -38,7 +40,20 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co
|
|||||||
|
|
||||||
public ICommand Command => command;
|
public ICommand Command => 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
|
public ButtonData OpenButtonData
|
||||||
{
|
{
|
||||||
@@ -46,15 +61,15 @@ internal class PluginEntryDataContext(PluginMetadata pluginMetadata, ICommand co
|
|||||||
{
|
{
|
||||||
if (File.Exists(csprojPath))
|
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()))
|
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
|
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));
|
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
|
public enum Mode
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||||
<Authors>Stone_Red</Authors>
|
<Authors>Stone_Red</Authors>
|
||||||
<Version>1.3.0.0</Version>
|
<Version>1.3.2.0</Version>
|
||||||
<PackageProjectUrl>https://github.com/Stone-Red-Code/DesktopMagic</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Stone-Red-Code/DesktopMagic</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/Stone-Red-Code/DesktopMagic</RepositoryUrl>
|
<RepositoryUrl>https://github.com/Stone-Red-Code/DesktopMagic</RepositoryUrl>
|
||||||
<AssemblyVersion>1.3.0.0</AssemblyVersion>
|
<AssemblyVersion>1.3.2.0</AssemblyVersion>
|
||||||
<FileVersion>1.3.0.0</FileVersion>
|
<FileVersion>1.3.1.0</FileVersion>
|
||||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -43,16 +44,17 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BusyIndicators" Version="2.1.2" />
|
<PackageReference Include="BusyIndicators" Version="2.1.3" />
|
||||||
<PackageReference Include="CuteUtils" Version="1.0.0" />
|
<PackageReference Include="CuteUtils" Version="1.0.0" />
|
||||||
<PackageReference Include="Interop.IWshRuntimeLibrary" Version="1.0.1" />
|
<PackageReference Include="Interop.IWshRuntimeLibrary" Version="1.0.1" />
|
||||||
<PackageReference Include="MaterialDesignThemes" Version="5.1.0" />
|
<PackageReference Include="Ical.Net" Version="4.3.1" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
|
||||||
<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" Version="4.2.0" />
|
||||||
<PackageReference Include="WPF-UI.Tray" Version="4.1.0" />
|
<PackageReference Include="WPF-UI.Markdown" Version="4.0.2" />
|
||||||
|
<PackageReference Include="WPF-UI.Tray" Version="4.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -75,4 +77,4 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ui:TitleBar ShowMinimize="False" ShowMaximize="False">
|
<ui:TitleBar x:Name="titleBar" ShowMinimize="False" ShowMaximize="False">
|
||||||
<ui:TitleBar.Icon>
|
<ui:TitleBar.Icon>
|
||||||
<ui:ImageIcon Source="{StaticResource Icon}" />
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
</ui:TitleBar.Icon>
|
</ui:TitleBar.Icon>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public partial class ColorDialog : FluentWindow
|
|||||||
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
||||||
|
|
||||||
label.Content = content;
|
label.Content = content;
|
||||||
|
titleBar.Title = title;
|
||||||
Title = title;
|
Title = title;
|
||||||
|
|
||||||
alphaSlider.Value = defaultColor.A;
|
alphaSlider.Value = defaultColor.A;
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<ui:FluentWindow x:Class="DesktopMagic.Dialogs.CreatePluginDialog"
|
||||||
|
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:tray="http://schemas.lepo.co/wpfui/2022/xaml/tray"
|
||||||
|
xmlns:pages="clr-namespace:DesktopMagic.Pages"
|
||||||
|
xmlns:plugins="clr-namespace:DesktopMagic.Plugins"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:dataContext="clr-namespace:DesktopMagic.DataContexts"
|
||||||
|
d:DataContext="{d:DesignInstance Type=dataContext:MainWindowDataContext}"
|
||||||
|
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||||
|
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
WindowCornerPreference="Round"
|
||||||
|
WindowBackdropType="Tabbed"
|
||||||
|
Height="230"
|
||||||
|
Width="300"
|
||||||
|
MinHeight="160"
|
||||||
|
MinWidth="300"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ExtendsContentIntoTitleBar="True">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ui:TitleBar Title="Create New Plugin" ShowMinimize="False" ShowMaximize="False">
|
||||||
|
<ui:TitleBar.Icon>
|
||||||
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
|
</ui:TitleBar.Icon>
|
||||||
|
</ui:TitleBar>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Margin="{StaticResource DefaultMargin}" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
|
<ui:TextBlock TextWrapping="Wrap" VerticalAlignment="Center" Foreground="{DynamicResource TextFillColorSecondaryBrush}">
|
||||||
|
Please check out the
|
||||||
|
</ui:TextBlock>
|
||||||
|
<ui:HyperlinkButton Padding="0" Content=" Plugin Creation Guide" VerticalAlignment="Center" VerticalContentAlignment="Center" NavigateUri="https://mod.io/g/desktopmagic/r/plugin-creation-guide/" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="15">
|
||||||
|
<Label Content="{DynamicResource enterPluginName}" HorizontalAlignment="Left" VerticalAlignment="Top" />
|
||||||
|
<ui:TextBox x:Name="textBox" HorizontalAlignment="Stretch" Margin="5" TextWrapping="Wrap" VerticalAlignment="Top" VerticalContentAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5">
|
||||||
|
<Button x:Name="okButton" Content="{DynamicResource ok }" HorizontalAlignment="Left" Margin="0 0 5 0" VerticalAlignment="Top" Width="100" Click="OkButton_Click" Cursor="Hand" />
|
||||||
|
<Button x:Name="cancelButton" Content="{DynamicResource cancel }" HorizontalAlignment="Left" Margin="0 0 0 0" VerticalAlignment="Top" Width="100" Click="CancelButton_Click" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</ui:FluentWindow>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace DesktopMagic.Dialogs;
|
||||||
|
|
||||||
|
public partial class CreatePluginDialog : Wpf.Ui.Controls.FluentWindow
|
||||||
|
{
|
||||||
|
public string ResponseText
|
||||||
|
{
|
||||||
|
get => textBox.Text;
|
||||||
|
set => textBox.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CreatePluginDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ui:TitleBar ShowMinimize="False" ShowMaximize="False">
|
<ui:TitleBar x:Name="titleBar" ShowMinimize="False" ShowMaximize="False">
|
||||||
<ui:TitleBar.Icon>
|
<ui:TitleBar.Icon>
|
||||||
<ui:ImageIcon Source="{StaticResource Icon}" />
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
</ui:TitleBar.Icon>
|
</ui:TitleBar.Icon>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public partial class InputDialog : Wpf.Ui.Controls.FluentWindow
|
|||||||
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
||||||
|
|
||||||
label.Content = content;
|
label.Content = content;
|
||||||
|
titleBar.Title = title;
|
||||||
Title = title;
|
Title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<ui:FluentWindow x:Class="DesktopMagic.Dialogs.ReleaseInfoDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:md="http://schemas.lepo.co/wpfui/2022/xaml/markdown"
|
||||||
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||||
|
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||||
|
WindowCornerPreference="Round"
|
||||||
|
WindowBackdropType="Tabbed"
|
||||||
|
Height="700"
|
||||||
|
Width="900"
|
||||||
|
MinHeight="500"
|
||||||
|
MinWidth="700"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ExtendsContentIntoTitleBar="True">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ui:TitleBar x:Name="titleBar" ShowMinimize="False" ShowMaximize="False">
|
||||||
|
<ui:TitleBar.Icon>
|
||||||
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
|
</ui:TitleBar.Icon>
|
||||||
|
</ui:TitleBar>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Margin="14,10,14,0">
|
||||||
|
<TextBlock x:Name="releaseNameTextBlock" FontSize="20" FontWeight="SemiBold" />
|
||||||
|
<TextBlock x:Name="publishedAtTextBlock" Foreground="{DynamicResource TextFillColorSecondaryBrush}" Margin="0,4,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Border Grid.Row="2" Margin="14,12,14,12" BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}" BorderThickness="1" CornerRadius="8">
|
||||||
|
<md:MarkdownViewer x:Name="releaseMarkdownViewer" Margin="8" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="14,0,14,12">
|
||||||
|
<Button x:Name="openButton" Width="150" Margin="0,0,8,0" Click="OpenButton_Click" />
|
||||||
|
<Button x:Name="okButton" Width="100" Click="OkButton_Click" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</ui:FluentWindow>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace DesktopMagic.Dialogs;
|
||||||
|
|
||||||
|
public partial class ReleaseInfoDialog : Wpf.Ui.Controls.FluentWindow
|
||||||
|
{
|
||||||
|
public bool OpenReleaseRequested { get; private set; }
|
||||||
|
|
||||||
|
public ReleaseInfoDialog(
|
||||||
|
string title,
|
||||||
|
string releaseName,
|
||||||
|
string publishedAt,
|
||||||
|
string markdown,
|
||||||
|
string openButtonText,
|
||||||
|
string okButtonText)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Resources.MergedDictionaries.Add(App.LanguageDictionary);
|
||||||
|
|
||||||
|
titleBar.Title = title;
|
||||||
|
Title = title;
|
||||||
|
releaseNameTextBlock.Text = releaseName;
|
||||||
|
publishedAtTextBlock.Text = publishedAt;
|
||||||
|
releaseMarkdownViewer.Markdown = string.IsNullOrWhiteSpace(markdown) ? "-" : markdown;
|
||||||
|
openButton.Content = openButtonText;
|
||||||
|
okButton.Content = okButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
OpenReleaseRequested = true;
|
||||||
|
DialogResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ui:TitleBar ShowMinimize="False" ShowMaximize="False">
|
<ui:TitleBar x:Name="titleBar" ShowMinimize="False" ShowMaximize="False">
|
||||||
<ui:TitleBar.Icon>
|
<ui:TitleBar.Icon>
|
||||||
<ui:ImageIcon Source="{StaticResource Icon}" />
|
<ui:ImageIcon Source="{StaticResource Icon}" />
|
||||||
</ui:TitleBar.Icon>
|
</ui:TitleBar.Icon>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public partial class ThemeDialog : Wpf.Ui.Controls.FluentWindow
|
|||||||
backgroundColorRechtangle.Background = new SolidColorBrush(MultiColorConverter.ConvertToMediaColor(theme.BackgroundColor));
|
backgroundColorRechtangle.Background = new SolidColorBrush(MultiColorConverter.ConvertToMediaColor(theme.BackgroundColor));
|
||||||
|
|
||||||
label.Content = content;
|
label.Content = content;
|
||||||
|
titleBar.Title = title;
|
||||||
Title = title;
|
Title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DesktopMagic.Helpers;
|
||||||
|
|
||||||
|
public sealed class GitHubLatestRelease
|
||||||
|
{
|
||||||
|
[JsonPropertyName("tag_name")]
|
||||||
|
public string TagName { get; init; } = "Unknown";
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; init; } = "Unknown";
|
||||||
|
|
||||||
|
[JsonPropertyName("body")]
|
||||||
|
public string Body { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("published_at")]
|
||||||
|
public DateTimeOffset? PublishedAt { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("html_url")]
|
||||||
|
public string HtmlUrl { get; init; } = "https://github.com/Stone-Red-Code/DesktopMagic/releases/latest";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GitHubReleaseService
|
||||||
|
{
|
||||||
|
private const string LatestReleaseApiUrl = "https://api.github.com/repos/Stone-Red-Code/DesktopMagic/releases/latest";
|
||||||
|
private static readonly HttpClient _httpClient = CreateReleaseHttpClient();
|
||||||
|
|
||||||
|
private static HttpClient CreateReleaseHttpClient()
|
||||||
|
{
|
||||||
|
HttpClient client = new()
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(10)
|
||||||
|
};
|
||||||
|
|
||||||
|
client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json");
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd("DesktopMagic");
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<GitHubLatestRelease?> GetLatestReleaseInfoAsync()
|
||||||
|
{
|
||||||
|
using HttpResponseMessage response = await _httpClient.GetAsync(LatestReleaseApiUrl);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
await using var responseStream = await response.Content.ReadAsStreamAsync();
|
||||||
|
return await JsonSerializer.DeserializeAsync<GitHubLatestRelease>(responseStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using Ical.Net;
|
||||||
|
using Ical.Net.CalendarComponents;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DesktopMagic.Helpers;
|
||||||
|
|
||||||
|
public sealed class CalendarEventEntry
|
||||||
|
{
|
||||||
|
public string Title { get; init; } = "Untitled";
|
||||||
|
public string? Location { get; init; }
|
||||||
|
public DateTime StartLocal { get; init; }
|
||||||
|
public DateTime EndLocal { get; init; }
|
||||||
|
public bool IsAllDay { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IcsCalendarHelper
|
||||||
|
{
|
||||||
|
private static readonly HttpClient httpClient = new()
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(12)
|
||||||
|
};
|
||||||
|
|
||||||
|
static IcsCalendarHelper()
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("DesktopMagic");
|
||||||
|
httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/calendar");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<CalendarEventEntry>> GetUpcomingEventsAsync(
|
||||||
|
string calendarUrl,
|
||||||
|
DateTime windowStartLocal,
|
||||||
|
DateTime windowEndLocal,
|
||||||
|
int maxEvents,
|
||||||
|
bool includeAllDayEvents,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using HttpResponseMessage response = await httpClient.GetAsync(calendarUrl, cancellationToken);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string icsContent = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.Load(icsContent);
|
||||||
|
DateTime windowStartUtc = windowStartLocal.ToUniversalTime();
|
||||||
|
DateTime windowEndUtc = windowEndLocal.ToUniversalTime();
|
||||||
|
|
||||||
|
List<CalendarEventEntry> results = [];
|
||||||
|
|
||||||
|
foreach (var occurrence in calendar.GetOccurrences(windowStartUtc, windowEndUtc))
|
||||||
|
{
|
||||||
|
if (occurrence.Source is not CalendarEvent calendarEvent)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime startLocal = occurrence.Period.StartTime.AsSystemLocal;
|
||||||
|
DateTime endLocal = occurrence.Period.EndTime?.AsSystemLocal ?? startLocal.AddHours(1);
|
||||||
|
bool isAllDay = !occurrence.Period.StartTime.HasTime;
|
||||||
|
|
||||||
|
if (!includeAllDayEvents && isAllDay)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endLocal <= windowStartLocal)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Add(new CalendarEventEntry
|
||||||
|
{
|
||||||
|
Title = string.IsNullOrWhiteSpace(calendarEvent.Summary) ? "Untitled" : calendarEvent.Summary,
|
||||||
|
Location = string.IsNullOrWhiteSpace(calendarEvent.Location) ? null : calendarEvent.Location,
|
||||||
|
StartLocal = startLocal,
|
||||||
|
EndLocal = endLocal,
|
||||||
|
IsAllDay = isAllDay
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
.OrderBy(eventEntry => eventEntry.StartLocal)
|
||||||
|
.Take(Math.Max(1, maxEvents))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,7 +100,7 @@ internal class SettingElementGenerator(uint pluginId)
|
|||||||
Wpf.Ui.Controls.TextBox textBox = new()
|
Wpf.Ui.Controls.TextBox textBox = new()
|
||||||
{
|
{
|
||||||
Text = eTextBox.Value,
|
Text = eTextBox.Value,
|
||||||
TextWrapping = TextWrapping.Wrap,
|
TextWrapping = TextWrapping.NoWrap,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,15 +73,44 @@
|
|||||||
</ui:NavigationView.MenuItems>
|
</ui:NavigationView.MenuItems>
|
||||||
|
|
||||||
<ui:NavigationView.FooterMenuItems>
|
<ui:NavigationView.FooterMenuItems>
|
||||||
<ui:NavigationViewItem Content="{DynamicResource requestFeature}" Click="ReportBugNavigationViewItem_Click">
|
<ui:NavigationViewItem Content="Documentation" Click="DocumentationNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="LightbulbFilament24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource requestFeature}">
|
||||||
<ui:NavigationViewItem.Icon>
|
<ui:NavigationViewItem.Icon>
|
||||||
<ui:SymbolIcon Symbol="ChatHelp24"/>
|
<ui:SymbolIcon Symbol="ChatHelp24"/>
|
||||||
</ui:NavigationViewItem.Icon>
|
</ui:NavigationViewItem.Icon>
|
||||||
|
<ui:NavigationViewItem.MenuItems>
|
||||||
|
<ui:NavigationViewItem Content="GitHub" Click="RequestFeatureNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Globe24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource email}" Click="RequestFeatureEmailNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Mail24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
</ui:NavigationViewItem.MenuItems>
|
||||||
</ui:NavigationViewItem>
|
</ui:NavigationViewItem>
|
||||||
<ui:NavigationViewItem Content="{DynamicResource reportBug}" Click="ReportBugNavigationViewItem_Click">
|
<ui:NavigationViewItem Content="{DynamicResource reportBug}">
|
||||||
<ui:NavigationViewItem.Icon>
|
<ui:NavigationViewItem.Icon>
|
||||||
<ui:SymbolIcon Symbol="Bug24"/>
|
<ui:SymbolIcon Symbol="Bug24"/>
|
||||||
</ui:NavigationViewItem.Icon>
|
</ui:NavigationViewItem.Icon>
|
||||||
|
<ui:NavigationViewItem.MenuItems>
|
||||||
|
<ui:NavigationViewItem Content="GitHub" Click="ReportBugNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Globe24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem Content="{DynamicResource email}" Click="ReportBugEmailNavigationViewItem_Click">
|
||||||
|
<ui:NavigationViewItem.Icon>
|
||||||
|
<ui:SymbolIcon Symbol="Mail24"/>
|
||||||
|
</ui:NavigationViewItem.Icon>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
</ui:NavigationViewItem.MenuItems>
|
||||||
</ui:NavigationViewItem>
|
</ui:NavigationViewItem>
|
||||||
</ui:NavigationView.FooterMenuItems>
|
</ui:NavigationView.FooterMenuItems>
|
||||||
</ui:NavigationView>
|
</ui:NavigationView>
|
||||||
@@ -94,10 +123,7 @@
|
|||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Grid.Column="0">
|
<ui:HyperlinkButton FontWeight="Bold" Content="{DynamicResource developedBy}" NavigateUri="https://me.stone-red.net"/>
|
||||||
<ui:HyperlinkButton FontWeight="Bold" Content="{DynamicResource developedBy}" NavigateUri="https://me.stone-red.net"/>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<ui:ToggleSwitch x:Name="autoStartCheckBox" Style="{StaticResource ToggleSwitchContentLeftStyle}" Grid.Column="2" Margin="0 0 5 0" Content="{DynamicResource autoStart}" IsChecked="{Binding IsAutoStartEnabled}" VerticalAlignment="Center"/>
|
<ui:ToggleSwitch x:Name="autoStartCheckBox" Style="{StaticResource ToggleSwitchContentLeftStyle}" Grid.Column="2" Margin="0 0 5 0" Content="{DynamicResource autoStart}" IsChecked="{Binding IsAutoStartEnabled}" VerticalAlignment="Center"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
using DesktopMagic.DataContexts;
|
using DesktopMagic.DataContexts;
|
||||||
|
using DesktopMagic.Dialogs;
|
||||||
|
using DesktopMagic.Helpers;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
using Wpf.Ui.Appearance;
|
using Wpf.Ui.Appearance;
|
||||||
@@ -14,6 +20,7 @@ public partial class MainWindow : FluentWindow
|
|||||||
private readonly Manager _manager = Manager.Instance;
|
private readonly Manager _manager = Manager.Instance;
|
||||||
private readonly MainWindowDataContext _mainWindowDataContext = new();
|
private readonly MainWindowDataContext _mainWindowDataContext = new();
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
SystemThemeWatcher.Watch(this);
|
SystemThemeWatcher.Watch(this);
|
||||||
@@ -42,6 +49,7 @@ public partial class MainWindow : FluentWindow
|
|||||||
|
|
||||||
_manager.IsLoaded = true;
|
_manager.IsLoaded = true;
|
||||||
_mainWindowDataContext.IsLoading = false;
|
_mainWindowDataContext.IsLoading = false;
|
||||||
|
await ShowLatestReleaseInfoAfterUpdateAsync();
|
||||||
|
|
||||||
App.Logger.LogInfo("Application loaded", source: "MainWindow");
|
App.Logger.LogInfo("Application loaded", source: "MainWindow");
|
||||||
}
|
}
|
||||||
@@ -123,6 +131,18 @@ public partial class MainWindow : FluentWindow
|
|||||||
_ = Process.Start(psi);
|
_ = Process.Start(psi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReportBugEmailNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string uri = "mailto:[email protected]";
|
||||||
|
ProcessStartInfo psi = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = uri
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = Process.Start(psi);
|
||||||
|
}
|
||||||
|
|
||||||
private void RequestFeatureNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
private void RequestFeatureNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string uri = "https://github.com/Stone-Red-Code/DesktopMagic/issues/new?template=feature_request.md";
|
string uri = "https://github.com/Stone-Red-Code/DesktopMagic/issues/new?template=feature_request.md";
|
||||||
@@ -134,6 +154,29 @@ public partial class MainWindow : FluentWindow
|
|||||||
_ = Process.Start(psi);
|
_ = Process.Start(psi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RequestFeatureEmailNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string uri = "mailto:[email protected]";
|
||||||
|
ProcessStartInfo psi = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = uri
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = Process.Start(psi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DocumentationNavigationViewItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string uri = "https://github.com/Stone-Red-Code/DesktopMagic";
|
||||||
|
ProcessStartInfo psi = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = uri
|
||||||
|
};
|
||||||
|
_ = Process.Start(psi);
|
||||||
|
}
|
||||||
|
|
||||||
private void NotifyIcon_LeftClick(Wpf.Ui.Tray.Controls.NotifyIcon sender, RoutedEventArgs e)
|
private void NotifyIcon_LeftClick(Wpf.Ui.Tray.Controls.NotifyIcon sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
RestoreWindow();
|
RestoreWindow();
|
||||||
@@ -154,4 +197,75 @@ public partial class MainWindow : FluentWindow
|
|||||||
{
|
{
|
||||||
Quit();
|
Quit();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private async Task ShowLatestReleaseInfoAfterUpdateAsync()
|
||||||
|
{
|
||||||
|
string currentVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0";
|
||||||
|
string? lastShownVersion = _manager.Settings.ReleaseInfoLastAppVersion;
|
||||||
|
|
||||||
|
if (string.Equals(lastShownVersion, currentVersion, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GitHubLatestRelease? latestRelease = await GetLatestReleaseInfoAsync();
|
||||||
|
if (latestRelease is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string releaseName = !string.IsNullOrWhiteSpace(latestRelease.Name) ? latestRelease.Name : latestRelease.TagName;
|
||||||
|
string publishedAt = latestRelease.PublishedAt?.ToLocalTime().ToString("g") ?? (string)FindResource("unknown");
|
||||||
|
string publishedAtText = string.Format((string)FindResource("releaseInfoPublishedFormat"), publishedAt);
|
||||||
|
string releaseNotesMarkdown = string.IsNullOrWhiteSpace(latestRelease.Body) ? "-" : latestRelease.Body.Trim();
|
||||||
|
|
||||||
|
ReleaseInfoDialog releaseInfoDialog = new(
|
||||||
|
(string)FindResource("releaseInfoTitle"),
|
||||||
|
releaseName,
|
||||||
|
publishedAtText,
|
||||||
|
releaseNotesMarkdown,
|
||||||
|
(string)FindResource("openReleasePage"),
|
||||||
|
(string)FindResource("ok"))
|
||||||
|
{
|
||||||
|
Owner = this,
|
||||||
|
Topmost = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = releaseInfoDialog.ShowDialog();
|
||||||
|
|
||||||
|
if (releaseInfoDialog.OpenReleaseRequested)
|
||||||
|
{
|
||||||
|
ProcessStartInfo processStartInfo = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = latestRelease.HtmlUrl
|
||||||
|
};
|
||||||
|
_ = Process.Start(processStartInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
_manager.Settings.ReleaseInfoLastAppVersion = currentVersion;
|
||||||
|
_manager.SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<GitHubLatestRelease?> GetLatestReleaseInfoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await GitHubReleaseService.GetLatestReleaseInfoAsync();
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
App.Logger.LogError($"Failed to retrieve latest release information: {ex.Message}", source: "MainWindow");
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException ex)
|
||||||
|
{
|
||||||
|
App.Logger.LogWarn($"Timed out while retrieving latest release information: {ex.Message}", source: "MainWindow");
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
App.Logger.LogError($"Failed to parse latest release information: {ex.Message}", source: "MainWindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ public sealed class Manager
|
|||||||
{new((string)App.LanguageDictionary["date"],3) { Author = "Stone_Red" }, typeof(DatePlugin)},
|
{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["cpuUsage"], 4) { Author = "Stone_Red" }, typeof(CpuMonitorPlugin)},
|
||||||
{new((string)App.LanguageDictionary["weather"], 5) { Author = "Stone_Red" }, typeof(WeatherPlugin)},
|
{new((string)App.LanguageDictionary["weather"], 5) { Author = "Stone_Red" }, typeof(WeatherPlugin)},
|
||||||
|
{new((string)App.LanguageDictionary["nextMeetingCountdown"], 6) { Author = "Stone_Red" }, typeof(NextMeetingCountdownPlugin)},
|
||||||
|
{new((string)App.LanguageDictionary["agenda"], 7) { Author = "Stone_Red" }, typeof(AgendaPlugin)},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Window management
|
// Window management
|
||||||
@@ -232,6 +234,9 @@ public sealed class Manager
|
|||||||
{
|
{
|
||||||
window.SetEditMode(editMode);
|
window.SetEditMode(editMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Application.Current.MainWindow.Topmost = editMode;
|
||||||
|
|
||||||
EditModeChanged?.Invoke(editMode);
|
EditModeChanged?.Invoke(editMode);
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
}
|
}
|
||||||
@@ -370,4 +375,4 @@ public enum PluginType
|
|||||||
{
|
{
|
||||||
DotNet,
|
DotNet,
|
||||||
Web
|
Web
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,18 +71,8 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</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" />
|
<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">
|
<ui:Button x:Name="newLayoutButton" Content="{DynamicResource newLayout}" Grid.Column="2" Icon="{ui:SymbolIcon Add24}" HorizontalAlignment="Stretch" Click="NewLayoutButton_Click" FontWeight="Regular"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
<ui:Button x:Name="removeLayoutButton" Content="{DynamicResource deleteLayout}" Icon="{ui:SymbolIcon Delete24}" Grid.Column="4" HorizontalAlignment="Stretch" Click="RemoveLayoutButton_Click" FontWeight="Regular"/>
|
||||||
<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>
|
</Grid>
|
||||||
</ui:Card>
|
</ui:Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ public partial class MainPage : Page
|
|||||||
if (control is not null)
|
if (control is not null)
|
||||||
{
|
{
|
||||||
control.MinWidth = 200;
|
control.MinWidth = 200;
|
||||||
|
control.MaxWidth = 200;
|
||||||
card.Content = control;
|
card.Content = control;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,9 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Margin="8" VerticalAlignment="Center" FontWeight="SemiBold" Text="{Binding Name, Mode=OneWay}" />
|
<TextBlock Margin="8" VerticalAlignment="Center" FontWeight="SemiBold" Text="{Binding Name, Mode=OneWay}" />
|
||||||
<StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal" Margin="{StaticResource DefaultMargin}">
|
<StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal" Margin="{StaticResource DefaultMargin}">
|
||||||
<Button x:Name="editThemeButton" HorizontalAlignment="Stretch" Click="EditThemeButton_Click" FontWeight="Regular" Tag="{Binding}">
|
<ui:Button x:Name="editThemeButton" Content="{DynamicResource editTheme}" Icon="{ui:SymbolIcon Color24}" HorizontalAlignment="Stretch" Click="EditThemeButton_Click" FontWeight="Regular" Tag="{Binding}">
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<ui:SymbolIcon Symbol="Color24" Margin="0 0 10 0" />
|
</ui:Button>
|
||||||
<TextBlock Text="{DynamicResource editTheme}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@@ -47,18 +44,8 @@
|
|||||||
<ColumnDefinition Width="0.5*" />
|
<ColumnDefinition Width="0.5*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Button x:Name="newThemeButton" Grid.Column="2" HorizontalAlignment="Stretch" Click="AddThemeButton_Click" FontWeight="Regular">
|
<ui:Button x:Name="newThemeButton" Content="{DynamicResource newTheme}" Icon="{ui:SymbolIcon Add24}" Grid.Column="2" HorizontalAlignment="Stretch" Click="AddThemeButton_Click" FontWeight="Regular"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
<ui:Button x:Name="deleteThemeButton" Content="{DynamicResource deleteTheme}" Icon="{ui:SymbolIcon Delete24}" Grid.Column="4" HorizontalAlignment="Stretch" Click="DeleteThemeButton_Click" FontWeight="Regular"/>
|
||||||
<ui:SymbolIcon Symbol="Add24" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource newTheme}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button x:Name="deleteThemeButton" Grid.Column="4" 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>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:Card>
|
</ui:Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -40,18 +40,8 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Button Grid.Column="0" Command="{Binding InstallUninstallButtonData.Command}" IsEnabled="{Binding InstallUninstallButtonData.IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
|
<ui:Button Content="{Binding InstallUninstallButtonData.Text}" Icon="{Binding InstallUninstallButtonData.Icon}" Grid.Column="0" Command="{Binding InstallUninstallButtonData.Command}" IsEnabled="{Binding InstallUninstallButtonData.IsEnabled}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
<ui:Button Content="{Binding OpenButtonData.Text}" Icon="{Binding OpenButtonData.Icon}" Grid.Column="1" Command="{Binding OpenButtonData.Command}" IsEnabled="{Binding OpenButtonData.IsEnabled}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5 0 5 0"/>
|
||||||
<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 0 5 0">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<ui:SymbolIcon Symbol="{Binding OpenButtonData.Icon}" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{Binding OpenButtonData.Text}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="5">
|
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="5">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
ScrollViewer.CanContentScroll="False"
|
ScrollViewer.CanContentScroll="False"
|
||||||
Title="{DynamicResource pluginManager}">
|
Title="{DynamicResource pluginManager}">
|
||||||
|
|
||||||
<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}">
|
<busyIndicator:BusyMask x:Name="BusyIndicator" IsBusy="{Binding IsLoading}" IndicatorType="ThreeDots" BusyContent="Please wait..." BusyContentMargin="0,20,0,0" Foreground="{DynamicResource TextFillColorPrimaryBrush}" Background="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||||
<Grid Margin="{StaticResource DefaultMargin}">
|
<Grid Margin="{StaticResource DefaultMargin}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto" />
|
<RowDefinition Height="auto" />
|
||||||
@@ -36,12 +36,12 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<ui:TextBox Grid.Column="0" Text="{Binding AllPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="AllPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchAllPlugins}" />
|
<ui:TextBox Grid.Column="0" Text="{Binding AllPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="AllPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchAllPlugins}" Icon="{ui:SymbolIcon Search24}" IconPlacement="Right" />
|
||||||
<ui:TextBox Grid.Column="2" Text="{Binding InstalledPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="InstalledPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchInstalledPlugins}" />
|
<ui:TextBox Grid.Column="2" Text="{Binding InstalledPluginsSearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="InstalledPluginsSearchTextBox_TextChanged" PlaceholderText="{DynamicResource searchInstalledPlugins}" Icon="{ui:SymbolIcon Search24}" IconPlacement="Right" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:Card>
|
</ui:Card>
|
||||||
|
|
||||||
<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}">
|
<busyIndicator:BusyMask Grid.Row="2" IsBusy="{Binding IsSearching}" IndicatorType="ThreeDots" BusyContent="Searching..." BusyContentMargin="0,50,0,0" Foreground="{DynamicResource TextFillColorPrimaryBrush}" Background="{DynamicResource ControlStrokeColorDefaultBrush}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" Padding="0" VerticalContentAlignment="Top" Margin="0 0 2.5 0">
|
<ScrollViewer VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" Padding="0" VerticalContentAlignment="Top" Margin="0 0 2.5 0">
|
||||||
<ItemsControl ItemsSource="{Binding AllPlugins}">
|
<ItemsControl ItemsSource="{Binding AllPlugins}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
@@ -106,24 +106,9 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Image Source="{Binding ModIoIcon}" Cursor="Hand" Height="30" MouseUp="Image_MouseUp" HorizontalAlignment="Left" />
|
<Image Source="{Binding ModIoIcon}" Cursor="Hand" Height="30" MouseUp="Image_MouseUp" HorizontalAlignment="Left" />
|
||||||
<Button Grid.Column="2" HorizontalAlignment="Stretch" Click="CreatePluginButton_Click">
|
<ui:Button Content="{DynamicResource createNewPlugin}" Icon="{ui:SymbolIcon Add24}" Grid.Column="2" HorizontalAlignment="Stretch" Click="CreatePluginButton_Click"/>
|
||||||
<StackPanel Orientation="Horizontal">
|
<ui:Button Content="{DynamicResource reloadPlugins}" Icon="{ui:SymbolIcon ArrowClockwise24}" Grid.Column="4" HorizontalAlignment="Stretch" Click="ReloadPluginsButton_Click"/>
|
||||||
<ui:SymbolIcon Symbol="Add24" Margin="0 0 10 0" />
|
<ui:Button Content="{Binding LoginButtonText}" Icon="{ui:SymbolIcon PersonKey20}" Grid.Column="6" HorizontalAlignment="Stretch" Click="LogInButton_Click"/>
|
||||||
<TextBlock Text="{DynamicResource createNewPlugin}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Grid.Column="4" HorizontalAlignment="Stretch" Click="ReloadPluginsButton_Click">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<ui:SymbolIcon Symbol="ArrowClockwise24" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{DynamicResource reloadPlugins}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Grid.Column="6" HorizontalAlignment="Stretch" Click="LogInButton_Click">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<ui:SymbolIcon Symbol="PersonKey20" Margin="0 0 10 0" />
|
|
||||||
<TextBlock Text="{Binding LoginButtonText}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:Card>
|
</ui:Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ public partial class PluginManager : Page
|
|||||||
string pluginPath = Path.Combine(pluginsPath, pluginGuid);
|
string pluginPath = Path.Combine(pluginsPath, pluginGuid);
|
||||||
uint pluginId = (uint)Random.Shared.Next(1000, 9999);
|
uint pluginId = (uint)Random.Shared.Next(1000, 9999);
|
||||||
|
|
||||||
InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager")
|
CreatePluginDialog inputDialog = new()
|
||||||
{
|
{
|
||||||
Owner = Window.GetWindow(this),
|
Owner = Window.GetWindow(this),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
Closing="Window_Closing"
|
Closing="Window_Closing"
|
||||||
ContentRendered="Window_ContentRendered"
|
ContentRendered="Window_ContentRendered"
|
||||||
x:Name="window">
|
x:Name="window">
|
||||||
<busyIndicator:BusyMask x:Name="busyMask" IsBusyAtStartup="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
<busyIndicator:BusyMask x:Name="busyMask" IsBusy="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
private System.Timers.Timer? reloadDebounceTimer;
|
private System.Timers.Timer? reloadDebounceTimer;
|
||||||
private bool isReloading = false;
|
private bool isReloading = false;
|
||||||
|
|
||||||
|
private WriteableBitmap? writeableBitmap;
|
||||||
|
private BitmapScalingMode lastBitmapScalingMode = BitmapScalingMode.Unspecified;
|
||||||
|
|
||||||
public bool IsRunning { get; private set; } = true;
|
public bool IsRunning { get; private set; } = true;
|
||||||
public PluginMetadata PluginMetadata { get; private set; }
|
public PluginMetadata PluginMetadata { get; private set; }
|
||||||
public string PluginFolderPath { get; private set; }
|
public string PluginFolderPath { get; private set; }
|
||||||
@@ -241,21 +244,21 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
assemblyLoadContext = CreateAssemblyLoadContext();
|
assemblyLoadContext = CreateAssemblyLoadContext();
|
||||||
|
|
||||||
// Show busy indicator
|
// Show busy indicator
|
||||||
await Dispatcher.InvokeAsync(() => busyMask.IsBusy = true);
|
_ = await Dispatcher.InvokeAsync(() => busyMask.IsBusy = true);
|
||||||
|
|
||||||
// Reload the plugin
|
// Reload the plugin
|
||||||
await ExecuteSource();
|
await ExecuteSource();
|
||||||
|
|
||||||
// Hide busy indicator
|
// 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");
|
App.Logger.LogInfo($"\"{PluginMetadata.Name}\" - Plugin reloaded successfully", source: "Plugin");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
App.Logger.LogError($"\"{PluginMetadata.Name}\" - Failed to reload plugin: {ex}", source: "Plugin");
|
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
|
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);
|
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(
|
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||||
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
|
||||||
ImageLockMode.ReadOnly, bitmap.PixelFormat);
|
|
||||||
|
|
||||||
BitmapSource bitmapSource = BitmapSource.Create(
|
try
|
||||||
bitmapData.Width, bitmapData.Height,
|
{
|
||||||
bitmap.HorizontalResolution, bitmap.VerticalResolution,
|
Dispatcher.Invoke(() =>
|
||||||
PixelFormats.Bgra32, null,
|
{
|
||||||
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
|
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();
|
writeableBitmap.Lock();
|
||||||
return bitmapSource;
|
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)
|
private void Window_ContentRendered(object? sender, EventArgs e)
|
||||||
@@ -525,11 +558,13 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
{
|
{
|
||||||
SetHorizontalAlignment();
|
SetHorizontalAlignment();
|
||||||
SetVerticalAlignment();
|
SetVerticalAlignment();
|
||||||
|
SetRotation();
|
||||||
SetThemeOverride();
|
SetThemeOverride();
|
||||||
SetThemeOverrideItems();
|
SetThemeOverrideItems();
|
||||||
|
|
||||||
pluginClassInstance.horizontalAlignment.OnValueChanged += SetHorizontalAlignment;
|
pluginClassInstance.horizontalAlignment.OnValueChanged += SetHorizontalAlignment;
|
||||||
pluginClassInstance.verticalAlignment.OnValueChanged += SetVerticalAlignment;
|
pluginClassInstance.verticalAlignment.OnValueChanged += SetVerticalAlignment;
|
||||||
|
pluginClassInstance.rotation.OnValueChanged += SetRotation;
|
||||||
pluginClassInstance.themeOverride.OnValueChanged += SetThemeOverride;
|
pluginClassInstance.themeOverride.OnValueChanged += SetThemeOverride;
|
||||||
|
|
||||||
DesktopMagicSettings desktopMagicSettings = MainWindowDataContext.GetSettings();
|
DesktopMagicSettings desktopMagicSettings = MainWindowDataContext.GetSettings();
|
||||||
@@ -564,6 +599,11 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
border.HorizontalAlignment = horizontalAlignment;
|
border.HorizontalAlignment = horizontalAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetRotation()
|
||||||
|
{
|
||||||
|
image.LayoutTransform = new RotateTransform(pluginClassInstance.rotation.Value);
|
||||||
|
}
|
||||||
|
|
||||||
void SetThemeOverride()
|
void SetThemeOverride()
|
||||||
{
|
{
|
||||||
if (pluginClassInstance.themeOverride.Value == "<None>")
|
if (pluginClassInstance.themeOverride.Value == "<None>")
|
||||||
@@ -819,14 +859,7 @@ public partial class PluginWindow : Window, IPluginWindow
|
|||||||
_ => BitmapScalingMode.Unspecified
|
_ => BitmapScalingMode.Unspecified
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update Image
|
UpdateImageFromBitmap(result, renderOptions);
|
||||||
BitmapSource frozenSource = BitmapToImageSource(result);
|
|
||||||
|
|
||||||
_ = Dispatcher.BeginInvoke(() =>
|
|
||||||
{
|
|
||||||
RenderOptions.SetBitmapScalingMode(image, renderOptions);
|
|
||||||
image.Source = frozenSource;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
ContentRendered="Window_ContentRendered"
|
ContentRendered="Window_ContentRendered"
|
||||||
x:Name="window">
|
x:Name="window">
|
||||||
|
|
||||||
<busyIndicator:BusyMask x:Name="busyMask" IsBusyAtStartup="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
<busyIndicator:BusyMask x:Name="busyMask" IsBusy="True" Background="Transparent" BusyContent="" BusyContentMargin="-5" IndicatorType="Cogs">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
<Rectangle x:Name="panel" Fill="#4CBAFFEF" Stroke="White" Margin="0,0,0,0" Visibility="Collapsed" />
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
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="developedBy">Entwickelt von Stone__Red</system:String>
|
||||||
<system:String x:Key="reportBug">Bug Melden</system:String>
|
<system:String x:Key="reportBug">Bug Melden</system:String>
|
||||||
<system:String x:Key="requestFeature">Feature Anfragen</system:String>
|
<system:String x:Key="requestFeature">Feature Anfragen</system:String>
|
||||||
|
<system:String x:Key="email">E-Mail</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>
|
||||||
@@ -37,6 +38,8 @@
|
|||||||
<system:String x:Key="cpuUsage">CPU Auslastung</system:String>
|
<system:String x:Key="cpuUsage">CPU Auslastung</system:String>
|
||||||
<system:String x:Key="musicVisualizer">Musik Visualisierer</system:String>
|
<system:String x:Key="musicVisualizer">Musik Visualisierer</system:String>
|
||||||
<system:String x:Key="weather">Wetter</system:String>
|
<system:String x:Key="weather">Wetter</system:String>
|
||||||
|
<system:String x:Key="nextMeetingCountdown">Nächstes Meeting Countdown</system:String>
|
||||||
|
<system:String x:Key="agenda">Agenda</system:String>
|
||||||
<system:String x:Key="amplifier">Signalverstärkung:</system:String>
|
<system:String x:Key="amplifier">Signalverstärkung:</system:String>
|
||||||
<system:String x:Key="newLayout">Neues Layout</system:String>
|
<system:String x:Key="newLayout">Neues Layout</system:String>
|
||||||
<system:String x:Key="deleteLayout">Layout Löschen</system:String>
|
<system:String x:Key="deleteLayout">Layout Löschen</system:String>
|
||||||
@@ -59,9 +62,14 @@
|
|||||||
<system:String x:Key="addedFormat">Erstellt: {0}</system:String>
|
<system:String x:Key="addedFormat">Erstellt: {0}</system:String>
|
||||||
<system:String x:Key="updatedFormat">Aktualisiert: {0}</system:String>
|
<system:String x:Key="updatedFormat">Aktualisiert: {0}</system:String>
|
||||||
<system:String x:Key="versionFormat">Version: {0}</system:String>
|
<system:String x:Key="versionFormat">Version: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoTitle">DesktopMagic wurde aktualisiert!</system:String>
|
||||||
|
<system:String x:Key="releaseInfoHeaderFormat">Neueste Version: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoPublishedFormat">Veröffentlicht: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoBodyLabel">Release Notes:</system:String>
|
||||||
|
<system:String x:Key="openReleasePage">Release-Seite öffnen</system:String>
|
||||||
<col:ArrayList x:Key="musicVisualizerOptionsComboboxItems">
|
<col:ArrayList x:Key="musicVisualizerOptionsComboboxItems">
|
||||||
<system:String>Unten</system:String>
|
<system:String>Unten</system:String>
|
||||||
<system:String>Mitte</system:String>
|
<system:String>Mitte</system:String>
|
||||||
<system:String>Oben</system:String>
|
<system:String>Oben</system:String>
|
||||||
</col:ArrayList>
|
</col:ArrayList>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
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="developedBy">Developed by Stone__Red</system:String>
|
||||||
<system:String x:Key="reportBug">Report Bug</system:String>
|
<system:String x:Key="reportBug">Report Bug</system:String>
|
||||||
<system:String x:Key="requestFeature">Request Feature</system:String>
|
<system:String x:Key="requestFeature">Request Feature</system:String>
|
||||||
|
<system:String x:Key="email">Email</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>
|
||||||
@@ -37,6 +38,8 @@
|
|||||||
<system:String x:Key="cpuUsage">CPU Usage</system:String>
|
<system:String x:Key="cpuUsage">CPU Usage</system:String>
|
||||||
<system:String x:Key="musicVisualizer">Music Visualizer</system:String>
|
<system:String x:Key="musicVisualizer">Music Visualizer</system:String>
|
||||||
<system:String x:Key="weather">Weather</system:String>
|
<system:String x:Key="weather">Weather</system:String>
|
||||||
|
<system:String x:Key="nextMeetingCountdown">Next Meeting Countdown</system:String>
|
||||||
|
<system:String x:Key="agenda">Agenda</system:String>
|
||||||
<system:String x:Key="amplifier">Signal Amplification:</system:String>
|
<system:String x:Key="amplifier">Signal Amplification:</system:String>
|
||||||
<system:String x:Key="newLayout">New Layout</system:String>
|
<system:String x:Key="newLayout">New Layout</system:String>
|
||||||
<system:String x:Key="deleteLayout">Delete Layout</system:String>
|
<system:String x:Key="deleteLayout">Delete Layout</system:String>
|
||||||
@@ -61,9 +64,14 @@
|
|||||||
<system:String x:Key="addedFormat">Added: {0}</system:String>
|
<system:String x:Key="addedFormat">Added: {0}</system:String>
|
||||||
<system:String x:Key="updatedFormat">Updated: {0}</system:String>
|
<system:String x:Key="updatedFormat">Updated: {0}</system:String>
|
||||||
<system:String x:Key="versionFormat">Version: {0}</system:String>
|
<system:String x:Key="versionFormat">Version: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoTitle">DesktopMagic has been updated!</system:String>
|
||||||
|
<system:String x:Key="releaseInfoHeaderFormat">Latest release: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoPublishedFormat">Published: {0}</system:String>
|
||||||
|
<system:String x:Key="releaseInfoBodyLabel">Release notes:</system:String>
|
||||||
|
<system:String x:Key="openReleasePage">Open release page</system:String>
|
||||||
<col:ArrayList x:Key="musicVisualizerOptionsComboboxItems">
|
<col:ArrayList x:Key="musicVisualizerOptionsComboboxItems">
|
||||||
<system:String>Bottom</system:String>
|
<system:String>Bottom</system:String>
|
||||||
<system:String>Middle</system:String>
|
<system:String>Middle</system:String>
|
||||||
<system:String>Top</system:String>
|
<system:String>Top</system:String>
|
||||||
</col:ArrayList>
|
</col:ArrayList>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public class DesktopMagicSettings : INotifyPropertyChanged
|
|||||||
|
|
||||||
public string? ModIoAccessToken { get; set; }
|
public string? ModIoAccessToken { get; set; }
|
||||||
|
|
||||||
|
public string? ReleaseInfoLastAppVersion { get; set; }
|
||||||
|
|
||||||
public DesktopMagicSettings()
|
public DesktopMagicSettings()
|
||||||
{
|
{
|
||||||
themes.CollectionChanged += (s, e) => CurrentLayout.UpdateTheme();
|
themes.CollectionChanged += (s, e) => CurrentLayout.UpdateTheme();
|
||||||
@@ -76,4 +78,4 @@ public class DesktopMagicSettings : INotifyPropertyChanged
|
|||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ namespace DesktopMagic.PluginTest;
|
|||||||
public class GifPlugin : Plugin
|
public class GifPlugin : Plugin
|
||||||
{
|
{
|
||||||
[Setting("gif-path", "GIF path")]
|
[Setting("gif-path", "GIF path")]
|
||||||
private readonly TextBox input = new TextBox("");
|
private readonly FileSelector input = new FileSelector();
|
||||||
|
|
||||||
[Setting("info")]
|
[Setting("info")]
|
||||||
private readonly Label info = new Label("");
|
private readonly Label info = new Label("Select a GIF file to load.");
|
||||||
|
|
||||||
private readonly List<Bitmap> bitmaps = [];
|
private readonly List<Bitmap> bitmaps = [];
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ public class GifPlugin : Plugin
|
|||||||
info.Value = $"Loading frame {i + 1} of {gif.GetFrameCount(FrameDimension.Time)}";
|
info.Value = $"Loading frame {i + 1} of {gif.GetFrameCount(FrameDimension.Time)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
info.Value = string.Empty;
|
info.Value = $"Loaded {bitmaps.Count} frames.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ public abstract class Plugin
|
|||||||
[Setting("desktopmagic-vertical-alignment", "Vertical Alignment", -998)]
|
[Setting("desktopmagic-vertical-alignment", "Vertical Alignment", -998)]
|
||||||
internal ComboBox verticalAlignment = new ComboBox("Center", "Top", "Bottom");
|
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("<None>");
|
internal ComboBox themeOverride = new ComboBox("<None>");
|
||||||
|
|
||||||
private IPluginData application = null!;
|
private IPluginData application = null!;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="StoneRed.DesktopMagic"
|
Name="StoneRed.DesktopMagic"
|
||||||
Publisher="CN=6D1FD759-1951-428A-9B03-964461C7DBF5"
|
Publisher="CN=6D1FD759-1951-428A-9B03-964461C7DBF5"
|
||||||
Version="1.3.0.0" />
|
Version="1.3.2.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Desktop Magic</DisplayName>
|
<DisplayName>Desktop Magic</DisplayName>
|
||||||
|
|||||||
Reference in New Issue
Block a user