已落地:
- components/{mod,interaction,layout,divider,gate}.rs + buttons/{mod,geometry,assemble,tests}.rs
- gallery/{mod,buttons,probe,scan}.rs + 四个家族桩(selection/inputs/feedback/navigation)
- cli.rs: --screen=shell|gallery + capture_flavor(截图默认 Light,删掉等系统主题的闸门)
- app.rs: gallery 路由 + DemoMessage 接线(400 行)
待办(下一个 commit):编译/测试全绿、浅深矩阵出图、1:1 并排与数值表。
110 lines
4.0 KiB
Rust
110 lines
4.0 KiB
Rust
//! 分隔线(`research/15` §22,A1:`DividerTokens.kt` / `Divider.kt` / `ListTokens.kt`)。
|
||
//!
|
||
//! | # | 检查项 | 参考值 | 落点 |
|
||
//! |---|---|---|---|
|
||
//! | 22.1 | 厚度 | **1dp** → 1px ±0 @100% DPI | `LayoutMetrics::divider_thickness` = [`STROKE_PX`] |
|
||
//! | 22.2 | 颜色 | **`OutlineVariant`**(不是 `Outline`,那个更深、会抢层级) | `Role::OutlineVariant` |
|
||
//! | 22.3 | 缩进 | 列表内 **左 16 / 右 16**;区块分隔 **0(通栏)** | [`list`] / [`full`] |
|
||
//! | 22.4 | 用量 | 同屏同类线 ≤3;同容器内不得既有描边又有分隔线 | 页面线自查(本模块不强制) |
|
||
//! | 22.5 | 何时不用 | tone 能分层就不画线;纵向分隔本项目不用 | 文档约定 |
|
||
//!
|
||
//! ## 语义纪律(22.5 的一句话口径)
|
||
//!
|
||
//! **分隔线是兜底手段,不是排版手段**:能靠 `Surface` / `SurfaceContainerLow` /
|
||
//! `SurfaceContainerHigh` 的色差把层级说清的地方就不画线;卡片已经有 1px `OutlineVariant`
|
||
//! 描边时,卡片**之间**不再画线(同时出现 = 双重分隔,rubric K3 那类廉价感)。
|
||
//!
|
||
//! ## 为什么只有两个入口
|
||
//!
|
||
//! §22.3 只承认两种缩进:通栏(区块分隔)与列表内 16。第三种缩进一定是「页面自己想出来的」,
|
||
//! 所以不提供任意数值入口——真要第三种,先在 `SubAgentsTalk/` 登记。
|
||
|
||
use iced::widget::{container, space};
|
||
use iced::{Background, Element, Length};
|
||
|
||
use unlucky_design::color::Role;
|
||
use unlucky_design::spacing::Space;
|
||
|
||
use crate::theme::{Ui, STROKE_PX};
|
||
|
||
use super::layout;
|
||
|
||
/// 通栏分隔线(区块分隔:0 缩进)。
|
||
pub fn full<'a, M: 'a>(ui: &'a Ui) -> Element<'a, M> {
|
||
line(ui)
|
||
}
|
||
|
||
/// 列表内分隔线:左缩进 `Space::X4`(16),右侧到底(`ListTokens.DividerLeadingSpace`)。
|
||
///
|
||
/// 右缩进按官方也是 16,但列表行本身已有 16 的内边距时再缩一次会变成 32——
|
||
/// 本项目把「行内边距」交给行,本函数只负责**线相对行**的缩进,故右端通到行尾。
|
||
pub fn list<'a, M: 'a>(ui: &'a Ui) -> Element<'a, M> {
|
||
layout::inset_start(ui.space(Space::X4), line(ui))
|
||
}
|
||
|
||
/// 一条 1px 的横线(厚度/颜色都只有一处定义)。
|
||
fn line<'a, M: 'a>(ui: &'a Ui) -> Element<'a, M> {
|
||
container(space::horizontal())
|
||
.width(Length::Fill)
|
||
.height(thickness(ui))
|
||
.style(|_theme| container::Style {
|
||
background: Some(Background::Color(ui.color(Role::OutlineVariant))),
|
||
..container::Style::default()
|
||
})
|
||
.into()
|
||
}
|
||
|
||
/// 线厚(= `LayoutMetrics::divider_thickness` = `stroke::HAIRLINE`,三者同一真源)。
|
||
pub fn thickness(ui: &Ui) -> f32 {
|
||
debug_assert_eq!(
|
||
ui.metrics().divider_thickness,
|
||
STROKE_PX,
|
||
"分隔线厚度必须是一像素线的唯一真源"
|
||
);
|
||
ui.metrics().divider_thickness
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use unlucky_design::color::SchemeFlavor;
|
||
use unlucky_design::typography::Platform;
|
||
|
||
use crate::theme::Families;
|
||
|
||
fn ui() -> Ui {
|
||
Ui::with_platform(
|
||
SchemeFlavor::Light,
|
||
Platform::Windows,
|
||
Families {
|
||
text: "Segoe UI",
|
||
display: "Segoe UI",
|
||
mono: "Consolas",
|
||
},
|
||
)
|
||
}
|
||
|
||
/// 22.1:厚度 1(不是 2,也不是 1.5)。
|
||
#[test]
|
||
fn thickness_is_exactly_one_pixel() {
|
||
let ui = ui();
|
||
assert_eq!(thickness(&ui), 1.0);
|
||
assert_eq!(thickness(&ui), STROKE_PX);
|
||
assert_eq!(thickness(&ui), ui.metrics().divider_thickness);
|
||
}
|
||
|
||
/// 22.3:列表内缩进 16,来自 `Space::X4` 而不是字面量。
|
||
#[test]
|
||
fn list_inset_comes_from_the_spacing_token() {
|
||
let ui = ui();
|
||
assert_eq!(ui.space(Space::X4), 16.0, "ListTokens.DividerLeadingSpace");
|
||
}
|
||
|
||
#[test]
|
||
fn both_forms_build() {
|
||
let ui = ui();
|
||
let _: Element<'_, ()> = full(&ui);
|
||
let _: Element<'_, ()> = list(&ui);
|
||
}
|
||
}
|