页面结构
星云桌面端的界面是 WinUI 3 写的。这一页讲页面怎么组织、怎么导航、怎么刷新。
导航是插件驱动的
侧栏不是写死的列表。启动器启动时读内核的贡献点注册表,把 NavItem 类型的贡献渲染成导航项。
// MainWindow.BuildNavFromContributions()
foreach (var c in Kernel.Contributions.Of(ContributionKind.NavItem))
{
if (c.Target == "separator") { /* 插分隔线 */ }
else if (c.Target == "footer") { /* 进底部区 */ }
else { /* 进主区 */ }
}这条设计是插件系统能长出界面的前提——插件往注册表里登记一个导航项,外壳下次重建时它就出现了。插件页也不占侧栏的固定位置(数量有限,排在插件组里)。
细节见窗口与导航。
页面清单
一级页面
| 页面 | 干什么 |
|---|---|
| 首页 | 版本列表 + 账户/启动卡。点启动后盖住两列内容 |
| 版本 | 已安装版本管理,搜索、分类过滤、右键菜单 |
| 下载 | 安装原版与社区资源 |
| 存档 | 跨版本列出所有世界,备份、删除、跨版本启动 |
| 账户 | Starcloud ID 卡片 + 账户列表 |
| 实验室 | 投影工坊、彩色文字 |
| 好友 | 两栏:会话列表 + 聊天 |
| 插件 | 安装、启停、卸载 |
| 设置 | 外观、路径、内存、Java、并发、下载源、跨设备互联、语言 |
| 运行状态 | 这一局在干什么:CPU/内存/GPU 曲线、启动配置、日志尾 |
| 联机 | 建房、进房、状态 |
联机页不进侧栏,它是好友页里的一个分区。
二级页面
二级页不占侧栏选中项,用 drill-in 转场推进。
| 页面 | 从哪进 |
|---|---|
| 版本详情 | 版本列表点条目 |
| 添加账户 | 账户页右上角 |
| 账户详情 | 账户列表点条目 |
| 插件面板页 | 插件页点插件的「面板」 |
| 投影管理 | 实验室里 |
三种导航方式
// 二级页:drill-in 转场,不改侧栏选中项
ContentFrame.Navigate(typeof(VersionDetailPage), versionId,
MotionService.TransitionFor(enabled, drillIn: true));
// 侧栏页:由贡献点路由
var pageType = ResolvePageType(tag);
ContentFrame.Navigate(pageType);
// 返回上一级
ContentFrame.Navigate(typeof(HomePage), null,
MotionService.BackTransition(enabled));TransitionFor 在动效关闭时返回 SuppressNavigationTransitionInfo——不是"不转场"。不转场在 WinUI 3 里会闪。
启动覆盖层
点启动后,首页两列内容被一层盖住,显示启动进度。
进度是「氛围进度」——每 250ms 爬 0.5%,到顶就停,不承诺精确。这是刻意的:真实的准备步骤(Java 探测、预检补全)没有可量化的百分比,给一个会动的数字比给一个卡在 0% 的进度条体验好。
// HomePage 的三只定时器
_logTimer.Interval = TimeSpan.FromMilliseconds(100); // 实时日志尾
_stageTimer.Interval = TimeSpan.FromMilliseconds(250); // 氛围进度爬升
_windowTimer.Interval= TimeSpan.FromMilliseconds(500); // 轮询游戏窗口是否出现运行状态页的三层刷新
这个页面的刷新刻意分三层,不对齐频率:
采样回调(1s,后台线程)→ 只更新数字、曲线、日志尾
(选中实例与实例集合都没变就不重建列表)
实例变化(起 / 止 / 下拉换选中)→ 整页重建
1s UI 定时器 → 只刷「已运行 00:42:17」如果全按 1s 整页重建,列表会一直闪;如果只靠采样回调,已运行时长在采样暂停时就不走了。
配套的生命周期处理:
// RunningStatusPage
NavigationCacheMode = NavigationCacheMode.Disabled;每次进入都是新实例,Loaded 订阅 / Unloaded 退订,避免事件订阅泄漏。
对话框
所有弹窗走 ThemedDialog 基类。
为什么需要这个基类:WinUI 3 的弹层(PopupRoot)不继承 Window.Content 上的 RequestedTheme,浅色主题下弹窗会跟随系统变成深色。所以显示前要把 RequestedTheme 显式钉到 xamlRoot 根元素的 ActualTheme。
同一时刻只允许一个 ContentDialog。所以下载进度、修复进度都画在弹窗内部,不嵌套二级对话框。涉及多阶段的弹窗(崩溃分析、自更新)用阶段机实现:取消当前阶段、显示新内容、再取消。
// 阶段机:args.Cancel + GetDeferral
private void OnClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
{
if (_stage < _stages.Count)
{
args.Cancel = true; // 拦住关闭
var deferral = args.GetDeferral(); // 异步换内容
_stage++;
ShowStage(_stages[_stage]);
deferral.Complete();
}
}