Skip to content

feat(markdown): integrate three-view orchestration in EditWrapper - #532

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
pengfeixx:md-editor-integration
Aug 20, 2026
Merged

feat(markdown): integrate three-view orchestration in EditWrapper#532
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
pengfeixx:md-editor-integration

Conversation

@pengfeixx

@pengfeixx pengfeixx commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Add a three-page view stack (edit page, read page, live-preview splitter) in EditWrapper with lazy-created MarkdownView. Wire 300ms throttled content sync with mdimg image rewrite, bidirectional ratio scroll sync with loop guard, theme injection on change and creation, markdown recognition on load and language switch with fallback/elevate mode transitions, and explicit show() after cross-container reparent.

EditWrapper 集成三页视图栈(编辑页/查看页/实时预览分栏),懒创建
MarkdownView;接入 300ms 节流内容同步(含 mdimg 图片路径改写)、带
防回环护栏的双向比例滚动同步、变更与创建时的主题注入、文件加载与
语言切换时的 Markdown 识别及模式回退/跃迁;跨容器 reparent 后显式
show() 防分栏零宽。

Log: 集成 Markdown 三视图编排与同步机制
PMS: TASK-393979
Influence: 打开 .md 默认实时预览;语言切为 Markdown 自动进入预览、
切走自动回退编辑视图。

Summary by Sourcery

Integrate coordinated Markdown editing, reading, and live-preview modes into EditWrapper.

New Features:

  • Add Markdown read and live-preview modes alongside the existing editor view, with automatic mode availability based on file type and language changes.
  • Provide synchronized Markdown rendering, proportional bidirectional scrolling, and theme updates across editor and preview views.

Bug Fixes:

  • Prevent preview panes from becoming invisible after moving them between containers.

Enhancements:

  • Create Markdown rendering and live-preview components lazily while preserving editor state across view transitions.
  • Support Markdown image path resolution and controlled content refreshes during editing.
  • Keep view-mode controls synchronized across the bottom bar, editor context menu, and preview context menu.

Tests:

  • Add renderer injection support to facilitate testing without starting the real Markdown rendering view.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @pengfeixx, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@pengfeixx
pengfeixx force-pushed the md-editor-integration branch from 13a33fc to bd6c294 Compare August 20, 2026 01:56
@sourcery-ai

sourcery-ai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR adds a three-view Markdown orchestration to EditWrapper (edit, read-only render, and live preview splitter), with lazy MarkdownView creation, a shared renderer interface, debounced content syncing, bidirectional scroll syncing, theme propagation, and automatic mode transitions based on Markdown detection and language changes.

Sequence diagram for Markdown file load and automatic view mode selection

sequenceDiagram
    actor User
    participant EditWrapper
    participant MarkdownLogic
    participant ViewModeFsm
    participant IMarkdownRenderer

    User->>EditWrapper: reinitOnFileLoad(encode)
    EditWrapper->>EditWrapper: updateMarkdownRecognition(filePath, definitionName)
    EditWrapper->>MarkdownLogic: isMarkdown(filePath, definitionName)
    MarkdownLogic-->>EditWrapper: isMarkdown
    EditWrapper->>ViewModeFsm: resolveDefaultMode(isMarkdown)
    ViewModeFsm-->>EditWrapper: ViewMode
    EditWrapper->>EditWrapper: setViewMode(mode)
    EditWrapper->>ViewModeFsm: canSwitchTo(mode, isMarkdown)
    ViewModeFsm-->>EditWrapper: allowed
    EditWrapper->>EditWrapper: ensureMarkdownViewCreated()
    EditWrapper->>EditWrapper: ensureLiveSplitterCreated()
    EditWrapper->>IMarkdownRenderer: setLayout(width, center)
    EditWrapper->>IMarkdownRenderer: setMarkdown(content)
    EditWrapper-->>User: viewModeChanged(mode)
Loading

File-Level Changes

Change Details Files
Introduce a three-page view stack (edit, read-only view, live preview splitter) and associated Markdown view/rendering infrastructure in EditWrapper.
  • Replace the direct editor layout with a QStackedWidget containing an edit page widget and a read page widget.
  • Add QSplitter-based live preview page creation for side-by-side edit and render views, including splitter sizing and stretch configuration.
  • Add member state for current view mode, Markdown detection flag, stacked pages, splitter, MarkdownView pointer, and a generic IMarkdownRenderer interface.
