diff --git a/src/shell/developer_tools/selection.rs b/src/shell/developer_tools/selection.rs index 35cac1f..ac34a63 100644 --- a/src/shell/developer_tools/selection.rs +++ b/src/shell/developer_tools/selection.rs @@ -15,6 +15,7 @@ pub(super) struct SourceSelection { lines: Vec, // 端点是 (行号, UTF-8 字节偏移),字节位置始终由完整字素边界产生,不用字符数直接切片。 anchor: (usize, usize), + // 活动端可在锚点之前;range 才排序,导航保留拖选方向,不能始终从排序后起点移动。 end: (usize, usize), // DeveloperToolsState 的按下/移动/释放路径使用此标记,决定指针移动是否扩展选区并触发行滚动。 pub dragging: bool, @@ -106,7 +107,8 @@ impl SourceSelection { byte = 0; } } - // 垂直移动保持的是字素列数,不是像素 x;目标行较短时取尽其字素,落到行尾。 + // 垂直移动保持的是当前行字素列数,不是像素 x;目标行较短时落到行尾。 + // 没有保存独立的期望列:穿过短行再移动到长行时,按短行上的新列继续,不恢复最初列。 ArrowUp | ArrowDown => { // 记录当前前缀字素数量,在目标行最多消费同样数量,不能直接复用 UTF-8 字节偏移。 let col = line[..byte].graphemes(true).count(); @@ -164,6 +166,7 @@ impl SourceSelection { } /// 拼接当前选择范围内的实际文本,保持多行换行与 UTF-8 边界。 // DeveloperToolsState::selected_text 在复制路径调用,返回自有字符串;不直接写系统剪贴板。 + // lines 来自 Painter 去掉行号后的显示副本;快照已展开 Tab/统一 CRLF,不保证逐字节等于原始源码。 pub fn text(&self, all: bool) -> String { // 先统一正向范围,随后选中行顺序按文档正序输出,拖选方向不改变复制文本次序。 let (a, b) = self.range(all); diff --git a/src/shell/developer_tools/sources.rs b/src/shell/developer_tools/sources.rs index 35a1e1a..751975b 100644 --- a/src/shell/developer_tools/sources.rs +++ b/src/shell/developer_tools/sources.rs @@ -151,6 +151,7 @@ pub(super) fn build( .as_deref() .and_then(|id| doc.node(id)); // 展示副本统一换行并展开 Tab,所以选择复制的是工具显示文本;原 doc.source 保持不变。 + // 节点选中背景和鼠标 SourceSelection 高亮是两套状态:此处只标记 AST 覆盖的整行。 let code_lines = doc .source // 仅显示投影统一 CRLF 并把 Tab 展开为四空格;原始文档文本保持不变。 diff --git a/src/shell/sources.rs b/src/shell/sources.rs index 673809a..ad8e80f 100644 --- a/src/shell/sources.rs +++ b/src/shell/sources.rs @@ -1,4 +1,5 @@ //! 云文件浏览的 provider 抽象与工作区状态。默认 provider 未连接,不假装已有外部文件。 +//! 当前源码仅定义注入接口,没有具体 CloudSourceProvider 实现或启动时安装逻辑。 //! 列出和打开文件的结果作为工具数据展示,错误保持在当前工具状态中供界面说明。 //! //! Cloud source integration seam. The host installs a provider when one exists; @@ -25,13 +26,13 @@ pub struct OpenedSource { /// 外部文件能力接口;只负责列出和读取,不与 VM 全局环境耦合。 // 由宿主注入具体实现;Send 允许转移线程,但方法使用 &mut self,不声明可并行共享调用。 pub trait CloudSourceProvider: std::fmt::Debug + Send { - /// 返回 provider 当前可列出的文件;未连接状态由 SourceWorkspace 的 Option 表达。 + /// SourceWorkspace::refresh 同步调用,返回文件元数据;这里只声明接口,不规定网络协议或缓存策略。 fn list_files(&mut self) -> Result>; - /// 通过 provider 取得指定文件内容,不在工具查看动作中执行该内容。 + /// SourceWorkspace::open 同步调用,按不透明 ID 读取完整文本;读取结果不进入 VM 执行队列。 fn open_file(&mut self, id: &str) -> Result; } #[derive(Debug, Default)] -/// Sources 云文件页面保留的 provider、列表、选择、内容与错误状态。 +/// Sources 云文件页面保留的 provider、列表、已打开内容与错误状态;不另存列表选中 ID。 pub struct SourceWorkspace { // None 表示未接入外部文件服务;默认工作区不提供虚构文件或隐式认证。 pub provider: Option>, diff --git a/src/shell/system_ui/interaction.rs b/src/shell/system_ui/interaction.rs index e3689f2..c550397 100644 --- a/src/shell/system_ui/interaction.rs +++ b/src/shell/system_ui/interaction.rs @@ -1,5 +1,5 @@ //! 即时绘制控件的跨帧交互状态:稳定 WidgetId、悬停、按下捕获、键盘焦点和释放激活。 -//! 控件用语义 key 标识,而不是屏幕位置或行号。释放时必须仍命中同一个启用目标,拖出控件不能意外触发点击。 +//! 控件用语义 key 标识,而不是屏幕位置或行号。释放时必须命中原启用目标;在外释放不激活,移回后释放仍可激活。 //! use crate::geometry::{Point, Rect}; use std::hash::{Hash, Hasher}; @@ -62,7 +62,9 @@ pub struct Target { pub id: WidgetId, // 本帧逻辑命中矩形,通常与绘制一致,行/分隔条可显式用不同的绘制与交互范围。 pub rect: Rect, + // 决定焦点资格、指针光标和 TextField 的按下处理例外;不会直接执行 action。 pub role: Role, + // 禁用仍参与几何命中以遮挡下层控件,但不能被 press 捕获、Tab 聚焦或释放激活。 pub enabled: bool, // 激活时克隆交给宿主处理;分隔条/滚动条没有点击动作,拖动参数由上层布局另存。 pub action: Option, @@ -70,8 +72,11 @@ pub struct Target { #[derive(Clone, Copy, Debug)] /// 绘制使用的组合交互状态,避免按钮自己重新推断焦点/捕获。 pub struct VisualState { + // enabled 且当前悬停身份匹配;与 selected 独立。 pub hovered: bool, + // 键盘捕获匹配或鼠标捕获且仍悬停;指针拖出只取消外观,不立即清掉捕获。 pub pressed: bool, + // 仅键盘来源焦点显示此状态;鼠标聚焦也能保存 focused 身份,但不会显示键盘焦点圈。 pub focused: bool, // 由当前页面或工具数据决定的选中态,不从 hover/press 推断,禁用也不自动清除此数据状态。 pub selected: bool, diff --git a/src/shell/system_ui/theme.rs b/src/shell/system_ui/theme.rs index 8307915..5c08eb8 100644 --- a/src/shell/system_ui/theme.rs +++ b/src/shell/system_ui/theme.rs @@ -7,9 +7,13 @@ pub const BG: Color = Color::rgb(255, 255, 255); pub const BAR: Color = Color::rgb(230, 233, 237); // 控件边框与未悬停分隔线共用,强调状态由 Theme.accent 覆盖而不是改写这个常量。 pub const BORDER: Color = Color::rgb(226, 226, 226); +// 宿主正文/按钮的默认前景,widgets 和各开发工具画文字时共用。 pub const TEXT: Color = Color::rgb(35, 42, 53); +// 次要说明、图标与禁用样式的基础灰色,具体状态仍可由控件混色。 pub const MUTED: Color = Color::rgb(103, 114, 130); +// 开发工具错误文本与失败状态使用的红色,不参与运行时错误判定。 pub const ERROR: Color = Color::rgb(175, 42, 49); +// 开发工具成功状态使用的绿色;是否成功由数据层决定,不由颜色反推。 pub const OK: Color = Color::rgb(31, 121, 80); // 供设置页选择的六个强调色候选;Theme::new 还会按可读性规则调整传入颜色。 pub const PALETTE: [Color; 6] = [