ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

VNote 对话框模块开发指南:`*Dialog2` 架构、文案规范与交互模式实战

VNote 对话框模块开发指南:`*Dialog2` 架构、文案规范与交互模式实战 VNote 对话框模块开发指南*Dialog2架构、文案规范与交互模式实战【免费下载链接】vnoteA pleasant note-taking platform in native C.项目地址: https://gitcode.com/gh_mirrors/vn/vnote本指南以 VNote原生 C 笔记平台对话框模块的模块文档为核心系统讲解*Dialog2系列对话框与 Controller 的 MVC 配对架构、依赖注入方式、全项目强制执行的标签大小写与标点规范、Banner 静默抑制交互模式、Git 同步用户名绑定、新建笔记模板解析与图片尺寸编辑等关键实现。读完你将掌握在src/widgets/dialogs/下新增或修改对话框时必须遵守的架构约定、文案规则与可测试性要求并能直接对照仓库源码理解每一条规则背后的设计动机。一、模块架构*Dialog2与 Controller 的 MVC 配对VNote 的对话框全部位于 src/widgets/dialogs命名遵循*Dialog2约定。每个对话框都对应 src/controllers 下的一个 Controller二者构成标准的 MVC 配对Controller 拥有业务逻辑对话框是纯粹的 View 层只负责展示数据、采集用户输入并发出信号绝不直接修改数据。对话框通过构造函数注入依赖这是全项目统一的 Widget 构造模式// 接收依赖的构造函数注入VNote 项目标准模式 class MyDialog2 : public ScrollDialog { Q_OBJECT public: explicit MyDialog2(ServiceLocator p_services, QWidget *p_parent nullptr); private: ServiceLocator m_services; };以 NewNoteDialog2 为例它的构造函数接收ServiceLocator 并在内部创建NewNoteController随后setupUI()通过m_services.getFileTypeCoreService()、m_services.getNotebookCoreService()等解析所需服务。这一模式让 Controller 与 Dialog 都可以脱离 GUI 单独测试——Controller 不继承任何 QWidget业务逻辑不依赖界面存在。从源码结构看*Dialog2是第二代架构的产物2后缀源于早期单例架构迁移的历史遗留新代码不再添加后缀它们继承自ScrollDialogscrolldialog.h后者提供了可滚动的窗体骨架、setInformationText()信息横幅与QDialogButtonBox按钮区等基础能力。例外是ImageInsertDialog与ImageSizeDialog它们是遗留风格对话框无2后缀、不接收ServiceLocator由MarkdownEditor直接驱动详见下文图片尺寸编辑一节。二、标签大小写规范混合大小写方案VNote 对话框采用混合大小写mixed capitalization方案这是全项目标准且清理工作已全部完成。下表是该规范的全部条目元素风格示例表单/字段标签addRow命名相邻控件的QLabel句子式无尾冒号Local root folder、Remote URL、Output directory、Cursor mark专有名词或既定技术术语的表单标签保留Title Case仍无冒号Personal Access TokenQInputDialog提示语label参数句子式保留尾冒号Workspace name:、Enter new parent tag (empty for root):首字母缩写URL、PAT、JSON、HTTP在任何标签内保留规范大小写Remote URL、JSON path占位符QLineEdit内的灰色提示文字句子式无句尾句号Folder to clone into (must not exist or be empty)、Optional (empty to open as read-only)按钮QPushButton、QDialogButtonBox按钮Title CaseOpen、Browse、Disable Sync、Close Notebook窗口/对话框标题setWindowTitle、QFileDialog标题Title CaseOpen Notebook、Select Local Root Folder、Manage Notebooks工具提示Tooltip句子式以句号结尾Remote git URL. Only HTTPS and file:// schemes are supported.Banner/信息文本消息setInformationText句子式以句号结尾Local root folder must be empty (contains 3 item(s)).、Cloning...单选按钮/复选框标签句子式Local folder、Keep both、Expand TabComboBox 条目标签句子式仅首词大写Bundled notebook、No wrap、Web service、Local JAR为什么是这些规则表单标签用句子式与 macOS 及现代 GNOME/KDE 的系统原生惯例一致在密集型表单中阅读更快同时在视觉上与按钮区分开来。字段标签不加尾冒号与设置页保持一致。设置页是应用内最大的标签字段面所有行都通过SettingsPageHelper::createSettingRow构建为无冒号标签Auto save policy、Line ending、Content layout。当标签已位于表单布局列、紧邻其控件时冒号是冗余的。QInputDialog提示保留冒号因为它们是引入输入框的提示句而非列标签Qt 自带的对话框也是这么写的。按钮与标题用 Title Case与 Windows 及 Qt 内置控件一致——QDialogButtonBox自带的Open、Cancel、Save等本就是 Title Case。Tooltip 加句号使 tooltip 字符串可直接复用为qInfo()日志行以及 accessible-name / accessible-description 的来源。保留首字母缩写大小写避免把Personal Access Token拆成 OWASP 风格的Personal access token以免在 GitHub/GitLab 用户搜索 PAT 时造成困惑。存疑时的决策路径参考opennotebookdialog2.cpp与exportdialog2.cpp——它们是句子式、无冒号标签的参照对话框。如果你的新对话框以专有名词或领域专属多词术语为主如同步状态、凭据遵循最相近同级对话框的约定而非机械套用句子式。绝不要在改动标签字符串的同时不去全局 grep 旧字符串以及翻译.ts文件——标签对用户可见且可能被测试通过findChild(...)引用。测试查找应使用object name而非标签文本。测试发现规则objectName 优先测试通过findChild(objectName)查找对话框控件而非标签文本。因此每个*Dialog2中的可交互控件必须设置objectName命名模式为const char *kContentEditName newNoteContentEdit; m_contentEdit-setObjectName(QLatin1String(kContentEditName));如上所示kFooName是对话框.cpp文件顶部的常量参见 newnotedialog2.cpp 中的kContentEditName、kFileTypeComboName、kNameEditName、kEncryptCheckBoxName。修改标签 TEXT 是 UX 变更修改 objectName 是测试变更两者必须解耦。三、Banner 抑制模式静默对话框 UX部分对话框目前主要是refine-open-notebook-dialog之后的opennotebookdialog2会刻意抑制ScrollDialog::setInformationText横幅在特定字段变化时的更新使用户打字过程中对话框保持安静、尺寸稳定。模式的四个步骤验证结果结构体对话框内部的RemoteValidation在valid和message之外携带一个额外的bool surfaceInBanner false;标志。每个验证分支自行决定消息是否足够可操作、值得上横幅。URL 方案错误是静默的用户还在打字不想要横幅闪烁文件夹内容错误已存在的非空目录则立即上横幅因为用户已停止打字并点击了文件夹。updateOpenButtonState读取surfaceInBanner为真时调用setInformationText(message, Error)显示为假时调用setInformationText(QString(), Info)清除。克隆的开始/进度/失败/取消事件无论surfaceInBanner如何都始终显示——它们不是打字过程中的事件。源码实现可在 opennotebookdialog2.cpp 验证远程模式下URL/PAT 错误只禁用 Open 按钮、横幅保持安静而本地根文件夹的路径非法、非目录、非空含隐藏/系统条目、父目录不存在或不可写等分支都会把surfaceInBanner置为true见 validateRemoteInputs。何时使用该模式对话框同时拥有击键驱动型验证噪音大与离散操作型验证如选择文件夹时使用。何时不要用每条验证消息都具有同等可操作性时不要使用——常规setInformationText流程更简单。四、Markdown 列表设置自动重编号设置 → Markdown Editor → Edit暴露Automatically renumber ordered lists自动重编号有序列表选项。MarkdownEditorConfig将其持久化为editor.markdown_editor.autoNumberOrderedLists见 markdowneditorconfig.cppC 默认值为truemarkdowneditorconfig.h。通过ConfigMgr2的默认值合并机制旧配置文件会继承该默认值同时保留用户显式写入的false。两个编辑器构建器编辑态与只读态都通过共享的applyMarkdownConfigFields()映射消费该配置参见 markdowneditorcontroller.cpp 与设置页 markdowneditorpage.cpp。加载笔记或切换该选项不会对未修改的源码进行重编号只有在后续的结构性列表编辑增删条目时才触发重编号。列表装饰与编号相互独立。每个内置主题的src/data/extra/themes/*/text-editor.theme都在markdown-editor-styles中提供ListItemGuide.text-color与ActiveListItem.background-color要求每个调色板中引导线可见、激活填充比光标行填充更柔和。颜色属于主题资产而非 C 样式表字面量自定义主题若缺少某个样式该装饰即被禁用——不要注入浅色主题默认值。五、Git 同步用户名从 URL 派生、非破坏性修复Configure Sync / Sync Info与Open NotebookRemote URL均暴露gitUsernameEdit字段。Gitee 要求 PAT 所有者的账户登录名它可能与仓库所有者不同——不要从 URL 路径推断它。WidgetsFactory::createUrlUserNameEdit将该字段绑定到 HTTPS URL 的非机密用户名组件使用QUrl进行编码widgetsfactory.cpp。要点URL 是唯一事实来源加载/重置/取消流程都会恢复用户名现有 Controller 无需第二个配置键即可持久化它。Token 仍然独立密码掩码、绝不预填、只通过凭据存储credentials store保存。用户名输入框对本地文件远程仓库及远程打开过程中禁用。仅修改 HTTPS 用户名即可就地重新启用认证并保留本地 Git 历史——它不会走破坏性的仓库变更流程不重新 clone。OK 与 Apply 会等待applyComplete才清除编辑框OK 仅在成功时关闭对话框因此已保存 token 的检索不会被对话框销毁取消。createUrlUserNameEdit的双向绑定逻辑URL 文本变化时用QUrl(url).userName()回填编辑框并用QSignalBlocker避免回环用户编辑用户名时反过来把用户名写回 URL 的QUrl::FullyEncoded形式。opennotebookdialog2.cpp与notebooksyncinfodialog2.cpp都是它的消费方。六、新建笔记模板解析会话缓存优先配置默认兜底NewNoteDialog2按以下顺序挑选初始模板newnotedialog2.cpp会话缓存NewNoteDialog2::s_lastTemplateByFileType一个进程生命周期的QHashfileTypeName, templateName只在笔记成功创建后写入被拒绝的名称或创建失败不算最后使用的模板。键存在即生效即使值为空——显式的 None 会在本次运行剩余时间内一直生效。配置默认值WidgetConfig::getNewNoteDefaultTemplate(fileTypeName)widgetconfig.cpp由vnotex.json中的newNoteDefaultTemplates对象{fileTypeName: templateName}映射支持。当键缺失时 VNote 播种{Markdown: title.md}对象已存在包括空对象则视为用户选择绝不重新播种。如果两者提供的模板在磁盘上已不存在NoteTemplateSelector::setCurrentTemplate返回false选择器回退到 None并删除过期的会话条目让配置默认值获得第二次机会。模板解析在每次文件类型变化时都会重新运行包括用户在 Name 字段输入后缀所触发的隐式类型变化——默认值按文件类型区分必须跟随类型。一旦用户手动挑选了模板m_templateChosenByUser置位解析即停止跟随编程式选择通过m_templateSelectorMuted被排除在该标志之外。捕获对话框BodyMode::LiteralContent没有模板选择器因此既不读也不写会话缓存。快捷笔记路径无关此机制其模板名按 scheme 持久化在SessionConfig::QuickNoteScheme::m_template中。七、快捷笔记窗口偏好分离窗口 全局热键设置 → Quick Access → Quick Note将Open in detached window按 scheme 持久化为session.json中的quickNoteSchemes[].detachedView。缺失表示false以兼容旧 scheme。该标志必须纳入 scheme 相等性判断仅切换该选项的编辑也必须持久化。托盘、工具栏、键盘与标签页栏请求共享同一个选择器并尊重所选 scheme 的目的地快捷笔记仍以 Edit 模式打开。新 scheme 使用ConfigMgr2::getDefaultNotebookPath()与FirstRunController共享Qt 可写 Documents 位置下的my_notebook无显式主目录回退。该辅助函数仅计算路径启动创建仍要求版本变更且零打开笔记本。现有 scheme包括显式空的 Folder不迁移。SystemTrayHelper将配置的NewQuickNote绑定注册为 OS 级QHotkey由MainWindow2持有与唤醒热键一致。托盘与工具栏只显示快捷键文本工具栏不得注册重复的窗口级快捷键。全局激活触发受保护的托盘动作因此启动就绪与可重入性对两个入口同样适用活动模态对话框期间的请求被忽略。若干生命周期细节值得注意托盘项在ViewArea2::corePropagationReady之前及同步请求期间保持禁用。隐藏/最小化主窗口的选择器与错误均为无父窗口取消与创建失败绝不打开 buffer。分离 scheme 不打扰主窗口成功的非分离捕获在主窗口隐藏/最小化时走常规MainWindow2::showMainWindow()路径。无父选择器在exec()显示后排队raise()与activateWindow()——仅靠全局热键不会给对话框前景焦点。回调以选择器为作用域并跳过已关闭的选择器绝不激活隐藏的主窗口也不让选择器永久置顶来绕开焦点问题。MessageBoxHelper对无父消息包括快捷笔记创建错误使用相同的延迟激活有父消息框保留现有行为。八、对话框清单下表是src/widgets/dialogs下对话框与 Controller 的完整配对关系对话框Controller用途NewNoteDialog2NewNoteController新建笔记NewFolderDialog2NewFolderController新建文件夹NewNotebookDialog2NewNotebookController新建笔记本OpenNotebookDialog2OpenNotebookController打开已有笔记本本地或远程克隆通过OpenV3NotebookRequested结果码转交 V3 导入流程ManageNotebooksDialog2ManageNotebooksController笔记本管理ImportFolderDialog2ImportFolderController将外部文件夹导入为笔记本OpenVNote3NotebookDialog2遗留迁移导入 VNote3 笔记本NotebookSyncInfoDialog2NotebookSyncInfoController查看/编辑笔记本同步配置ExportDialog2export controller / 内联导出笔记NewQuickAccessItemDialog内联添加快捷访问条目在设置内使用SnippetInfoWidget2/ 代码片段对话框SnippetController代码片段元数据ImageInsertDialog内联MarkdownEditor插入图片也是尺寸编辑界面见下ImageSizeDialog内联MarkdownEditor对已有图片执行Image Set Size…注意NewNotebookDialog2与ManageNotebooksDialog2共享NotebookInfoWidget表单拥有字段布局与可编辑性对话框/Controller 保留创建、同步、验证与持久化职责。根目录与类型在创建后不可变不支持的字段保持可见但不可编辑只读文本仍可选中。setNotebookInfo()填充或重置字段时不发射inputEdited()因此加载笔记本永远不会弄脏管理对话框。创建时评估名称代码片段编辑既有笔记本时保留其字面名称。九、插入图片编码保留原始字节ImageInsertDialog::getImageData()对来自文件和 URL 的图片保留原始字节对剪贴板QImage图片文件插入使用 Qt 默认质量编码为 JPEGalpha 以原始像素尺寸独立于设备像素比合成到白色背景上。Base64 插入保留带 alpha 的无损 PNG选择图片文件绝不能改变该源。MarkdownEditor从编码后的字节推导保存/上传的文件名后缀。这意味着同一张图走不同通道可能得到不同编码——文件路径插入保留文件原样剪贴板插入经 JPEG 压缩Base64 插入保留 PNG alpha。十、图片尺寸编辑Set Size…与往返验证两个界面均为遗留风格对话框无2后缀、无ServiceLocator、由MarkdownEditor直接驱动遵循既有ImageInsertDialog的形态ImageInsertDialog增加可选的Width (px)/Height (px)字段。默认留空源图片自然尺寸仅作为placeholder文本出现。预填会让每次插入都变成 HTMLimg。ImageSizeDialog是Image Set Size…动作从光标下的图片预填。两个字段都留空表示无尺寸。任何非零尺寸都会让生成的引用变成 HTMLimg …/而非 Markdown 链接——Markdown 没有可移植的方式表达尺寸WxH仅本编辑器可理解其他工具大多不支持。vte::MarkdownUtils::generateImageLink(title, url, alt, w, h)在一个位置做出该选择。Set Size…转换表MarkdownEditor::setImageSize()markdowneditor.cpp实现全部转换。所有编辑通过单个QTextCursor编辑块完成一次撤销步骤按区间降序应用使较早的区间保持有效当前新尺寸结果Markdown非空用generateImageTag(alt, dest, title, w, h)替换该区域带WxH尺寸的 Markdown空用alt替换——去掉尺寸保持 Markdown无尺寸的 Markdown空无操作HTML非空就地编辑width/height缺失的一个插入到src之后移除新值为 0 的维度的每一处出现设置某个维度时更新第一次出现并移除所有后续重复HTML!hasUnknownAttrs() !hasDuplicateAttrs()且往返验证通过空用alt替换HTML其他情况空就地移除所有width/height保留标签Markdown 图片未必无尺寸WxH扩展由本编辑器的 cmark fork 解析并被PreviewMgr遵循因此ImageSizeDialog对带尺寸的图片会打开时预填而留空两者以移除尺寸的提示必须真正生效。绝不要重新生成 VNote 未创作的 HTML 标签——用户手写的class、style、data-*、loading及任何其他属性必须保留。这就是带尺寸场景采用属性编辑而非重发标签的原因。HTML → Markdown 的转换由验证过的往返把关而非字符黑名单。构建候选 Markdown 后用fetchImageLinks()重新解析并要求恰好一个覆盖整个候选区的图片且其解码后的 url、alt、title 与源完全一致见 markdownRoundTrips。黑名单被证明不够用裸a\_b.png目标解析回来变成a_b.png是一个悄然不同的文件。往返只比较有效先到先得属性值因此观察不到被丢弃的重复属性——这就是额外要求!hasDuplicateAttrs()前置条件的原因。清理width100 width200必须同时移除两者只揭露第二个会让图片仍被静默设定尺寸。解析与生成位于 vtextedit 子模块详见 libs/vtextedit/AGENTS.md § Image References。十一、大小写清理的完成状态跨新架构设置页settings/、settingswidget.cpp与*Dialog2对话框的句子式清理已全部完成内容字符串表单标签、复选框/单选标签、下拉框条目、分组/章节正文标签、tooltip、占位符使用句子式。按钮、窗口/对话框标题、设置页标题、SettingsPageHelper::addSection卡片标题保持 Title Case。专有名词/产品名PlantUml、MathJax、Graphviz、VNote、Vi、Git、首字母缩写URL、PAT、JSON、JAR、HTML、PDF、键盘按键名Tab、Ctrl以及既定术语Personal Access Token、Remote URL保留规范大小写。冒号清理同样完成src/widgets/下没有任何表单/字段标签以:结尾。仅存的尾冒号包括QInputDialog提示语按规则保留、markdowneditor.cpp中的消息体标题、syncconflictdialog2.cpp:64引入列表的句子式说明标签以及quickaccesspage.cpp:72中非可视的设置搜索词该字符串只传给addSearchItem从不渲染。翻译.ts文件src/data/core/translations/被有意保留未动未来的lupdate会刷新源条目。十二、相关模块src/widgets/AGENTS.md — Widget 模块总览、ViewArea2 框架、2后缀约定、Widget 构造模式src/controllers/AGENTS.md — 与这些对话框配对的 Controller 的 MVC 规则AGENTS.md — 项目级架构、MVC 规则、代码风格总结VNote 的对话框体系是一个严格遵循 MVC 与依赖注入的成熟实现——*Dialog2负责纯展示、Controller 承载逻辑、ServiceLocator贯穿全程。新对话框只要遵循本文的架构模式、文案规范表、objectName 测试规则并理解 Banner 抑制、模板解析、图片尺寸往返验证等既有交互的边界条件就能与整个代码库保持一致。【免费下载链接】vnoteA pleasant note-taking platform in native C.项目地址: https://gitcode.com/gh_mirrors/vn/vnote创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表