Repository Wiki
ChanIok/SpinningMomo

信箱模式与预览

信箱模式与预览是 SpinningMomo 桌面端(Win32 原生悬浮窗)中的视觉增强能力:features::letterbox 通过一个可切换的 enabled 状态控制悬浮窗预览画面的"黑边"呈现,features::preview 负责预览画面的采集、渲染与交互。本页梳理两者的模块组成、切换命令链路、状态初始化与配置入口。

目的与范围

本页覆盖以下内容:

  • src/features/letterbox 模块的文件构成与对外接口(toggle_letterbox、LetterboxState)。
  • letterbox.toggle 命令在 src/core/commands/builtin.cpp 中的注册与回调链路(含触发悬浮窗重绘)。
  • src/core/initializer/initializer.cpp 启动时从 settings 同步 letterbox 启用状态的流程。
  • AppState 中 letterbox 状态的持有方式(前向声明 + unique_ptr)。
  • src/features/preview 模块的文件构成(capture / interaction / rendering / shaders / state)。
  • 相关 i18n 文案键(menu.letterbox_toggle)。

不属于本页范围、由兄弟页面承接的主题:

  • 悬浮窗口本身的创建与消息循环:参见 ui::*(floating_window)相关页面。
  • 设置(settings)持久化的整体机制:仅在此引用其 features.letterbox.enabled 字段。
  • 命令注册表(command registry)的通用基础设施:参见 core/commands 相关页面。
  • 录制(recording)、截图(screenshot)等与采集相关但彼此独立的能力。

诚实声明:本页在采证阶段受工具预算限制,仅完整核验了命令注册、初始化同步、状态持有与文件布局等证据点;letterbox.cpp / usecase.cpp 与 preview/rendering.cpp 等实现文件的内部算法未逐行读取,文中会明确标注"实现细节未在本次采证中核验"。

概述

信箱模式在视频/预览语境中通常指:当内容宽高比与显示区域不一致时,用黑色条带(letterbox bars)填充多余区域,避免画面拉伸变形。在 SpinningMomo 中:

  • 该能力被建模为一个布尔开关状态 LetterboxState::enabled,保存在全局 AppState 中。
  • 用户通过菜单命令 letterbox.toggle(i18n 文案 zh-CN 为"黑边模式",en-US 为 "Letterbox")切换该状态。
  • 切换回调在调用 toggle_letterbox 之后紧接着调用 ui::floating_window::request_repaint(state),即状态变更立即驱动悬浮窗重绘,让预览画面按新状态渲染。
  • 应用启动时(initializer.cpp),letterbox 的启用状态会从持久化 settings 中同步一次:state.letterbox->enabled = state.settings->raw.features.letterbox.enabled;,保证上次退出时的选择在下次启动时恢复。

features::preview 模块是预览画面的实现载体,按文件名划分为采集(capture)、交互、渲染、着色器与状态五部分(见下文"模块组成")。

从架构分层看(依据 AGENTS.md 对仓库结构的描述):core::* 提供框架基础设施(命令、初始化、状态等),features::* 承载业务逻辑(letterbox、preview 等),ui::* 提供 Win32 原生 UI(floating_window、tray_icon、context_menu、webview_window)。信箱模式正是这三层协作的最小样例:命令层触发 → 特性层改状态 → UI 层重绘。

架构

Loading diagram...

架构要点说明:

  • 命令层(sg_Commands):builtin.cpp 在命令注册表中登记 letterbox.toggle,并为其绑定 i18n 键 menu.letterbox_toggle,因此该命令天然出现在托盘/上下文菜单中并可本地化。
  • 特性层(sg_Letterbox / sg_Preview):letterbox 的全部业务状态收敛在 LetterboxState;行为入口收敛在自由函数 toggle_letterbox。preview 模块按文件职责拆分(采集、渲染、交互、着色器、状态),与渲染相关的细节未在本次采证中核验,虚线箭头表示"状态被消费"的推断关系。
  • 核心层(sg_Core):AppState 通过前向声明 + std::unique_ptr<features::letterbox::LetterboxState> 持有特性状态,避免在公共头文件中拉入特性实现头,降低编译耦合。initializer.cpp 负责把持久化设置灌入运行时状态。
  • UI 层(sg_UI):切换回调显式调用 request_repaint,说明重绘不是隐式副作用,而是命令回调里的一次显式请求——这是理解"状态变更如何变为像素变化"的关键一步。

