Files
Unlucky-Launcher/crates/app-iced/src/components/inputs/menu/shadow.rs
T
AskaEth df0944894f fix(app-iced): 面板预算按内容推导 + 版本菜单不画 L2 阴影(M238)
两条都在「视口不终止内容」这一族里,修法各一半:

① 三栏档面板预算从死的 `PANEL_BUDGET = 300` 改成 `panel_budget(menu_items)`
   (内边距 24 + 首屏 76 + 间距 8 + 钉死区 88 + 菜单高 + 列表↔面板 21)。
   旧常数不含列表↔面板那 21、也不随版本菜单行数变,于是 760/900 档面板实得
   286/294px,滚动视口被挤到 54/62px——六枚 loader chip 只露出 10px(736 与 1180
   同样严重,纵向预算与窗宽无关)。修后 760 档视口 98px(chips 31px 整枚在屏)、
   900 档 106px;列表按整行让出:760 档 4→3 整行、900 档 7→6 整行;只有一条
   加载器版本时菜单只占 64px,列表仍是 4 整行。

② 版本菜单改用 `Menu::flat()`:不是「把阴影裁掉」,而是**不在滚动容器里画阴影**。
   `iced_tiny_skia` 0.14 画 quad 阴影走 `draw_pixmap(.., mask: None)`
   (engine.rs:136-143),绕过裁切掩码;`iced_wgpu` 那边是整层 scissor
   (quad.rs:96-101)⇒ 同一棵树两个后端画出来不一样。修前菜单底下 54px 一条被压暗
   (浅色 249→231,−7.2%,含 8px 缝与实例名框内上半部),修后逐像素等于画布色。
   菜单的 L2 令牌不动,真实浮层照旧带阴影。

判据(两条,抓得住的/抓不住的都写进文档注释):
- 结构:`rows/tail.rs` 断言三栏档 760/900 的滚动视口 ≥ 首屏 76px(对生产常量做算术);
- 像素:`options_shot/compact.rs` 断言 chip 描边上下两行都在、且实例名框上方那条缝
  与框内都是纯画布色。
变异自证:预算退回 300 ⇒ 结构断言红("滚动视口只有 54px < 首屏 76px");阴影无视
`flat()` ⇒ 像素断言红("y=660 x=304 是 [231,231,232] 而不是画布色")。

没验:滚动中/底部三态拍不出来(harness 无 `scroll_to` 派发通道,已另开 finding)。
2026-11-11 08:28:06 +08:00

55 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 菜单「要不要画 L2 阴影」(M238)。
//!
//! 从 `menu.rs` 拆出来只为守 400 行硬线(`components::gate::every_source_file_stays_under_the_line_limit`):
//! 那边加完「内联列表口径」正好 400 行、零余量。搬移的是**新增**的两小块,没有改动既有逻辑。
use iced::{Color, Shadow, Vector};
use unlucky_design::color::Role;
use unlucky_design::elevation::ShadowSpec;
use crate::theme::Ui;
use super::Menu;
impl<'a, M> Menu<'a, M> {
/// **内联列表口径**:不画 L2 阴影(容器色 / 圆角 / 项几何照旧)。
///
/// 用在「菜单被放进**会被裁切的容器**(滚动视口)」的场合——下载页的加载器版本列表就是
/// 这一种:它是面板里的一串版本,不是浮在页面上的弹出层。
///
/// 为什么必须在这里让步而不是「让它裁掉」:`iced_tiny_skia` 0.14 画 quad 阴影时用
/// `pixmap.draw_pixmap(.., mask: None)`(`iced_tiny_skia/src/engine.rs:136-143`),**绕过**
/// 了 `clip_mask` ⇒ 阴影照画到视口之外,压暗下面钉死区的实例名(M238 实测:浅色画面布
/// `(249,249,250)` 被压成 `(231,231,232)`,−7.2%)。`iced_wgpu` 那条路走整层的 scissor
/// (`iced_wgpu/src/quad.rs:96-101`)⇒ 同一棵树在 GPU / CPU 两个后端上**画出来的不一样**。
/// 不画阴影让两边一致,也把「浮层」语义收回到真正浮起来的地方。
///
/// 层级观感:`Component::Menu.spec().elevation` 仍是 **L2**(令牌表不动),真实浮层
/// (gallery / 将来的下拉)照旧带阴影;只有「内联列表」这一种用法放弃阴影,靠
/// `SurfaceContainer` 底色 + 4 圆角与画布区分(L1 卡片 → L2 菜单这条阶梯不被削平)。
pub fn flat(mut self) -> Self {
self.shadow = false;
self
}
}
/// 面板阴影:`elevated` 为假([`Menu::flat`])时给 `Shadow::default()`——**不是**把 α 调小,
/// 而是让 tiny-skia 的 `shadow.color.a > 0.0` 一判就直接跳过整块阴影绘制。
///
/// 单独抽出来是为了让「内联列表不画阴影」这条可断言:`Element` 没有几何/样式内省,
/// 单测只能钉这个纯函数(见 `menu/tests.rs`)。
pub(super) fn panel_shadow(ui: &Ui, spec: ShadowSpec, elevated: bool) -> Shadow {
if !elevated {
return Shadow::default();
}
Shadow {
color: Color {
a: spec.alpha,
..ui.color(Role::Shadow)
},
offset: Vector::new(0.0, spec.offset_y_px),
blur_radius: spec.blur_px,
}
}