docs: explain Tessera modules and execution flows in Chinese
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
//! winit 原生应用生命周期和窗口事件入口。这里创建窗口、发布逻辑视口、转发输入,并把不可变文档画到原生表面。
|
||||
//! 窗口线程不执行 Endor 业务回调;共享状态通过锁读取,实际 VM 更新由 runtime worker 串行完成。
|
||||
//! 侧栏、页面与开发工具有各自命中区域。拖动捕获必须保留最初的视口目标,不能随指针经过其他组件而跳转。
|
||||
//!
|
||||
use std::{
|
||||
num::NonZeroU32,
|
||||
sync::{Arc, RwLock},
|
||||
@@ -24,6 +28,7 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// worker 到原生事件循环的通知,携带更新/错误/退出状态而不是可执行 VM 引用。
|
||||
pub enum AppEvent {
|
||||
RuntimeFailed { message: String },
|
||||
DocumentUpdated,
|
||||
@@ -31,6 +36,7 @@ pub enum AppEvent {
|
||||
ConsoleFailed { message: String, source: String },
|
||||
}
|
||||
|
||||
/// 一个原生窗口的宿主状态;主窗口还协调子窗口资源,共用同一运行时与输入通道。
|
||||
pub struct TesseraApp {
|
||||
resource: u64,
|
||||
control: crate::api::RuntimeControl,
|
||||
@@ -63,6 +69,7 @@ pub struct TesseraApp {
|
||||
}
|
||||
|
||||
impl TesseraApp {
|
||||
/// 构造本类型并初始化其状态;资源建立和默认字段以函数体为准。
|
||||
pub fn new(
|
||||
namespace: Arc<RwLock<NamespaceState>>,
|
||||
activity: Activity,
|
||||
@@ -79,6 +86,7 @@ impl TesseraApp {
|
||||
))
|
||||
}
|
||||
|
||||
/// 建立窗口宿主状态并复用权威输入客户端,避免子窗口使用滞后的独立事件队列。
|
||||
fn with_event_client(
|
||||
namespace: Arc<RwLock<NamespaceState>>,
|
||||
activity: Activity,
|
||||
@@ -118,6 +126,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 将已发布窗口文档与原生资源对齐;session 更换时丢弃旧域的子窗口。
|
||||
fn reconcile_windows(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.resource != crate::window::MAIN_WINDOW {
|
||||
return;
|
||||
@@ -183,6 +192,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 只应用当前运行时身份的窗口命令;执行前从共享队列取走,避免重复执行。
|
||||
fn apply_window_commands(&mut self) {
|
||||
let Ok(mut namespace) = self.namespace.write() else {
|
||||
return;
|
||||
@@ -236,6 +246,7 @@ impl TesseraApp {
|
||||
self.publish_viewport();
|
||||
}
|
||||
|
||||
/// 将真实原生窗口尺寸换算成扣除侧栏的逻辑页面尺寸,并发布给 worker。
|
||||
fn publish_viewport(&self) {
|
||||
let Some(window) = &self.window else {
|
||||
return;
|
||||
@@ -280,12 +291,14 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 仅请求原生事件循环安排重绘,不在调用处立即递归绘制。
|
||||
fn request_redraw(&self) {
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// 通过托管开发工具入口打开指定工具页面,保留对应窗口上下文。
|
||||
fn open_developer_tool(&mut self, name: &str) {
|
||||
let input = self.namespace.read().ok().map(|namespace| {
|
||||
let frame = &namespace.document.frame;
|
||||
@@ -313,6 +326,7 @@ impl TesseraApp {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// 接收完整文档更新,协调子窗口和原生命令,再更新输入焦点并请求重绘。
|
||||
fn document_updated(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.reconcile_windows(event_loop);
|
||||
self.apply_window_commands();
|
||||
@@ -343,6 +357,7 @@ impl TesseraApp {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// 先处理宿主管理输入、选择与快捷键,再按焦点决定是否交给页面。
|
||||
fn handle_keyboard(&mut self, event: &winit::event::KeyEvent) {
|
||||
let command = self.modifiers.super_key() || self.modifiers.control_key();
|
||||
if command
|
||||
@@ -486,6 +501,7 @@ impl TesseraApp {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// 判断指针是否被侧栏或管理面板覆盖,阻止一次点击同时落入页面。
|
||||
fn guest_covered(&self, point: Point) -> bool {
|
||||
if self.sidebar.covers(point) {
|
||||
return true;
|
||||
@@ -501,6 +517,7 @@ impl TesseraApp {
|
||||
.contains(point)
|
||||
}
|
||||
|
||||
/// 排除宿主管理区域后,把原生指针转换到页面使用的坐标系。
|
||||
fn guest_point(&self) -> Option<Point> {
|
||||
if self.guest_covered(self.pointer) {
|
||||
return None;
|
||||
@@ -518,6 +535,7 @@ impl TesseraApp {
|
||||
Some(current.document.frame.surface.to_world(point))
|
||||
}
|
||||
|
||||
/// 从页面坐标与当前帧命中结果构造带版本的点击输入。
|
||||
fn guest_hit(&self) -> Option<crate::events::Input> {
|
||||
let point = self.guest_point()?;
|
||||
let current = self.namespace.read().ok()?;
|
||||
@@ -540,6 +558,7 @@ impl TesseraApp {
|
||||
})
|
||||
}
|
||||
|
||||
/// 将标签快捷键转换为当前窗口/帧中的明确页面动作。
|
||||
fn send_tab_shortcut(&mut self, close: bool) -> bool {
|
||||
let input = self.namespace.read().ok().and_then(|namespace| {
|
||||
let frame = &namespace.document.frame;
|
||||
@@ -577,6 +596,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理侧栏展开、工具入口和页面管理,尺寸变化还需重新发布可用视口。
|
||||
fn sidebar_action(&mut self, action: crate::sidebar::Action) {
|
||||
use crate::sidebar::Action;
|
||||
match action {
|
||||
@@ -619,6 +639,7 @@ impl TesseraApp {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
/// 在手势开始时捕获具体相机视口 ID,后续拖动保持同一目标。
|
||||
fn begin_pan(&mut self) -> bool {
|
||||
self.pan_viewport = None;
|
||||
let Some(point) = self.guest_point() else {
|
||||
@@ -632,6 +653,7 @@ impl TesseraApp {
|
||||
self.pan_viewport.is_some() || frame.surface.mode != crate::page::Mode::Window
|
||||
}
|
||||
|
||||
/// 把窗口位移/缩放转换为捕获视口的纯数据相机事件,处理侧栏和页面视角的尺度差异。
|
||||
fn page_view(&mut self, kind: &str, delta: [f32; 2]) {
|
||||
if !self.panning && self.guest_covered(self.pointer) {
|
||||
return;
|
||||
@@ -724,6 +746,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 把已经构造的页面输入交给有界客户端,保持 GUI 线程不直接调用 VM。
|
||||
fn send_guest(&mut self, event: crate::events::Input) {
|
||||
if !self.event_client.send(event) {
|
||||
self.developer_tools.input_error = "Native event queue is unavailable or full".into();
|
||||
@@ -731,6 +754,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 把页面拥有焦点时的键盘动作转换为托管事件,保留按下/释放配对。
|
||||
fn handle_guest_key(&mut self, event: &winit::event::KeyEvent) -> bool {
|
||||
let Key::Named(key) = &event.logical_key else {
|
||||
return false;
|
||||
@@ -800,6 +824,7 @@ impl TesseraApp {
|
||||
false
|
||||
}
|
||||
|
||||
/// 按平台窗口属性创建原生窗口、软件表面和输入法支持,再公布实际逻辑视口。
|
||||
fn create_native(&mut self, event_loop: &ActiveEventLoop) -> anyhow::Result<()> {
|
||||
if self.window.is_some() {
|
||||
return Ok(());
|
||||
@@ -847,6 +872,7 @@ impl TesseraApp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 停止新的交互并请求运行时清理;原生事件循环等待完成通知后退出。
|
||||
fn request_exit(&mut self) {
|
||||
if self.exit_requested {
|
||||
return;
|
||||
@@ -863,6 +889,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 把原生应用记录的最终错误反馈给 main 的退出结果。
|
||||
pub fn exit_result(&self) -> anyhow::Result<()> {
|
||||
if let Some(error) = &self.native_error {
|
||||
anyhow::bail!("{error}");
|
||||
@@ -870,6 +897,7 @@ impl TesseraApp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 包装一次实际绘制,统一记录故障并避免对同一错误反复发送通知。
|
||||
fn redraw(&mut self) {
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
@@ -891,6 +919,7 @@ impl TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 读取完整发布文档,生成整窗显示列表,写入原生像素表面并呈现;错误交由外层记录。
|
||||
fn try_redraw(&mut self) -> anyhow::Result<()> {
|
||||
let guest_cursor = self.guest_hit().is_some()
|
||||
|| self
|
||||
@@ -956,6 +985,7 @@ impl TesseraApp {
|
||||
}
|
||||
|
||||
impl ApplicationHandler<AppEvent> for TesseraApp {
|
||||
/// 处理 OS 直接结束事件循环的收尾,尝试在明确时限内等待运行时清理。
|
||||
fn exiting(&mut self, _: &ActiveEventLoop) {
|
||||
// OS termination can bypass CloseRequested. The loop is already ending;
|
||||
// use a bounded fallback rather than silently dropping every callback.
|
||||
@@ -973,8 +1003,10 @@ impl ApplicationHandler<AppEvent> for TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 当前不在进入等待前轮询工作;文档通知和原生事件负责唤醒循环,避免空闲忙转。
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {}
|
||||
|
||||
/// 应用获得原生活跃上下文时建立窗口,并仅在需要时请求初始页面。
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.resource == crate::window::MAIN_WINDOW
|
||||
&& !self.startup_page_requested
|
||||
@@ -998,6 +1030,7 @@ impl ApplicationHandler<AppEvent> for TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理 worker 发回的文档更新、故障和退出完成通知,不在 GUI 线程执行源回调。
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: AppEvent) {
|
||||
if self.control.is_exiting() && !matches!(&event, AppEvent::ShutdownComplete { .. }) {
|
||||
return;
|
||||
@@ -1029,6 +1062,7 @@ impl ApplicationHandler<AppEvent> for TesseraApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理原生尺寸、输入、焦点和关闭事件;先判断宿主控件,再转发页面事件。
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
|
||||
Reference in New Issue
Block a user