fix(design,app-iced): 图标尺寸与禁用 α 收敛到官方字节码,两族拆开(M86)

图标:IconSize::Button 16→18(AAR ButtonDefaults.<clinit> 的 bipush 18);新增
IconSize::SwitchThumb=16(SwitchTokens.Selected/UnselectedIconSize)承接开关滑块,按钮与
滑块不再借同一档;IconSize::Nav 的值不动但文档订正(无消费,官方导航/列表图标是 24)。
ButtonSmallTokens.IconSize=20 在整包 AAR 里零消费(javap 搜 getIconSize 无外部调用),
是死令牌,不再作为取值依据。

禁用 α:官方本来就分两族——按钮容器 0.10(*ButtonTokens.DisabledContainerOpacity)与
面状控件 0.12(chip 描边 / slider 轨道 / switch 轨道)。原来的缺陷是一个全局 12% 同时
服务两族(按钮那半边一直错、另半边一直对),现拆成 disabled_button_colors/…_content
与 disabled_colors/disabled_content_over_role 两个入口;interaction 首次超 400 行,
禁用口径与测试搬到 interaction/disabled.rs。

标题栏 caption 三键:官方 SmallIconButtonTokens.IconSize=24,按用户点名缩到 20
(容器 46×48 不动),登记为项目偏离;icons.rs 加按令牌取尺寸的入口 view_at,按
(种类,尺寸) 分别烘焙 SVG,20px 的 stroke 取 1.8 保视觉线宽 1.5px。
This commit is contained in:
AskaEth committed 2026-11-02 13:12:11 +08:00
1 parent c825e1aa3d
commit b566097107
16 files changed
+664 -175

No files matched your search