模块组成

letterbox 模块文件构成

src/features/letterbox/ 目录下共 5 个文件(模块结构已在采证中通过 ListFiles 核验):

文件角色(依据仓库结构与命名惯例推断)
state.hpp定义 features::letterbox::LetterboxState,持有 enabled 等运行时状态
usecase.hpp / usecase.cpp声明并实现用例层自由函数,含 toggle_letterbox(state)
letterbox.hpp / letterbox.cpp模块对外的头文件与补充实现(内部细节未在本次采证中核验)

preview 模块文件构成

src/features/preview/ 目录下共 10 个文件(已核验):

文件角色(依据仓库结构与命名惯例推断)
state.hpp预览模块的运行时状态定义
capture.hpp / capture.cpp预览画面帧的采集入口
rendering.hpp / rendering.cpp预览画面的绘制/渲染实现
shaders.hpp渲染所用着色器(letterbox 黑边等视觉效果通常在此实现)
interaction.hpp / interaction.cpp预览区域内的鼠标/触摸等交互处理
preview.hpp / preview.cpp模块门面与整体流程编排

注意:上表"角色"列为基于文件命名的职责推断,rendering.cpp / shaders.hpp 内部如何实现信箱黑边(例如是否基于宽高比计算条带矩形、是否在 shader 中做 UV 裁剪)未在本次采证中核验。

核心流程

命令注册与切换回调

命令注册发生在 src/core/commands/builtin.cpp。该文件在头部包含了 letterbox 的状态与用例头:

cpp
#include "features/adb_mode/usecase.hpp" #include "features/letterbox/state.hpp" #include "features/letterbox/usecase.hpp"

Source: builtin.cpp

随后通过 register_command 登记 letterbox.toggle 命令,回调体在切换状态后立即请求悬浮窗重绘:

