feat(design): M69 令牌补齐 —— 圆角分层 / 组件微度量 / 骨架值与一像素线
G13(D32 分层):新增 shape::ComponentRadius(白名单只有 Micro=2)、 MICRO_CONTROL_MAX_SIZE=24 与纯函数 radius_layer_for();微半径走 radius_px(size) -> Option<f32>,尺寸 >24dp 一律 None,靠签名而不是自觉拦。 ShapeScale 与 rank() 一个字未动,2 不进刻度。 G11:LayoutMetrics 加 search_bar_height=56 / nav_rail_width=88 / nav_drawer_width=240 / divider_thickness=1;删除语义相反的 nav_expanded_width=88 / nav_collapsed_width=48(D33:展开 drawer 240 / 折叠 rail 88;值 88 的后继是新名字 nav_rail_width,48 无后继)。 G10:新增 component_map::ComponentMetrics(12 字段 + 4 个命名常量 + indicator_width),入口 Component::metrics();框内几何与 LayoutMetrics 的 骨架值分工写进模块文档,骨架级尺寸在 CM 里留 0 不许重复。 G9:Component 末尾追加 Checkbox / Radio / Search / NavigationDrawer / Divider(20 -> 25),同步 table.rs 槽位、ALL 与 name();ComponentSpec 新增 micro_radius(Some 时 shape 必须是占位 None,由构造器 + 测试钉死)。 G12:新增 spacing::stroke::HAIRLINE = 1.0(crate 根再导出为 unlucky_design::stroke),divider_thickness 直接引用它。 其余:lib.rs 的「OKLCh 近似」文案改回真实口径(真 HCT / ColorSpec2021); component_map 单测拆到 tests.rs 以免 mod.rs 超 400 行;测试 145 -> 165。
This commit is contained in:
1 parent
b89cc91b78
commit
141e9b12c7
9 files changed
+1066
-145
No files matched your search
@@ -2,6 +2,17 @@
|
||||
//!
|
||||
//! 形状是「唯一真源」的一部分:组件不许写裸半径(`border_radius(8)`),一律引用
|
||||
//! [`ShapeScale`];需要动起来的(按下、选中、展开)用 [`ShapeMorph`] 的 from→to 数据。
|
||||
//!
|
||||
//! ## 圆角分两层(D32)
|
||||
//!
|
||||
//! 官方实现与官方刻度表并不总一致:Checkbox 的圆角是 **2dp**,但 `ShapeScale` 的刻度是
|
||||
//! `{0,4,8,12,16,20,28,32,48,full}`——2 不在里面。D32 的判决是**分层**而不是放宽判据:
|
||||
//!
|
||||
//! - **容器级**(卡片、弹窗、抽屉、按钮、面板、输入框外壳)只许用 [`ShapeScale`] 的刻度值;
|
||||
//! - **微控件级**(≤24dp 的 Checkbox 等)用 [`ComponentRadius`] 的白名单值。
|
||||
//!
|
||||
//! 一个元素该查哪一层由 [`radius_layer_for`] 判定(按尺寸,唯一入口);微控件层取值走
|
||||
//! [`ComponentRadius::radius_px`],**尺寸超界时返回 `None`**——这不是文档约定,是签名保证。
|
||||
|
||||
use crate::motion::Spring;
|
||||
|
||||
@@ -87,6 +98,79 @@ impl ShapeScale {
|
||||
}
|
||||
}
|
||||
|
||||
/// 微控件(≤24dp)的尺寸上限(dp)。
|
||||
///
|
||||
/// 超过这条线的元素属**容器级**,圆角必须查 [`ShapeScale`] 的严格刻度;[`ComponentRadius`]
|
||||
/// 只在这条线以内有效——由 [`ComponentRadius::radius_px`] 在运行期拦住(传大尺寸拿不到微半径)。
|
||||
/// 24 这个门槛不是官方数字,是 D32 给「微控件」划的界(Checkbox 20、指示点、徽标都在以内)。
|
||||
pub const MICRO_CONTROL_MAX_SIZE: f32 = 24.0;
|
||||
|
||||
/// 微控件圆角(D32 第二层):**来源官方组件实现,不是 [`ShapeScale`] 的成员**。
|
||||
///
|
||||
/// 为什么要有这一层:官方 Checkbox 的圆角是 **2dp**(`Checkbox.kt:738 RadiusSize = 2.dp`,
|
||||
/// `CheckboxTokens.ContainerShape = RoundedCornerShape(2)`),而 M3 形状刻度只有
|
||||
/// `{0,4,8,12,16,20,28,32,48,full}`。把 2 塞进刻度会废掉「无任意圆角」这条守门(D30 rubric K1),
|
||||
/// 不收又只能在组件里抄 `2.0` 字面量。D32 的判决是**分层**,不是放宽:
|
||||
///
|
||||
/// | 层 | 管什么 | 取值 |
|
||||
/// |---|---|---|
|
||||
/// | [`ShapeScale`] | 容器级:卡片/弹窗/抽屉/按钮/面板/输入框外壳 | 严格刻度,不许自造 |
|
||||
/// | `ComponentRadius` | 微控件级:Checkbox 等 ≤24dp 的元素 | 白名单,当前只有 [`ComponentRadius::Micro`] |
|
||||
///
|
||||
/// 用法:先 [`radius_layer_for`] 判层,微控件再 `radius_px(size)`。**返回 `None` 说明尺寸超界,
|
||||
/// 必须改查 [`ShapeScale`]**,不许把微半径硬套到容器上。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ComponentRadius {
|
||||
/// 2dp:官方 Checkbox 容器圆角。
|
||||
Micro,
|
||||
}
|
||||
|
||||
impl ComponentRadius {
|
||||
/// 白名单里的全部微半径(D32 当前只有一项;加值必须先在 `design-令牌约定` 登记)。
|
||||
pub const ALL: [ComponentRadius; 1] = [ComponentRadius::Micro];
|
||||
|
||||
/// 半径(dp)。
|
||||
pub const fn dp(self) -> f32 {
|
||||
match self {
|
||||
ComponentRadius::Micro => 2.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 微控件的实际半径:尺寸超过 [`MICRO_CONTROL_MAX_SIZE`] 时返回 **`None`**。
|
||||
///
|
||||
/// 这是 D32 里「能编译期拦住就别靠自觉」的落点——文档约定拦不住,签名拦得住:
|
||||
/// 拿到 `None` 的调用点没法「顺手用一下 2dp」,只能改查 [`ShapeScale`]。
|
||||
/// 另外半径不超过边长一半(2dp 半径放到更小的元素上会溢成圆)。
|
||||
pub fn radius_px(self, size_px: f32) -> Option<f32> {
|
||||
if size_px > 0.0 && size_px <= MICRO_CONTROL_MAX_SIZE {
|
||||
Some(self.dp().min(size_px / 2.0))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 一个元素的圆角该查哪一层([`radius_layer_for`] 的返回)。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum RadiusLayer {
|
||||
/// 容器级:查 [`ShapeScale`] 的严格刻度。
|
||||
Scale,
|
||||
/// 微控件级(≤24dp):查 [`ComponentRadius`] 的白名单。
|
||||
Component,
|
||||
}
|
||||
|
||||
/// 纯函数:按元素尺寸判定圆角该查哪一层。
|
||||
///
|
||||
/// 「某尺寸的元素该查哪一个」是 D32 里唯一需要判断的地方,所以只留这一个入口——
|
||||
/// 组件层不许自己写 `if size <= 24.0`,那等于把阈值抄了第二份。
|
||||
pub fn radius_layer_for(size_px: f32) -> RadiusLayer {
|
||||
if size_px > 0.0 && size_px <= MICRO_CONTROL_MAX_SIZE {
|
||||
RadiusLayer::Component
|
||||
} else {
|
||||
RadiusLayer::Scale
|
||||
}
|
||||
}
|
||||
|
||||
/// 一次形状形变(Expressive 的「按下胀开」「展开成药丸」等)。
|
||||
///
|
||||
/// 数据形态是纯的:起止档位 + 弹簧参数。适配层负责跑时钟,`progress` 由弹簧/缓动给出。
|
||||
@@ -165,4 +249,45 @@ mod tests {
|
||||
assert_eq!(morph.radius_px(200.0, -2.0), 8.0);
|
||||
assert_eq!(morph.radius_px(200.0, 3.0), 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn component_radius_is_whitelisted_and_refuses_containers() {
|
||||
// D32:微半径白名单当前只有 2dp,且永远不是 ShapeScale 的成员。
|
||||
assert_eq!(ComponentRadius::ALL.len(), 1);
|
||||
assert_eq!(ComponentRadius::Micro.dp(), 2.0);
|
||||
for scale in [
|
||||
ShapeScale::None,
|
||||
ShapeScale::ExtraSmall,
|
||||
ShapeScale::Small,
|
||||
ShapeScale::Medium,
|
||||
ShapeScale::Large,
|
||||
ShapeScale::LargeIncreased,
|
||||
ShapeScale::ExtraLarge,
|
||||
ShapeScale::ExtraLargeIncreased,
|
||||
ShapeScale::ExtraExtraLarge,
|
||||
] {
|
||||
assert_ne!(scale.dp(), 2.0, "{scale:?} 不该等于微半径(2 不在刻度里)");
|
||||
}
|
||||
|
||||
// 20×20 的 checkbox 拿 2dp;超 24 的元素必须拿不到。
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(20.0), Some(2.0));
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(24.0), Some(2.0));
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(24.5), None);
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(200.0), None);
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(0.0), None);
|
||||
// 半径不超过边长一半。
|
||||
assert_eq!(ComponentRadius::Micro.radius_px(2.0), Some(1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn radius_layer_follows_the_size_threshold() {
|
||||
assert_eq!(radius_layer_for(20.0), RadiusLayer::Component);
|
||||
assert_eq!(
|
||||
radius_layer_for(MICRO_CONTROL_MAX_SIZE),
|
||||
RadiusLayer::Component
|
||||
);
|
||||
assert_eq!(radius_layer_for(24.5), RadiusLayer::Scale);
|
||||
assert_eq!(radius_layer_for(320.0), RadiusLayer::Scale);
|
||||
assert_eq!(radius_layer_for(0.0), RadiusLayer::Scale);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user