126 lines
5.3 KiB
Rust
126 lines
5.3 KiB
Rust
//! Chip 的角色映射(`research/12b` §13.5 / §13.6 / §13.7)。
|
||
|
||
use unlucky_design::color::Role;
|
||
|
||
use crate::theme::{Ui, STROKE_PX};
|
||
|
||
use super::super::super::interaction::{self, Base, Interaction};
|
||
use super::{disabled_container, disabled_ink, state_layer, ChipPaint, Frame};
|
||
|
||
/// Chip 的三种(§13.6 / §13.7):Assist / Filter / Input。
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum ChipVariant {
|
||
/// Assist:**永远未选中**,前置图标 `Primary`(`AssistChipTokens.IconColor`)。
|
||
Assist,
|
||
/// Filter:可选中(`SecondaryContainer`),选中态无描边。
|
||
Filter,
|
||
/// Input:可选中 + 后置关闭图标。
|
||
Input,
|
||
}
|
||
|
||
/// Chip 的外观。
|
||
///
|
||
/// 与 Checkbox/Radio 的区别:Chip 的**状态层就是它自己的容器**(M3 的 chip 没有圆形状态层),
|
||
/// 所以未选中 + hover 的 fill 是「状态层叠在 surface 上」的实色。
|
||
///
|
||
/// **选中也一样吃状态层**(M299):底是实色 `SecondaryContainer`,hover / pressed 叠在它上面
|
||
/// ——走 [`interaction::selected_fill`] 那个唯一入口。M299 之前这一支写死实色,`state` 参数
|
||
/// 在选中分支里根本没用上(gallery 的 `chip·filter·on` 一行三态逐像素相同)。
|
||
pub fn chip(
|
||
ui: &Ui,
|
||
base: Base,
|
||
variant: ChipVariant,
|
||
selected: bool,
|
||
state: Interaction,
|
||
radius: f32,
|
||
) -> ChipPaint {
|
||
let selected = selected && !matches!(variant, ChipVariant::Assist);
|
||
let label_role = if selected {
|
||
Role::OnSecondaryContainer
|
||
} else if matches!(variant, ChipVariant::Assist) {
|
||
Role::OnSurface
|
||
} else {
|
||
Role::OnSurfaceVariant
|
||
};
|
||
|
||
if state.is_disabled() {
|
||
let ink = disabled_ink(ui, base, Role::OnSurface);
|
||
return ChipPaint {
|
||
frame: Frame {
|
||
fill: selected.then(|| disabled_container(ui, base)),
|
||
outline: (!selected).then(|| disabled_container(ui, base)),
|
||
stroke: STROKE_PX,
|
||
radius,
|
||
},
|
||
label: ink,
|
||
icon: ink,
|
||
};
|
||
}
|
||
|
||
let outline = (!selected).then(|| ui.color(Role::OutlineVariant));
|
||
let fill = if selected {
|
||
// 选中的 chip 也一样吃状态层(M299):M3 的 chip 状态层就是容器自己,
|
||
// 选中后底是实色 `SecondaryContainer`,hover / pressed 叠在它上面。
|
||
Some(interaction::selected_fill(
|
||
ui,
|
||
state,
|
||
Role::OnSecondaryContainer,
|
||
Role::SecondaryContainer,
|
||
))
|
||
} else {
|
||
state_layer(ui, base, state, label_role)
|
||
};
|
||
// Assist / 选中态的前置图标是 `Primary`(`AssistChipTokens.IconColor` /
|
||
// `InputChipTokens.SelectedLeadingIconColor`);其余跟文字同色。
|
||
let icon_role = if matches!(variant, ChipVariant::Assist) || selected {
|
||
Role::Primary
|
||
} else {
|
||
label_role
|
||
};
|
||
|
||
ChipPaint {
|
||
frame: Frame {
|
||
fill,
|
||
outline,
|
||
stroke: STROKE_PX,
|
||
radius,
|
||
},
|
||
label: ui.color(label_role),
|
||
icon: ui.color(icon_role),
|
||
}
|
||
}
|
||
|
||
/// 「**当前项**」标记的外观(M206):**就是选中 chip 的那一套**,一个像素都不另起一套。
|
||
///
|
||
/// 为什么要一个具名入口而不是让调用点自己拼 `chip(.., Filter, true, Enabled, ..)`:
|
||
///
|
||
/// 1. 这个语义(「这行就是当前生效的那个」)此前被画成了 [`super::super::Chip::badge`] 的
|
||
/// **禁用**配色(`OnSurface`@38% 叠 surface,M204 实测 **1.84:1 浅 / 2.28:1 深**)——那是
|
||
/// 「暂不可用」的语汇,而当前项不是不可用,它是**已经选中的那个**。修法不是给禁用配色调亮,
|
||
/// 而是让当前项改用**选中**的语汇;
|
||
/// 2. 与左侧导航里相邻的「下载」组的选中 chip(`Chip::filter(.., true, ..)`,容器
|
||
/// `SecondaryContainer`、文字 `OnSecondaryContainer`,实测 **7.21:1 浅 / 7.21:1 深**)
|
||
/// **同族**:两页表达「选中」只用一套颜色,用户不用在两页之间切换心智模型。身份由
|
||
/// `pages::settings::java::tests::marker` 的
|
||
/// `the_current_default_marker_speaks_the_download_groups_language` 钉住——谁把其中一页
|
||
/// 改成另一套配色,那条立刻红。具体的 8 bit 色值不写在这里:`tests/no_hardcoded_tokens.rs`
|
||
/// 连注释里的十六进制色值也拦,色值只该在报告与令牌表里出现。
|
||
///
|
||
/// 与选中 chip 的唯一差别**不在颜色**:标记没有消息(`on_press == None`),所以它不吃 hover /
|
||
/// pressed / disabled 五态(见 `Chip::current` 的文档)。对比度下限是 [`crate::components::selection::paint`]
|
||
/// 同一条正文口径(WCAG AA 4.5:1,`unlucky_design::palette::wcag::AA_TEXT`)。
|
||
///
|
||
/// 与选中 chip 的唯一差别**不在颜色**:标记没有消息(`on_press == None`),所以它不吃 hover /
|
||
/// pressed / disabled 五态(见 `Chip::current` 的文档)。对比度下限是 [`crate::components::selection::paint`]
|
||
/// 同一条正文口径(WCAG AA 4.5:1,`unlucky_design::palette::wcag::AA_TEXT`)。
|
||
pub fn current_item(ui: &Ui, radius: f32) -> ChipPaint {
|
||
chip(
|
||
ui,
|
||
Base::CANVAS,
|
||
ChipVariant::Filter,
|
||
true,
|
||
Interaction::Enabled,
|
||
radius,
|
||
)
|
||
}
|