//! 绘制宿主设置页面和主题选择区域。 //! 这里只投影当前设置和交互目标,平台原生窗口行为由对应系统适配层负责。 //! use super::*; /// 绘制强调色候选、自定义颜色编辑框及主题预览;实际应用和错误校验由输入动作处理。 // developer_tools::build 选择 Settings 时调用,产生工具控件和 Action;颜色解析与主题应用由输入处理路径完成。 pub(super) fn build(p: &mut Painter, state: &mut DeveloperToolsState, viewport: Rect) { // 强调色、编辑框和预览使用同一工具坐标原点,宽度来自宿主分配,不读取业务 Page 布局。 let x = viewport.x; let w = viewport.width; p.label(x + 24.0, 62.0, "Appearance", TEXT, false); p.label( x + 24.0, 90.0, "Accent color · tabs, focus and execution separators", MUTED, false, ); // 色块动作携带调色板索引,绘制按完整 Color 相等标记选中;实际修改由统一动作处理器执行。 // Theme 动作同步本槽 custom 文本;切换工具只传播 accent,不保证各槽旧 custom 文本始终等于当前主题。 for (i, color) in PALETTE.iter().enumerate() { // 每个调色板项固定宽度和间距,索引 i 作为 Action::Theme 的参数与 PALETTE 一一对应。 let r = Rect::new(x + 24.0 + i as f32 * 44.0, 122.0, 32.0, 32.0); p.ui.swatch(r, Action::Theme(i), *color, *color == state.accent); } p.label( x + 24.0, 176.0, "Custom hex · Enter to apply (bright colors adjusted)", MUTED, false, ); // 自定义颜色框显示尚未应用的编辑文本;聚焦只改变外观和输入接收位置,不在这里解析 hex。 let custom = Rect::new(x + 24.0, 204.0, 200.0, 32.0); p.ui.text_field( custom, Action::Focus(Focus::Custom), state.focus == Some(Focus::Custom), ); p.label( custom.x + 10.0, custom.y + 6.0, &format!( "{}{}", state.custom, // 尾部竖线只提示焦点,state.custom 仍是未提交编辑值,不能因显示该值就认为主题已更新。 if state.focus == Some(Focus::Custom) { "│" } else { "" } ), TEXT, true, ); // 校验错误由输入处理路径写入,设置页只显示;错误不会在每次重绘时重复执行或清除。 // 只有已有错误才画错误行,不在 build 中重新校验或清除,保留用户提交操作的反馈。 if !state.input_error.is_empty() { p.label(x + 24.0, 245.0, &state.input_error, ERROR, false); } p.fill(Rect::new(x + 24.0, 285.0, w - 48.0, 1.0), BORDER); p.label(x + 24.0, 305.0, "Theme preview", TEXT, false); // 预览复用当前强调色和标准成功色,让用户看到主题如何影响执行分隔线;示例文字不送入 VM。 p.fill(Rect::new(x + 24.0, 340.0, w - 48.0, 2.0), state.accent); p.label(x + 32.0, 353.0, "› \"Hello\"", TEXT, true); p.label(x + 32.0, 380.0, "‹ \"Hello\"", OK, true); p.label( x + 24.0, 430.0, "Settings last for this window session. Source is not persisted.", MUTED, false, ); }