Add WinUI and core source
build-winui / winui (push) Has been cancelled
build-winui / winui (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using YMhut.Box.Core.Settings;
|
||||
using YMhut.Box.WinUI.Services;
|
||||
|
||||
namespace YMhut.Box.WinUI.Controls;
|
||||
|
||||
public sealed class WeatherCapsuleControl : UserControl
|
||||
{
|
||||
private readonly ITitleWeatherService _weatherService;
|
||||
private readonly ISettingsService _settingsService = AppServices.GetRequiredService<ISettingsService>();
|
||||
private readonly Button _button;
|
||||
private readonly AnimatedWeatherIconControl _weatherIcon;
|
||||
private readonly ProgressRing _loadingRing;
|
||||
private readonly TextBlock _locationText;
|
||||
private readonly TextBlock _tempText;
|
||||
private readonly TextBlock _conditionText;
|
||||
private readonly Flyout _flyout;
|
||||
|
||||
private TitleWeatherSnapshot _snapshot = TitleWeatherSnapshot.Loading;
|
||||
private TitleWeatherSnapshot? _lastAvailableSnapshot;
|
||||
private int _loadVersion;
|
||||
|
||||
public WeatherCapsuleControl(ITitleWeatherService weatherService)
|
||||
{
|
||||
_weatherService = weatherService;
|
||||
HorizontalAlignment = HorizontalAlignment.Right;
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
|
||||
_weatherIcon = new AnimatedWeatherIconControl(20)
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
_loadingRing = new ProgressRing
|
||||
{
|
||||
Width = 18,
|
||||
Height = 18,
|
||||
IsActive = true,
|
||||
Visibility = Visibility.Collapsed
|
||||
};
|
||||
_locationText = ModernUi.Text(_snapshot.Location, 12, FontWeights.SemiBold, ModernUi.TextSecondary, maxLines: 1);
|
||||
_conditionText = ModernUi.Text(_snapshot.Condition, 10.5, foreground: ModernUi.TextSecondary, maxLines: 1);
|
||||
_tempText = ModernUi.Text(_snapshot.TemperatureText, 15, FontWeights.SemiBold, ModernUi.TextPrimary, maxLines: 1);
|
||||
ConfigureCompactLine(_locationText, 14);
|
||||
ConfigureCompactLine(_conditionText, 13);
|
||||
ConfigureCompactLine(_tempText, 18);
|
||||
|
||||
var visual = new Grid
|
||||
{
|
||||
ColumnSpacing = 9,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition(),
|
||||
new ColumnDefinition { Width = GridLength.Auto }
|
||||
}
|
||||
};
|
||||
var iconHost = new Grid { Width = 22, Height = 22, Children = { _weatherIcon, _loadingRing } };
|
||||
visual.Children.Add(iconHost);
|
||||
var text = new Grid
|
||||
{
|
||||
MinWidth = 76,
|
||||
MaxWidth = 130,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
}
|
||||
};
|
||||
text.Children.Add(_locationText);
|
||||
Grid.SetRow(_conditionText, 1);
|
||||
text.Children.Add(_conditionText);
|
||||
Grid.SetColumn(text, 1);
|
||||
visual.Children.Add(text);
|
||||
Grid.SetColumn(_tempText, 2);
|
||||
visual.Children.Add(_tempText);
|
||||
|
||||
_flyout = new Flyout { Placement = FlyoutPlacementMode.BottomEdgeAlignedRight };
|
||||
_flyout.Opening += (_, _) => _flyout.Content = BuildFlyoutContent();
|
||||
|
||||
_button = new Button
|
||||
{
|
||||
Height = 38,
|
||||
MinWidth = 168,
|
||||
MaxWidth = 242,
|
||||
Margin = new Thickness(0, 4, 0, 4),
|
||||
Padding = new Thickness(12, 4, 12, 4),
|
||||
CornerRadius = new CornerRadius(19),
|
||||
Background = ModernUi.Surface,
|
||||
BorderBrush = ModernUi.Stroke,
|
||||
BorderThickness = new Thickness(1),
|
||||
Content = visual,
|
||||
Flyout = _flyout
|
||||
};
|
||||
AutomationProperties.SetName(_button, AppLocalizer.T("天气", "Weather"));
|
||||
ToolTipService.SetToolTip(_button, AppLocalizer.T("查看天气详情", "Show weather details"));
|
||||
Content = _button;
|
||||
ApplySnapshot(_snapshot);
|
||||
}
|
||||
|
||||
public async Task LoadAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await RefreshAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task RefreshAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var loadVersion = Interlocked.Increment(ref _loadVersion);
|
||||
DispatcherQueue.TryEnqueue(() => ApplySnapshot(TitleWeatherSnapshot.Loading, loadingOverlay: true));
|
||||
var snapshot = await _weatherService.GetCurrentAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (loadVersion != _loadVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DispatcherQueue.TryEnqueue(() => ApplySnapshot(snapshot));
|
||||
}
|
||||
|
||||
public void RefreshLanguage()
|
||||
{
|
||||
ApplySnapshot(_snapshot);
|
||||
}
|
||||
|
||||
private void ApplySnapshot(TitleWeatherSnapshot snapshot, bool loadingOverlay = false)
|
||||
{
|
||||
var displaySnapshot = loadingOverlay && _lastAvailableSnapshot is not null
|
||||
? _lastAvailableSnapshot
|
||||
: snapshot;
|
||||
_snapshot = displaySnapshot;
|
||||
if (displaySnapshot.IsAvailable)
|
||||
{
|
||||
_lastAvailableSnapshot = displaySnapshot;
|
||||
}
|
||||
|
||||
var loading = loadingOverlay || ReferenceEquals(snapshot, TitleWeatherSnapshot.Loading);
|
||||
_loadingRing.IsActive = loading;
|
||||
_loadingRing.Visibility = loading ? Visibility.Visible : Visibility.Collapsed;
|
||||
_loadingRing.Opacity = loading ? 0.78 : 0;
|
||||
_weatherIcon.Visibility = Visibility.Visible;
|
||||
_weatherIcon.Opacity = loading ? 0.62 : 1;
|
||||
_weatherIcon.Update(displaySnapshot, _settingsService.Current.AnimationsEnabled);
|
||||
_locationText.Text = displaySnapshot.Location;
|
||||
_conditionText.Text = displaySnapshot.Condition;
|
||||
_tempText.Text = displaySnapshot.TemperatureText;
|
||||
_button.Background = displaySnapshot.IsAvailable ? ModernUi.Surface : ModernUi.SurfaceAlt;
|
||||
_button.BorderBrush = displaySnapshot.IsAvailable ? ModernUi.Stroke : ModernUi.StrokeStrong;
|
||||
ToolTipService.SetToolTip(_button, loading ? AppLocalizer.T("天气正在刷新", "Weather is refreshing") : BuildTooltip(displaySnapshot));
|
||||
AutomationProperties.SetName(_button, loading ? AppLocalizer.T("天气正在刷新", "Weather is refreshing") : BuildTooltip(displaySnapshot));
|
||||
}
|
||||
|
||||
private string BuildTooltip(TitleWeatherSnapshot snapshot)
|
||||
{
|
||||
return snapshot.IsAvailable
|
||||
? $"{snapshot.Location} {snapshot.Condition} {snapshot.TemperatureText}"
|
||||
: AppLocalizer.T("天气暂不可用,点击重试", "Weather unavailable. Click to retry.");
|
||||
}
|
||||
|
||||
private UIElement BuildFlyoutContent()
|
||||
{
|
||||
var snapshot = _snapshot;
|
||||
var refresh = ModernUi.PillButton(AppLocalizer.T("刷新", "Refresh"), "\uE72C", async () => await RefreshAsync(), primary: true);
|
||||
refresh.HorizontalAlignment = HorizontalAlignment.Right;
|
||||
Grid.SetColumn(refresh, 2);
|
||||
|
||||
return new StackPanel
|
||||
{
|
||||
Width = 280,
|
||||
Padding = new Thickness(4),
|
||||
Spacing = 12,
|
||||
Children =
|
||||
{
|
||||
new Grid
|
||||
{
|
||||
ColumnSpacing = 12,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition(),
|
||||
new ColumnDefinition { Width = GridLength.Auto }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
BuildFlyoutIcon(snapshot),
|
||||
BuildTitleBlock(snapshot),
|
||||
refresh
|
||||
}
|
||||
},
|
||||
BuildDetailLine(AppLocalizer.T("体感", "Feels like"), snapshot.FeelsLikeText, "\uE706"),
|
||||
BuildDetailLine(AppLocalizer.T("湿度", "Humidity"), snapshot.HumidityText, "\uE81F"),
|
||||
BuildDetailLine(AppLocalizer.T("风速", "Wind"), snapshot.WindText, "\uE9CA"),
|
||||
BuildDetailLine(AppLocalizer.T("今日温度", "Today"), snapshot.RangeText, "\uE787"),
|
||||
BuildDetailLine(AppLocalizer.T("更新时间", "Updated"), snapshot.UpdatedText, "\uE823"),
|
||||
snapshot.ErrorMessage is null
|
||||
? new Border { Height = 0 }
|
||||
: ModernUi.Card(
|
||||
ModernUi.Text(snapshot.ErrorMessage, 12, foreground: ModernUi.TextSecondary, maxLines: 3),
|
||||
new Thickness(10),
|
||||
radius: 8,
|
||||
background: ModernUi.SurfaceAlt)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static StackPanel BuildTitleBlock(TitleWeatherSnapshot snapshot)
|
||||
{
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Spacing = 1,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Children =
|
||||
{
|
||||
ModernUi.Text(snapshot.Location, 17, FontWeights.SemiBold, maxLines: 1),
|
||||
ModernUi.Text($"{snapshot.Condition} · {snapshot.TemperatureText} · {snapshot.QueryLevel}", 13, foreground: ModernUi.TextSecondary, maxLines: 1)
|
||||
}
|
||||
};
|
||||
Grid.SetColumn(panel, 1);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private UIElement BuildFlyoutIcon(TitleWeatherSnapshot snapshot)
|
||||
{
|
||||
var icon = new AnimatedWeatherIconControl(44);
|
||||
icon.Update(snapshot, _settingsService.Current.AnimationsEnabled);
|
||||
return new Border
|
||||
{
|
||||
Width = 52,
|
||||
Height = 52,
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Background = ModernUi.AccentSoft,
|
||||
BorderBrush = ModernUi.Stroke,
|
||||
BorderThickness = new Thickness(1),
|
||||
Child = icon
|
||||
};
|
||||
}
|
||||
|
||||
private static void ConfigureCompactLine(TextBlock text, double lineHeight)
|
||||
{
|
||||
text.TextWrapping = TextWrapping.NoWrap;
|
||||
text.TextTrimming = TextTrimming.CharacterEllipsis;
|
||||
text.LineHeight = lineHeight;
|
||||
text.VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
|
||||
private static UIElement BuildDetailLine(string label, string value, string glyph)
|
||||
{
|
||||
var grid = new Grid { ColumnSpacing = 10 };
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition());
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
grid.Children.Add(ModernUi.IconTile(glyph, 30, ModernUi.SurfaceAlt, ModernUi.TextSecondary, 13));
|
||||
|
||||
var title = ModernUi.Text(label, 13, FontWeights.SemiBold, ModernUi.TextSecondary, maxLines: 1);
|
||||
Grid.SetColumn(title, 1);
|
||||
grid.Children.Add(title);
|
||||
|
||||
var text = ModernUi.Text(value, 13, FontWeights.SemiBold, ModernUi.TextPrimary, maxLines: 1);
|
||||
Grid.SetColumn(text, 2);
|
||||
grid.Children.Add(text);
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user