docs: clarify source viewing and host interaction boundaries

This commit is contained in:
2026-11-12 21:00:31 +08:00
parent b15d0872d7
commit 91801dc417
5 changed files with 19 additions and 5 deletions
+4 -1
View File
@@ -15,6 +15,7 @@ pub(super) struct SourceSelection {
lines: Vec<String>,
// 端点是 (行号, 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);
+1
View File
@@ -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 展开为四空格;原始文档文本保持不变。
+4 -3
View File
@@ -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<Vec<SourceFile>>;
/// 通过 provider 取得指定文件内容,不在工具查看动作中执行该内容。
/// SourceWorkspace::open 同步调用,按不透明 ID 读取完整文本;读取结果不进入 VM 执行队列。
fn open_file(&mut self, id: &str) -> Result<OpenedSource>;
}
#[derive(Debug, Default)]
/// Sources 云文件页面保留的 provider、列表、选择、内容与错误状态。
/// Sources 云文件页面保留的 provider、列表、已打开内容与错误状态;不另存列表选中 ID。
pub struct SourceWorkspace {
// None 表示未接入外部文件服务;默认工作区不提供虚构文件或隐式认证。
pub provider: Option<Box<dyn CloudSourceProvider>>,
+6 -1
View File
@@ -1,5 +1,5 @@
//! 即时绘制控件的跨帧交互状态:稳定 WidgetId、悬停、按下捕获、键盘焦点和释放激活。
//! 控件用语义 key 标识,而不是屏幕位置或行号。释放时必须仍命中同一个启用目标,拖出控件不能意外触发点击。
//! 控件用语义 key 标识,而不是屏幕位置或行号。释放时必须命中原启用目标;在外释放不激活,移回后释放仍可激活。
//!
use crate::geometry::{Point, Rect};
use std::hash::{Hash, Hasher};
@@ -62,7 +62,9 @@ pub struct Target<A> {
pub id: WidgetId,
// 本帧逻辑命中矩形,通常与绘制一致,行/分隔条可显式用不同的绘制与交互范围。
pub rect: Rect,
// 决定焦点资格、指针光标和 TextField 的按下处理例外;不会直接执行 action。
pub role: Role,
// 禁用仍参与几何命中以遮挡下层控件,但不能被 press 捕获、Tab 聚焦或释放激活。
pub enabled: bool,
// 激活时克隆交给宿主处理;分隔条/滚动条没有点击动作,拖动参数由上层布局另存。
pub action: Option<A>,
@@ -70,8 +72,11 @@ pub struct Target<A> {
#[derive(Clone, Copy, Debug)]
/// 绘制使用的组合交互状态,避免按钮自己重新推断焦点/捕获。
pub struct VisualState {
// enabled 且当前悬停身份匹配;与 selected 独立。
pub hovered: bool,
// 键盘捕获匹配或鼠标捕获且仍悬停;指针拖出只取消外观,不立即清掉捕获。
pub pressed: bool,
// 仅键盘来源焦点显示此状态;鼠标聚焦也能保存 focused 身份,但不会显示键盘焦点圈。
pub focused: bool,
// 由当前页面或工具数据决定的选中态,不从 hover/press 推断,禁用也不自动清除此数据状态。
pub selected: bool,
+4
View File
@@ -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] = [