src/editor/editwrapper.cpp
src/editor/editwrapper.h
Implement view mode FSM-based switching, Markdown recognition, and automatic fallback/elevate behavior on file load and syntax definition changes.
  • Add setViewMode(ViewMode) that delegates mode validity and text-only read mode decisions to ViewModeFsm, switches pages, and manages read-only state.
  • Implement updateMarkdownRecognition() to detect Markdown via MarkdownLogic, emit markdownAvailabilityChanged, and apply FSM-driven fallback/elevate behavior when Markdown is lost or gained.
  • Call updateMarkdownRecognition and default-mode resolution during file load and reloadFileHighlight to keep view mode in sync with language changes.
src/editor/editwrapper.cpp
src/editor/editwrapper.h
Add lazy creation and initialization of MarkdownView, including render readiness, theme application, scroll sync, and custom context menu integration.
  • Implement ensureMarkdownViewCreated() to construct MarkdownView only when needed, initialize it (except in test builds), wire its ready signal to RenderThrottle, and attach it to the read page layout.
  • Wire MarkdownView scrollRatioChanged to drive TextEdit’s scroll bar with loop-guarding to avoid recursive updates.
  • Configure MarkdownView’s context menu to show a "view mode" submenu based on TextEdit’s actions and apply the current editor theme immediately on creation.
src/editor/editwrapper.cpp
src/editor/editwrapper.h
Introduce render throttling and content sync between TextEdit and the Markdown renderer, including image path rewriting and initial synchronous rendering when entering view modes.
  • Connect QPlainTextEdit::textChanged to RenderThrottle in non-edit modes, feeding current text into the throttler.
  • Handle RenderThrottle::renderRequested by resolving relative image paths via MarkdownLogic and forwarding Markdown to the renderer.
  • On view mode changes, configure renderer mode/layout, update RenderThrottle readiness, immediately push current content, and flush to avoid waiting for the debounce timer on first switch.
src/editor/editwrapper.cpp
src/editor/editwrapper.h
Implement bidirectional scroll synchronization between the editor and the rendered view in live preview mode with ratio-based mapping and loop guard.
  • Connect TextEdit vertical scroll bar valueChanged to compute a scroll ratio via ScrollSync and call renderer->scrollToRatio in LivePreview mode when not already syncing.
  • Use ScrollSync::clampRatio and ratio-to-position mapping in the MarkdownView->TextEdit direction, guarded by a m_bScrollSyncing flag to prevent feedback loops.
  • Apply initial scroll synchronization when entering LivePreview to align the rendered view to the current editor scroll position.
src/editor/editwrapper.cpp
src/editor/editwrapper.h
Propagate theme changes and view mode availability to UI components and tests, and remove obsolete cursor mode handling.
  • Update OnThemeChangeSlot to apply serialized theme JSON to the Markdown renderer via applyTheme when present.
  • Emit viewModeChanged and markdownAvailabilityChanged on mode/availability changes and wire them to BottomBar and TextEdit to update view mode actions and combobox state.
  • Add setMarkdownRendererForTest() to inject a mock renderer in tests and remove the handleCursorModeChanged method and its associated signal connection from EditWrapper.
src/editor/editwrapper.cpp
src/editor/editwrapper.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Add a three-page view stack (edit page, read page, live-preview splitter)
in EditWrapper with lazy-created MarkdownView. Wire 300ms throttled
content sync with mdimg image rewrite, bidirectional ratio scroll sync
with loop guard, theme injection on change and creation, markdown
recognition on load and language switch with fallback/elevate mode
transitions, and explicit show() after cross-container reparent.

EditWrapper 集成三页视图栈(编辑页/查看页/实时预览分栏),懒创建
MarkdownView;接入 300ms 节流内容同步(含 mdimg 图片路径改写)、带
防回环护栏的双向比例滚动同步、 变更与创建时的主题注入、文件加载与
语言切换时的 Markdown 识别及模式回退/跃迁;跨容器 reparent 后显式
show() 防分栏零宽。

Log: 集成 Markdown 三视图编排与同步机制
PMS: TASK-393979
Influence: 打开 .md 默认实时预览;语言切为 Markdown 自动进入预览、
切走自动回退编辑视图。
@pengfeixx
pengfeixx force-pushed the md-editor-integration branch from bd6c294 to 358801d Compare August 20, 2026 02:05
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:92分