@@ -13,9 +13,29 @@
//! | focus | 0.10 | [`StateLayer::Focus`] |
//! | pressed | 0.10 | [`StateLayer::Pressed`] |
//! | dragged | 0.16 | [`StateLayer::Dragged`](按钮家族不用,列表拖拽用) |
//! | disabled 容器 | 0.12 | [`DISABLED_CONTAINER_ALPHA`](**A1 是 0.10f**,本文件按令牌 0.12,差异登记 D8) |
//! | disabled 容器(**按钮家族**) | 0.10 | [`DISABLED_CONTAINER_ALPHA`](按钮 `*ButtonTokens.DisabledContainerOpacity`) |
//! | disabled 容器/描边(**面状控件族**) | 0.12 | [`DISABLED_SURFACE_ALPHA`](chip / slider 轨道 / switch 轨道 / TextField 禁用描边) |
//! | disabled 内容 | 0.38 | [`DISABLED_CONTENT_ALPHA`] |
//!
//! ## ⚠️ 禁用 α 有**两类**(M86 收口,别再合成一个数)
//!
//! 官方禁用 α 从来不是「一个 12%」:
//!
//! - **按钮容器 0.10**:`BaselineButtonTokens` / `FilledButtonTokens` / `OutlinedButtonTokens` /
//! `TextButtonTokens` / `ElevatedButtonTokens` / `TonalButtonTokens` 的 `DisabledContainerOpacity`
//! = `0.1f`(AAR 字节码 `<clinit>` 里的 `ldc 0.1f`);
//! - **面状控件 0.12**:`AssistChipTokens.FlatDisabledOutlineOpacity` / `FilterChipTokens.
//! FlatDisabledSelectedContainerOpacity` / `SliderTokens.DisabledInactiveTrackOpacity` /
//! `SwitchTokens.DisabledTrackOpacity` 都是 `0.12f`。
//!
//! 本模块过去只有一个 12% 常量供两条线共用:**按钮那半边一直错、chip/slider/switch 那半边一直对**。
//! 现在按族分两个入口,谁也别再拿一个数服务两族:
//!
//! | 入口 | α | 谁用 |
//! |---|---|---|
//! | [`disabled_button_colors`] / [`disabled_button_content`] | 0.10 | **只有按钮家族**(`buttons::assemble`) |
//! | [`disabled_colors`] / [`disabled_content_over_role`] | 0.12 | **面状控件族**:chip 容器与描边、switch 轨道、slider 未激活轨、TextField 禁用描边(`disabled_colors` 这个名字是历史遗留,**语义是面状控件族**) |
//!
//! 上表右列是**唯一**允许的取值口;左列只是给人看的,代码里一个数字都不许出现
//! (`components::gate` 会扫)。
//!
@@ -33,10 +53,9 @@
use iced::widget::button;
use iced::Color;
use unlucky_design::color::state::{DISABLED_CONTAINER_ALPHA, DISABLED_CONTENT_ALPHA};
use unlucky_design::color::{Role, Scheme, StateLayer};
use unlucky_design::color::{Role, StateLayer};
use crate::theme::{to_iced, Ui};
use crate::theme::Ui;
mod tracker;
@@ -63,7 +82,7 @@ pub enum Interaction {
Focused,
/// 按下:叠 10%。
Pressed,
/// 禁用:容器 12% / 内容 38%,**不叠状态层**。
/// 禁用:按钮容器 10% / 面状控件 12% / 内容 38%,**不叠状态层**。
Disabled,
}
@@ -125,60 +144,11 @@ pub fn overlay_color(ui: &Ui, state: Interaction, content: Role, container: Role
.map(|layer| ui.state_over(layer, content, container))
}
/// 禁用态内容色(**按角色、按有没有容器**求值)——A1 口径的唯一入口。
///
/// - `role`:由变体决定(Filled/Outlined/Text/Elevated = `OnSurfaceVariant`;
/// FilledTonal 与三式 IconButton = `OnSurface`,见 `Variant::disabled_content`);
/// - `on_container = true`:叠在 **12% 容器**上(有容器的三式);
/// `false`:直接叠在 **surface** 上(Text / Outlined / standard IconButton)。
///
/// 之前这里写死 `OnSurfaceVariant` 且只有一个「底」,导致 FilledTonal 与 IconButton 两族偏浅
/// 15/255(视觉 r3 用像素量的)。
pub fn disabled_content_over_role(ui: &Ui, role: Role, on_container: bool) -> Color {
let scheme = Scheme::baseline(ui.flavor());
let base = if on_container {
scheme
.get(Role::OnSurface)
.with_alpha(DISABLED_CONTAINER_ALPHA)
.over(scheme.get(Role::Surface))
} else {
scheme.get(Role::Surface)
};
mod disabled;
to_iced(
scheme
.get(role)
.with_alpha(DISABLED_CONTENT_ALPHA)
.over(base),
)
}
/// 禁用态的一对颜色 `(容器, 内容)`,内容按令牌口径叠在**算好的容器**之上。
///
/// - 容器 = `OnSurface` @ 12%([`DISABLED_CONTAINER_ALPHA`];A1 是 0.10f,见 D8)叠在 `surface` 上;
/// - 内容 = **默认** `OnSurfaceVariant` @ 38%;按变体的取值走 [`disabled_content_over_role`]。
///
/// `ui` 不暴露 [`Scheme`],这里按同一个 flavor 求一次基线方案——调的是
/// `unlucky_design` 的**同一个**函数与同一套角色,不是第二份令牌。
///
/// 顺序必须「先容器后内容」:Compose 也是这么画的,先叠容器再叠 label,
/// 所以内容叠的角色是容器实色而不是底下的 surface。
pub fn disabled_colors(ui: &Ui, surface: Role) -> (Color, Color) {
let scheme = Scheme::baseline(ui.flavor());
let base = scheme.get(surface);
let container = scheme
.get(Role::OnSurface)
.with_alpha(DISABLED_CONTAINER_ALPHA)
.over(base);
// 这里是**默认口径**(OnSurfaceVariant);按变体取值请走 [`disabled_content_over_role`]
// ——FilledTonal 与三式 IconButton 的禁用内容是 `OnSurface`(A1)。
let content = scheme
.get(Role::OnSurfaceVariant)
.with_alpha(DISABLED_CONTENT_ALPHA)
.over(container);
(to_iced(container), to_iced(content))
}
pub use disabled::{
disabled_button_colors, disabled_button_content, disabled_colors, disabled_content_over_role,
};
#[cfg(test)]
mod tests {
@@ -198,15 +168,16 @@ mod tests {
Ui::with_platform(flavor, Platform::Windows, FAMILIES)
}
/// 契约 §3 的 α 全表:**钉住令牌值**(改令牌会让这条红,改组件抓不到)。
/// 契约 §3 的状态层 α 全表:**钉住令牌值**(改令牌会让这条红,改组件抓不到)。
///
/// 禁用态的两族 α(按钮 0.10 / 面状控件 0.12)在 [`super::disabled`] 的测试里,
/// 那边还负责「两族不能同源」的判据。
#[test]
fn state_layer_alphas_are_the_contract_values() {
assert!((StateLayer::Hover.alpha() - 0.08).abs() < f32::EPSILON);
assert!((StateLayer::Focus.alpha() - 0.10).abs() < f32::EPSILON);
assert!((StateLayer::Pressed.alpha() - 0.10).abs() < f32::EPSILON);
assert!((StateLayer::Dragged.alpha() - 0.16).abs() < f32::EPSILON);
assert!((DISABLED_CONTAINER_ALPHA - 0.12).abs() < f32::EPSILON);
assert!((DISABLED_CONTENT_ALPHA - 0.38).abs() < f32::EPSILON);
}
#[test]
@@ -265,34 +236,4 @@ mod tests {
assert_ne!(hover, pressed, "8% 与 10% 必须能区分");
assert_eq!(hover.a, 1.0, "结果必须不透明(GPU/CPU 逐像素一致)");
}
#[test]
fn disabled_colors_come_from_the_token_functions() {
let ui = ui(SchemeFlavor::Light);
let scheme = Scheme::baseline(SchemeFlavor::Light);
let (container, content) = disabled_colors(&ui, Role::Surface);
let base = scheme.get(Role::Surface);
let expected_container = scheme
.get(Role::OnSurface)
.with_alpha(DISABLED_CONTAINER_ALPHA)
.over(base);
assert_eq!(
container,
to_iced(expected_container),
"容器 = on-surface@12% 叠表面"
);
assert_eq!(
content,
to_iced(
scheme
.get(Role::OnSurfaceVariant)
.with_alpha(DISABLED_CONTENT_ALPHA)
.over(expected_container)
),
);
assert_ne!(container, ui.color(Role::Surface), "12% 叠上去必须能看出来");
assert_eq!(container.a, 1.0, "禁用容器也是实色(CPU/GPU 一致)");
assert_eq!(content.a, 1.0, "禁用内容也是实色");
}
}