cpp
1register_command(registry, { 2 .id = "letterbox.toggle", 3 .i18n_key = "menu.letterbox_toggle", 4 // ... 其余注册字段未在本次采证中读取 5 }); 6// 回调(依据 Grep 上下文片段): 7// [&state]() { 8// features::letterbox::toggle_letterbox(state); 9// ui::floating_window::request_repaint(state); 10// }

Source: builtin.cpp

关键设计意图:

  1. 命令即菜单项:.i18n_key 指向 menu.letterbox_toggle,使得同一命令定义同时驱动菜单文案与行为,避免两处维护。
  2. 显式重绘请求:request_repaint 紧跟 toggle_letterbox,说明悬浮窗采用"请求-重绘"模式而非脏标记自动传播;任何新增的视觉状态切换都必须记得补上这一步,否则会出现状态已变但画面未更新的 bug。
  3. lambda 捕获方式:回调以 [&state] 捕获 AppState 引用,命令注册表与全局状态共享同一份 AppState 实例。

启动时状态同步

src/core/initializer/initializer.cpp 在初始化阶段把持久化设置灌入运行时状态:

cpp
// 从 settings 同步 letterbox 启用状态 state.letterbox->enabled = state.settings->raw.features.letterbox.enabled;

Source: initializer.cpp

该行上方还有 #include "features/letterbox/state.hpp"(见同文件 L27)。设计意图:settings 是"用户偏好的持久化事实",letterbox 运行时状态是"本次会话的可变事实",初始化时做一次单向同步,避免运行期间反向写回造成意外覆盖。

状态持有方式

src/core/state/app_state.hpp 使用前向声明 + 智能指针持有特性状态:

cpp
1namespace features::letterbox { 2struct LetterboxState; 3} 4 5// 在 AppState 成员区: 6std::unique_ptr<features::letterbox::LetterboxState> letterbox;

Source: app_state.hpp

cpp
letterbox(std::make_unique<features::letterbox::LetterboxState>()),

Source: app_state.cpp

设计意图:Pimpl 式持有让 app_state.hpp 无需包含 letterbox/state.hpp 的完整定义,抑制头文件级联编译依赖;同时保持生命周期归属清晰(由 AppState 独占管理)。

切换时序

Loading diagram...

国际化文案

键zh-CNen-US
menu.letterbox_toggle黑边模式Letterbox

Sources:

配置选项

配置项类型默认值说明
features.letterbox.enabledbool未见默认值(未在本次采证中核验)持久化的信箱模式开关;初始化时一次性灌入 LetterboxState::enabled
命令 letterbox.toggle——菜单可触发命令,i18n 键 menu.letterbox_toggle

说明:设置持久化(settings 的加载/保存/默认值定义)属于 settings 模块职责,本页仅记录与 letterbox 相关的这一个字段。

API 参考

features::letterbox::toggle_letterbox(state)

  • 说明:切换信箱模式启用状态的用例入口,由 letterbox.toggle 命令回调调用。
  • 参数:state — AppState 的引用(从回调中 [&state] 捕获获得;具体形参类型未在本次采证中核验)。
  • 返回值:未核验(回调中未使用返回值,推断为 void)。
  • 副作用:修改 LetterboxState::enabled;调用方需自行请求重绘(见命令回调中的 request_repaint)。

features::letterbox::LetterboxState

  • 说明:信箱模式运行时状态结构体,由 AppState 以 unique_ptr 独占持有。
  • 已核验字段:enabled(bool,见 initializer.cpp 中的赋值)。
  • 其余字段:未在本次采证中核验。

命令 letterbox.toggle

  • id:letterbox.toggle
  • i18n_key:menu.letterbox_toggle
  • 行为:调用 toggle_letterbox 后调用 ui::floating_window::request_repaint。
  • 注册位置:src/core/commands/builtin.cpp 的 register_command 调用。

故障模式、边界与并发

依据已核验源码可确认的行为:

  • 状态与画面不同步:request_repaint 是命令回调中的显式步骤。任何新代码路径若直接修改 LetterboxState::enabled 而不调用 request_repaint,将出现"状态已变、画面未更新"的可见 bug。这是本能力最典型的故障模式。
  • 初始化竞态:initializer.cpp 的同步发生在初始化阶段(一次性),之后运行期内的设置修改如何回写到 letterbox 状态(或是否需要重启/重新初始化才生效)未在本次采证中核验。
  • 并发:命令回调与渲染均围绕共享 AppState。是否所有访问都发生在同一线程(例如 Win32 消息循环线程)未在本次采证中核验;从 [&state] 直接捕获引用的模式看,至少命令回调假定 AppState 在其执行期间稳定存活。
  • 持久化一致性:settings->raw.features.letterbox.enabled 仅在启动时单向流入运行时状态;本次采证未发现 toggle 后反向写回 settings 的证据(是否存在自动持久化未核验)。

扩展点

  • 新增视觉开关:可完全复刻 letterbox 的三步模式——(1) 在特性状态结构体中加入布尔字段;(2) 在 builtin.cpp 用 register_command 注册新命令并绑定 i18n 键;(3) 回调中修改状态并调用 request_repaint。
  • 新增文案:在 src/locales/*.json 中补齐对应 menu.* 键即可获得多语言菜单项。
  • 渲染侧定制:preview/shaders.hpp 与 preview/rendering.cpp 是实现黑边/裁剪等视觉效果的落点(内部细节未在本次采证中核验)。

相关链接