settings_re11

This commit is contained in:
lincube
2026-03-15 17:08:07 +08:00
parent c7fb48c8ee
commit f83c6ede1d
49 changed files with 3243 additions and 815 deletions

View File

@@ -17,7 +17,7 @@
SizeToContent="Manual"
ShowInTaskbar="False"
SystemDecorations="BorderOnly"
Background="{DynamicResource EditorWindowBackgroundBrush}"
Background="Transparent"
Title="Component Editor">
<Window.Resources>
<!-- Material Design 3 Brushes -->

View File

@@ -96,6 +96,7 @@ public sealed class DesktopComponentRuntimeDescriptor
ArgumentNullException.ThrowIfNull(settingsFacade);
var settingsService = settingsFacade.Settings;
var appearanceTheme = HostAppearanceThemeProvider.GetOrCreate();
var componentAccessor = settingsService.GetComponentAccessor(Definition.Id, placementId);
var componentSettingsStore = new ComponentSettingsService(settingsService);
componentSettingsStore.SetScopedComponentContext(Definition.Id, placementId);
@@ -116,6 +117,7 @@ public sealed class DesktopComponentRuntimeDescriptor
placementId,
settingsFacade,
settingsService,
appearanceTheme,
componentAccessor,
componentSettingsStore);
@@ -133,6 +135,7 @@ public sealed class DesktopComponentRuntimeDescriptor
placementId,
settingsFacade,
settingsService,
appearanceTheme,
componentAccessor,
componentSettingsStore));
}

View File

