docs: explain Tessera modules and execution flows in Chinese
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
//! 即时绘制控件的跨帧交互状态:稳定 WidgetId、悬停、按下捕获、键盘焦点和释放激活。
|
||||
//! 控件用语义 key 标识,而不是屏幕位置或行号。释放时必须仍命中同一个启用目标,拖出控件不能意外触发点击。
|
||||
//!
|
||||
use crate::geometry::{Point, Rect};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// Stable across frames, resizing and text changes. Use semantic keys, never row indices.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
/// 由语义 key 生成的稳定控件身份,跨帧移动和文字变化不应改变它。
|
||||
pub struct WidgetId(u64);
|
||||
impl WidgetId {
|
||||
/// 构造本类型并初始化其状态;资源建立和默认字段以函数体为准。
|
||||
pub fn new(key: impl Hash) -> Self {
|
||||
let mut hash = std::collections::hash_map::DefaultHasher::new();
|
||||
key.hash(&mut hash);
|
||||
@@ -12,11 +17,13 @@ impl WidgetId {
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
/// 分隔条和滚动条的操作方向。
|
||||
pub enum Axis {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
/// 管理控件角色,决定焦点资格和系统光标。
|
||||
pub enum Role {
|
||||
Button,
|
||||
Tab,
|
||||
@@ -28,11 +35,13 @@ pub enum Role {
|
||||
Scrollbar(Axis),
|
||||
}
|
||||
impl Role {
|
||||
/// 判断控件角色是否可以接受键盘焦点。
|
||||
fn focusable(self) -> bool {
|
||||
!matches!(self, Self::Splitter(_) | Self::Scrollbar(_) | Self::Row)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
/// 宿主控件请求的系统光标种类。
|
||||
pub enum Cursor {
|
||||
Default,
|
||||
Pointer,
|
||||
@@ -43,6 +52,7 @@ pub enum Cursor {
|
||||
Disabled,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
/// 本帧可命中的管理控件,绑定几何、角色、启用条件与可选 action。
|
||||
pub struct Target<A> {
|
||||
pub id: WidgetId,
|
||||
pub rect: Rect,
|
||||
@@ -51,6 +61,7 @@ pub struct Target<A> {
|
||||
pub action: Option<A>,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// 绘制使用的组合交互状态,避免按钮自己重新推断焦点/捕获。
|
||||
pub struct VisualState {
|
||||
pub hovered: bool,
|
||||
pub pressed: bool,
|
||||
@@ -62,6 +73,7 @@ pub struct VisualState {
|
||||
/// Retained input state for an immediate-mode frame. Actions fire on release inside
|
||||
/// the same enabled control, so dragging away cancels a click.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
/// 即时绘制之外保留的输入状态,确保按下与释放跨帧仍匹配同一控件。
|
||||
pub struct Interaction {
|
||||
pointer: Option<Point>,
|
||||
hovered: Option<WidgetId>,
|
||||
@@ -71,18 +83,22 @@ pub struct Interaction {
|
||||
keyboard_focus: bool,
|
||||
}
|
||||
impl Interaction {
|
||||
/// 按逆绘制顺序选择指针处的管理控件,避免重叠控件同时激活。
|
||||
pub fn hit<'a, A>(&self, targets: &'a [Target<A>]) -> Option<&'a Target<A>> {
|
||||
let p = self.pointer?;
|
||||
targets.iter().rev().find(|t| t.rect.contains(p))
|
||||
}
|
||||
/// 更新指针与悬停目标,但不在移动时直接触发点击。
|
||||
pub fn pointer_moved<A>(&mut self, p: Point, targets: &[Target<A>]) {
|
||||
self.pointer = Some(p);
|
||||
self.hovered = self.hit(targets).map(|t| t.id);
|
||||
}
|
||||
/// 清除离开窗口后的指针/悬停状态。
|
||||
pub fn pointer_left(&mut self) {
|
||||
self.pointer = None;
|
||||
self.hovered = None;
|
||||
}
|
||||
/// 捕获启用控件的稳定身份,并按角色更新键盘焦点。
|
||||
pub fn press<A: Clone>(&mut self, targets: &[Target<A>]) -> Option<Target<A>> {
|
||||
self.key_active = None;
|
||||
self.keyboard_focus = false;
|
||||
@@ -91,22 +107,26 @@ impl Interaction {
|
||||
self.focused = target.as_ref().filter(|t| t.role.focusable()).map(|t| t.id);
|
||||
target
|
||||
}
|
||||
/// 只有释放时仍处于原启用目标内才返回 action,拖出后释放不触发。
|
||||
pub fn release<A: Clone>(&mut self, targets: &[Target<A>]) -> Option<A> {
|
||||
let active = self.pointer_active.take()?;
|
||||
self.hit(targets)
|
||||
.filter(|t| t.enabled && t.id == active && t.role != Role::TextField)
|
||||
.and_then(|t| t.action.clone())
|
||||
}
|
||||
/// 撤销当前操作的后续交付资格,并执行对应取消清理。
|
||||
pub fn cancel(&mut self) {
|
||||
self.pointer_active = None;
|
||||
self.key_active = None;
|
||||
}
|
||||
/// 撤销失焦后的键盘/指针激活状态,防止回到窗口时补发旧操作。
|
||||
pub fn blur(&mut self) {
|
||||
self.cancel();
|
||||
self.focused = None;
|
||||
self.keyboard_focus = false;
|
||||
self.pointer_left();
|
||||
}
|
||||
/// 用本帧目标集合校正跨帧焦点和捕获,已消失控件不保留可激活身份。
|
||||
pub fn reconcile<A>(&mut self, targets: &[Target<A>]) -> bool {
|
||||
let before = (
|
||||
self.hovered,
|
||||
@@ -127,11 +147,13 @@ impl Interaction {
|
||||
self.key_active,
|
||||
)
|
||||
}
|
||||
/// 返回当前仍持有焦点的稳定控件身份。
|
||||
pub fn focused<'a, A>(&self, targets: &'a [Target<A>]) -> Option<&'a Target<A>> {
|
||||
targets
|
||||
.iter()
|
||||
.find(|t| Some(t.id) == self.focused && t.enabled)
|
||||
}
|
||||
/// 沿启用且可聚焦的目标循环导航,支持正向与反向遍历。
|
||||
pub fn focus_next<A: Clone>(&mut self, targets: &[Target<A>], back: bool) -> Option<Target<A>> {
|
||||
self.cancel();
|
||||
self.keyboard_focus = true;
|
||||
@@ -154,18 +176,21 @@ impl Interaction {
|
||||
self.focused = Some(target.id);
|
||||
Some(target.clone())
|
||||
}
|
||||
/// 为当前焦点控件记录键盘激活,后续释放仍须匹配目标。
|
||||
pub fn key_press<A>(&mut self, targets: &[Target<A>]) {
|
||||
self.key_active = self
|
||||
.focused(targets)
|
||||
.filter(|t| t.role != Role::TextField)
|
||||
.map(|t| t.id);
|
||||
}
|
||||
/// 结束键盘激活并按有效控件身份返回操作。
|
||||
pub fn key_release<A: Clone>(&mut self, targets: &[Target<A>]) -> Option<A> {
|
||||
let active = self.key_active.take()?;
|
||||
self.focused(targets)
|
||||
.filter(|t| t.id == active)
|
||||
.and_then(|t| t.action.clone())
|
||||
}
|
||||
/// 组合悬停、按下、焦点、选中和启用状态,供控件选择正确样式。
|
||||
pub fn visual(&self, id: WidgetId, selected: bool, enabled: bool) -> VisualState {
|
||||
let hovered = enabled && self.hovered == Some(id);
|
||||
VisualState {
|
||||
@@ -177,9 +202,11 @@ impl Interaction {
|
||||
enabled,
|
||||
}
|
||||
}
|
||||
/// 返回本次拖动/按压捕获的控件,移动过程中不随悬停目标跳变。
|
||||
pub fn captured(&self, id: WidgetId) -> bool {
|
||||
self.pointer_active == Some(id)
|
||||
}
|
||||
/// 按命中角色选择系统光标,例如文本、分隔条或可点击按钮。
|
||||
pub fn cursor<A>(&self, targets: &[Target<A>]) -> Cursor {
|
||||
let target = self
|
||||
.pointer_active
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 宿主管理控件的共享组件入口。主题、绘制原语和跨帧交互状态分离,用户 Endor 页面不依赖这里的控件树。
|
||||
//!
|
||||
//! Tessera's host management widgets. No Endor AST, VM, UiNode or ui_bridge dependency.
|
||||
//! Shares only low-level drawing, font shaping and geometry with guest content.
|
||||
mod interaction;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 宿主控件的颜色与状态调色。根据背景、强调色和交互状态生成可读颜色,不改变业务状态。
|
||||
//!
|
||||
use crate::display_list::Color;
|
||||
pub const BG: Color = Color::rgb(255, 255, 255);
|
||||
// A visibly separate chrome layer against the warm, near-white content canvas.
|
||||
@@ -16,25 +18,31 @@ pub const PALETTE: [Color; 6] = [
|
||||
Color::rgb(52, 123, 78),
|
||||
];
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// 当前管理界面的配色基础,派生状态颜色由方法统一计算。
|
||||
pub struct Theme {
|
||||
pub accent: Color,
|
||||
}
|
||||
impl Theme {
|
||||
/// 构造本类型并初始化其状态;资源建立和默认字段以函数体为准。
|
||||
pub fn new(accent: Color) -> Self {
|
||||
Self {
|
||||
accent: readable_accent(accent),
|
||||
}
|
||||
}
|
||||
/// 从当前主题生成对应交互状态颜色,保持控件在状态变化时的视觉一致性。
|
||||
pub fn soft(self) -> Color {
|
||||
mix(BG, self.accent, 0.10)
|
||||
}
|
||||
/// 从当前主题生成对应交互状态颜色,保持控件在状态变化时的视觉一致性。
|
||||
pub fn hover(self) -> Color {
|
||||
mix(BG, self.accent, 0.17)
|
||||
}
|
||||
/// 从当前主题生成对应交互状态颜色,保持控件在状态变化时的视觉一致性。
|
||||
pub fn pressed(self) -> Color {
|
||||
mix(BG, self.accent, 0.29)
|
||||
}
|
||||
}
|
||||
/// 按比例混合颜色通道,作为状态颜色的基础运算。
|
||||
pub fn mix(a: Color, b: Color, amount: f32) -> Color {
|
||||
let channel =
|
||||
|a: u8, b: u8| (a as f32 + (b as f32 - a as f32) * amount.clamp(0.0, 1.0)).round() as u8;
|
||||
@@ -44,6 +52,7 @@ pub fn mix(a: Color, b: Color, amount: f32) -> Color {
|
||||
channel(a.blue, b.blue),
|
||||
)
|
||||
}
|
||||
/// 根据背景选择可读的强调色,供选中与焦点状态使用。
|
||||
pub fn readable_accent(mut c: Color) -> Color {
|
||||
while (c.red as u32 * 299 + c.green as u32 * 587 + c.blue as u32 * 114) / 1000 > 155 {
|
||||
c = Color::rgb(
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! 用统一主题绘制宿主管理控件,并同步注册交互目标。
|
||||
//! 每个控件的图形、角色、启用状态和 action 一起登记,绘制几何与命中几何保持一致。
|
||||
//!
|
||||
use super::{
|
||||
Axis, Interaction, Role, Target, VisualState, WidgetId,
|
||||
theme::{self, Theme},
|
||||
@@ -9,6 +12,7 @@ use crate::{
|
||||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// 一次按钮绘制的借用描述,包括显示、角色和启用/选中状态。
|
||||
pub struct Button<'a> {
|
||||
pub label: &'a str,
|
||||
pub selected: bool,
|
||||
@@ -16,6 +20,7 @@ pub struct Button<'a> {
|
||||
pub role: Role,
|
||||
}
|
||||
impl<'a> Button<'a> {
|
||||
/// 构造本类型并初始化其状态;资源建立和默认字段以函数体为准。
|
||||
pub fn new(label: &'a str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
@@ -24,20 +29,24 @@ impl<'a> Button<'a> {
|
||||
role: Role::Button,
|
||||
}
|
||||
}
|
||||
/// 设置当前控件描述的选择、启用或角色信息,供后续绘制和目标登记使用。
|
||||
pub fn selected(mut self, value: bool) -> Self {
|
||||
self.selected = value;
|
||||
self
|
||||
}
|
||||
/// 设置当前控件描述的选择、启用或角色信息,供后续绘制和目标登记使用。
|
||||
pub fn enabled(mut self, value: bool) -> Self {
|
||||
self.enabled = value;
|
||||
self
|
||||
}
|
||||
/// 设置当前控件描述的选择、启用或角色信息,供后续绘制和目标登记使用。
|
||||
pub fn role(mut self, value: Role) -> Self {
|
||||
self.role = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// 本帧宿主控件绘制上下文,共同拥有显示列表与交互目标登记入口。
|
||||
pub struct Ui<A> {
|
||||
pub list: DisplayList,
|
||||
pub targets: Vec<Target<A>>,
|
||||
@@ -45,6 +54,7 @@ pub struct Ui<A> {
|
||||
interaction: Interaction,
|
||||
}
|
||||
impl<A: Hash> Ui<A> {
|
||||
/// 构造本类型并初始化其状态;资源建立和默认字段以函数体为准。
|
||||
pub fn new(theme: Theme, interaction: Interaction) -> Self {
|
||||
Self {
|
||||
list: DisplayList::default(),
|
||||
@@ -53,6 +63,7 @@ impl<A: Hash> Ui<A> {
|
||||
interaction,
|
||||
}
|
||||
}
|
||||
/// 使用当前主题绘制控件填充、轮廓或文字原语。
|
||||
pub fn fill(&mut self, r: Rect, color: Color) {
|
||||
self.list.push(DrawCommand::FillRoundedRect {
|
||||
rect: r,
|
||||
@@ -60,6 +71,7 @@ impl<A: Hash> Ui<A> {
|
||||
color,
|
||||
});
|
||||
}
|
||||
/// 使用当前主题绘制控件填充、轮廓或文字原语。
|
||||
fn outline(&mut self, r: Rect, color: Color, width: f32) {
|
||||
self.list.push(DrawCommand::StrokeRoundedRect {
|
||||
rect: Rect::new(
|
||||
@@ -73,6 +85,7 @@ impl<A: Hash> Ui<A> {
|
||||
color,
|
||||
});
|
||||
}
|
||||
/// 使用当前主题绘制控件填充、轮廓或文字原语。
|
||||
pub fn label(&mut self, x: f32, y: f32, value: &str, color: Color, code: bool) {
|
||||
self.list.push(DrawCommand::Text {
|
||||
origin: Point::new(x, y),
|
||||
@@ -89,6 +102,7 @@ impl<A: Hash> Ui<A> {
|
||||
color,
|
||||
});
|
||||
}
|
||||
/// 把控件身份、几何、角色、启用状态和 action 一起加入本帧交互目标。
|
||||
fn register(
|
||||
&mut self,
|
||||
id: WidgetId,
|
||||
@@ -108,6 +122,7 @@ impl<A: Hash> Ui<A> {
|
||||
});
|
||||
response
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
fn surface(&mut self, rect: Rect, s: VisualState, base: Option<Color>) {
|
||||
let color = if !s.enabled {
|
||||
base
|
||||
@@ -127,6 +142,7 @@ impl<A: Hash> Ui<A> {
|
||||
self.outline(rect, self.theme.accent, 1.5);
|
||||
}
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn button(&mut self, rect: Rect, action: A, button: Button<'_>) -> VisualState {
|
||||
let id = WidgetId::new(&action);
|
||||
let s = self.register(
|
||||
@@ -168,6 +184,7 @@ impl<A: Hash> Ui<A> {
|
||||
self.list.push(DrawCommand::PopClip);
|
||||
s
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn text_field(&mut self, rect: Rect, action: A, focused: bool) {
|
||||
let s = self.register(
|
||||
WidgetId::new(&action),
|
||||
@@ -191,6 +208,7 @@ impl<A: Hash> Ui<A> {
|
||||
);
|
||||
}
|
||||
/// A terminal prompt shares its surface with output; its caret is the focus cue.
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn inline_input(&mut self, rect: Rect, action: A) {
|
||||
self.register(
|
||||
WidgetId::new(&action),
|
||||
@@ -201,6 +219,7 @@ impl<A: Hash> Ui<A> {
|
||||
true,
|
||||
);
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn row(&mut self, paint: Rect, hit: Rect, action: A, selected: bool) {
|
||||
let s = self.register(
|
||||
WidgetId::new(&action),
|
||||
@@ -212,6 +231,7 @@ impl<A: Hash> Ui<A> {
|
||||
);
|
||||
self.surface(paint, s, None);
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn swatch(&mut self, rect: Rect, action: A, color: Color, selected: bool) {
|
||||
let s = self.register(
|
||||
WidgetId::new(&action),
|
||||
@@ -249,6 +269,7 @@ impl<A: Hash> Ui<A> {
|
||||
self.label(rect.x + 10.0, rect.y + 6.0, "✓", theme::BG, false);
|
||||
}
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn splitter(&mut self, rect: Rect, id: WidgetId, axis: Axis) {
|
||||
let hit = Rect::new(
|
||||
rect.x - 3.0,
|
||||
@@ -273,6 +294,7 @@ impl<A: Hash> Ui<A> {
|
||||
};
|
||||
self.fill(line, color);
|
||||
}
|
||||
/// 绘制对应宿主管理控件,并使用同一矩形注册命中/捕获所需的交互信息。
|
||||
pub fn scrollbar(&mut self, rect: Rect, id: WidgetId, axis: Axis) {
|
||||
let mut s = self.register(id, rect, Role::Scrollbar(axis), None, false, true);
|
||||
s.pressed |= self.interaction.captured(id);
|
||||
|
||||
Reference in New Issue
Block a user