更新后端服务UI

客户端重构部分界面UI
This commit is contained in:
QWQLwToo
2026-06-30 11:45:52 +08:00
parent 7745e7a2d4
commit 1d0e862299
13 changed files with 942 additions and 121 deletions
+56 -7
View File
@@ -25,6 +25,13 @@ internal static class ModernUi
internal static SolidColorBrush Danger { get; } = Brush("#C42B1C");
internal static SolidColorBrush Info { get; } = Brush("#0078D4");
// Hover-specific brushes — updated by SetPalette so they track theme changes.
internal static SolidColorBrush HoverSurface { get; } = Brush("#EBEBEB");
internal static SolidColorBrush HoverAccentSoft { get; } = Brush("#D0EAFF");
internal static SolidColorBrush PrimaryHover { get; } = Brush("#005AA3");
internal static SolidColorBrush PrimaryPressed { get; } = Brush("#004F8E");
internal static SolidColorBrush Transparent { get; } = Brush("#00FFFFFF");
internal static void SetPalette(bool dark)
{
SetBrush(AppBackground, dark ? "#202020" : "#F7F7F7");
@@ -41,6 +48,12 @@ internal static class ModernUi
SetBrush(Bronze, dark ? "#F7B189" : "#CA5010");
SetBrush(Danger, dark ? "#F1707B" : "#C42B1C");
SetBrush(Info, dark ? "#60CDFF" : "#0078D4");
// Hover brushes
SetBrush(HoverSurface, dark ? "#3A3A3A" : "#EBEBEB");
SetBrush(HoverAccentSoft, dark ? "#0D4F78" : "#D0EAFF");
SetBrush(PrimaryHover, dark ? "#4BB8F0" : "#005AA3");
SetBrush(PrimaryPressed, dark ? "#38A8E5" : "#004F8E");
// Transparent is always the same — no update needed
}
internal static SolidColorBrush Brush(string hex)
@@ -138,16 +151,40 @@ internal static class ModernUi
};
}
/// <summary>
/// Overrides the WinUI 3 Button ControlTemplate's PointerOver/Pressed theme resource keys
/// at the instance level, so VSM animations still run but with the correct custom colors.
/// Call this after constructing a Button whose Background/BorderBrush were set manually.
/// </summary>
internal static void ApplyHoverResources(
Button button,
Brush hoverBackground,
Brush hoverBorder,
Brush? pressedBackground = null,
Brush? pressedBorder = null,
Brush? hoverForeground = null)
{
button.Resources["ButtonBackgroundPointerOver"] = hoverBackground;
button.Resources["ButtonBorderBrushPointerOver"] = hoverBorder;
button.Resources["ButtonBackgroundPressed"] = pressedBackground ?? hoverBackground;
button.Resources["ButtonBorderBrushPressed"] = pressedBorder ?? hoverBorder;
if (hoverForeground is not null)
{
button.Resources["ButtonForegroundPointerOver"] = hoverForeground;
button.Resources["ButtonForegroundPressed"] = hoverForeground;
}
}
internal static Button IconButton(string glyph, string tooltip, Action? click = null)
{
var button = new Button
{
Width = 40,
Width = 36,
Height = 36,
Padding = new Thickness(0),
CornerRadius = new CornerRadius(6),
Background = Brush("#00FFFFFF"),
BorderBrush = Brush("#00FFFFFF"),
Background = Transparent,
BorderBrush = Transparent,
Content = new FontIcon
{
Glyph = glyph,
@@ -155,6 +192,7 @@ internal static class ModernUi
Foreground = TextSecondary
}
};
ApplyHoverResources(button, SurfaceAlt, Stroke, HoverSurface, StrokeStrong);
ToolTipService.SetToolTip(button, tooltip);
AutomationProperties.SetName(button, tooltip);
if (click is not null)
@@ -167,6 +205,7 @@ internal static class ModernUi
internal static Button PillButton(string text, string glyph, Action? click = null, bool primary = false)
{
var fgBrush = primary ? Brush("#FFFFFFFF") : TextPrimary;
var panel = new StackPanel
{
Orientation = Orientation.Horizontal,
@@ -175,8 +214,8 @@ internal static class ModernUi
VerticalAlignment = VerticalAlignment.Center,
Children =
{
new FontIcon { Glyph = glyph, FontSize = 15 },
Text(text, 14, FontWeights.SemiBold, primary ? Brush("#FFFFFFFF") : TextPrimary)
new FontIcon { Glyph = glyph, FontSize = 15, Foreground = fgBrush },
Text(text, 14, FontWeights.SemiBold, fgBrush)
}
};
@@ -184,12 +223,22 @@ internal static class ModernUi
{
Padding = new Thickness(14, 8, 14, 8),
CornerRadius = new CornerRadius(6),
Background = primary ? Accent : Brush("#00FFFFFF"),
Foreground = primary ? Brush("#FFFFFFFF") : TextPrimary,
Background = primary ? Accent : Transparent,
Foreground = fgBrush,
BorderBrush = primary ? Accent : Stroke,
BorderThickness = new Thickness(1),
Content = panel
};
if (primary)
{
ApplyHoverResources(button, PrimaryHover, PrimaryHover, PrimaryPressed, PrimaryPressed, Brush("#FFFFFFFF"));
}
else
{
ApplyHoverResources(button, SurfaceAlt, StrokeStrong, HoverSurface, StrokeStrong);
}
ToolTipService.SetToolTip(button, text);
AutomationProperties.SetName(button, text);
if (click is not null)
+3
View File
@@ -208,6 +208,7 @@ public sealed class HomePage : Page
BorderThickness = new Thickness(1),
Content = new Border { Padding = new Thickness(16, 12, 16, 12), Child = grid }
};
ModernUi.ApplyHoverResources(button, ModernUi.SurfaceAlt, ModernUi.StrokeStrong, ModernUi.HoverSurface, ModernUi.StrokeStrong);
button.Click += async (_, _) => await ShowAnnouncementDialogAsync();
ToolTipService.SetToolTip(button, AppLocalizer.T("查看完整公告", "View full announcement"));
return button;
@@ -663,6 +664,7 @@ public sealed class HomePage : Page
BorderThickness = new Thickness(1),
Content = grid
};
ModernUi.ApplyHoverResources(button, ModernUi.HoverSurface, ModernUi.StrokeStrong, ModernUi.Stroke, ModernUi.StrokeStrong);
ToolTipService.SetToolTip(button, $"{ToolText.Name(module)}\n{ToolText.Description(module)}");
button.Click += (_, _) => _openTool(module);
return button;
@@ -707,6 +709,7 @@ public sealed class HomePage : Page
BorderThickness = new Thickness(1),
Content = grid
};
ModernUi.ApplyHoverResources(button, ModernUi.HoverSurface, ModernUi.StrokeStrong, ModernUi.Stroke, ModernUi.StrokeStrong);
ToolTipService.SetToolTip(button, label);
button.Click += (_, _) => _openToolbox(label);
return button;
+36 -19
View File
@@ -293,13 +293,14 @@ public sealed class SettingsPage : Page
},
new StackPanel
{
Spacing = 10,
Spacing = 8,
Children =
{
_cpuStrip,
_memoryStrip,
BuildControlStatusLine("\uE950", AppLocalizer.T("处理器", "Processor"), _controlProcessorStatus),
BuildControlStatusLine("\uE823", AppLocalizer.T("运行", "Uptime"), _controlUptimeStatus)
new Border { Height = 1, Background = ModernUi.Stroke, Margin = new Thickness(0, 2, 0, 2) },
BuildControlStatusLine("", AppLocalizer.T("处理器", "Processor"), _controlProcessorStatus),
BuildControlStatusLine("", AppLocalizer.T("运行", "Uptime"), _controlUptimeStatus)
}
}
}
@@ -535,12 +536,13 @@ public sealed class SettingsPage : Page
MinHeight = 38,
Padding = new Thickness(7, 4, 7, 4),
CornerRadius = new CornerRadius(8),
Background = ModernUi.Brush("#00FFFFFF"),
BorderBrush = ModernUi.Brush("#00FFFFFF"),
Background = ModernUi.Transparent,
BorderBrush = ModernUi.Transparent,
BorderThickness = new Thickness(1),
Content = grid,
Tag = key
};
ModernUi.ApplyHoverResources(button, ModernUi.SurfaceAlt, ModernUi.Stroke, ModernUi.HoverSurface, ModernUi.StrokeStrong);
button.Click += (_, _) => ShowSettingsPage(key);
AutomationProperties.SetName(button, title);
return button;
@@ -563,8 +565,15 @@ public sealed class SettingsPage : Page
foreach (var pair in _settingsPageButtons)
{
var selected = string.Equals(pair.Key, _activeSettingsPage, StringComparison.OrdinalIgnoreCase);
pair.Value.Background = selected ? ModernUi.AccentSoft : ModernUi.Brush("#00FFFFFF");
pair.Value.BorderBrush = selected ? ModernUi.Accent : ModernUi.Brush("#00FFFFFF");
pair.Value.Background = selected ? ModernUi.AccentSoft : ModernUi.Transparent;
pair.Value.BorderBrush = selected ? ModernUi.Accent : ModernUi.Transparent;
// Keep hover resources in sync with selected state
ModernUi.ApplyHoverResources(
pair.Value,
selected ? ModernUi.HoverAccentSoft : ModernUi.SurfaceAlt,
selected ? ModernUi.Accent : ModernUi.Stroke,
selected ? ModernUi.HoverAccentSoft : ModernUi.HoverSurface,
selected ? ModernUi.Accent : ModernUi.StrokeStrong);
if (pair.Value.Content is Grid grid && grid.Children.FirstOrDefault() is Border iconTile)
{
iconTile.Background = selected ? ModernUi.AccentSoft : ModernUi.SurfaceAlt;
@@ -902,19 +911,27 @@ public sealed class SettingsPage : Page
private UIElement BuildActionRow(string glyph, string title, UIElement value, Func<Task> action)
{
var row = BaseRow(glyph, title, value);
var chevron = new FontIcon { Glyph = "", FontSize = 14, Foreground = ModernUi.TextSecondary, VerticalAlignment = VerticalAlignment.Center };
row.Children.Add(chevron);
Grid.SetColumn(chevron, 2);
var tooltip = AppLocalizer.T($"打开 {title}", $"Open {title}");
var button = ModernUi.IconButton("\uE974", tooltip, async () => await action());
button.Width = 42;
button.Height = 38;
button.MinWidth = 42;
button.MinHeight = 38;
button.VerticalAlignment = VerticalAlignment.Center;
button.HorizontalAlignment = HorizontalAlignment.Right;
ToolTipService.SetToolTip(button, tooltip);
AutomationProperties.SetName(button, tooltip);
row.Children.Add(button);
Grid.SetColumn(button, 2);
return row;
var rowButton = new Button
{
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Stretch,
Padding = new Thickness(0),
CornerRadius = new CornerRadius(6),
Background = ModernUi.Transparent,
BorderBrush = ModernUi.Transparent,
BorderThickness = new Thickness(0),
Content = row
};
ModernUi.ApplyHoverResources(rowButton, ModernUi.SurfaceAlt, ModernUi.Transparent, ModernUi.HoverSurface, ModernUi.Transparent);
rowButton.Click += async (_, _) => await action();
ToolTipService.SetToolTip(rowButton, tooltip);
AutomationProperties.SetName(rowButton, tooltip);
return rowButton;
}
private UIElement BuildToggleRow(string glyph, string title, string subtitle, ToggleSwitch toggle)
+39 -8
View File
@@ -342,13 +342,13 @@ public sealed class ToolboxPage : Page
var grid = new Grid { ColumnSpacing = 20, RowSpacing = 12 };
grid.ColumnDefinitions.Add(new ColumnDefinition());
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.72, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.85, GridUnitType.Star) });
grid.Children.Add(title);
Grid.SetColumn(right, 1);
grid.Children.Add(right);
grid.SizeChanged += (_, e) =>
{
var narrow = e.NewSize.Width < 940;
var narrow = e.NewSize.Width < 860;
grid.ColumnDefinitions.Clear();
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Add(new ColumnDefinition());
@@ -361,7 +361,7 @@ public sealed class ToolboxPage : Page
}
else
{
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.72, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.85, GridUnitType.Star) });
Grid.SetColumn(right, 1);
Grid.SetRow(right, 0);
}
@@ -708,6 +708,12 @@ public sealed class ToolboxPage : Page
count,
ToolText.CategoryGlyph(category))
};
ModernUi.ApplyHoverResources(
button,
selected ? ModernUi.HoverAccentSoft : ModernUi.HoverSurface,
selected ? ModernUi.Accent : ModernUi.StrokeStrong,
selected ? ModernUi.HoverAccentSoft : ModernUi.SurfaceAlt,
selected ? ModernUi.Accent : ModernUi.StrokeStrong);
button.Click += (_, _) =>
{
@@ -780,6 +786,12 @@ public sealed class ToolboxPage : Page
}
}
};
ModernUi.ApplyHoverResources(
button,
selected ? ModernUi.HoverAccentSoft : ModernUi.HoverSurface,
selected ? ModernUi.Accent : ModernUi.StrokeStrong,
selected ? ModernUi.HoverAccentSoft : ModernUi.SurfaceAlt,
selected ? ModernUi.Accent : ModernUi.StrokeStrong);
button.Click += async (_, _) =>
{
_selectedScope = scope;
@@ -942,11 +954,13 @@ public sealed class ToolboxPage : Page
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch,
CornerRadius = new CornerRadius(8),
Background = ModernUi.Brush("#00FFFFFF"),
BorderBrush = ModernUi.Brush("#00FFFFFF"),
Background = ModernUi.Transparent,
BorderBrush = ModernUi.Transparent,
BorderThickness = new Thickness(0),
ContextFlyout = BuildToolFlyout(module, favorite)
};
// Suppress the built-in template hover overlay on the transparent inner button
ModernUi.ApplyHoverResources(button, ModernUi.Transparent, ModernUi.Transparent, ModernUi.Transparent, ModernUi.Transparent);
grid.IsHitTestVisible = false;
ToolTipService.SetToolTip(button, $"{viewModel.Name}\n{viewModel.Description}");
AutomationProperties.SetName(button, viewModel.Name);
@@ -958,14 +972,20 @@ public sealed class ToolboxPage : Page
favoriteButton.VerticalAlignment = VerticalAlignment.Top;
favoriteButton.Margin = new Thickness(0, 12, 12, 0);
Canvas.SetZIndex(favoriteButton, 2);
return new Border
var normalBg = ModernUi.Surface;
var normalBorder = favorite ? ModernUi.Bronze : IsRisky(module) ? ModernUi.Danger : ModernUi.Stroke;
var hoverBg = ModernUi.HoverSurface;
var hoverBorder = favorite ? ModernUi.Bronze : IsRisky(module) ? ModernUi.Danger : ModernUi.StrokeStrong;
var outerBorder = new Border
{
Width = buttonWidth,
Height = compact ? 170 : 236,
Margin = new Thickness(7),
CornerRadius = new CornerRadius(8),
Background = ModernUi.Surface,
BorderBrush = favorite ? ModernUi.Bronze : IsRisky(module) ? ModernUi.Danger : ModernUi.Stroke,
Background = normalBg,
BorderBrush = normalBorder,
BorderThickness = new Thickness(1),
Child = new Grid
{
@@ -973,6 +993,17 @@ public sealed class ToolboxPage : Page
},
ContextFlyout = BuildToolFlyout(module, favorite)
};
outerBorder.PointerEntered += (_, _) =>
{
outerBorder.Background = hoverBg;
outerBorder.BorderBrush = hoverBorder;
};
outerBorder.PointerExited += (_, _) =>
{
outerBorder.Background = normalBg;
outerBorder.BorderBrush = normalBorder;
};
return outerBorder;
}
private MenuFlyout BuildToolFlyout(IToolModule module, bool favorite)
@@ -206,6 +206,12 @@ public abstract partial class AdaptiveToolPage
private Border BuildCompactGalleryInputCard(IToolModule module)
{
// Auto-load tools with no user input: collapse to a minimal reload card
if (_spec.AutoRunOnOpen && _spec.PrimaryInput is ToolPrimaryInputKind.None)
{
return BuildAutoLoadInputCard(module);
}
var panel = new StackPanel { Spacing = _spec.PageDensity == ToolPageDensity.Compact ? 10 : 14 };
panel.Children.Add(BuildGallerySectionHeader(
AppLocalizer.T("输入", "Input"),
@@ -660,4 +666,59 @@ public abstract partial class AdaptiveToolPage
_ => AppLocalizer.T("文本", "Text")
};
}
/// <summary>
/// Minimal input card for auto-load tools that have no user-supplied input.
/// Shows only the tool description, a reload button, and a cancel button.
/// </summary>
private Border BuildAutoLoadInputCard(IToolModule module)
{
var reloadButton = ModernUi.PillButton(
AppLocalizer.T("重新加载", "Reload"),
"",
async () => await RunCurrentToolAsync(),
primary: true);
var cancelButton = ModernUi.PillButton(
AppLocalizer.T("取消", "Cancel"),
"",
() => CancelRunningTool());
var actions = new StackPanel
{
Orientation = Orientation.Horizontal,
Spacing = 10,
Children = { reloadButton, cancelButton }
};
var grid = new Grid { ColumnSpacing = 12 };
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(module.Metadata.IconGlyph, 38, ModernUi.AccentSoft, ModernUi.Accent, 17));
var text = new StackPanel
{
Spacing = 2,
VerticalAlignment = VerticalAlignment.Center,
Children =
{
ModernUi.Text(AppLocalizer.T("数据源", "Data source"), 11, FontWeights.SemiBold, ModernUi.Accent, maxLines: 1),
ModernUi.Text(AppLocalizer.T("自动加载", "Auto load"), 14, FontWeights.SemiBold, maxLines: 1),
ModernUi.Text(CompactInputDescription(module), 12, foreground: ModernUi.TextSecondary, maxLines: 2)
}
};
Grid.SetColumn(text, 1);
grid.Children.Add(text);
Grid.SetColumn(_progressRing, 2);
grid.Children.Add(_progressRing);
return ModernUi.Card(new StackPanel
{
Spacing = 12,
Children = { grid, actions }
}, new Thickness(14), radius: 8);
}
}
@@ -68,6 +68,18 @@ public abstract partial class AdaptiveToolPage
return true;
}
// Ranked list / news / hotboard tools get a dedicated renderer before generic JSON
var rankedListToolIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"movie_box_office", "baidu_hot", "bili_hot", "zhihu_hot", "weibo_hot",
"cctv_news", "tech_news", "football_news", "history_today",
"earthquake_info", "gold_price", "oil_price", "hotboard"
};
if (rankedListToolIds.Contains(module.Id) || _spec.Result == ToolResultKind.RankedList || _spec.Result == ToolResultKind.NewsCards)
{
if (TryRenderRankedList(module, output, lines)) return true;
}
if (TryRenderJson(output, module))
{
return true;
@@ -637,4 +649,194 @@ public abstract partial class AdaptiveToolPage
_ => "结果摘要"
};
}
/// <summary>
/// Renders ranked/hotboard/news results with a dedicated rank-badge card layout.
/// Handles both JSON array output and numbered-text-line output (e.g. "1. Title · meta").
/// </summary>
private bool TryRenderRankedList(IToolModule module, string output, IReadOnlyList<string> lines)
{
// Try JSON array first
var trimmed = output.TrimStart();
if (trimmed.StartsWith('['))
{
try
{
using var document = JsonDocument.Parse(output);
if (document.RootElement.ValueKind != JsonValueKind.Array)
{
return false;
}
var items = document.RootElement.EnumerateArray().Take(50).ToList();
if (items.Count == 0)
{
return false;
}
_resultCards.Children.Add(BuildInfoGrid([
(AppLocalizer.T("条目数", "Items"), items.Count.ToString()),
(AppLocalizer.T("类型", "Kind"), AppLocalizer.T("榜单 / 资讯", "Ranked list"))
]));
for (var index = 0; index < items.Count; index++)
{
var item = items[index];
var rank = index + 1;
var title = FirstJsonString(item, "title", "name", "text", "subject", "headline") ?? $"#{rank}";
var meta = FirstJsonString(item, "score", "hot", "value", "heat", "count", "view", "desc", "description", "meta", "category", "type");
var url = FirstJsonString(item, "url", "link", "href");
// Some APIs return an explicit rank field
if (item.TryGetProperty("rank", out var rankElem) && rankElem.TryGetInt32(out var explicitRank))
{
rank = explicitRank;
}
_resultCards.Children.Add(BuildRankedItemCard(rank, title, meta, url));
}
return true;
}
catch
{
// Fall through to text parsing
}
}
// Try numbered text lines: "1. Title · meta", "1、Title", "#1 Title"
var rankedLines = lines
.Select(ParseRankedLine)
.Where(item => item.HasValue)
.Select(item => item!.Value)
.Take(50)
.ToList();
if (rankedLines.Count < 2)
{
return false;
}
_resultCards.Children.Add(BuildInfoGrid([
(AppLocalizer.T("条目数", "Items"), rankedLines.Count.ToString()),
(AppLocalizer.T("来源", "Source"), AppLocalizer.T("实时榜单", "Live ranking"))
]));
foreach (var item in rankedLines)
{
_resultCards.Children.Add(BuildRankedItemCard(item.Rank, item.Title, item.Meta, null));
}
return true;
}
private static (int Rank, string Title, string? Meta)? ParseRankedLine(string line)
{
if (string.IsNullOrWhiteSpace(line)) return null;
// Formats: "1. Title · meta", "1. Title", "#1 Title", "1、Title"
var match = Regex.Match(line.Trim(), @"^(?:#?(\d+)[.、\s]+|(\d+)\.\s*)(.+)$");
if (!match.Success) return null;
var rankStr = match.Groups[1].Value.Length > 0 ? match.Groups[1].Value : match.Groups[2].Value;
if (!int.TryParse(rankStr, out var rank)) return null;
var rest = match.Groups[3].Value.Trim();
string? meta = null;
string title = rest;
// Split "Title · meta" or "Title - meta" or "Title | meta"
var sep = rest.IndexOfAny([' ']);
var dotSep = rest.IndexOf(" · ", StringComparison.Ordinal);
var dashSep = rest.IndexOf(" - ", StringComparison.Ordinal);
var pipeSep = rest.IndexOf(" | ", StringComparison.Ordinal);
var tabSep = rest.IndexOf('\t');
var splitAt = new[] { dotSep, dashSep, pipeSep, tabSep }.Where(pos => pos > 0).OrderBy(pos => pos).FirstOrDefault(-1);
if (splitAt > 0)
{
title = rest[..splitAt].Trim();
meta = rest[(splitAt + 1)..].TrimStart(' ', '-', '·', '|', '\t').Trim();
}
return string.IsNullOrWhiteSpace(title) ? null : (rank, title, string.IsNullOrWhiteSpace(meta) ? null : meta);
}
private static string? FirstJsonString(JsonElement element, params string[] keys)
{
foreach (var key in keys)
{
if (element.TryGetProperty(key, out var prop) && prop.ValueKind == JsonValueKind.String)
{
var value = prop.GetString();
if (!string.IsNullOrWhiteSpace(value)) return value;
}
// Also try camelCase variants with number suffixes
if (element.TryGetProperty(key.ToLowerInvariant(), out var lowerProp) && lowerProp.ValueKind == JsonValueKind.String)
{
var value = lowerProp.GetString();
if (!string.IsNullOrWhiteSpace(value)) return value;
}
}
return null;
}
private static Border BuildRankedItemCard(int rank, string title, string? meta, string? url)
{
// Rank badge color: gold/silver/bronze for top 3, default for rest
var rankFg = rank switch
{
1 => ModernUi.Brush("#CA5010"), // gold-ish / bronze
2 => ModernUi.TextSecondary,
3 => ModernUi.TextSecondary,
_ => ModernUi.TextSecondary
};
var rankBg = rank switch
{
1 => ModernUi.AccentSoft,
2 => ModernUi.SurfaceAlt,
3 => ModernUi.SurfaceAlt,
_ => ModernUi.SurfaceAlt
};
var rankBadge = ModernUi.Badge(rank.ToString(), rank == 1 ? ModernUi.Accent : rankFg, rankBg);
rankBadge.MinWidth = 32;
rankBadge.HorizontalAlignment = HorizontalAlignment.Center;
var text = new StackPanel
{
Spacing = 2,
VerticalAlignment = VerticalAlignment.Center,
Children =
{
ModernUi.Text(ModernUi.Ellipsize(title, 100), 14, FontWeights.SemiBold, maxLines: 1),
}
};
if (!string.IsNullOrWhiteSpace(meta))
{
text.Children.Add(ModernUi.Text(ModernUi.Ellipsize(meta, 80), 12, foreground: ModernUi.TextSecondary, maxLines: 1));
}
var row = new Grid { ColumnSpacing = 12, MinHeight = 40 };
row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(36) });
row.ColumnDefinitions.Add(new ColumnDefinition());
row.Children.Add(rankBadge);
Grid.SetColumn(text, 1);
row.Children.Add(text);
if (!string.IsNullOrWhiteSpace(url))
{
row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
var linkButton = ModernUi.IconButton("", AppLocalizer.T("打开链接", "Open link"),
async () => await Windows.System.Launcher.LaunchUriAsync(new Uri(url!)));
Grid.SetColumn(linkButton, 2);
row.Children.Add(linkButton);
}
return ModernUi.Card(row, new Thickness(10, 8, 10, 8), radius: 8, background: ModernUi.SurfaceAlt);
}
}
@@ -946,8 +946,8 @@ public abstract partial class AdaptiveToolPage : ToolPageBase
var wrap = new VariableSizedWrapGrid
{
Orientation = Orientation.Horizontal,
ItemWidth = 190,
ItemHeight = 74
ItemWidth = 200,
ItemHeight = 60
};
wrap.Children.Add(_numberBox);
if (_secondaryNumberBox.Visibility == Visibility.Visible)