尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

gpui-kit Color Picker 原语指南:基于 GPUI 构建可控、可访问的颜色选择器

gpui-kit Color Picker 原语指南:基于 GPUI 构建可控、可访问的颜色选择器 gpui-kit Color Picker 原语指南基于 GPUI 构建可控、可访问的颜色选择器【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit导读Color Picker 是 gpui-kit 中gpui-base基础层提供的一个无样式unstyled受控组件原语它只负责颜色选择的「行为与交互结构」不替应用决定任何视觉语言。应用方需要自己组合触发器、色板、弹出层、十六进制输入框与 HSLA 滑块并用 GPUI 标准样式完成全部外观。本文以 website/base/primitives/color-picker.md 为核心骨架结合 crates/base/src/color_picker.rs 的源码实现与 crates/base/examples/showcase/components/color_picker.rs 的可运行示例完整讲解ColorPicker、ColorSwatch、ColorPickerState三者如何协作以及如何在原生窗口与 WASM 预览中运行、订阅事件、同步状态并满足无障碍要求。读完本文你将掌握gpui-kit 颜色选择原语的导入方式与运行入口、受控状态机的全部公开 API 与事件模型、十六进制与 HSLA 双向同步的底层原理、悬浮预览与点击提交的交互范式以及一个可复制的完整 Rust 示例的组装过程。设计哲学行为与语义而非视觉语言与gpui-base的每一个原语一致Color Picker 遵循「行为与语义结构先行视觉语言交给应用」的原则。原语层交付的是触发器trigger的焦点管理与无障碍语义打开Confirm与关闭Cancel的键盘行为色板中每个色块的单选radio语义与可访问名称悬浮预览、点击提交的回调契约十六进制输入与四个 HSLA 滑块之间的状态同步。而「色板长什么样、弹层开在哪、按钮用什么配色」全部由应用方通过 GPUI 的Styled、ParentElement等标准 trait 自行组装。这一点在源码的文档注释中反复强调The application owns the palette, popup, layout, and every visual decision.crates/base/src/color_picker.rs。运行示例gpui-base的示例是一个单一的原生 Cargo 入口可从组件清单中挑选某个组件运行。颜色选择器对应的命令为cargo run -p gpui-base-examples -- color-picker示例包名为gpui-base-examples定义于 crates/base/examples/native/Cargo.toml默认二进制为componentsdefault-run components组件名color-picker是 crates/base/examples/showcase/mod.rs 中COMPONENTS清单的一项同一份 showcase 源码既被原生入口编译也被编译为 WASM 预览——这正是文档中「同一文件两种预览」说法的来源。原生入口的逻辑集中在 crates/base/examples/showcase/mod.rs 的run/run_embedded中初始化gpui_base::init(cx)、创建 840×640 的居中窗口、打开BaseShowcase而 WASM 侧则额外内嵌 Inter 字体。导入方式在应用代码中按如下方式导入本原语use gpui_kit::base::{ColorPicker, ColorPickerEvent, ColorPickerState, ColorSwatch};其中ColorPicker——受控的根元素承载触发器语义ColorSwatch——色板中单个可选颜色ColorPickerState——共享状态实体EntityColorPickerEvent——状态对外发出的事件枚举。如果你使用的是gpui-kit的组件层crates/component则导入路径变为use gpui_kit::component::color_picker::{ColorPicker, ColorPickerState, ColorPickerEvent};组件层直接复用了 base 层的ColorPickerEvent与ColorPickerState见 crates/component/src/color_picker.rs并在此基础上补充了主题色板、TabBar、Slider 等成品 UI形成「开箱即用」的组件版本。Anatomy 与 API 职责划分示例将ColorPicker、ColorSwatch、ColorPickerState三者组合使用。三者的职责可以概括为一张表类型角色核心职责ColorPicker受控根元素触发器的无障碍语义与焦点、Confirm 打开、Cancel 关闭、aria-expanded暴露、键盘上下文ColorPickerColorSwatch色板中的可选项radio 语义、十六进制可访问名称、悬浮hover与激活click回调、选中态ColorPickerState状态与交互模型持有已提交颜色、瞬态预览、受控打开态、活动面板、hexInputState与四个SliderState并保持同步提交时发出ColorPickerEvent::Change代码层面的权威实现在 crates/base/src/color_picker.rs原生与浏览器预览编译的是同一份文件。ColorPicker根元素ColorPicker::new(id)返回一个无样式受控根公开的主要 builder 方法crates/base/src/color_picker.rs方法说明open(bool)应用控制的打开态disabled(bool)禁用键盘交互并从 Tab 遍历中移除触发器track_focus(FocusHandle)为触发器提供焦点句柄accessibility_label(impl IntoSharedString)根元素暴露的可访问名称role(impl IntoRoleOverride)覆盖无障碍角色默认Role::Buttonon_open_change(handler)处理打开态更新的请求Confirm 切换、Cancel 关闭打开的弹层其渲染逻辑crates/base/src/color_picker.rs会为根元素设置aria_expanded(open)、key_context(ColorPicker)、可选的aria_label与焦点跟踪并绑定Confirm/Cancel两个 actionConfirm回调handler(!open)Cancel仅在打开时消费事件并回调handler(false)。ColorSwatch色块ColorSwatch::new(id, color)构造一个可选的色块crates/base/src/color_picker.rs方法说明color()该色块代表的颜色selected(bool)标记为当前选中色disabled(bool)忽略指针与键盘激活accessibility_label(...)覆盖可访问名称默认是颜色的十六进制值role(...)覆盖无障碍角色默认Role::RadioButtonon_click(handler)激活回调携带该色块的Hsla与ClickEventon_hover(handler)悬浮进入/离开回调携带颜色与布尔值tab_index(isize)/tab_stop(bool)控制焦点遍历默认0/true渲染时crates/base/src/color_picker.rs默认以hex_string(color)作为aria_label同时设置aria_toggled与aria_selected两种选中语义——源码注释明确说明辅助技术可能只读取其中一种因此两者都声明。状态与事件模型ColorPickerStateColorPickerState是整座交互模型的中心crates/base/src/color_picker.rs它同时拥有已提交颜色value: OptionHsla瞬态预览preview: OptionHsla悬浮或编辑时显示受控打开态open: bool活动面板索引active_tab: usize一个 hex 输入状态hex_input: EntityInputState四个 HSLA 分量滑块sliders: HslaSlidershue / saturation / lightness / alpha内部同步标记needs_slider_sync与suppress_input_change。它自己并不渲染色板与弹层——这些由应用拥有。它只保证「任何一条路径输入框、滑块、色板、默认值写入颜色后其余部件保持一致」。事件ColorPickerEvent#[derive(Clone)] pub enum ColorPickerEvent { Change(OptionHsla), }提交颜色select_color、update_color、commit_hex成功会通过cx.emit(ColorPickerEvent::Change(value))发出事件None表示颜色被清除。ColorPickerState同时实现了EventEmitterColorPickerEvent应用可用cx.subscribe(state, ...)订阅。公开状态 API方法行为new(window, cx)创建空、关闭的 picker内部用正则^#[0-9a-fA-F]{0,8}$HEX_PATTERN约束输入框并订阅输入框与四个滑块的事件default_value(color)设置初始的已提交与预览色因无窗口时无法写滑块会置位needs_slider_syncvalue()/preview()返回已提交色 / 瞬态预览色displayed_color()返回预览色回退到已提交色hex_input()/sliders()返回十六进制输入状态实体与HslaSlidersset_value(color, window, cx)替换已提交色不发出事件clear_value(window, cx)清除已提交色不发出事件sync_pending_value(window, cx)把default_value的待同步值刷入输入框与滑块需在 render 中调用无待同步内容时是 no-oppreview_color(color, window, cx)更新瞬态预览并回写 hex 输入框clear_preview(window, cx)丢弃瞬态预览恢复已提交色preview_hex(value, window, cx)解析 hex 为瞬态预览非法/未完成输入不改变现状返回bool表示是否解析成功commit_hex(value, window, cx)解析并提交 hex成功时关闭 picker返回OptionHslaselect_color(color, window, cx)提交颜色并关闭 picker色板点击路径update_color(color, window, cx)提交颜色但不改变打开态滑块拖动路径set_open(bool, cx)/toggle_open(cx)/is_open()打开态控制set_active_tab(usize, cx)/active_tab()选择/读取应用定义的面板如 Palette / HSLA两条提交路径关闭 vs 保持打开源码在select_color与update_color之间刻意做了区分crates/base/src/color_picker.rs色板点击后立即关闭而滑块拖动时保持打开让用户可以连续调节。commit_hex在输入框按 EnterInputEvent::PressEnter时走提交并关闭路径。这一行为有测试直接锁定crates/base/src/color_picker.rspalette_selection_closes_but_a_slider_update_stays_openupdate_color后is_open()仍为trueselect_color后变为false且两次提交共发出两条Change事件。为什么 default_value 需要 sync_pending_valuedefault_value设置的初始颜色在new之后无法立即写入滑块——因为SliderState::set_value需要mut Window而 builder 阶段没有窗口。因此状态记下needs_slider_sync true要求应用在首次 render 时调用一次sync_pending_value(window, cx)完成冲刷一旦执行过内部置needs_slider_sync false之后每次调用都是 no-op。对应测试 crates/base/src/color_picker.rsdefault_value_reaches_the_hex_field_and_sliders验证了default_value(hsla(0., 1., 0.5, 1.))同步后 hex 输入框显示#FF0000、lightness 滑块值为0.5。同步防回环的底层机制输入框与滑块之间的同步存在一个潜在回环滑块 → hex → 解析回Hsla会损失精度hex 只有 8bit/通道再写回滑块会与拖动中的滑块「打架」。源码用两个机制化解write_hex_input先置suppress_input_change true再写输入框让输入框触发的InputEvent::Change被消费而不回流crates/base/src/color_picker.rs滑块变化走独立的update_value_from_slider只回写 hex 输入框、不再写回滑块crates/base/src/color_picker.rs并注明「sliders are the source of this change and rewriting them would fight the drag in progress」反过来update_value写滑块时用全精度Hsla直接驱动self.sliders.write(value, window, cx)而不是让 hex 往返crates/base/src/color_picker.rs。hex 解析与格式化规则parse_hexcrates/base/src/color_picker.rs支持#rgb、#rgba、#rrggbb、#rrggbbaa四种宽度#可有可无单字符宽度会自我重复#fff即白色全部非法输入返回None。hex_stringcrates/base/src/color_picker.rs输出#RRGGBB半透明时输出#RRGGBBAA。两个函数都被测试覆盖parses_every_supported_hex_width#fff↔#FFFFFF、#ff000080的 alpha 约为 0.5crates/base/src/color_picker.rsrejects_malformed_hex#nope、#12、#1234567、#f0000等一律拒绝crates/base/src/color_picker.rsformats_alpha_only_when_translucent不透明输出 6 位、半透明输出 8 位crates/base/src/color_picker.rs。悬浮预览的交互契约preview_color/clear_preview实现了「悬浮即预览、离开即还原」的范式悬浮一个色块时把其颜色写入preview并回写 hex 输入框离开时若预览不同于已提交值则恢复为已提交值。测试preview_does_not_change_the_committed_valuecrates/base/src/color_picker.rs断言预览后value()保持提交值、displayed_color()返回预览值clear_preview后displayed_color()回到提交值。完整 Rust 示例文档中的可运行实现直接嵌入自源码文件 crates/base/examples/showcase/components/color_picker.rs。其组装流程如下刷新待同步值render 开头调用state.sync_pending_value(window, cx)读取展示数据is_open()、value()、displayed_color()无值时回退到example_rgb(0x171717)、hex_input().read(cx).value()、焦点句柄触发器一个带 id、边框、色块预览与 hex 文本的div点击时toggle_open色板五色0xdc2626、0xd97706、0x16a34a、0x2563eb、0x7c3aed映射为ColorSwatch::new((swatch, index), color)悬浮调用preview_color/clear_preview点击调用select_colorhex 输入用InputBase包裹state.hex_input().clone()鼠标按下时聚焦输入框根元素ColorPicker::new(example-color-picker).open(open).track_focus(focus_handle).accessibility_label(Brand color).on_open_change(...)内部放入触发器弹层用Popup::new(example-color-picker-popup, root)在打开态注入内容。状态实体在BaseShowcase::new中创建并常驻cx.new(|cx| ColorPickerState::new(window, cx).default_value(example_rgb(0x2563eb)))随后cx.observe(color_picker, |_, _, cx| cx.notify())让状态变化驱动重绘crates/base/examples/showcase/mod.rs。示例中同时演示了组件层的完整成品crates/component的ColorPicker::new(state)通过BaseColorPicker根 Popover 自绘ColorPickerButton组装弹出内容包含「Palette / HSLA」双 Tab、主题色板stone/red/orange/yellow/green/cyan/blue/purple/pink 九族取自DEFAULT_COLORS、可选的featured_colors与带渐变轨道的四个Slider见 crates/component/src/color_picker.rs。在组件层使用开箱即用版本若不想自己拼装crates/component提供了成品ColorPickeruse gpui_kit::component::color_picker::{ColorPicker, ColorPickerState, ColorPickerEvent}; // 创建状态务必把实体保留在父视图上 let color_picker cx.new(|cx| ColorPickerState::new(window, cx) .default_value(cx.theme().primary) ); // 渲染组件 ColorPicker::new(color_picker)常用定制项ColorPicker::new(color_picker) .featured_colors(vec![cx.theme().red, cx.theme().green, cx.theme().blue]) .icon(IconName::Palette) // 用图标替代颜色方块 .label(Background Color) .anchor(Anchor::TopRight) // 默认 Anchor::TopLeft .large() // 或 .small() / .xsmall()默认 medium尺寸通过Sizabletrait 支持xsmall/small/medium/largefeatured_colors默认回退到主题色red/red_light/blue/blue_light/green/green_light/yellow/yellow_light/cyan/cyan_light/magenta/magenta_light见 crates/component/src/color_picker.rs事件订阅与 base 层一致let _subscription cx.subscribe(color_picker, |_, _, ev, _| match ev { ColorPickerEvent::Change(color) { if let Some(color) color { println!(Selected color: {}, color.to_hex()); } } });无障碍要点绝不可仅用颜色传达选择必须提供文本化的颜色值hex与键盘操作途径根元素ColorPicker通过aria_expanded(open)暴露触发器展开态每个ColorSwatch以自身 hex 值作为可访问名称aria_label并同时声明aria_toggled与aria_selected色板保持键盘可达tab_index/tab_stop可配置Confirm/Cancel键绑定enter/escape见init中的 crates/base/src/color_picker.rs承担打开与关闭组件层的accessibility_label在存在时优先于可见label被播报测试an_explicit_accessibility_label_replaces_the_visible_one验证了「显式名称覆盖可见标签、且不改动绘制内容」的语义crates/component/src/color_picker.rs。状态持有与生命周期建议把ColorPickerState的实体保留在父视图父 render 类型字段或 GPUI 实体不要每次 render 重建持久实体在回调中更新状态后调用cx.notify()触发重绘不要为了写回状态而在每次 render 中重建实体default_value只在创建时生效后续请使用set_value/clear_value/select_color/update_color等运行时方法组件层的ColorPicker::new(state)使用state.entity_id()派生稳定元素 idcrates/component/src/color_picker.rs便于保持可预测的焦点与测试语义。Notes设计系统集成自检清单在可接受的地方使用稳定元素 ID如(swatch, index)、example-color-picker在消费方设计系统中验证以下外观focus焦点、hover悬浮、active激活、selected选中、disabled禁用、reduced-motion减弱动效、high-contrast高对比度——这些状态分别由ColorPicker/ColorSwatch的语义、样式 trait 与组件层的 hover/active 修饰共同支撑需要调试状态流时可参考 crates/base/src/color_picker.rs 的完整测试套件hex 解析、预览隔离、无效输入、提交路径、打开态控制与键盘行为均有对应用例。【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表