@@ -84,7 +84,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
[
DailyIcon0, DailyIcon1, DailyIcon2, DailyIcon3, DailyIcon4
];
_dailyIconKinds = Enumerable.Repeat(HyperOS3WeatherVisualKind.CloudyDay, _dailyIconBlocks.Length).ToArray();
_dailyIconKinds = Enumerable.Repeat(HyperOS3WeatherVisualKind.Unknown, _dailyIconBlocks.Length).ToArray();
ConfigureTextOverflowGuards();
_refreshTimer.Tick += OnRefreshTimerTick;
_animationTimer.Tick += OnAnimationTick;
@@ -328,12 +328,17 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
snapshot,
_timeZoneService?.CurrentTimeZone,
_timeZoneService?.GetCurrentTime() ?? DateTime.Now);
var kind = HyperOS3WeatherTheme.ResolveVisualKind(snapshot.Current.WeatherCode, isNight);
var currentVisual = XiaomiWeatherVisualResolver.Resolve(
snapshot.Current.WeatherText,
snapshot.Current.WeatherCode,
isNight,
_languageCode);
var kind = currentVisual.VisualKind;
ApplyVisualTheme(kind);
SetLoadingSkeleton(false);
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveHeroIconAsset(kind));
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(currentVisual.PrimaryIconAsset);
CityTextBlock.Text = ResolveLocation(snapshot.LocationName, fallbackLocationName);
ConditionTextBlock.Text = ResolveWeatherText(snapshot.Current.WeatherText, kind);
ConditionTextBlock.Text = currentVisual.DisplayText;
TemperatureTextBlock.Text = FormatTemperature(snapshot.Current.TemperatureC);
var today = snapshot.DailyForecasts.FirstOrDefault();
@@ -354,12 +359,17 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
.OrderBy(entry => Math.Abs((entry.Time - target).TotalMinutes))
.FirstOrDefault();
var weatherCode = item?.Source.WeatherCode ?? snapshot.Current.WeatherCode;
var hourKind = HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, IsNightHour(target));
var hourVisual = XiaomiWeatherVisualResolver.Resolve(
item?.Source.WeatherText,
weatherCode,
IsNightHour(target),
_languageCode);
var hourKind = hourVisual.VisualKind;
_hourlyTempBlocks[i].Text = i == sunsetSlotIndex
? L("weather.hourly.sunset", "Sunset")
: FormatTemperature(item?.Source.TemperatureC ?? snapshot.Current.TemperatureC);
_hourlyTimeBlocks[i].Text = target.ToString("HH:mm", CultureInfo.InvariantCulture);
_hourlyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(hourKind));
_hourlyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(hourVisual.CompactIconAsset);
}
var todayDate = DateOnly.FromDateTime(now);
@@ -368,21 +378,26 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var date = todayDate.AddDays(i + 1);
var daily = snapshot.DailyForecasts.FirstOrDefault(entry => entry.Date == date) ?? snapshot.DailyForecasts.FirstOrDefault();
var weatherCode = daily?.DayWeatherCode ?? daily?.NightWeatherCode ?? snapshot.Current.WeatherCode;
var dayKind = HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, false);
var dayText = ResolveWeatherText(daily?.DayWeatherText ?? daily?.NightWeatherText, dayKind);
var dayVisual = XiaomiWeatherVisualResolver.Resolve(
daily?.DayWeatherText ?? daily?.NightWeatherText,
weatherCode,
false,
_languageCode);
var dayKind = dayVisual.VisualKind;
var dayText = dayVisual.DisplayText;
_dailyLabelBlocks[i].Text = $"{ResolveDayLabel(date, i + 1)}·{dayText}";
_dailyHighBlocks[i].Text = FormatTemperatureValue(daily?.HighTemperatureC);
_dailyLowBlocks[i].Text = FormatTemperatureValue(daily?.LowTemperatureC);
_dailyIconKinds[i] = dayKind;
_dailyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(dayKind));
_dailyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(dayVisual.CompactIconAsset);
}
}
private void ApplyFallback()
{
ApplyVisualTheme(HyperOS3WeatherVisualKind.CloudyDay);
ApplyVisualTheme(HyperOS3WeatherVisualKind.Unknown);
SetLoadingSkeleton(false);
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveHeroIconAsset(HyperOS3WeatherVisualKind.CloudyDay));
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveHeroIconAsset(HyperOS3WeatherVisualKind.Unknown));
CityTextBlock.Text = L("weather.widget.location_unknown", "Unknown location");
ConditionTextBlock.Text = L("weather.widget.loading", "Loading...");
TemperatureTextBlock.Text = "--°";
@@ -393,7 +408,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
{
_hourlyTempBlocks[i].Text = i == 3 ? L("weather.hourly.sunset", "Sunset") : "--°";
_hourlyTimeBlocks[i].Text = timelineStart.AddHours(i).ToString("HH:mm", CultureInfo.InvariantCulture);
_hourlyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(HyperOS3WeatherVisualKind.CloudyDay));
_hourlyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(HyperOS3WeatherVisualKind.Unknown));
}
for (var i = 0; i < _dailyLabelBlocks.Length; i++)
@@ -401,8 +416,8 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
_dailyLabelBlocks[i].Text = $"{ResolveDayLabel(DateOnly.FromDateTime(DateTime.Now).AddDays(i + 1), i + 1)}·{L("weather.widget.condition_cloudy", "Cloudy")}";
_dailyHighBlocks[i].Text = "--";
_dailyLowBlocks[i].Text = "--";
_dailyIconKinds[i] = HyperOS3WeatherVisualKind.CloudyDay;
_dailyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(HyperOS3WeatherVisualKind.CloudyDay));
_dailyIconKinds[i] = HyperOS3WeatherVisualKind.Unknown;
_dailyIconBlocks[i].Source = HyperOS3WeatherAssetLoader.LoadImage(HyperOS3WeatherTheme.ResolveMiniIconAsset(HyperOS3WeatherVisualKind.Unknown));
}
}
@@ -417,7 +432,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
BackgroundMotionLayer.Background = background ?? CreateGradientBrush(palette.GradientFrom, palette.GradientTo);
BackgroundTintLayer.Background = CreateSolidBrush(palette.Tint);
var isNightVisual = kind is HyperOS3WeatherVisualKind.ClearNight or HyperOS3WeatherVisualKind.CloudyNight;
var isNightVisual = kind is HyperOS3WeatherVisualKind.ClearNight or HyperOS3WeatherVisualKind.PartlyCloudyNight or HyperOS3WeatherVisualKind.CloudyNight;
var backgroundSamples = WeatherTypographyAccessibility.BuildBackgroundSamples(
palette.GradientFrom,
palette.GradientTo,
@@ -612,7 +627,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var dailyKind = i < _dailyIconKinds.Length
? _dailyIconKinds[i]
: HyperOS3WeatherVisualKind.CloudyDay;
: HyperOS3WeatherVisualKind.Unknown;
var dailyIconVisualSize = Math.Clamp(
dailyIconSize * ResolveDailyMiniIconScaleBoost(dailyKind),
8,
@@ -851,18 +866,10 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
return score;
}
private string ResolveWeatherText(string? weatherText, HyperOS3WeatherVisualKind kind)
private string ResolveWeatherText(string? weatherText, int? weatherCode, HyperOS3WeatherVisualKind kind)
{
if (!string.IsNullOrWhiteSpace(weatherText)) return weatherText;
return kind switch
{
HyperOS3WeatherVisualKind.ClearDay or HyperOS3WeatherVisualKind.ClearNight => L("weather.widget.condition_clear", "Clear"),
HyperOS3WeatherVisualKind.CloudyDay or HyperOS3WeatherVisualKind.CloudyNight => L("weather.widget.condition_cloudy", "Cloudy"),
HyperOS3WeatherVisualKind.RainLight or HyperOS3WeatherVisualKind.RainHeavy => L("weather.widget.condition_rain", "Rain"),
HyperOS3WeatherVisualKind.Storm => L("weather.widget.condition_storm", "Thunderstorm"),
HyperOS3WeatherVisualKind.Snow => L("weather.widget.condition_snow", "Snow"),
_ => L("weather.widget.condition_fog", "Fog")
};
_ = kind;
return XiaomiWeatherVisualResolver.ResolveDisplayText(weatherText, weatherCode, _languageCode);
}
private DateTime ConvertToConfiguredTime(DateTimeOffset sourceTime)
@@ -999,7 +1006,8 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
kind switch
{
HyperOS3WeatherVisualKind.RainLight or HyperOS3WeatherVisualKind.RainHeavy or HyperOS3WeatherVisualKind.Storm or HyperOS3WeatherVisualKind.Snow => 1.16,
HyperOS3WeatherVisualKind.ClearNight or HyperOS3WeatherVisualKind.CloudyNight => 1.08,
HyperOS3WeatherVisualKind.ClearNight or HyperOS3WeatherVisualKind.PartlyCloudyNight or HyperOS3WeatherVisualKind.CloudyNight => 1.08,
HyperOS3WeatherVisualKind.Haze or HyperOS3WeatherVisualKind.Fog => 1.04,
_ => 1.0
};
@@ -1008,9 +1016,13 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
{
HyperOS3WeatherVisualKind.CloudyDay => 1.30,
HyperOS3WeatherVisualKind.CloudyNight => 1.28,
HyperOS3WeatherVisualKind.PartlyCloudyDay => 1.28,
HyperOS3WeatherVisualKind.PartlyCloudyNight => 1.26,
HyperOS3WeatherVisualKind.ClearDay => 1.26,
HyperOS3WeatherVisualKind.ClearNight => 1.24,
HyperOS3WeatherVisualKind.Haze => 1.20,
HyperOS3WeatherVisualKind.Fog => 1.18,
HyperOS3WeatherVisualKind.Sleet => 1.14,
HyperOS3WeatherVisualKind.RainLight => 1.14,
HyperOS3WeatherVisualKind.RainHeavy => 1.12,
HyperOS3WeatherVisualKind.Snow => 1.12,

View File

@@ -22,10 +22,15 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
{
private enum WeatherVisualKind
{
Unknown,
ClearDay,
ClearNight,
PartlyCloudyDay,
PartlyCloudyNight,
CloudyDay,
CloudyNight,
Haze,
Sleet,
RainLight,
RainHeavy,
Storm,
@@ -482,7 +487,12 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
private void ApplySnapshot(WeatherSnapshot snapshot, string fallbackLocationName)
{
var isNight = ResolveIsNight(snapshot);
var visualKind = ResolveVisualKind(snapshot.Current.WeatherCode, isNight);
var visual = XiaomiWeatherVisualResolver.Resolve(
snapshot.Current.WeatherText,
snapshot.Current.WeatherCode,
isNight,
_languageCode);
var visualKind = ResolveVisualKind(visual.VisualKind);
ApplyVisualTheme(visualKind);
var rawLocation = string.IsNullOrWhiteSpace(snapshot.LocationName)
@@ -490,8 +500,8 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
: snapshot.LocationName;
CityTextBlock.Text = ResolvePreciseDisplayLocation(rawLocation, _languageCode, L("weather.widget.location_unknown", "Unknown location"));
ConditionTextBlock.Text = ResolveWeatherConditionText(snapshot.Current.WeatherText, visualKind);
SetMainWeatherIcon(visualKind);
ConditionTextBlock.Text = visual.DisplayText;
SetMainWeatherIcon(visual.PrimaryIconAsset, visualKind);
SetLoadingSkeleton(false);
TemperatureTextBlock.Text = FormatTemperature(snapshot.Current.TemperatureC);
@@ -505,7 +515,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
{
var fallbackKind = ResolveFallbackVisualKind();
ApplyVisualTheme(fallbackKind);
SetMainWeatherIcon(fallbackKind);
SetMainWeatherIcon(null, fallbackKind);
SetLoadingSkeleton(false);
CityTextBlock.Text = L("weather.widget.location_not_configured", "Weather location is not configured");
ConditionTextBlock.Text = L("weather.widget.configure_hint", "Open Settings > Weather to configure");
@@ -520,7 +530,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
{
var loadingKind = IsNightNow() ? WeatherVisualKind.CloudyNight : WeatherVisualKind.CloudyDay;
ApplyVisualTheme(loadingKind);
SetMainWeatherIcon(loadingKind);
SetMainWeatherIcon(null, loadingKind);
SetLoadingSkeleton(true);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -535,8 +545,8 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
private void ApplyFailedState(string locationName)
{
ApplyVisualTheme(WeatherVisualKind.Fog);
SetMainWeatherIcon(WeatherVisualKind.Fog);
ApplyVisualTheme(WeatherVisualKind.Unknown);
SetMainWeatherIcon(HyperOS3WeatherTheme.ResolveHeroIconAsset(HyperOS3WeatherVisualKind.Unknown), WeatherVisualKind.Unknown);
SetLoadingSkeleton(false);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -545,7 +555,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
ConditionTextBlock.Text = L("weather.widget.fetch_failed", "Weather fetch failed");
TemperatureTextBlock.Text = "--°";
RangeTextBlock.Text = L("weather.widget.range_unknown", "-- / --");
ApplyHourlyForecastItems(BuildPlaceholderHourlyForecastItems(WeatherVisualKind.Fog));
ApplyHourlyForecastItems(BuildPlaceholderHourlyForecastItems(WeatherVisualKind.Unknown));
ApplyAdaptiveTypography();
_latestSnapshot = null;
}
@@ -560,7 +570,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
BackgroundTintLayer.Background = CreateSolidBrush(palette.Tint);
var particleBrush = ResolveParticleBrush(ToThemeKind(kind), palette.ParticleColor);
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight;
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight;
var backgroundSamples = WeatherTypographyAccessibility.BuildBackgroundSamples(
palette.GradientFrom,
palette.GradientTo,
@@ -690,17 +700,28 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
private static WeatherVisualKind ResolveVisualKind(int? weatherCode, bool isNight)
{
return HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight) switch
return ResolveVisualKind(HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight));
}
private static WeatherVisualKind ResolveVisualKind(HyperOS3WeatherVisualKind kind)
{
return kind switch
{
HyperOS3WeatherVisualKind.Unknown => WeatherVisualKind.Unknown,
HyperOS3WeatherVisualKind.ClearDay => WeatherVisualKind.ClearDay,
HyperOS3WeatherVisualKind.ClearNight => WeatherVisualKind.ClearNight,
HyperOS3WeatherVisualKind.PartlyCloudyDay => WeatherVisualKind.PartlyCloudyDay,
HyperOS3WeatherVisualKind.PartlyCloudyNight => WeatherVisualKind.PartlyCloudyNight,
HyperOS3WeatherVisualKind.CloudyDay => WeatherVisualKind.CloudyDay,
HyperOS3WeatherVisualKind.CloudyNight => WeatherVisualKind.CloudyNight,
HyperOS3WeatherVisualKind.Haze => WeatherVisualKind.Haze,
HyperOS3WeatherVisualKind.Sleet => WeatherVisualKind.Sleet,
HyperOS3WeatherVisualKind.RainLight => WeatherVisualKind.RainLight,
HyperOS3WeatherVisualKind.RainHeavy => WeatherVisualKind.RainHeavy,
HyperOS3WeatherVisualKind.Storm => WeatherVisualKind.Storm,
HyperOS3WeatherVisualKind.Snow => WeatherVisualKind.Snow,
_ => WeatherVisualKind.Fog
HyperOS3WeatherVisualKind.Fog => WeatherVisualKind.Fog,
_ => WeatherVisualKind.Unknown
};
}
@@ -721,35 +742,28 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
{
return kind switch
{
WeatherVisualKind.Unknown => HyperOS3WeatherVisualKind.Unknown,
WeatherVisualKind.ClearDay => HyperOS3WeatherVisualKind.ClearDay,
WeatherVisualKind.ClearNight => HyperOS3WeatherVisualKind.ClearNight,
WeatherVisualKind.PartlyCloudyDay => HyperOS3WeatherVisualKind.PartlyCloudyDay,
WeatherVisualKind.PartlyCloudyNight => HyperOS3WeatherVisualKind.PartlyCloudyNight,
WeatherVisualKind.CloudyDay => HyperOS3WeatherVisualKind.CloudyDay,
WeatherVisualKind.CloudyNight => HyperOS3WeatherVisualKind.CloudyNight,
WeatherVisualKind.Haze => HyperOS3WeatherVisualKind.Haze,
WeatherVisualKind.Sleet => HyperOS3WeatherVisualKind.Sleet,
WeatherVisualKind.RainLight => HyperOS3WeatherVisualKind.RainLight,
WeatherVisualKind.RainHeavy => HyperOS3WeatherVisualKind.RainHeavy,
WeatherVisualKind.Storm => HyperOS3WeatherVisualKind.Storm,
WeatherVisualKind.Snow => HyperOS3WeatherVisualKind.Snow,
_ => HyperOS3WeatherVisualKind.Fog
WeatherVisualKind.Fog => HyperOS3WeatherVisualKind.Fog,
_ => HyperOS3WeatherVisualKind.Unknown
};
}
private string ResolveWeatherConditionText(string? weatherText, WeatherVisualKind kind)
private string ResolveWeatherConditionText(string? weatherText, int? weatherCode, WeatherVisualKind kind)
{
if (!string.IsNullOrWhiteSpace(weatherText))
{
return weatherText;
}
return kind switch
{
WeatherVisualKind.ClearDay or WeatherVisualKind.ClearNight => L("weather.widget.condition_clear", "Clear"),
WeatherVisualKind.CloudyDay or WeatherVisualKind.CloudyNight => L("weather.widget.condition_cloudy", "Cloudy"),
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy => L("weather.widget.condition_rain", "Rain"),
WeatherVisualKind.Storm => L("weather.widget.condition_storm", "Thunderstorm"),
WeatherVisualKind.Snow => L("weather.widget.condition_snow", "Snow"),
WeatherVisualKind.Fog => L("weather.widget.condition_fog", "Fog"),
_ => L("weather.widget.condition_unknown", "Unknown")
};
_ = kind;
return XiaomiWeatherVisualResolver.ResolveDisplayText(weatherText, weatherCode, _languageCode);
}
private static (double? Low, double? High) ResolveTemperatureRange(WeatherSnapshot snapshot)
@@ -1321,15 +1335,16 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
return kind switch
{
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy or WeatherVisualKind.Storm or WeatherVisualKind.Snow => 1.16,
WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.Haze or WeatherVisualKind.Fog => 1.04,
_ => 1.0
};
}
private void SetMainWeatherIcon(WeatherVisualKind kind)
private void SetMainWeatherIcon(string? assetUri, WeatherVisualKind fallbackKind)
{
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(
HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(kind)));
assetUri ?? HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(fallbackKind)));
}
private void SetLoadingSkeleton(bool isLoading)
@@ -1549,7 +1564,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
var sway = _activeVisualKind == WeatherVisualKind.Snow
? Math.Sin(_animationPhase + (i * 0.45)) * 0.55
: _activeVisualKind == WeatherVisualKind.Fog
: _activeVisualKind is WeatherVisualKind.Fog or WeatherVisualKind.Haze
? Math.Sin((_animationPhase * 0.7) + (i * 0.31)) * 0.18
: 0;
@@ -1579,16 +1594,16 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
var thickness = _activeVisualKind switch
{
WeatherVisualKind.Snow => NextRange(2.2, 4.3),
WeatherVisualKind.Fog => NextRange(10.0, 22.0),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(10.0, 22.0),
_ => NextRange(1.0, 2.2)
};
var opacity = _activeVisualKind switch
{
WeatherVisualKind.Storm => NextRange(0.26, 0.52),
WeatherVisualKind.RainHeavy => NextRange(0.24, 0.46),
WeatherVisualKind.RainLight => NextRange(0.18, 0.34),
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => NextRange(0.18, 0.34),
WeatherVisualKind.Snow => NextRange(0.40, 0.72),
WeatherVisualKind.Fog => NextRange(0.08, 0.20),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(0.08, 0.20),
_ => NextRange(0.10, 0.24)
};
@@ -1600,7 +1615,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
{
WeatherVisualKind.Storm => -24,
WeatherVisualKind.RainHeavy => -20,
WeatherVisualKind.RainLight => -14,
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => -14,
WeatherVisualKind.Snow => -6,
_ => 0
});

View File

@@ -3,15 +3,21 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using LanMountainDesktop.Models;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components;
public enum HyperOS3WeatherVisualKind
{
Unknown,
ClearDay,
ClearNight,
PartlyCloudyDay,
PartlyCloudyNight,
CloudyDay,
CloudyNight,
Haze,
Sleet,
RainLight,
RainHeavy,
Storm,
@@ -91,38 +97,53 @@ public static class HyperOS3WeatherTheme
private static readonly IReadOnlyDictionary<HyperOS3WeatherVisualKind, string> BackgroundAssets =
new Dictionary<HyperOS3WeatherVisualKind, string>
{
[HyperOS3WeatherVisualKind.Unknown] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_back.png",
[HyperOS3WeatherVisualKind.ClearDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_cross_sky_day.png",
[HyperOS3WeatherVisualKind.ClearNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_cross_sky_night.png",
[HyperOS3WeatherVisualKind.PartlyCloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_cross_sky_day.png",
[HyperOS3WeatherVisualKind.PartlyCloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_cross_sky_night.png",
[HyperOS3WeatherVisualKind.CloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_front.png",
[HyperOS3WeatherVisualKind.CloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_back.png",
[HyperOS3WeatherVisualKind.Haze] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_haze.png",
[HyperOS3WeatherVisualKind.Sleet] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_front.png",
[HyperOS3WeatherVisualKind.RainLight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_front.png",
[HyperOS3WeatherVisualKind.RainHeavy] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_back.png",
[HyperOS3WeatherVisualKind.Storm] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_cross_sky_night.png",
[HyperOS3WeatherVisualKind.Snow] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_top.png",
[HyperOS3WeatherVisualKind.Fog] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_sky_back.png"
[HyperOS3WeatherVisualKind.Fog] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_fog.png"
};
private static readonly IReadOnlyDictionary<HyperOS3WeatherVisualKind, string> HeroIconAssets =
new Dictionary<HyperOS3WeatherVisualKind, string>
{
[HyperOS3WeatherVisualKind.Unknown] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_cloudy.webp",
[HyperOS3WeatherVisualKind.ClearDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_hero_sun_soft.png",
[HyperOS3WeatherVisualKind.ClearNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_hero_moon_soft.png",
[HyperOS3WeatherVisualKind.CloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_hero_sun_soft.png",
[HyperOS3WeatherVisualKind.CloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_hero_moon_soft.png",
[HyperOS3WeatherVisualKind.RainLight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_rain_light_soft.png",
[HyperOS3WeatherVisualKind.RainHeavy] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_rain_heavy_soft.png",
[HyperOS3WeatherVisualKind.Storm] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_storm_soft.png",
[HyperOS3WeatherVisualKind.Snow] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_snow_soft.png",
[HyperOS3WeatherVisualKind.Fog] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_hero_sun_soft.png"
[HyperOS3WeatherVisualKind.PartlyCloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_partly_cloudy_day.webp",
[HyperOS3WeatherVisualKind.PartlyCloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_partly_cloudy_night.webp",
[HyperOS3WeatherVisualKind.CloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_cloudy.webp",
[HyperOS3WeatherVisualKind.CloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_cloudy.webp",
[HyperOS3WeatherVisualKind.Haze] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_haze.webp",
[HyperOS3WeatherVisualKind.Sleet] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_sleet.webp",
[HyperOS3WeatherVisualKind.RainLight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_rain_light.webp",
[HyperOS3WeatherVisualKind.RainHeavy] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_rain_heavy.webp",
[HyperOS3WeatherVisualKind.Storm] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_thunder.webp",
[HyperOS3WeatherVisualKind.Snow] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_snow.webp",
[HyperOS3WeatherVisualKind.Fog] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_fog_soft.png"
};
private static readonly IReadOnlyDictionary<HyperOS3WeatherVisualKind, string> MiniIconAssets =
new Dictionary<HyperOS3WeatherVisualKind, string>
{
[HyperOS3WeatherVisualKind.Unknown] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_cloudy_soft.png",
[HyperOS3WeatherVisualKind.ClearDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_day_soft.png",
[HyperOS3WeatherVisualKind.ClearNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_night_soft.png",
[HyperOS3WeatherVisualKind.CloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_day_soft.png",
[HyperOS3WeatherVisualKind.CloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_night_soft.png",
[HyperOS3WeatherVisualKind.PartlyCloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_day_soft.png",
[HyperOS3WeatherVisualKind.PartlyCloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_partly_cloudy_night_soft.png",
[HyperOS3WeatherVisualKind.CloudyDay] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_cloudy_soft.png",
[HyperOS3WeatherVisualKind.CloudyNight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_cloudy_soft.png",
[HyperOS3WeatherVisualKind.Haze] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_haze.webp",
[HyperOS3WeatherVisualKind.Sleet] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_sleet.webp",
[HyperOS3WeatherVisualKind.RainLight] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_rain_light_soft.png",
[HyperOS3WeatherVisualKind.RainHeavy] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_rain_heavy_soft.png",
[HyperOS3WeatherVisualKind.Storm] = "avares://LanMountainDesktop/Assets/Weather/HyperOS3/Icons/icon_mini_storm_soft.png",
@@ -133,6 +154,14 @@ public static class HyperOS3WeatherTheme
private static readonly IReadOnlyDictionary<HyperOS3WeatherVisualKind, HyperOS3WeatherPalette> Palettes =
new Dictionary<HyperOS3WeatherVisualKind, HyperOS3WeatherPalette>
{
[HyperOS3WeatherVisualKind.Unknown] = new(
GradientFrom: "#6B7785",
GradientTo: "#98A4B3",
Tint: "#55606E",
PrimaryText: "#F8FBFF",
SecondaryText: "#E1E8F0",
TertiaryText: "#C2CCD8",
ParticleColor: "#24FFFFFF"),
[HyperOS3WeatherVisualKind.ClearDay] = new(
GradientFrom: "#5F7FA3",
GradientTo: "#9BB4CF",
@@ -149,6 +178,22 @@ public static class HyperOS3WeatherTheme
SecondaryText: "#D9E4F0",
TertiaryText: "#B4C3D6",
ParticleColor: "#00FFFFFF"),
[HyperOS3WeatherVisualKind.PartlyCloudyDay] = new(
GradientFrom: "#607D9F",
GradientTo: "#9BB2C8",
Tint: "#55728F",
PrimaryText: "#F8FCFF",
SecondaryText: "#E4EDF7",
TertiaryText: "#C4D4E4",
ParticleColor: "#12FFFFFF"),
[HyperOS3WeatherVisualKind.PartlyCloudyNight] = new(
GradientFrom: "#5A6E87",
GradientTo: "#8FA4BC",
Tint: "#4D6178",
PrimaryText: "#F8FBFF",
SecondaryText: "#D9E5F0",
TertiaryText: "#B6C5D7",
ParticleColor: "#1FE8F2FF"),
[HyperOS3WeatherVisualKind.CloudyDay] = new(
GradientFrom: "#5D799A",
GradientTo: "#95ADC6",
@@ -165,6 +210,22 @@ public static class HyperOS3WeatherTheme
SecondaryText: "#D4E0ED",
TertiaryText: "#B0BFD2",
ParticleColor: "#30F0F5FF"),
[HyperOS3WeatherVisualKind.Haze] = new(
GradientFrom: "#6A7E95",
GradientTo: "#A5B2BE",
Tint: "#657789",
PrimaryText: "#F7FBFF",
SecondaryText: "#E3E8EE",
TertiaryText: "#C1CBD6",
ParticleColor: "#6FD6DEE8"),
[HyperOS3WeatherVisualKind.Sleet] = new(
GradientFrom: "#61788F",
GradientTo: "#9AB0C4",
Tint: "#587087",
PrimaryText: "#F7FBFF",
SecondaryText: "#DCE6F0",
TertiaryText: "#B8C7D7",
ParticleColor: "#98DCEBFF"),
[HyperOS3WeatherVisualKind.RainLight] = new(
GradientFrom: "#4F6786",
GradientTo: "#7A92AF",
@@ -210,6 +271,14 @@ public static class HyperOS3WeatherTheme
private static readonly IReadOnlyDictionary<HyperOS3WeatherVisualKind, HyperOS3WeatherMotion> Motions =
new Dictionary<HyperOS3WeatherVisualKind, HyperOS3WeatherMotion>
{
[HyperOS3WeatherVisualKind.Unknown] = new(
DriftX: 8.0, DriftY: 5.0, ZoomBase: 1.050, ZoomAmplitude: 0.010,
MotionOpacityBase: 0.24, MotionOpacityPulse: 0.05,
LightOpacityBase: 0.60, LightOpacityPulse: 0.05,
ShadeOpacityBase: 0.80, ShadeOpacityPulse: 0.03,
PhaseStep: 0.018, ParticleCount: 0,
ParticleSpeedMin: 0, ParticleSpeedMax: 0,
ParticleLengthMin: 0, ParticleLengthMax: 0, ParticleDriftPerTick: 0),
[HyperOS3WeatherVisualKind.ClearDay] = new(
DriftX: 8.0, DriftY: 4.0, ZoomBase: 1.055, ZoomAmplitude: 0.012,
MotionOpacityBase: 0.22, MotionOpacityPulse: 0.05,
@@ -226,6 +295,22 @@ public static class HyperOS3WeatherTheme
PhaseStep: 0.018, ParticleCount: 0,
ParticleSpeedMin: 0, ParticleSpeedMax: 0,
ParticleLengthMin: 0, ParticleLengthMax: 0, ParticleDriftPerTick: 0),
[HyperOS3WeatherVisualKind.PartlyCloudyDay] = new(
DriftX: 10.0, DriftY: 6.0, ZoomBase: 1.058, ZoomAmplitude: 0.013,
MotionOpacityBase: 0.26, MotionOpacityPulse: 0.05,
LightOpacityBase: 0.65, LightOpacityPulse: 0.06,
ShadeOpacityBase: 0.76, ShadeOpacityPulse: 0.03,
PhaseStep: 0.017, ParticleCount: 0,
ParticleSpeedMin: 0, ParticleSpeedMax: 0,
ParticleLengthMin: 0, ParticleLengthMax: 0, ParticleDriftPerTick: 0),
[HyperOS3WeatherVisualKind.PartlyCloudyNight] = new(
DriftX: 12.0, DriftY: 7.0, ZoomBase: 1.061, ZoomAmplitude: 0.013,
MotionOpacityBase: 0.30, MotionOpacityPulse: 0.06,
LightOpacityBase: 0.55, LightOpacityPulse: 0.05,
ShadeOpacityBase: 0.82, ShadeOpacityPulse: 0.03,
PhaseStep: 0.019, ParticleCount: 0,
ParticleSpeedMin: 0, ParticleSpeedMax: 0,
ParticleLengthMin: 0, ParticleLengthMax: 0, ParticleDriftPerTick: 0),
[HyperOS3WeatherVisualKind.CloudyDay] = new(
DriftX: 12.0, DriftY: 7.0, ZoomBase: 1.060, ZoomAmplitude: 0.013,
MotionOpacityBase: 0.32, MotionOpacityPulse: 0.06,
@@ -242,6 +327,22 @@ public static class HyperOS3WeatherTheme
PhaseStep: 0.021, ParticleCount: 0,
ParticleSpeedMin: 0.35, ParticleSpeedMax: 0.80,
ParticleLengthMin: 16, ParticleLengthMax: 30, ParticleDriftPerTick: 0.12),
[HyperOS3WeatherVisualKind.Haze] = new(
DriftX: 9.0, DriftY: 5.0, ZoomBase: 1.052, ZoomAmplitude: 0.010,
MotionOpacityBase: 0.30, MotionOpacityPulse: 0.04,
LightOpacityBase: 0.54, LightOpacityPulse: 0.04,
ShadeOpacityBase: 0.85, ShadeOpacityPulse: 0.03,
PhaseStep: 0.018, ParticleCount: 0,
ParticleSpeedMin: 0.20, ParticleSpeedMax: 0.45,
ParticleLengthMin: 12, ParticleLengthMax: 28, ParticleDriftPerTick: 0.10),
[HyperOS3WeatherVisualKind.Sleet] = new(
DriftX: 7.0, DriftY: 9.0, ZoomBase: 1.048, ZoomAmplitude: 0.011,
MotionOpacityBase: 0.31, MotionOpacityPulse: 0.06,
LightOpacityBase: 0.52, LightOpacityPulse: 0.05,
ShadeOpacityBase: 0.82, ShadeOpacityPulse: 0.04,
PhaseStep: 0.026, ParticleCount: 20,
ParticleSpeedMin: 1.20, ParticleSpeedMax: 2.40,
ParticleLengthMin: 8, ParticleLengthMax: 18, ParticleDriftPerTick: 0.34),
[HyperOS3WeatherVisualKind.RainLight] = new(
DriftX: 6.0, DriftY: 10.0, ZoomBase: 1.050, ZoomAmplitude: 0.010,
MotionOpacityBase: 0.30, MotionOpacityPulse: 0.08,
@@ -296,16 +397,20 @@ public static class HyperOS3WeatherTheme
public static HyperOS3WeatherVisualKind ResolveVisualKind(int? weatherCode, bool isNight)
{
return weatherCode switch
return XiaomiWeatherCodeMapper.ResolveBucket(weatherCode) switch
{
0 => isNight ? HyperOS3WeatherVisualKind.ClearNight : HyperOS3WeatherVisualKind.ClearDay,
1 or 2 => isNight ? HyperOS3WeatherVisualKind.CloudyNight : HyperOS3WeatherVisualKind.CloudyDay,
3 or 7 => HyperOS3WeatherVisualKind.RainLight,
8 or 9 => HyperOS3WeatherVisualKind.RainHeavy,
4 => HyperOS3WeatherVisualKind.Storm,
13 or 14 or 15 or 16 => HyperOS3WeatherVisualKind.Snow,
18 or 32 => HyperOS3WeatherVisualKind.Fog,
_ => isNight ? HyperOS3WeatherVisualKind.CloudyNight : HyperOS3WeatherVisualKind.CloudyDay
WeatherConditionBucket.Unknown => HyperOS3WeatherVisualKind.Unknown,
WeatherConditionBucket.Clear => isNight ? HyperOS3WeatherVisualKind.ClearNight : HyperOS3WeatherVisualKind.ClearDay,
WeatherConditionBucket.PartlyCloudy => isNight ? HyperOS3WeatherVisualKind.PartlyCloudyNight : HyperOS3WeatherVisualKind.PartlyCloudyDay,
WeatherConditionBucket.Cloudy => isNight ? HyperOS3WeatherVisualKind.CloudyNight : HyperOS3WeatherVisualKind.CloudyDay,
WeatherConditionBucket.Haze => HyperOS3WeatherVisualKind.Haze,
WeatherConditionBucket.Sleet => HyperOS3WeatherVisualKind.Sleet,
WeatherConditionBucket.RainLight => HyperOS3WeatherVisualKind.RainLight,
WeatherConditionBucket.RainHeavy => HyperOS3WeatherVisualKind.RainHeavy,
WeatherConditionBucket.Storm => HyperOS3WeatherVisualKind.Storm,
WeatherConditionBucket.Snow => HyperOS3WeatherVisualKind.Snow,
WeatherConditionBucket.Fog => HyperOS3WeatherVisualKind.Fog,
_ => HyperOS3WeatherVisualKind.Unknown
};
}
@@ -360,12 +465,14 @@ public static class HyperOS3WeatherTheme
{
return kind switch
{
HyperOS3WeatherVisualKind.RainLight or HyperOS3WeatherVisualKind.RainHeavy or HyperOS3WeatherVisualKind.Storm
HyperOS3WeatherVisualKind.Sleet or HyperOS3WeatherVisualKind.RainLight or HyperOS3WeatherVisualKind.RainHeavy or HyperOS3WeatherVisualKind.Storm
=> "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_rain_drop.png",
HyperOS3WeatherVisualKind.Haze
=> "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_haze.png",
HyperOS3WeatherVisualKind.Fog
=> "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_fog.png",
HyperOS3WeatherVisualKind.Snow
=> "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_snow_flake.png",
HyperOS3WeatherVisualKind.Fog
=> "avares://LanMountainDesktop/Assets/Weather/HyperOS3/hyper_haze.png",
_ => null
};
}

View File

@@ -20,10 +20,15 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
{
private enum WeatherVisualKind
{
Unknown,
ClearDay,
ClearNight,
PartlyCloudyDay,
PartlyCloudyNight,
CloudyDay,
CloudyNight,
Haze,
Sleet,
RainLight,
RainHeavy,
Storm,
@@ -480,7 +485,12 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
private void ApplySnapshot(WeatherSnapshot snapshot, string fallbackLocationName)
{
var isNight = ResolveIsNight(snapshot);
var visualKind = ResolveVisualKind(snapshot.Current.WeatherCode, isNight);
var visual = XiaomiWeatherVisualResolver.Resolve(
snapshot.Current.WeatherText,
snapshot.Current.WeatherCode,
isNight,
_languageCode);
var visualKind = ResolveVisualKind(visual.VisualKind);
ApplyVisualTheme(visualKind);
var rawLocation = string.IsNullOrWhiteSpace(snapshot.LocationName)
@@ -488,8 +498,8 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
: snapshot.LocationName;
CityTextBlock.Text = ResolvePreciseDisplayLocation(rawLocation, _languageCode, L("weather.widget.location_unknown", "Unknown location"));
ConditionTextBlock.Text = ResolveWeatherConditionText(snapshot.Current.WeatherText, visualKind);
SetMainWeatherIcon(visualKind);
ConditionTextBlock.Text = visual.DisplayText;
SetMainWeatherIcon(visual.PrimaryIconAsset, visualKind);
SetLoadingSkeleton(false);
TemperatureTextBlock.Text = FormatTemperature(snapshot.Current.TemperatureC);
@@ -503,7 +513,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
{
var fallbackKind = ResolveFallbackVisualKind();
ApplyVisualTheme(fallbackKind);
SetMainWeatherIcon(fallbackKind);
SetMainWeatherIcon(null, fallbackKind);
SetLoadingSkeleton(false);
CityTextBlock.Text = L("weather.widget.location_not_configured", "Weather location is not configured");
ConditionTextBlock.Text = L("weather.widget.condition_unknown", "Unknown");
@@ -518,7 +528,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
{
var loadingKind = IsNightNow() ? WeatherVisualKind.CloudyNight : WeatherVisualKind.CloudyDay;
ApplyVisualTheme(loadingKind);
SetMainWeatherIcon(loadingKind);
SetMainWeatherIcon(null, loadingKind);
SetLoadingSkeleton(true);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -533,8 +543,8 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
private void ApplyFailedState(string locationName)
{
ApplyVisualTheme(WeatherVisualKind.Fog);
SetMainWeatherIcon(WeatherVisualKind.Fog);
ApplyVisualTheme(WeatherVisualKind.Unknown);
SetMainWeatherIcon(HyperOS3WeatherTheme.ResolveHeroIconAsset(HyperOS3WeatherVisualKind.Unknown), WeatherVisualKind.Unknown);
SetLoadingSkeleton(false);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -543,7 +553,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
ConditionTextBlock.Text = L("weather.widget.fetch_failed", "Weather fetch failed");
TemperatureTextBlock.Text = "--°";
RangeTextBlock.Text = L("weather.widget.range_unknown", "-- / --");
ApplyHourlyForecastItems(BuildPlaceholderHourlyForecastItems(WeatherVisualKind.Fog));
ApplyHourlyForecastItems(BuildPlaceholderHourlyForecastItems(WeatherVisualKind.Unknown));
ApplyAdaptiveTypography();
_latestSnapshot = null;
}
@@ -558,7 +568,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
BackgroundTintLayer.Background = CreateSolidBrush(palette.Tint);
var particleBrush = ResolveParticleBrush(ToThemeKind(kind), palette.ParticleColor);
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight;
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight;
var backgroundSamples = WeatherTypographyAccessibility.BuildBackgroundSamples(
palette.GradientFrom,
palette.GradientTo,
@@ -676,17 +686,28 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
private static WeatherVisualKind ResolveVisualKind(int? weatherCode, bool isNight)
{
return HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight) switch
return ResolveVisualKind(HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight));
}
private static WeatherVisualKind ResolveVisualKind(HyperOS3WeatherVisualKind kind)
{
return kind switch
{
HyperOS3WeatherVisualKind.Unknown => WeatherVisualKind.Unknown,
HyperOS3WeatherVisualKind.ClearDay => WeatherVisualKind.ClearDay,
HyperOS3WeatherVisualKind.ClearNight => WeatherVisualKind.ClearNight,
HyperOS3WeatherVisualKind.PartlyCloudyDay => WeatherVisualKind.PartlyCloudyDay,
HyperOS3WeatherVisualKind.PartlyCloudyNight => WeatherVisualKind.PartlyCloudyNight,
HyperOS3WeatherVisualKind.CloudyDay => WeatherVisualKind.CloudyDay,
HyperOS3WeatherVisualKind.CloudyNight => WeatherVisualKind.CloudyNight,
HyperOS3WeatherVisualKind.Haze => WeatherVisualKind.Haze,
HyperOS3WeatherVisualKind.Sleet => WeatherVisualKind.Sleet,
HyperOS3WeatherVisualKind.RainLight => WeatherVisualKind.RainLight,
HyperOS3WeatherVisualKind.RainHeavy => WeatherVisualKind.RainHeavy,
HyperOS3WeatherVisualKind.Storm => WeatherVisualKind.Storm,
HyperOS3WeatherVisualKind.Snow => WeatherVisualKind.Snow,
_ => WeatherVisualKind.Fog
HyperOS3WeatherVisualKind.Fog => WeatherVisualKind.Fog,
_ => WeatherVisualKind.Unknown
};
}
@@ -707,35 +728,28 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
{
return kind switch
{
WeatherVisualKind.Unknown => HyperOS3WeatherVisualKind.Unknown,
WeatherVisualKind.ClearDay => HyperOS3WeatherVisualKind.ClearDay,
WeatherVisualKind.ClearNight => HyperOS3WeatherVisualKind.ClearNight,
WeatherVisualKind.PartlyCloudyDay => HyperOS3WeatherVisualKind.PartlyCloudyDay,
WeatherVisualKind.PartlyCloudyNight => HyperOS3WeatherVisualKind.PartlyCloudyNight,
WeatherVisualKind.CloudyDay => HyperOS3WeatherVisualKind.CloudyDay,
WeatherVisualKind.CloudyNight => HyperOS3WeatherVisualKind.CloudyNight,
WeatherVisualKind.Haze => HyperOS3WeatherVisualKind.Haze,
WeatherVisualKind.Sleet => HyperOS3WeatherVisualKind.Sleet,
WeatherVisualKind.RainLight => HyperOS3WeatherVisualKind.RainLight,
WeatherVisualKind.RainHeavy => HyperOS3WeatherVisualKind.RainHeavy,
WeatherVisualKind.Storm => HyperOS3WeatherVisualKind.Storm,
WeatherVisualKind.Snow => HyperOS3WeatherVisualKind.Snow,
_ => HyperOS3WeatherVisualKind.Fog
WeatherVisualKind.Fog => HyperOS3WeatherVisualKind.Fog,
_ => HyperOS3WeatherVisualKind.Unknown
};
}
private string ResolveWeatherConditionText(string? weatherText, WeatherVisualKind kind)
private string ResolveWeatherConditionText(string? weatherText, int? weatherCode, WeatherVisualKind kind)
{
if (!string.IsNullOrWhiteSpace(weatherText))
{
return weatherText;
}
return kind switch
{
WeatherVisualKind.ClearDay or WeatherVisualKind.ClearNight => L("weather.widget.condition_clear", "Clear"),
WeatherVisualKind.CloudyDay or WeatherVisualKind.CloudyNight => L("weather.widget.condition_cloudy", "Cloudy"),
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy => L("weather.widget.condition_rain", "Rain"),
WeatherVisualKind.Storm => L("weather.widget.condition_storm", "Thunderstorm"),
WeatherVisualKind.Snow => L("weather.widget.condition_snow", "Snow"),
WeatherVisualKind.Fog => L("weather.widget.condition_fog", "Fog"),
_ => L("weather.widget.condition_unknown", "Unknown")
};
_ = kind;
return XiaomiWeatherVisualResolver.ResolveDisplayText(weatherText, weatherCode, _languageCode);
}
private static (double? Low, double? High) ResolveTemperatureRange(WeatherSnapshot snapshot)
@@ -1171,15 +1185,16 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
return kind switch
{
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy or WeatherVisualKind.Storm or WeatherVisualKind.Snow => 1.16,
WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.Haze or WeatherVisualKind.Fog => 1.04,
_ => 1.0
};
}
private void SetMainWeatherIcon(WeatherVisualKind kind)
private void SetMainWeatherIcon(string? assetUri, WeatherVisualKind fallbackKind)
{
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(
HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(kind)));
assetUri ?? HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(fallbackKind)));
}
private void SetLoadingSkeleton(bool isLoading)
@@ -1399,7 +1414,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
var sway = _activeVisualKind == WeatherVisualKind.Snow
? Math.Sin(_animationPhase + (i * 0.45)) * 0.55
: _activeVisualKind == WeatherVisualKind.Fog
: _activeVisualKind is WeatherVisualKind.Fog or WeatherVisualKind.Haze
? Math.Sin((_animationPhase * 0.7) + (i * 0.31)) * 0.18
: 0;
@@ -1429,16 +1444,16 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
var thickness = _activeVisualKind switch
{
WeatherVisualKind.Snow => NextRange(2.2, 4.3),
WeatherVisualKind.Fog => NextRange(10.0, 22.0),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(10.0, 22.0),
_ => NextRange(1.0, 2.2)
};
var opacity = _activeVisualKind switch
{
WeatherVisualKind.Storm => NextRange(0.26, 0.52),
WeatherVisualKind.RainHeavy => NextRange(0.24, 0.46),
WeatherVisualKind.RainLight => NextRange(0.18, 0.34),
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => NextRange(0.18, 0.34),
WeatherVisualKind.Snow => NextRange(0.40, 0.72),
WeatherVisualKind.Fog => NextRange(0.08, 0.20),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(0.08, 0.20),
_ => NextRange(0.10, 0.24)
};
@@ -1450,7 +1465,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
{
WeatherVisualKind.Storm => -24,
WeatherVisualKind.RainHeavy => -20,
WeatherVisualKind.RainLight => -14,
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => -14,
WeatherVisualKind.Snow => -6,
_ => 0
});

View File

@@ -22,10 +22,15 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
{
private enum WeatherVisualKind
{
Unknown,
ClearDay,
ClearNight,
PartlyCloudyDay,
PartlyCloudyNight,
CloudyDay,
CloudyNight,
Haze,
Sleet,
RainLight,
RainHeavy,
Storm,
@@ -427,7 +432,12 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
private void ApplySnapshot(WeatherSnapshot snapshot, string fallbackLocationName)
{
var isNight = ResolveIsNight(snapshot);
var visualKind = ResolveVisualKind(snapshot.Current.WeatherCode, isNight);
var visual = XiaomiWeatherVisualResolver.Resolve(
snapshot.Current.WeatherText,
snapshot.Current.WeatherCode,
isNight,
_languageCode);
var visualKind = ResolveVisualKind(visual.VisualKind);
ApplyVisualTheme(visualKind);
var rawLocation = string.IsNullOrWhiteSpace(snapshot.LocationName)
@@ -435,8 +445,8 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
: snapshot.LocationName;
CityTextBlock.Text = ResolvePreciseDisplayLocation(rawLocation, _languageCode, L("weather.widget.location_unknown", "Unknown location"));
ConditionTextBlock.Text = ResolveWeatherConditionText(snapshot.Current.WeatherText, visualKind);
SetWeatherIcon(visualKind);
ConditionTextBlock.Text = visual.DisplayText;
SetWeatherIcon(visual.PrimaryIconAsset, visualKind);
SetLoadingSkeleton(false);
TemperatureTextBlock.Text = FormatTemperature(snapshot.Current.TemperatureC);
@@ -449,7 +459,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
{
var fallbackKind = ResolveFallbackVisualKind();
ApplyVisualTheme(fallbackKind);
SetWeatherIcon(fallbackKind);
SetWeatherIcon(null, fallbackKind);
SetLoadingSkeleton(false);
CityTextBlock.Text = L("weather.widget.location_not_configured", "Weather location is not configured");
ConditionTextBlock.Text = L("weather.widget.configure_hint", "Open Settings > Weather to configure");
@@ -463,7 +473,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
{
var loadingKind = IsNightNow() ? WeatherVisualKind.CloudyNight : WeatherVisualKind.CloudyDay;
ApplyVisualTheme(loadingKind);
SetWeatherIcon(loadingKind);
SetWeatherIcon(null, loadingKind);
SetLoadingSkeleton(true);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -477,8 +487,8 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
private void ApplyFailedState(string locationName)
{
ApplyVisualTheme(WeatherVisualKind.Fog);
SetWeatherIcon(WeatherVisualKind.Fog);
ApplyVisualTheme(WeatherVisualKind.Unknown);
SetWeatherIcon(HyperOS3WeatherTheme.ResolveHeroIconAsset(HyperOS3WeatherVisualKind.Unknown), WeatherVisualKind.Unknown);
SetLoadingSkeleton(false);
CityTextBlock.Text = ResolvePreciseDisplayLocation(
locationName,
@@ -500,7 +510,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
BackgroundMotionLayer.Background = ResolveWeatherBackgroundBrush(kind, palette);
BackgroundTintLayer.Background = CreateSolidBrush(palette.Tint);
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight;
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight;
var backgroundSamples = WeatherTypographyAccessibility.BuildBackgroundSamples(
palette.GradientFrom,
palette.GradientTo,
@@ -610,17 +620,28 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
private static WeatherVisualKind ResolveVisualKind(int? weatherCode, bool isNight)
{
return HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight) switch
return ResolveVisualKind(HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight));
}
private static WeatherVisualKind ResolveVisualKind(HyperOS3WeatherVisualKind kind)
{
return kind switch
{
HyperOS3WeatherVisualKind.Unknown => WeatherVisualKind.Unknown,
HyperOS3WeatherVisualKind.ClearDay => WeatherVisualKind.ClearDay,
HyperOS3WeatherVisualKind.ClearNight => WeatherVisualKind.ClearNight,
HyperOS3WeatherVisualKind.PartlyCloudyDay => WeatherVisualKind.PartlyCloudyDay,
HyperOS3WeatherVisualKind.PartlyCloudyNight => WeatherVisualKind.PartlyCloudyNight,
HyperOS3WeatherVisualKind.CloudyDay => WeatherVisualKind.CloudyDay,
HyperOS3WeatherVisualKind.CloudyNight => WeatherVisualKind.CloudyNight,
HyperOS3WeatherVisualKind.Haze => WeatherVisualKind.Haze,
HyperOS3WeatherVisualKind.Sleet => WeatherVisualKind.Sleet,
HyperOS3WeatherVisualKind.RainLight => WeatherVisualKind.RainLight,
HyperOS3WeatherVisualKind.RainHeavy => WeatherVisualKind.RainHeavy,
HyperOS3WeatherVisualKind.Storm => WeatherVisualKind.Storm,
HyperOS3WeatherVisualKind.Snow => WeatherVisualKind.Snow,
_ => WeatherVisualKind.Fog
HyperOS3WeatherVisualKind.Fog => WeatherVisualKind.Fog,
_ => WeatherVisualKind.Unknown
};
}
@@ -641,35 +662,28 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
{
return kind switch
{
WeatherVisualKind.Unknown => HyperOS3WeatherVisualKind.Unknown,
WeatherVisualKind.ClearDay => HyperOS3WeatherVisualKind.ClearDay,
WeatherVisualKind.ClearNight => HyperOS3WeatherVisualKind.ClearNight,
WeatherVisualKind.PartlyCloudyDay => HyperOS3WeatherVisualKind.PartlyCloudyDay,
WeatherVisualKind.PartlyCloudyNight => HyperOS3WeatherVisualKind.PartlyCloudyNight,
WeatherVisualKind.CloudyDay => HyperOS3WeatherVisualKind.CloudyDay,
WeatherVisualKind.CloudyNight => HyperOS3WeatherVisualKind.CloudyNight,
WeatherVisualKind.Haze => HyperOS3WeatherVisualKind.Haze,
WeatherVisualKind.Sleet => HyperOS3WeatherVisualKind.Sleet,
WeatherVisualKind.RainLight => HyperOS3WeatherVisualKind.RainLight,
WeatherVisualKind.RainHeavy => HyperOS3WeatherVisualKind.RainHeavy,
WeatherVisualKind.Storm => HyperOS3WeatherVisualKind.Storm,
WeatherVisualKind.Snow => HyperOS3WeatherVisualKind.Snow,
_ => HyperOS3WeatherVisualKind.Fog
WeatherVisualKind.Fog => HyperOS3WeatherVisualKind.Fog,
_ => HyperOS3WeatherVisualKind.Unknown
};
}
private string ResolveWeatherConditionText(string? weatherText, WeatherVisualKind kind)
private string ResolveWeatherConditionText(string? weatherText, int? weatherCode, WeatherVisualKind kind)
{
if (!string.IsNullOrWhiteSpace(weatherText))
{
return weatherText;
}
return kind switch
{
WeatherVisualKind.ClearDay or WeatherVisualKind.ClearNight => L("weather.widget.condition_clear", "Clear"),
WeatherVisualKind.CloudyDay or WeatherVisualKind.CloudyNight => L("weather.widget.condition_cloudy", "Cloudy"),
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy => L("weather.widget.condition_rain", "Rain"),
WeatherVisualKind.Storm => L("weather.widget.condition_storm", "Thunderstorm"),
WeatherVisualKind.Snow => L("weather.widget.condition_snow", "Snow"),
WeatherVisualKind.Fog => L("weather.widget.condition_fog", "Fog"),
_ => L("weather.widget.condition_unknown", "Unknown")
};
_ = kind;
return XiaomiWeatherVisualResolver.ResolveDisplayText(weatherText, weatherCode, _languageCode);
}
private static (double? Low, double? High) ResolveTemperatureRange(WeatherSnapshot snapshot)
@@ -965,15 +979,16 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
return kind switch
{
WeatherVisualKind.RainLight or WeatherVisualKind.RainHeavy or WeatherVisualKind.Storm or WeatherVisualKind.Snow => 1.16,
WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.ClearNight or WeatherVisualKind.PartlyCloudyNight or WeatherVisualKind.CloudyNight => 1.08,
WeatherVisualKind.Haze or WeatherVisualKind.Fog => 1.04,
_ => 1.0
};
}
private void SetWeatherIcon(WeatherVisualKind kind)
private void SetWeatherIcon(string? assetUri, WeatherVisualKind fallbackKind)
{
WeatherIconImage.Source = HyperOS3WeatherAssetLoader.LoadImage(
HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(kind)));
assetUri ?? HyperOS3WeatherTheme.ResolveHeroIconAsset(ToThemeKind(fallbackKind)));
}
private void SetLoadingSkeleton(bool isLoading)
@@ -1190,7 +1205,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
var sway = _activeVisualKind == WeatherVisualKind.Snow
? Math.Sin(_animationPhase + (i * 0.45)) * 0.55
: _activeVisualKind == WeatherVisualKind.Fog
: _activeVisualKind is WeatherVisualKind.Fog or WeatherVisualKind.Haze
? Math.Sin((_animationPhase * 0.7) + (i * 0.31)) * 0.18
: 0;
@@ -1220,16 +1235,16 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
var thickness = _activeVisualKind switch
{
WeatherVisualKind.Snow => NextRange(2.2, 4.3),
WeatherVisualKind.Fog => NextRange(10.0, 22.0),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(10.0, 22.0),
_ => NextRange(1.0, 2.2)
};
var opacity = _activeVisualKind switch
{
WeatherVisualKind.Storm => NextRange(0.26, 0.52),
WeatherVisualKind.RainHeavy => NextRange(0.24, 0.46),
WeatherVisualKind.RainLight => NextRange(0.18, 0.34),
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => NextRange(0.18, 0.34),
WeatherVisualKind.Snow => NextRange(0.40, 0.72),
WeatherVisualKind.Fog => NextRange(0.08, 0.20),
WeatherVisualKind.Fog or WeatherVisualKind.Haze => NextRange(0.08, 0.20),
_ => NextRange(0.10, 0.24)
};
@@ -1241,7 +1256,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
{
WeatherVisualKind.Storm => -24,
WeatherVisualKind.RainHeavy => -20,
WeatherVisualKind.RainLight => -14,
WeatherVisualKind.RainLight or WeatherVisualKind.Sleet => -14,
WeatherVisualKind.Snow => -6,
_ => 0
});

View File

@@ -0,0 +1,45 @@
using System;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components;
internal readonly record struct WeatherVisualSpec(
HyperOS3WeatherVisualKind VisualKind,
string DisplayText,
string? BackgroundAsset,
string? PrimaryIconAsset,
string? CompactIconAsset,
string? ParticleAsset);
internal static class XiaomiWeatherVisualResolver
{
public static WeatherVisualSpec Resolve(string? weatherText, int? weatherCode, bool isNight, string locale)
{
var visualKind = HyperOS3WeatherTheme.ResolveVisualKind(weatherCode, isNight);
return new WeatherVisualSpec(
visualKind,
ResolveDisplayText(weatherText, weatherCode, locale),
HyperOS3WeatherTheme.ResolveBackgroundAsset(visualKind),
HyperOS3WeatherTheme.ResolveHeroIconAsset(visualKind),
HyperOS3WeatherTheme.ResolveMiniIconAsset(visualKind),
HyperOS3WeatherTheme.ResolveParticleAsset(visualKind));
}
public static string ResolveDisplayText(string? weatherText, int? weatherCode, string locale)
{
if (!string.IsNullOrWhiteSpace(weatherText))
{
return weatherText.Trim();
}
var mappedText = XiaomiWeatherCodeMapper.ResolveDisplayText(weatherCode, locale);
if (!string.IsNullOrWhiteSpace(mappedText))
{
return mappedText;
}
return locale.StartsWith("zh", StringComparison.OrdinalIgnoreCase)
? "\u672a\u77e5\u5929\u6c14"
: "Unknown";
}
}

View File

@@ -239,6 +239,7 @@ public partial class MainWindow
private void ApplyTopStatusComponentVisibility()
{
var showClock = _topStatusComponentIds.Contains(BuiltInComponentIds.Clock);
var hasVisibleTopStatusComponent = false;
if (ClockWidget is not null)
{
@@ -248,8 +249,33 @@ public partial class MainWindow
ClockWidget.SetDisplayFormat(_clockDisplayFormat);
var columnSpan = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? 2 : 3;
Grid.SetColumnSpan(ClockWidget, columnSpan);
hasVisibleTopStatusComponent = true;
}
}
if (TopStatusBarHost is not null)
{
TopStatusBarHost.IsVisible = hasVisibleTopStatusComponent;
}
if (WallpaperPreviewClockWidget is not null)
{
WallpaperPreviewClockWidget.IsVisible = showClock;
if (showClock)
{
WallpaperPreviewClockWidget.SetDisplayFormat(_clockDisplayFormat);
}
}
if (WallpaperPreviewTopStatusBarHost is not null)
{
WallpaperPreviewTopStatusBarHost.IsVisible = hasVisibleTopStatusComponent;
}
if (GridPreviewTopStatusBarHost is not null)
{
GridPreviewTopStatusBarHost.IsVisible = hasVisibleTopStatusComponent;
}
}
private TaskbarContext GetCurrentTaskbarContext()

View File

@@ -71,7 +71,19 @@ public partial class MainWindow
private void OnSettingsChanged(object? sender, SettingsChangedEvent e)
{
_ = sender;
_ = e;
if (e.Scope == SettingsScope.App && e.ChangedKeys is { Count: > 0 })
{
var changedKeys = e.ChangedKeys.ToArray();
if (changedKeys.All(key =>
string.Equals(key, nameof(AppSettingsSnapshot.ThemeColorMode), StringComparison.OrdinalIgnoreCase) ||
string.Equals(key, nameof(AppSettingsSnapshot.SystemMaterialMode), StringComparison.OrdinalIgnoreCase) ||
string.Equals(key, nameof(AppSettingsSnapshot.SelectedWallpaperSeed), StringComparison.OrdinalIgnoreCase)))
{
return;
}
}
ScheduleReloadFromExternalSettings();
}
@@ -198,16 +210,20 @@ public partial class MainWindow
private ThemeColorContext BuildAdaptiveThemeContext()
{
var palette = _themeSettingsService.BuildPalette(_isNightMode, _wallpaperPath, _selectedThemeColor.ToString());
var accentColor = palette.MonetColors is { Count: > 0 }
? palette.MonetColors[0]
: _selectedThemeColor;
var appearanceSnapshot = _appearanceThemeService.GetCurrent();
return new ThemeColorContext(
accentColor,
IsLightBackground: !_isNightMode,
IsLightNavBackground: !_isNightMode,
IsNightMode: _isNightMode,
MonetColors: palette.MonetColors);
appearanceSnapshot.AccentColor,
IsLightBackground: !appearanceSnapshot.IsNightMode,
IsLightNavBackground: !appearanceSnapshot.IsNightMode,
IsNightMode: appearanceSnapshot.IsNightMode,
MonetPalette: appearanceSnapshot.MonetPalette,
MonetColors: appearanceSnapshot.MonetPalette.MonetColors,
UseNeutralSurfaces: string.Equals(
appearanceSnapshot.ThemeColorMode,
ThemeAppearanceValues.ColorModeDefaultNeutral,
StringComparison.OrdinalIgnoreCase),
SystemMaterialMode: appearanceSnapshot.SystemMaterialMode);
}
private void ApplyAdaptiveThemeResources()
@@ -222,7 +238,8 @@ public partial class MainWindow
GlassEffectService.ApplyGlassResources(applicationResources, context);
}
_defaultDesktopBackground = GetThemeBrush("AdaptiveSurfaceBaseBrush");
_defaultDesktopBackground = GetThemeBrush("AdaptiveWindowBackgroundBrush")
?? GetThemeBrush("AdaptiveSurfaceBaseBrush");
}
private void TryRestoreWallpaper(string? savedWallpaperPath, string? type = null, string? color = null)
@@ -391,9 +408,9 @@ public partial class MainWindow
return;
}
var palette = _themeSettingsService.BuildPalette(enabled, _wallpaperPath, _selectedThemeColor.ToString());
_recommendedColors = palette.RecommendedColors;
_monetColors = palette.MonetColors;
var snapshot = _appearanceThemeService.GetCurrent();
_recommendedColors = snapshot.MonetPalette.RecommendedColors;
_monetColors = snapshot.MonetPalette.MonetColors;
}
private static double CalculateRelativeLuminance(Color color)
@@ -521,13 +538,18 @@ public partial class MainWindow
{
var latestWeatherState = _weatherSettingsService.Get();
var latestUpdateState = _updateSettingsService.Get();
var latestThemeState = _themeSettingsService.Get();
return new AppSettingsSnapshot
{
GridShortSideCells = _targetShortSideCells,
GridSpacingPreset = _gridSpacingPreset,
DesktopEdgeInsetPercent = _desktopEdgeInsetPercent,
IsNightMode = _isNightMode,
ThemeColor = _selectedThemeColor.ToString(),
ThemeColor = latestThemeState.ThemeColor,
ThemeColorMode = latestThemeState.ThemeColorMode,
SystemMaterialMode = latestThemeState.SystemMaterialMode,
SelectedWallpaperSeed = latestThemeState.SelectedWallpaperSeed,
UseSystemChrome = latestThemeState.UseSystemChrome,
WallpaperPath = _wallpaperPath,
WallpaperType = _wallpaperType,
WallpaperColor = _wallpaperSolidColor?.ToString(),

View File

@@ -19,7 +19,7 @@
CanResize="False"
UseLayoutRounding="True"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
Background="Transparent"
Title="LanMountainDesktop">
<Design.DataContext>

View File

@@ -78,6 +78,7 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
TaskbarActionId.MinimizeToWindows
];
private readonly ISettingsFacadeService _settingsFacade = HostSettingsFacadeProvider.GetOrCreate();
private readonly IAppearanceThemeService _appearanceThemeService = HostAppearanceThemeProvider.GetOrCreate();
private readonly IGridSettingsService _gridSettingsService;
private readonly IThemeAppearanceService _themeSettingsService;
private readonly IWeatherSettingsService _weatherSettingsService;
@@ -206,6 +207,7 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
_componentEditorWindowService = new ComponentEditorWindowService(_settingsFacade);
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
_settingsService.Changed += OnSettingsChanged;
_appearanceThemeService.Changed += OnAppearanceThemeChanged;
PropertyChanged += OnWindowPropertyChanged;
InitializeDesktopSurfaceSwipeHandlers();
InitializeDesktopComponentDragHandlers();
@@ -340,6 +342,7 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
_wallpaperBitmap?.Dispose();
_wallpaperBitmap = null;
_settingsService.Changed -= OnSettingsChanged;
_appearanceThemeService.Changed -= OnAppearanceThemeChanged;
PropertyChanged -= OnWindowPropertyChanged;
DesktopHost.SizeChanged -= OnDesktopHostSizeChanged;
if (Application.Current is App app && app.SettingsWindowService is { } settingsWindowService)
@@ -349,6 +352,23 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider
base.OnClosed(e);
}
private void OnAppearanceThemeChanged(object? sender, AppearanceThemeSnapshot snapshot)
{
_ = sender;
Dispatcher.UIThread.Post(() =>
{
if (!IsVisible)
{
return;
}
ApplyAdaptiveThemeResources();
_recommendedColors = snapshot.MonetPalette.RecommendedColors;
_monetColors = snapshot.MonetPalette.MonetColors;
}, DispatcherPriority.Background);
}
private int CalculateDefaultShortSideCellCountFromDpi()
{
var dpi = 96d * RenderScaling;

View File

@@ -31,13 +31,224 @@
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander Header="{Binding ThemeColorLabel}">
<ui:SettingsExpander Header="{Binding ThemeColorModeLabel}"
Description="{Binding ThemeColorSourceDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="PaintBrush" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ComboBox Width="240"
ItemsSource="{Binding ThemeColorModes}"
SelectedItem="{Binding SelectedThemeColorMode}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="vm:SelectionOption">
<TextBlock Text="{Binding Label}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander Header="{Binding SystemMaterialLabel}"
Description="{Binding SystemMaterialDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="WindowDevTools" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ComboBox Width="220"
ItemsSource="{Binding SystemMaterialModes}"
SelectedItem="{Binding SelectedSystemMaterialMode}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="vm:SelectionOption">
<TextBlock Text="{Binding Label}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander Header="{Binding ThemeColorLabel}"
Description="{Binding ThemeColorSourceDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Color" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ColorPicker Color="{Binding ThemeColorPickerValue}" />
</ui:SettingsExpander.Footer>
<ui:SettingsExpanderItem>
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal"
Spacing="12"
IsVisible="{Binding ShowNeutralPreview}">
<StackPanel Width="96"
Spacing="6">
<Border Height="54"
Background="{Binding NeutralLightPreviewBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewNeutralLightLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
<StackPanel Width="96"
Spacing="6">
<Border Height="54"
Background="{Binding NeutralDarkPreviewBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewNeutralDarkLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal"
Spacing="12"
IsVisible="{Binding ShowMonetPreview}">
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding PrimarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewPrimaryLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding SecondarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewSecondaryLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding TertiarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewTertiaryLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding NeutralSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewNeutralLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
<Button x:Name="CustomSeedButton"
Width="92"
Padding="0"
Background="Transparent"
BorderThickness="0"
HorizontalAlignment="Left"
IsVisible="{Binding IsThemeColorEditable}">
<Button.Flyout>
<Flyout Placement="BottomEdgeAlignedLeft"
Closed="OnCustomSeedFlyoutClosed">
<StackPanel Width="300"
Spacing="12">
<ColorPicker Color="{Binding CustomSeedPickerValue}" />
<Button Content="{Binding SeedApplyButtonText}"
HorizontalAlignment="Right"
Click="OnApplyCustomSeedClick" />
</StackPanel>
</Flyout>
</Button.Flyout>
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding SeedSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewSeedLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
</Button>
<Button x:Name="WallpaperSeedButton"
Width="92"
Padding="0"
Background="Transparent"
BorderThickness="0"
HorizontalAlignment="Left"
IsVisible="{Binding IsWallpaperMode}"
IsEnabled="{Binding IsWallpaperSeedSelectable}">
<Button.Flyout>
<Flyout Placement="BottomEdgeAlignedLeft">
<StackPanel Width="280"
Spacing="12">
<TextBlock Text="{Binding WallpaperSeedFlyoutTitle}"
FontWeight="SemiBold" />
<ItemsControl ItemsSource="{Binding WallpaperSeedCandidates}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:ThemeSeedCandidateOption">
<Button Padding="0"
Margin="0,0,8,8"
Background="Transparent"
BorderThickness="0"
Click="OnWallpaperSeedCandidateClick">
<StackPanel Width="76"
Spacing="6">
<Border Height="44"
Background="{Binding Brush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="12" />
<TextBlock Text="{Binding Label}"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock Text="{Binding $parent[vm:AppearanceSettingsPageViewModel].WallpaperSeedCurrentText}"
HorizontalAlignment="Center"
FontSize="10"
IsVisible="{Binding IsSelected}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Flyout>
</Button.Flyout>
<StackPanel Width="92"
Spacing="6">
<Border Height="54"
Background="{Binding SeedSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1"
CornerRadius="14" />
<TextBlock Text="{Binding PreviewSeedLabel}"
HorizontalAlignment="Center"
TextAlignment="Center" />
</StackPanel>
</Button>
</StackPanel>
</StackPanel>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
</StackPanel>
</ScrollViewer>

View File

@@ -1,6 +1,10 @@
using System;
using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.ViewModels;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace LanMountainDesktop.Views.SettingsPages;
@@ -15,16 +19,51 @@ namespace LanMountainDesktop.Views.SettingsPages;
public partial class AppearanceSettingsPage : SettingsPageBase
{
public AppearanceSettingsPage()
: this(new AppearanceSettingsPageViewModel(HostSettingsFacadeProvider.GetOrCreate()))
: this(new AppearanceSettingsPageViewModel(
HostSettingsFacadeProvider.GetOrCreate(),
HostAppearanceThemeProvider.GetOrCreate()))
{
}
public AppearanceSettingsPage(AppearanceSettingsPageViewModel viewModel)
{
ViewModel = viewModel;
ViewModel.RestartRequested += OnRestartRequested;
DataContext = ViewModel;
InitializeComponent();
}
public AppearanceSettingsPageViewModel ViewModel { get; }
private void OnRestartRequested(string reason)
{
RequestRestart(reason);
}
private void OnApplyCustomSeedClick(object? sender, RoutedEventArgs e)
{
_ = sender;
_ = e;
ViewModel.ApplyCustomSeedCommand.Execute(null);
CustomSeedButton?.Flyout?.Hide();
}
private void OnCustomSeedFlyoutClosed(object? sender, EventArgs e)
{
_ = sender;
_ = e;
ViewModel.CancelCustomSeedPreview();
}
private void OnWallpaperSeedCandidateClick(object? sender, RoutedEventArgs e)
{
_ = e;
if (sender is Button { DataContext: ThemeSeedCandidateOption option })
{
ViewModel.SelectWallpaperSeed(option.Value);
}
WallpaperSeedButton?.Flyout?.Hide();
}
}

View File

@@ -0,0 +1,36 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:LanMountainDesktop.ViewModels"
xmlns:controls="using:LanMountainDesktop.Controls"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
x:Class="LanMountainDesktop.Views.SettingsPages.PrivacySettingsPage"
x:DataType="vm:PrivacySettingsPageViewModel">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Classes="settings-page-container">
<controls:IconText Icon="Info"
Text="{Binding PrivacyHeader}"
Margin="0,0,0,4" />
<ui:SettingsExpander Header="{Binding CrashUploadHeader}"
Description="{Binding CrashUploadDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="ShieldDismiss" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ToggleSwitch IsChecked="{Binding UploadAnonymousCrashData}" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander Header="{Binding UsageUploadHeader}"
Description="{Binding UsageUploadDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Info" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ToggleSwitch IsChecked="{Binding UploadAnonymousUsageData}" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,30 @@
using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.ViewModels;
namespace LanMountainDesktop.Views.SettingsPages;
[SettingsPageInfo(
"privacy",
"Privacy",
SettingsPageCategory.About,
IconKey = "Shield",
SortOrder = 34,
TitleLocalizationKey = "settings.privacy.title",
DescriptionLocalizationKey = "settings.privacy.description")]
public partial class PrivacySettingsPage : SettingsPageBase
{
public PrivacySettingsPage()
: this(new PrivacySettingsPageViewModel(HostSettingsFacadeProvider.GetOrCreate()))
{
}
public PrivacySettingsPage(PrivacySettingsPageViewModel viewModel)
{
ViewModel = viewModel;
DataContext = ViewModel;
InitializeComponent();
}
public PrivacySettingsPageViewModel ViewModel { get; }
}

View File

@@ -7,9 +7,9 @@ namespace LanMountainDesktop.Views.SettingsPages;
[SettingsPageInfo(
"status-bar",
"Status Bar",
SettingsPageCategory.Appearance,
SettingsPageCategory.Components,
IconKey = "Apps",
SortOrder = 17,
SortOrder = 15,
TitleLocalizationKey = "settings.status_bar.title",
DescriptionLocalizationKey = "settings.status_bar.description")]
public partial class StatusBarSettingsPage : SettingsPageBase

View File

@@ -13,7 +13,7 @@
WindowStartupLocation="Manual"
SystemDecorations="BorderOnly"
FontFamily="{DynamicResource AppFontFamily}"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
Background="Transparent"
Icon="/Assets/avalonia-logo.ico"
Title="{Binding Title}">
@@ -38,13 +38,13 @@
</Window.Styles>
<Grid Classes="settings-scope"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
RowDefinitions="Auto,Auto,*">
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
RowDefinitions="Auto,*">
<Border x:Name="WindowTitleBarHost"
Height="48"
Padding="12,0,12,0"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
BorderBrush="{DynamicResource AdaptiveSettingsWindowBorderBrush}"
BorderThickness="0,0,0,1"
PointerPressed="OnWindowTitleBarPointerPressed">
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto"
@@ -113,22 +113,8 @@
</Grid>
</Border>
<ui:InfoBar x:Name="RestartInfoBar"
Grid.Row="1"
IsOpen="{Binding IsRestartRequested}"
Margin="16,8,16,0"
Severity="Informational"
IsClosable="False"
Title="{Binding RestartTitle}"
Message="{Binding RestartMessage}">
<ui:InfoBar.ActionButton>
<Button Click="OnRestartNowClick"
Content="{Binding RestartButtonText}" />
</ui:InfoBar.ActionButton>
</ui:InfoBar>
<ui:NavigationView x:Name="RootNavigationView"
Grid.Row="2"
Grid.Row="1"
Margin="0,8,0,0"
Background="Transparent"
PaneDisplayMode="Auto"

View File

@@ -33,6 +33,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
private readonly Dictionary<string, Control> _cachedPages = new(StringComparer.OrdinalIgnoreCase);
private readonly bool _useSystemChrome;
private bool _isResponsiveRefreshPending;
private bool _isRestartPromptVisible;
public SettingsWindow()
: this(
@@ -129,6 +130,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
? ViewModel.GetDefaultRestartMessage()
: reason;
ViewModel.IsRestartRequested = true;
PendingRestartStateService.SetPending(PendingRestartStateService.SettingsWindowReason, true);
ShowRestartPrompt();
}
public void ApplyChromeMode(bool useSystemChrome)
@@ -269,14 +272,11 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
}
}
private async void OnRestartNowClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnRestartNowClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
_ = sender;
_ = e;
_hostApplicationLifecycle.TryRestart(new HostApplicationLifecycleRequest(
Source: "SettingsWindow",
Reason: "User accepted restart from settings window."));
await Task.CompletedTask;
ShowRestartPrompt();
}
private void OnCloseDrawerClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
@@ -306,6 +306,58 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
ViewModel.IsRestartRequested = ViewModel.IsRestartRequested || PendingRestartStateService.HasPendingRestart;
}
private void ShowRestartPrompt()
{
void ShowPrompt()
{
UiExceptionGuard.FireAndForgetGuarded(
ShowRestartPromptCoreAsync,
"SettingsWindow.ShowRestartPrompt");
}
if (Dispatcher.UIThread.CheckAccess())
{
ShowPrompt();
return;
}
Dispatcher.UIThread.Post(ShowPrompt, DispatcherPriority.Send);
}
private async Task ShowRestartPromptCoreAsync()
{
if (_isRestartPromptVisible)
{
return;
}
_isRestartPromptVisible = true;
try
{
var dialog = new ContentDialog
{
Title = ViewModel.RestartDialogTitle,
Content = ViewModel.RestartMessage,
PrimaryButtonText = ViewModel.RestartDialogPrimaryText,
CloseButtonText = ViewModel.RestartDialogCloseText,
DefaultButton = ContentDialogButton.Primary
};
var result = await dialog.ShowAsync(this);
if (result == ContentDialogResult.Primary)
{
_hostApplicationLifecycle.TryRestart(new HostApplicationLifecycleRequest(
Source: "SettingsWindow",
Reason: "User accepted restart from settings window."));
}
}
finally
{
_isRestartPromptVisible = false;
}
}
private void OnOpened(object? sender, EventArgs e)
{
_ = sender;
@@ -642,6 +694,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
"GridDots" => Symbol.GridDots,
"PuzzlePiece" => Symbol.PuzzlePiece,
"ShoppingBag" => Symbol.ShoppingBag,
"Shield" => Symbol.ShieldDismiss,
"Info" => Symbol.Info,
"ArrowSync" => Symbol.ArrowSync,
_ => Symbol.Settings