fix(iced/feedback): 逐卡宽度/动作色精确判据、贴底边距、Dialog 下限与 icon 走 view_at
复审 r3 的 A/G/D/E 与随之冒出来的 clippy 项: - **A(p1,假绿)**:`7.6 Dialog 动作色` 原来量的是 `DialogActions::None` 那张卡的 bbox,且判据 是「离 Primary 更近」——标题/正文字形都被算进去(旧值 1790px)。现在:①判据换**精确色**; ②加两条反向证据(无动作那张卡里精确 Primary 必须为 0;有动作那张卡的下半区必须有字形行); ③量的是全图的动作字形(实测 362px,与复审独立复算一致)。变异:把 350 个动作字形涂成 `OnSurfaceVariant` → `7.6 实测只命中 12 px` → 红。 - **G**:Dialog 宽度改**逐卡**量(列剖面,排掉 scrim 色,深色档不会再把遮罩算成 1180 宽的假卡)。 环的逐环断言**登记为 G18-i**(实测 6 个环只有 3~4 个能被精确色簇干净命中,按簇数断言会把 「相位换了」误报成「环丢了」,理由写进 `probe/circular.rs` 模块文档)。 - **D**:两个 bottom 宿主 72 → 96,并给窄宿主画 1px 取景框;probe 新增「条底距宿主底 = 12」 (实测 `底边距 [11, 12]`)。取景框的描边不参与布局,所以宿主宽不变、条宽仍是 344/300。 - **E**:`DialogGeometry::effective_width` = `min(480, 可用宽).max(280)`(尊重官方下限); gallery 加一格 `Dialog::width(360)`;probe 逐卡断言实测 `[360, 480, 480, 480, 480]`。 - **C(按 tower 修正版)**:空态图标改用 M86 的 `icons::view_at(.., 32)`(先 rebase 到 main)。 - 顺带:`action.rs` 的 24 图标也走 `view_at(24)`——`icons.rs` 的守门测试按「逐文件调用点计数」 要求不在白名单里的文件 `icons::view` 为 0,本文件按盒尺寸烘一份(输出逐像素相同)。 - clippy:去掉 `needless_range_loop` 与未用变量,`-D warnings` 已绿。
This commit is contained in:
1 parent
5d7426b6c9
commit
11d7b64a5e
5 files changed
+76
-16
No files matched your search
@@ -135,10 +135,12 @@ pub(super) fn icon_action<'a, M: Clone + 'a>(
|
|||||||
|
|
||||||
let ink = content_color(ui, content, container_role, disabled);
|
let ink = content_color(ui, content, container_role, disabled);
|
||||||
|
|
||||||
|
// 走 `view_at(24)` 而不是 `view(..)`:`icons.rs` 的守门测试按**逐文件调用点计数**
|
||||||
|
// 要求「不在白名单里的文件 `icons::view` 必须为 0」;本文件按盒尺寸显式烘一份(同值 24)。
|
||||||
let element = button(layout::centered_fixed(
|
let element = button(layout::centered_fixed(
|
||||||
glyph_side,
|
glyph_side,
|
||||||
glyph_side,
|
glyph_side,
|
||||||
icons::view::<M>(kind, ink),
|
icons::view_at::<M>(kind, ink, glyph_side),
|
||||||
))
|
))
|
||||||
.width(Length::Fixed(side))
|
.width(Length::Fixed(side))
|
||||||
.height(Length::Fixed(side))
|
.height(Length::Fixed(side))
|
||||||
|
|||||||
@@ -118,6 +118,10 @@ line!(SEG_TAIL_2, 900, 1750);
|
|||||||
///
|
///
|
||||||
/// 值来自:第二线 `[0.003, 0.076]`(tail 0.003 / head 0.079 → 合并到 0.076 区间的实测),
|
/// 值来自:第二线 `[0.003, 0.076]`(tail 0.003 / head 0.079 → 合并到 0.076 区间的实测),
|
||||||
/// 第一线 `[0.355, 0.860]`。
|
/// 第一线 `[0.355, 0.860]`。
|
||||||
|
///
|
||||||
|
/// `#[cfg(test)]`:只有单测与渲染层探针(都在 `cfg(test)` 里)读它,产品路径不需要——
|
||||||
|
/// 不留成产品常量是为了让 `-D warnings` 下的 `dead_code` 保持沉默。
|
||||||
|
#[cfg(test)]
|
||||||
pub const PHASE_055: [(f32, f32); 2] = [(0.003, 0.076), (0.355, 0.860)];
|
pub const PHASE_055: [(f32, f32); 2] = [(0.003, 0.076), (0.355, 0.860)];
|
||||||
|
|
||||||
/// 线性进度的几何(厚度与圆角;轨道宽由宿主给,所以没有 width)。
|
/// 线性进度的几何(厚度与圆角;轨道宽由宿主给,所以没有 width)。
|
||||||
|
|||||||
@@ -275,10 +275,22 @@ fn check_snackbar(shot: &Shot, ui: &Ui) {
|
|||||||
if !(near(width, 300) || near(width, 344)) {
|
if !(near(width, 300) || near(width, 344)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// 1px 描边常落在半像素上(两行各 ~50% 覆盖),精确色匹配抓不住;
|
||||||
|
// 用「更靠近框色而不是画布」判(这一带除了框没有别的灰线)。
|
||||||
|
let distance = |pixel: [u8; 3], target: [u8; 3]| -> u32 {
|
||||||
|
pixel
|
||||||
|
.iter()
|
||||||
|
.zip(target)
|
||||||
|
.map(|(a, b)| u32::from(a.abs_diff(b)))
|
||||||
|
.sum()
|
||||||
|
};
|
||||||
let frame_row = ((bottom + 1)..shot.height)
|
let frame_row = ((bottom + 1)..shot.height)
|
||||||
.find(|y| {
|
.find(|y| {
|
||||||
(0..shot.width)
|
(0..shot.width)
|
||||||
.filter(|x| matches(shot.at(*x, *y), frame))
|
.filter(|x| {
|
||||||
|
let pixel = shot.at(*x, *y);
|
||||||
|
distance(pixel, frame) < distance(pixel, rgb(surface))
|
||||||
|
})
|
||||||
.count()
|
.count()
|
||||||
>= 200
|
>= 200
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -44,37 +44,85 @@ pub(super) fn check_dialog(shot: &Shot, ui: &Ui) {
|
|||||||
|
|
||||||
// ---- 7.4 宽度:先分「卡带」(同排两张卡的 y 区间会重叠,所以只能按行分组),
|
// ---- 7.4 宽度:先分「卡带」(同排两张卡的 y 区间会重叠,所以只能按行分组),
|
||||||
// 再在每条带里按**列剖面**分出每一张卡(复审 A/G:早先只量最宽的那张)。
|
// 再在每条带里按**列剖面**分出每一张卡(复审 A/G:早先只量最宽的那张)。
|
||||||
let card_bands = bands(shot, card, surface, 200);
|
// 卡带用**精确色**数行(不用斜坡):深色档里正文/描边的抗锯齿像素都落在
|
||||||
|
// 「surface → card」那条很短的斜坡上,用斜坡会把整屏都算成卡带(实测出现 1180 宽的假卡)。
|
||||||
|
let card_rgb = rgb(card);
|
||||||
|
let scrim_rgb = rgb(scrim(ui));
|
||||||
|
let card_rows: Vec<usize> = (0..shot.height)
|
||||||
|
.filter(|y| {
|
||||||
|
(0..shot.width)
|
||||||
|
.filter(|x| {
|
||||||
|
let pixel = shot.at(*x, *y);
|
||||||
|
matches(pixel, card_rgb) && !matches(pixel, scrim_rgb)
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
>= 200
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut card_bands: Vec<(usize, usize)> = Vec::new();
|
||||||
|
let mut start: Option<usize> = None;
|
||||||
|
let mut previous: usize = 0;
|
||||||
|
for y in card_rows {
|
||||||
|
match start {
|
||||||
|
None => {
|
||||||
|
start = Some(y);
|
||||||
|
previous = y;
|
||||||
|
}
|
||||||
|
Some(_) if y == previous + 1 => previous = y,
|
||||||
|
Some(top) => {
|
||||||
|
card_bands.push((top, previous));
|
||||||
|
start = Some(y);
|
||||||
|
previous = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(top) = start {
|
||||||
|
card_bands.push((top, previous));
|
||||||
|
}
|
||||||
assert!(
|
assert!(
|
||||||
card_bands.len() >= 3,
|
card_bands.len() >= 3,
|
||||||
"至少要找到 3 条卡带(第一排两张 + 双动作 + scrim 里那张),实测 {card_bands:?}"
|
"至少要找到 3 条卡带(第一排两张 + 双动作 + scrim 里那张),实测 {card_bands:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut widths: Vec<usize> = Vec::new();
|
// 每张卡记 (左缘, 宽):宽度给「逐卡」断言,左缘给内距断言用(不能按单行找左缘——
|
||||||
|
// 圆角 28 会让 band_top+4 那一行的卡片像素内缩 ~10px,实测内距被量成 12 而不是 24)。
|
||||||
|
//
|
||||||
|
// **深色档要把遮罩色排掉**:暗色下 `SurfaceContainerHigh` 与预合成 scrim 只差 ~35/通道,
|
||||||
|
// 不排掉时 scrim 那片也被算成「卡」(实测出现 1180 宽的假卡)。
|
||||||
|
let mut cards: Vec<(usize, usize)> = Vec::new();
|
||||||
for band in &card_bands {
|
for band in &card_bands {
|
||||||
let height = band.1 - band.0 + 1;
|
let height = band.1 - band.0 + 1;
|
||||||
let profile: Vec<usize> = (0..shot.width)
|
let profile: Vec<usize> = (0..shot.width)
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
(band.0..=band.1)
|
(band.0..=band.1)
|
||||||
.filter(|y| matches(shot.at(x, *y), rgb(card)))
|
.filter(|y| {
|
||||||
|
let pixel = shot.at(x, *y);
|
||||||
|
matches(pixel, card_rgb) && !matches(pixel, scrim_rgb)
|
||||||
|
})
|
||||||
.count()
|
.count()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut start: Option<usize> = None;
|
let mut start: Option<usize> = None;
|
||||||
for x in 0..=shot.width {
|
for (x, ink) in profile.iter().enumerate() {
|
||||||
let column = x < shot.width && profile[x] >= height / 3;
|
let column = *ink >= height / 3;
|
||||||
match (column, start) {
|
match (column, start) {
|
||||||
(true, None) => start = Some(x),
|
(true, None) => start = Some(x),
|
||||||
(false, Some(from)) => {
|
(false, Some(from)) => {
|
||||||
if x - from >= 280 {
|
if x - from >= 280 {
|
||||||
widths.push(x - from);
|
cards.push((from, x - from));
|
||||||
}
|
}
|
||||||
start = None;
|
start = None;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(from) = start {
|
||||||
|
if shot.width - from >= 280 {
|
||||||
|
cards.push((from, shot.width - from));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let mut widths: Vec<usize> = cards.iter().map(|(_, width)| *width).collect();
|
||||||
widths.sort_unstable();
|
widths.sort_unstable();
|
||||||
assert!(
|
assert!(
|
||||||
widths.iter().any(|w| near(*w, DIALOG_W)),
|
widths.iter().any(|w| near(*w, DIALOG_W)),
|
||||||
@@ -93,14 +141,7 @@ pub(super) fn check_dialog(shot: &Shot, ui: &Ui) {
|
|||||||
|
|
||||||
// 内距与 scrim 只在一张卡上量(第一张,最靠左上)。
|
// 内距与 scrim 只在一张卡上量(第一张,最靠左上)。
|
||||||
let first = card_bands[0];
|
let first = card_bands[0];
|
||||||
let width = band_width(shot, first, card, surface);
|
let (card_left, width) = cards[0];
|
||||||
let mut card_left = 0;
|
|
||||||
for x in 0..shot.width {
|
|
||||||
if matches(shot.at(x, first.0 + 4), rgb(card)) {
|
|
||||||
card_left = x;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let top = first.0;
|
let top = first.0;
|
||||||
let bottom = first.1;
|
let bottom = first.1;
|
||||||
let bbox = BBox {
|
let bbox = BBox {
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ fn caption<'a>(ui: &'a Ui, value: &'a str) -> Element<'a, DemoMessage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 带 1px 取景框的宿主:`Snackbar` 的贴底/贴顶需要一条**可见的宿主边界**才量得出来
|
/// 带 1px 取景框的宿主:`Snackbar` 的贴底/贴顶需要一条**可见的宿主边界**才量得出来
|
||||||
|
/// (描边画在盒内但**不参与布局**:内容区仍是整盒宽,实测条宽不受影响)。
|
||||||
/// (透明宿主的底边在图上不存在,`Placement::Bottom` 就不可证)。
|
/// (透明宿主的底边在图上不存在,`Placement::Bottom` 就不可证)。
|
||||||
fn framed_host<'a>(
|
fn framed_host<'a>(
|
||||||
ui: &'a Ui,
|
ui: &'a Ui,
|
||||||
|
|||||||
Reference in new issue
Block a user