fix(app-iced): M90 r2 —— 找回窗口装饰 + 按需节拍 + 数据根一致 + 假绿守卫
复审 P1-1(产品页丢窗口装饰):shell 加内容槽位 view_with_content(ui, nav, title, content, forced),
app/view.rs 把 pages::page(...) 塞进真外壳;shell::view 保留给 Route::Agent 回落(删了会让私有
view/nav::rail 变死代码,-D warnings 红)。content() 拆到 shell/fallback.rs 让 mod.rs 回到 365 行。
1180 浅/深新图右上角三个 caption 键实测:ink 中心 y=23.5(栏高 48 的一半)、x 中心 1064.5/1110.5/
1156.5 = 46px 分档的 1065/1111/1157(±0.5px),close 命中区右缘 = 1180 窗角。
偏离 #7 改写成「交互面缺失」并注明本轮已修。
P1-2(harness.rs 402 行):tests 外置到 pages/harness/tests.rs(343 + 64 行);
components/gate.rs 的行数守门从走查 components/ 扩到 src/**(新测试当场抓出 app/tests.rs 416 行,
已按同样办法拆成 app/tests.rs + app/tests/presenter.rs)。
P2-1(常驻重绘 + 每拍重建模型):subscription 只在「截图 / 有 Busy-Running / 有 toast / 3 s 派发
观察窗」时订阅 window::frames();refresh_view 加 view_dirty 门。实测交互模式 9 s:只有 2 行 tick
trace(n=30/60,观察窗内),之后 6.5 s 一条都没有;refreshes 全程停在 7。
P2-2(两个数据根):--data-dir 时 SettingsBridge::with_path(config_path(root)),与后端同根。
P3-2(假绿):新增 RecorderBackend(dispatch 只记账、drain_events 恒空)+ launching_only_dispatches
_and_never_echoes_locally;植入本地回显后该测试实测转红(status 变 Downloading · 0%、can_stop 翻
true),撤回后转绿。
P3-3:尺寸字面量守门目录加 app;P3-1:--force-state 经 pages::Model::forced 作用到产品页控件
(hover 图里页头/列表/动作行/rail/caption 全部可见状态层);视觉 P2-2:加载器名统一走 badge()。
gallery 回归改口径:r2 与「主线 + r1」的 gallery 逐字节相同(cmp IDENTICAL);与 r1 记的
c825e1a 基线不同,差异来自主线 M86/M88 的令牌收口,不是本 mission。
This commit is contained in:
1 parent
b484998fc9
commit
022778a5a7
17 files changed
+980
-496
No files matched your search
@@ -15,6 +15,7 @@
|
||||
//! 一屏只出现 4 档字号(22 / 16 / 14 / 12),且**没有实心 accent**(rubric K2:一屏最多一个实心
|
||||
//! Primary,骨架期一个都不要)。阴影只给浮层,骨架里没有浮层,所以**全屏零阴影**(rubric K3)。
|
||||
|
||||
mod fallback;
|
||||
mod nav;
|
||||
|
||||
pub use nav::NavItem;
|
||||
@@ -25,7 +26,6 @@ use iced::{mouse, Alignment, Background, Border, Color, Element, Length, Padding
|
||||
use iced::{window::Direction, Theme as IcedTheme};
|
||||
|
||||
use unlucky_design::color::{Role, StateLayer};
|
||||
use unlucky_design::component_map::Component;
|
||||
use unlucky_design::elevation::MaterialLayer;
|
||||
use unlucky_design::icons::IconKind;
|
||||
use unlucky_design::spacing::Space;
|
||||
@@ -36,7 +36,44 @@ use crate::cli::ForcedState;
|
||||
use crate::icons;
|
||||
use crate::theme::Ui;
|
||||
|
||||
/// 整棵树。
|
||||
/// 整棵树(**内容槽位版**:外壳画标题栏 / 缩放热区 / 导航轨,中间那块交给调用方)。
|
||||
///
|
||||
/// 这是产品页面走的入口(M90 r2 / P1-1)。为什么要它:`decorations: false` 的无边框窗口里,
|
||||
/// 拖动 / 缩放 / 最小化 / 关闭这四条消息的构造点**只有本模块**,页面树自己画的「标题栏留白带」
|
||||
/// 没有任何控件,于是产品页会变成「拖不动、缩不了、点不到关闭」。
|
||||
///
|
||||
/// 分工写死:
|
||||
///
|
||||
/// - 本模块负责:标题栏(含 8px 级拖动热区与三个 caption 键)、东/南两条缩放热区、导航轨、
|
||||
/// 根容器底色;
|
||||
/// - 调用方负责:`content`(页面内容区)。它拿到的可用宽度是 `窗口宽 − rail 88`,
|
||||
/// 而页面自己的 `layout::frame_metrics` 仍以**窗口宽**为输入(它内部会再减 rail 与页边距),
|
||||
/// 两边的账不会算串。
|
||||
///
|
||||
/// [`view`] 是它的兼容包装:`content` 换成 M68 的「页头 + 一张卡」。
|
||||
pub fn view_with_content<'a>(
|
||||
ui: &'a Ui,
|
||||
selected: NavItem,
|
||||
app_title: &'a str,
|
||||
content: Element<'a, Message>,
|
||||
forced: Option<ForcedState>,
|
||||
) -> Element<'a, Message> {
|
||||
let work_area = column![
|
||||
titlebar(ui, app_title, forced),
|
||||
row![nav::rail(ui, selected, forced), content]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
window_frame(ui, work_area.into())
|
||||
}
|
||||
|
||||
/// 整棵树(M68 骨架版:内容区是「页头 + 一张占位卡」)。
|
||||
///
|
||||
/// **保留给 `Route::Agent` 回落**(AI 独立页 P1 才落地)。删掉它会连带
|
||||
/// `content` / `label` / `nav::rail` 变成死代码,而 `mod shell` 是私有的 ⇒ `-D warnings` 直接红。
|
||||
pub fn view<'a>(
|
||||
ui: &'a Ui,
|
||||
selected: NavItem,
|
||||
@@ -45,18 +82,17 @@ pub fn view<'a>(
|
||||
body: &'a str,
|
||||
forced: Option<ForcedState>,
|
||||
) -> Element<'a, Message> {
|
||||
let work_area = column![
|
||||
titlebar(ui, app_title, forced),
|
||||
row![
|
||||
nav::rail(ui, selected, forced),
|
||||
content(ui, page_title, body)
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
view_with_content(
|
||||
ui,
|
||||
selected,
|
||||
app_title,
|
||||
fallback::content(ui, page_title, body),
|
||||
forced,
|
||||
)
|
||||
}
|
||||
|
||||
/// 无边框窗口的「外壳壳子」:工作区 + 两条缩放热区 + 根容器底色。
|
||||
fn window_frame<'a>(ui: &'a Ui, work_area: Element<'a, Message>) -> Element<'a, Message> {
|
||||
// 无边框窗口没有系统缩放边框 → 自己给两条热区(东/南)。
|
||||
// **必须叠层而不是兄弟列**:当兄弟列会吃掉 4px 布局宽(右留白变 28、caption 按钮离窗角 4px,
|
||||
// 视觉复审 P3-1)。叠层容器自己铺满,再用 align 把 4px 热区贴到边上,布局宽保持 1180。
|
||||
@@ -211,59 +247,6 @@ fn caption<'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 内容区
|
||||
|
||||
fn content<'a>(ui: &'a Ui, title: &'a str, body: &'a str) -> Element<'a, Message> {
|
||||
let metrics = ui.metrics();
|
||||
// 卡片档位由组件表决定(圆角/字级),组件层不自己挑
|
||||
let card_spec = Component::OutlinedCard.spec();
|
||||
let body_role = card_spec.support.unwrap_or(card_spec.text);
|
||||
|
||||
let header = container(label(
|
||||
ui,
|
||||
TypeRole::TitleLarge,
|
||||
true,
|
||||
title,
|
||||
ui.color(Role::OnSurface),
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.height(metrics.page_header_height)
|
||||
.align_y(Alignment::Center)
|
||||
.padding(Padding::from([0.0, ui.space(Space::X6)]));
|
||||
|
||||
let card = container(
|
||||
column![
|
||||
label(
|
||||
ui,
|
||||
card_spec.text,
|
||||
true,
|
||||
"Shell ready",
|
||||
ui.color(Role::OnSurface),
|
||||
),
|
||||
label(ui, body_role, false, body, ui.color(Role::OnSurfaceVariant)),
|
||||
]
|
||||
.spacing(ui.space(Space::X2)),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.padding(metrics.card_padding)
|
||||
.style(move |_theme| container::Style {
|
||||
background: Some(Background::Color(ui.layer(MaterialLayer::Card))),
|
||||
border: ui.outlined(card_spec.shape),
|
||||
..container::Style::default()
|
||||
});
|
||||
|
||||
let body_area = container(card)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(ui.space(Space::X6))
|
||||
.align_y(Alignment::Start);
|
||||
|
||||
column![header, body_area]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 小工具
|
||||
|
||||
/// 一个文字元素:字号/行高/字重全部来自令牌排版档。
|
||||
@@ -308,6 +291,7 @@ pub(super) fn filled(color: Color) -> container::Style {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use unlucky_design::color::SchemeFlavor;
|
||||
use unlucky_design::component_map::Component;
|
||||
use unlucky_design::typography::Platform;
|
||||
|
||||
use crate::theme::Families;
|
||||
|
||||
Reference in new issue
Block a user