mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
0.2.7
修改天气组件,ci工作流
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -514,16 +514,44 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
BackgroundMotionLayer.Background = ResolveWeatherBackgroundBrush(kind, palette);
|
||||
BackgroundTintLayer.Background = CreateSolidBrush(palette.Tint);
|
||||
|
||||
var primary = CreateSolidBrush(palette.PrimaryText);
|
||||
var particleBrush = ResolveParticleBrush(ToThemeKind(kind), palette.ParticleColor);
|
||||
var isNightVisual = kind is WeatherVisualKind.ClearNight or WeatherVisualKind.CloudyNight;
|
||||
var cityBrush = CreateSolidBrush(palette.SecondaryText, isNightVisual ? (byte)0xDC : (byte)0xCC);
|
||||
var conditionSecondary = CreateSolidBrush(palette.PrimaryText, isNightVisual ? (byte)0xEE : (byte)0xE2);
|
||||
var rangeSecondary = CreateSolidBrush(palette.PrimaryText, isNightVisual ? (byte)0xE6 : (byte)0xD9);
|
||||
var forecastTimeBrush = CreateSolidBrush(palette.TertiaryText, isNightVisual ? (byte)0xCA : (byte)0xB6);
|
||||
var forecastTempBrush = CreateSolidBrush(palette.PrimaryText, isNightVisual ? (byte)0xEA : (byte)0xDC);
|
||||
HourlyPanelBorder.Background = CreateSolidBrush(isNightVisual ? "#12FFFFFF" : "#0CFFFFFF");
|
||||
LocationIcon.Foreground = primary;
|
||||
var backgroundSamples = WeatherTypographyAccessibility.BuildBackgroundSamples(
|
||||
palette.GradientFrom,
|
||||
palette.GradientTo,
|
||||
palette.Tint,
|
||||
isNightVisual);
|
||||
var primary = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.PrimaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagLargeTextContrast);
|
||||
var cityBrush = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.SecondaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagNormalTextContrast,
|
||||
isNightVisual ? (byte)0xE6 : (byte)0xD4);
|
||||
var conditionSecondary = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.PrimaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagLargeTextContrast,
|
||||
isNightVisual ? (byte)0xED : (byte)0xDF);
|
||||
var rangeSecondary = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.PrimaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagLargeTextContrast,
|
||||
isNightVisual ? (byte)0xE2 : (byte)0xCE);
|
||||
var forecastTimeBrush = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.TertiaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagNormalTextContrast,
|
||||
isNightVisual ? (byte)0xC8 : (byte)0xAC);
|
||||
var forecastTempBrush = WeatherTypographyAccessibility.CreateReadableBrush(
|
||||
palette.PrimaryText,
|
||||
backgroundSamples,
|
||||
WeatherTypographyAccessibility.WcagNormalTextContrast,
|
||||
isNightVisual ? (byte)0xEE : (byte)0xE1);
|
||||
HourlyPanelBorder.Background = Brushes.Transparent;
|
||||
LocationIcon.Foreground = cityBrush;
|
||||
CityTextBlock.Foreground = cityBrush;
|
||||
TemperatureTextBlock.Foreground = primary;
|
||||
ConditionTextBlock.Foreground = conditionSecondary;
|
||||
@@ -726,9 +754,11 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
{
|
||||
const int itemCount = 6;
|
||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||
var timelineStart = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Kind);
|
||||
var fallbackDaily = ResolveDailyForecastForDate(snapshot, DateOnly.FromDateTime(now))
|
||||
?? snapshot.DailyForecasts.FirstOrDefault();
|
||||
var (low, high) = ResolveTemperatureRange(snapshot);
|
||||
var sunsetSlotIndex = ResolveSunsetSlotIndex(snapshot, timelineStart, itemCount);
|
||||
|
||||
var hourlyCandidates = snapshot.HourlyForecasts
|
||||
.Select(hourly => (Hourly: hourly, Time: ConvertToConfiguredTime(hourly.Time).DateTime))
|
||||
@@ -740,10 +770,8 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
var items = new List<HourlyForecastItem>(itemCount);
|
||||
for (var i = 0; i < itemCount; i++)
|
||||
{
|
||||
var targetTime = now.AddHours(i);
|
||||
var displayLabel = i == 0
|
||||
? L("weather.hourly.now", "Now")
|
||||
: targetTime.ToString("HH:mm", CultureInfo.InvariantCulture);
|
||||
var targetTime = timelineStart.AddHours(i);
|
||||
var displayLabel = targetTime.ToString("HH:mm", CultureInfo.InvariantCulture);
|
||||
|
||||
var candidate = TryFindNearestHourlyCandidate(hourlyCandidates, targetTime);
|
||||
var weatherCode = candidate?.Hourly.WeatherCode ??
|
||||
@@ -757,12 +785,15 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
snapshot.Current.TemperatureC,
|
||||
low,
|
||||
high);
|
||||
var temperatureLabel = i == sunsetSlotIndex
|
||||
? L("weather.hourly.sunset", "Sunset")
|
||||
: FormatTemperature(estimatedTemp);
|
||||
|
||||
items.Add(new HourlyForecastItem(
|
||||
targetTime,
|
||||
displayLabel,
|
||||
iconKind,
|
||||
FormatTemperature(estimatedTemp)));
|
||||
temperatureLabel));
|
||||
}
|
||||
|
||||
return items;
|
||||
@@ -773,17 +804,16 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
const int itemCount = 6;
|
||||
var items = new List<HourlyForecastItem>(itemCount);
|
||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||
var timelineStart = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Kind);
|
||||
var iconKind = ToThemeKind(visualKind);
|
||||
for (var i = 0; i < itemCount; i++)
|
||||
{
|
||||
var targetTime = now.AddHours(i);
|
||||
var targetTime = timelineStart.AddHours(i);
|
||||
items.Add(new HourlyForecastItem(
|
||||
targetTime,
|
||||
i == 0
|
||||
? L("weather.hourly.now", "Now")
|
||||
: targetTime.ToString("HH:mm", CultureInfo.InvariantCulture),
|
||||
targetTime.ToString("HH:mm", CultureInfo.InvariantCulture),
|
||||
iconKind,
|
||||
"--°"));
|
||||
i == 3 ? L("weather.hourly.sunset", "Sunset") : "--°"));
|
||||
}
|
||||
|
||||
return items;
|
||||
@@ -867,6 +897,67 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
return null;
|
||||
}
|
||||
|
||||
private int ResolveSunsetSlotIndex(WeatherSnapshot snapshot, DateTime startTime, int slotCount)
|
||||
{
|
||||
if (slotCount <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var todayForecast = ResolveDailyForecastForDate(snapshot, DateOnly.FromDateTime(startTime));
|
||||
if (todayForecast is null || !TryParseClockTime(todayForecast.SunsetTime, out var sunsetClock))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var sunsetTime = startTime.Date + sunsetClock;
|
||||
var bestIndex = -1;
|
||||
var bestDelta = double.MaxValue;
|
||||
for (var i = 0; i < slotCount; i++)
|
||||
{
|
||||
var slotTime = startTime.AddHours(i);
|
||||
var deltaMinutes = Math.Abs((slotTime - sunsetTime).TotalMinutes);
|
||||
if (deltaMinutes >= bestDelta)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bestDelta = deltaMinutes;
|
||||
bestIndex = i;
|
||||
}
|
||||
|
||||
return bestDelta <= 60 ? bestIndex : -1;
|
||||
}
|
||||
|
||||
private static bool TryParseClockTime(string? text, out TimeSpan value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var candidate = text.Trim();
|
||||
if (TimeSpan.TryParse(candidate, CultureInfo.InvariantCulture, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTimeOffset.TryParse(candidate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dto))
|
||||
{
|
||||
value = dto.TimeOfDay;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(candidate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dt))
|
||||
{
|
||||
value = dt.TimeOfDay;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsNightHour(DateTime time)
|
||||
{
|
||||
return time.Hour < 6 || time.Hour >= 18;
|
||||
@@ -1079,62 +1170,66 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
var (layoutWidth, layoutHeight) = ResolveLayoutViewport();
|
||||
var scaleX = Math.Clamp(layoutWidth / 608d, 0.58, 1.90);
|
||||
var scaleY = Math.Clamp(layoutHeight / 288d, 0.58, 1.90);
|
||||
var uiScale = Math.Clamp(Math.Min(scaleX, scaleY), 0.58, 1.75);
|
||||
var innerWidth = Math.Max(120, layoutWidth);
|
||||
var innerHeight = Math.Max(72, layoutHeight);
|
||||
var compactness = Math.Clamp((1.0 - scaleY) / 0.55, 0, 1);
|
||||
|
||||
ContentGrid.RowSpacing = Math.Clamp(7 * scaleY, 2, 12);
|
||||
TopRowGrid.ColumnSpacing = Math.Clamp(10 * scaleX, 6, 16);
|
||||
TopRowGrid.RowSpacing = Math.Clamp(5 * scaleY, 2, 9);
|
||||
BottomInfoStack.Margin = new Thickness(0, 0, 0, Math.Clamp(2 * scaleY, 0, 5));
|
||||
BottomInfoStack.Spacing = Math.Clamp(2 * scaleY, 1, 5);
|
||||
ContentGrid.RowSpacing = Math.Clamp((4.2 - (compactness * 0.7)) * scaleY, 2, 8);
|
||||
TopRowGrid.ColumnSpacing = Math.Clamp(8 * scaleX, 6, 13);
|
||||
BottomInfoStack.Margin = new Thickness(0, 0, 0, Math.Clamp((1.0 - (compactness * 0.4)) * scaleY, 0, 2));
|
||||
|
||||
var summaryHeight = Math.Clamp(116 * scaleY, 82, 164);
|
||||
var bodyHeight = Math.Max(52, innerHeight - summaryHeight - ContentGrid.RowSpacing);
|
||||
var contentHeight = Math.Max(60, innerHeight - ContentGrid.RowSpacing);
|
||||
var topZoneRatio = Math.Clamp(0.38 + (compactness * 0.09), 0.36, 0.50);
|
||||
var topZoneHeight = Math.Clamp(contentHeight * topZoneRatio, 60, 170);
|
||||
var bottomZoneHeight = Math.Max(42, contentHeight - topZoneHeight);
|
||||
var topScaleH = Math.Clamp(topZoneHeight / 102d, 0.62, 2.0);
|
||||
var topScaleW = Math.Clamp(innerWidth / 620d, 0.62, 2.0);
|
||||
var topScale = Math.Clamp((topScaleH * 0.68) + (topScaleW * 0.32), 0.62, 2.0);
|
||||
var bottomScaleH = Math.Clamp(bottomZoneHeight / 122d, 0.56, 2.0);
|
||||
var bottomScale = Math.Clamp((bottomScaleH * 0.74) + (scaleX * 0.26), 0.56, 1.95);
|
||||
var bodyHeight = bottomZoneHeight;
|
||||
|
||||
TemperatureTextBlock.FontSize = Math.Clamp(94 * uiScale, 56, 126);
|
||||
TemperatureTextBlock.FontWeight = ToVariableWeight(320);
|
||||
TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2 * uiScale, -5, 0), 0, 0);
|
||||
TemperatureTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.22, 84, 168);
|
||||
TemperatureTextBlock.FontSize = Math.Clamp(88 * topScale, 56, 132);
|
||||
TemperatureTextBlock.FontWeight = ToVariableWeight(315);
|
||||
TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-1.2 * topScale, -4, 0), 0, 0);
|
||||
TemperatureTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.24, 88, 196);
|
||||
|
||||
CityInfoBadge.Padding = new Thickness(
|
||||
Math.Clamp(10 * uiScale, 6, 14),
|
||||
Math.Clamp(4 * uiScale, 2, 8));
|
||||
CityInfoBadge.CornerRadius = new CornerRadius(Math.Clamp(11 * uiScale, 8, 16));
|
||||
LocationIcon.FontSize = Math.Clamp(14 * uiScale, 10, 20);
|
||||
CityTextBlock.FontSize = Math.Clamp(21 * uiScale, 13, 31);
|
||||
CityTextBlock.FontWeight = ToVariableWeight(560);
|
||||
CityTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.25, 80, 220);
|
||||
CityInfoBadge.Padding = new Thickness(0);
|
||||
CityInfoBadge.CornerRadius = new CornerRadius(0);
|
||||
LocationIcon.FontSize = Math.Clamp(12 * topScale, 9, 17);
|
||||
CityTextBlock.FontSize = Math.Clamp(18 * topScale, 11, 26);
|
||||
CityTextBlock.FontWeight = ToVariableWeight(540);
|
||||
CityTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.36, 112, 300);
|
||||
|
||||
ConditionInfoBadge.Padding = new Thickness(0);
|
||||
ConditionInfoBadge.CornerRadius = new CornerRadius(Math.Clamp(8 * uiScale, 4, 12));
|
||||
ConditionRangeStack.Spacing = Math.Clamp(12 * uiScale, 6, 18);
|
||||
ConditionTextBlock.FontSize = Math.Clamp(34 * uiScale, 16, 46);
|
||||
RangeTextBlock.FontSize = Math.Clamp(34 * uiScale, 16, 46);
|
||||
ConditionTextBlock.FontWeight = ToVariableWeight(610);
|
||||
ConditionInfoBadge.CornerRadius = new CornerRadius(0);
|
||||
ConditionRangeStack.Spacing = Math.Clamp(7 * topScale, 4, 13);
|
||||
ConditionTextBlock.FontSize = Math.Clamp(19 * topScale, 12, 27);
|
||||
RangeTextBlock.FontSize = Math.Clamp(20 * topScale, 12, 30);
|
||||
ConditionTextBlock.FontWeight = ToVariableWeight(600);
|
||||
RangeTextBlock.FontWeight = ToVariableWeight(620);
|
||||
ConditionTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.16, 46, 170);
|
||||
RangeTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.20, 60, 200);
|
||||
ConditionTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.24, 58, 220);
|
||||
RangeTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.30, 88, 270);
|
||||
BottomInfoStack.Spacing = Math.Clamp(2.2 * topScale, 1, 6);
|
||||
|
||||
var iconSize = Math.Clamp(68 * uiScale, 40, 90);
|
||||
var iconSize = Math.Clamp(68 * topScale, 42, 98);
|
||||
WeatherIconImage.Width = iconSize;
|
||||
WeatherIconImage.Height = iconSize;
|
||||
|
||||
HourlyPanelBorder.Padding = new Thickness(
|
||||
Math.Clamp(5 * scaleX, 3, 10),
|
||||
Math.Clamp(3 * scaleY, 1, 7));
|
||||
HourlyPanelBorder.CornerRadius = new CornerRadius(Math.Clamp(14 * uiScale, 8, 20));
|
||||
HourlyGrid.ColumnSpacing = Math.Clamp(9 * scaleX, 4, 14);
|
||||
HourlyPanelBorder.Padding = new Thickness(0, Math.Clamp(1 * scaleY, 0, 2), 0, 0);
|
||||
HourlyPanelBorder.Margin = new Thickness(0, Math.Clamp(1.2 * scaleY, 0, 3), 0, 0);
|
||||
HourlyPanelBorder.CornerRadius = new CornerRadius(0);
|
||||
HourlyGrid.ColumnSpacing = Math.Clamp(7 * scaleX, 4, 11);
|
||||
|
||||
var hourlyColumnCount = Math.Max(1, _hourlyTimeBlocks.Length);
|
||||
var hourlyInnerWidth = Math.Max(
|
||||
96,
|
||||
innerWidth - HourlyPanelBorder.Padding.Left - HourlyPanelBorder.Padding.Right - (HourlyGrid.ColumnSpacing * (hourlyColumnCount - 1)));
|
||||
var hourlyCellWidth = Math.Max(34, hourlyInnerWidth / hourlyColumnCount);
|
||||
var stackSpacing = Math.Clamp(2 * scaleY, 1, 4);
|
||||
var hourlyTempSize = Math.Clamp(bodyHeight * 0.24, 14, 30);
|
||||
var hourlyTimeSize = Math.Clamp(bodyHeight * 0.20, 10, 24);
|
||||
var hourlyIconSize = Math.Clamp(bodyHeight * 0.28, 14, 34);
|
||||
var stackSpacing = Math.Clamp((1.6 + (bottomScale * 0.8)) * scaleY, 1, 4);
|
||||
var hourlyTempSize = Math.Clamp(Math.Max(13, bodyHeight * 0.22) * (0.76 + (bottomScale * 0.24)), 13, 31);
|
||||
var hourlyTimeSize = Math.Clamp(Math.Max(10, bodyHeight * 0.17) * (0.78 + (bottomScale * 0.22)), 10, 23);
|
||||
var hourlyIconSize = Math.Clamp(Math.Max(14, bodyHeight * 0.25) * (0.78 + (bottomScale * 0.22)), 14, 35);
|
||||
|
||||
for (var i = 0; i < _hourlyTimeBlocks.Length; i++)
|
||||
{
|
||||
@@ -1142,8 +1237,8 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
_hourlyTimeBlocks[i].FontSize = hourlyTimeSize;
|
||||
_hourlyIconBlocks[i].Width = hourlyIconSize;
|
||||
_hourlyIconBlocks[i].Height = hourlyIconSize;
|
||||
_hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 36, 128);
|
||||
_hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 36, 128);
|
||||
_hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 34, 112);
|
||||
_hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 34, 112);
|
||||
_hourlyTimeBlocks[i].FontWeight = ToVariableWeight(500);
|
||||
_hourlyTempBlocks[i].FontWeight = ToVariableWeight(590);
|
||||
if (_hourlyTimeBlocks[i].Parent is StackPanel hourlyStack)
|
||||
@@ -1166,8 +1261,16 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
private void SetLoadingSkeleton(bool isLoading)
|
||||
{
|
||||
CityInfoBadge.Background = isLoading ? CreateSolidBrush("#24FFFFFF") : Brushes.Transparent;
|
||||
ConditionInfoBadge.Background = isLoading ? CreateSolidBrush("#1CFFFFFF") : Brushes.Transparent;
|
||||
var opacity = isLoading ? 0.58 : 1.0;
|
||||
TemperatureTextBlock.Opacity = opacity;
|
||||
ConditionTextBlock.Opacity = opacity;
|
||||
RangeTextBlock.Opacity = opacity;
|
||||
CityTextBlock.Opacity = isLoading ? 0.50 : 0.96;
|
||||
for (var i = 0; i < _hourlyTempBlocks.Length; i++)
|
||||
{
|
||||
_hourlyTempBlocks[i].Opacity = opacity;
|
||||
_hourlyTimeBlocks[i].Opacity = isLoading ? 0.74 : 0.94;
|
||||
}
|
||||
}
|
||||
|
||||
private static FontWeight ToVariableWeight(double weight)
|
||||
|
||||
Reference in New Issue
Block a user