■ 【总体评价】

代码实现了 Markdown 三视图模式的集成,架构设计清晰且性能优化合理
逻辑基本正确但因删除光标模式处理函数可能导致 UI 反馈缺失扣8分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓

EditWrapper::setViewMode 中正确处理了 QStackedWidget 页面切换和跨容器 reparent 后的显式 show() 调用,避免了 0 宽不可见问题。但删除了 handleCursorModeChanged 及其信号连接,可能导致 BottomBar 无法显示光标模式(INSERT/OVERWRITE/R/O)。
潜在问题:光标模式 UI 反馈可能丢失;m_pRenderer 生命周期管理依赖 Qt 父子机制,若测试注入 Mock 可能存在指针状态不一致。
建议:确认 BottomBar 是否有其他路径接收光标模式变化;在 ensureMarkdownViewCreated 中明确 m_pRenderer 的所有权或在使用 Mock 时处理清理逻辑。

  • 2.代码质量(良好)✓

代码注释详尽,清晰解释了三页视图栈、FSM 状态机和滚动同步的设计。命名规范,符合 Qt 风格。Lambda 表达式使用得当,逻辑封装合理。
潜在问题:m_pRenderer 作为原始裸指针,在复杂生命周期管理中容易出错。
建议:考虑对 m_pMarkdownView 使用智能指针或在析构函数中显式管理。

  • 3.代码性能(高效)✓

使用 RenderThrottle 进行 300ms 去抖,避免高频输入导致的渲染卡顿。MarkdownViewQSplitter 采用懒创建模式,减少了启动时的开销。
建议:保持当前优化策略。

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
总体风险描述:本次 diff 为 UI 视图模式集成,不涉及外部输入解析或系统命令执行,无明显安全风险。
建议:无。

■ 【改进建议代码示例】

// 建议在 EditWrapper 构造函数中保留或通过其他方式恢复光标模式的状态同步
// 如果 handleCursorModeChanged 确实不再需要,应确保 BottomBar 有其他机制获取该状态
// 以下是恢复连接的示例代码

// 在构造函数中添加:
connect(m_pTextEdit, &TextEdit::cursorModeChanged, this, [this](TextEdit::CursorMode mode) {
    switch (mode) {
    case TextEdit::Insert:
        m_pBottomBar->setCursorStatus(tr("INSERT"));
        break;
    case TextEdit::Overwrite:
        m_pBottomBar->setCursorStatus(tr("OVERWRITE"));
        break;
    case TextEdit::Readonly:
        m_pBottomBar->setCursorStatus(tr("R/O"));
        break;
    }
});

// 在 setViewMode 中,针对 Edit 模式返回 Page0 时,建议显式调用 show() 以防万一
if (m_pEditPage->parentWidget() != m_viewStack) {
    m_viewStack->insertWidget(0, m_pEditPage);
    m_pEditPage->show(); // 显式恢复显示
}
m_viewStack->setCurrentWidget(m_pEditPage);

@pengfeixx

Copy link
Copy Markdown
Contributor Author

感谢 @humanfans#522 中的探索与贡献,QTextBrowser + QTextDocument::setMarkdown 的轻量实现为我们提供了很好的参照!

本 PR 采用 Milkdown(WebEngine)渲染内核,以三视图状态机(编辑/查看/实时预览)编排视图栈,配合 300ms 节流内容同步、双向比例滚动同步、编辑器主题注入与 mdimg:// 本地图片方案,并为后续升级 WYSIWYG 所见即所得编辑预留了零重构路径。

相较 #522,QTextDocument::setMarkdown 方案在渲染能力上存在一些局限:代码块无语法高亮、不支持数学公式,且每次输入全量重建文档、缺少节流与滚动同步(刷新后预览回到顶部),本地相对路径图片与外部链接也无法使用,因此选择了渲染能力更完整的 Web 内核方案。

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: lzwind, pengfeixx

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@pengfeixx

Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot
deepin-bot Bot merged commit ffef267 into linuxdeepin:master Aug 20, 2026
20 checks passed
@pengfeixx
pengfeixx deleted the md-editor-integration branch August 20, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants