fix(app-iced): M75 复审 P1-A/P1-B/P2 —— 内容色跟态走 + 断言收紧 + 矩阵排版
P1-A 禁用内容色没下发(视觉 reviewer:text/standard 的 enabled 与 disabled 逐像素相同): assemble 里内容色恒取 ui.color(content) 再被 text().color() 钉死,Style.text_color 的 38% 永远下发不出去。新增 assemble::content_color(ui, forced, content),文字按钮与 IconButton 共用;probe 补两条像素断言(禁用 38% 墨迹必须存在、禁用容器内不得有满档 OnPrimary)。 P1-B 渲染层断言抓不住 2px 级回归(reviewer 变异验证:高度 42 仍绿): 高度改成精确 == 40;补「带图标左内边距 16(墨迹 ≈21)」断言;分隔线从「行数」改成 「墨量覆盖率 ≈1.0」(1.5dp 会到 ≈1.5)。 P2-1 probe 给了图却读不到时硬失败(只有完全没给 ULL_GALLERY_SHOT 才跳过)。 P2-2 Geometry::stroke 真正接到 Border.width;IconGeometry::icon 改由 side−2×padding 推导。 P2-3 矩阵 cell 宽 192→160(总宽 1040<1180,disabled 列不再被裁);禁用格保留前后图标。 P2-4 补注:snap 仅 wgpu 实现,CPU 验收图不受它影响。
This commit is contained in:
1 parent
8072f1cc7e
commit
e905fa3922
8 files changed
+195
-68
No files matched your search
@@ -13,7 +13,7 @@ use unlucky_design::shape::ShapeScale;
|
||||
use unlucky_design::typography::TypeRole;
|
||||
|
||||
use crate::icons;
|
||||
use crate::theme::{Ui, STROKE_PX};
|
||||
use crate::theme::Ui;
|
||||
|
||||
use super::super::interaction::{self, Interaction};
|
||||
use super::super::layout;
|
||||
@@ -30,6 +30,8 @@ pub(super) struct StyleSpec {
|
||||
pub border: Option<Role>,
|
||||
/// 圆角档位(来自组件表,五式统一 `Full`)。
|
||||
pub shape: ShapeScale,
|
||||
/// 描边宽度(`Geometry::stroke`:有描边的变体 = `stroke::HAIRLINE`,其余 0)。
|
||||
pub stroke: f32,
|
||||
/// 圆角折算用的边长(药丸 = 可见高)。
|
||||
pub radius_side: f32,
|
||||
/// 强制态(gallery 取证);`None` = 跟随运行期的 `button::Status`。
|
||||
@@ -48,6 +50,11 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
) -> Element<'a, M> {
|
||||
let geometry = Geometry::of(ui, variant, spec.leading.is_some());
|
||||
let (fill, content, border) = variant.roles();
|
||||
let forced = spec.state.or(if spec.on_press.is_none() {
|
||||
Some(Interaction::Disabled)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
// 按钮家族没有浮层级高程:L1(Elevated)只用容器色调差表达(rubric §7 不画阴影)
|
||||
debug_assert!(
|
||||
@@ -57,7 +64,11 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
),
|
||||
"按钮家族不许有浮层高程"
|
||||
);
|
||||
let content_color = ui.color(content);
|
||||
// **内容色必须跟着态走**:禁用时文字/图标是 on-surface@38%,不是满档内容色。
|
||||
// 早先这里恒取 `ui.color(content)`,再被 `text(...).color(..)` 钉死,`Style.text_color`
|
||||
// 里的 38% 永远下发不出去 —— 表现为「text / standard icon 的 enabled 格与 disabled 格
|
||||
// 逐像素相同」(视觉 reviewer 实测 changed_px = 0;filled 禁用标签 1.23:1)。
|
||||
let ink = content_color(ui, forced, content);
|
||||
|
||||
let label = text(spec.label)
|
||||
.size(ui.style(TypeRole::LabelLarge).size_px)
|
||||
@@ -65,7 +76,7 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
ui.style(TypeRole::LabelLarge).line_height_px,
|
||||
)))
|
||||
.font(ui.font_of(ui.emphasized(TypeRole::LabelLarge)))
|
||||
.color(content_color);
|
||||
.color(ink);
|
||||
|
||||
let mut row = Row::new().align_y(Alignment::Center);
|
||||
if let Some(kind) = spec.leading {
|
||||
@@ -73,7 +84,7 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
row = row.push(layout::fixed(
|
||||
geometry.icon,
|
||||
geometry.icon,
|
||||
icons::view::<M>(kind, content_color),
|
||||
icons::view::<M>(kind, ink),
|
||||
));
|
||||
}
|
||||
row = row.push(label);
|
||||
@@ -81,7 +92,7 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
row = row.push(layout::fixed(
|
||||
geometry.icon,
|
||||
geometry.icon,
|
||||
icons::view::<M>(kind, content_color),
|
||||
icons::view::<M>(kind, ink),
|
||||
));
|
||||
}
|
||||
let row = row.spacing(geometry.gap);
|
||||
@@ -93,12 +104,6 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
.height(Length::Fill)
|
||||
.align_y(iced::Alignment::Center);
|
||||
|
||||
let forced = spec.state.or(if spec.on_press.is_none() {
|
||||
Some(Interaction::Disabled)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
let style = style_closure(
|
||||
ui,
|
||||
StyleSpec {
|
||||
@@ -106,6 +111,7 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
content,
|
||||
border,
|
||||
shape: variant.shape(),
|
||||
stroke: geometry.stroke,
|
||||
radius_side: geometry.height,
|
||||
forced,
|
||||
},
|
||||
@@ -122,6 +128,17 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
}
|
||||
}
|
||||
|
||||
/// 内容色(文字与图标共用):禁用态走令牌的 38%,其余态就是角色色。
|
||||
///
|
||||
/// 单独成函数是为了让「内容色跟态走」这件事只有一处实现——`assemble`(文字按钮)与 `icon`
|
||||
/// (图标按钮)都调它,两条路径不会再各自钉死满档色。
|
||||
pub(super) fn content_color(ui: &Ui, forced: Option<Interaction>, content: Role) -> iced::Color {
|
||||
match forced {
|
||||
Some(Interaction::Disabled) => interaction::disabled_colors(ui, Role::Surface).1,
|
||||
_ => ui.color(content),
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成 `button::style` 闭包:状态层 / 禁用 / 描边全在这里落地。
|
||||
pub(super) fn style_closure(
|
||||
ui: &Ui,
|
||||
@@ -166,15 +183,16 @@ pub(super) fn style_closure(
|
||||
} else {
|
||||
iced::Color::TRANSPARENT
|
||||
},
|
||||
width: if spec.border.is_some() {
|
||||
STROKE_PX
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
// 线宽来自 `Geometry::stroke`(有描边的变体 = HAIRLINE,其余 0);
|
||||
// `spec.border.is_none()` 时颜色是透明的,宽度也就无所谓了。
|
||||
width: spec.stroke,
|
||||
radius: ui.radius(spec.shape, spec.radius_side),
|
||||
},
|
||||
// rubric §7:L0/L1 不用阴影(Elevated 的 L1 由容器色调差表达)
|
||||
shadow: Shadow::default(),
|
||||
// ⚠️ `snap` 目前**只有 wgpu 后端实现**;我们的验收图走 tiny-skia(CPU),
|
||||
// 所以这个 `true` 在验收图上不起作用——分隔线的「1dp 摊成 2 行」正是这个原因,
|
||||
// 不是「iced 没有 snap」(reviewer P2-4 纠正的正是我上一版报告里的说法)。
|
||||
snap: true,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user