mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-26 20:24:26 +08:00
settings_re11
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
SizeToContent="Manual"
|
||||
ShowInTaskbar="False"
|
||||
SystemDecorations="BorderOnly"
|
||||
Background="{DynamicResource EditorWindowBackgroundBrush}"
|
||||
Background="Transparent"
|
||||
Title="Component Editor">
|
||||
<Window.Resources>
|
||||
<!-- Material Design 3 Brushes -->
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
CanResize="False"
|
||||
UseLayoutRounding="True"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
|
||||
Background="Transparent"
|
||||
Title="LanMountainDesktop">
|
||||
|
||||
<Design.DataContext>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user