From fe26a7456acbc190cd7d5ac00d777a9acd17a1e8 Mon Sep 17 00:00:00 2001 From: Meteorology Platform Date: Wed, 19 Aug 2026 07:26:10 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=20Markdown=20?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E5=B9=B6=E5=AE=9E=E7=8E=B0=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E6=9D=A1=E5=8F=8C=E5=90=91=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 启用 Markdown 预览,移除 root/chroot 环境下禁用预览的逻辑 - 实现编辑器到预览、预览到编辑器的双向滚动同步 - 使用 QWebChannel 实时传递预览区滚动事件,并保留轮询兜底 - 适配 Qt6/Dtk6 编译环境,修复 CMake 与 Dtk6 API 兼容问题 - 更新 debian 构建依赖,启用 qt6-webengine 支持 --- CMakeLists.txt | 24 ++- debian/control | 18 +- src/editwrapper.cpp | 3 +- src/findbar.cpp | 21 +- src/findbar.h | 4 +- src/jumplinebar.cpp | 2 +- src/main.cpp | 2 - src/replacebar.cpp | 19 +- src/replacebar.h | 4 +- src/settings.cpp | 9 +- src/widgets/bottombar.cpp | 1 + src/widgets/markdownpreviewwidget.cpp | 277 ++++++++++++++++---------- src/widgets/markdownpreviewwidget.h | 46 +++-- src/widgets/toast.cpp | 30 ++- src/widgets/toast.h | 4 +- src/window.cpp | 13 +- 16 files changed, 290 insertions(+), 187 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd63cbc..6563bc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ configure_file(src/environments.h.in environments.h @ONLY) file(GLOB SRCS "src/*.h" "src/*.cpp" "src/thememodule/*.h" "src/thememodule/*.cpp" "src/widgets/*.h" "src/widgets/*.cpp" - "src/wayland/kywc-toplevel-v1-protocol.c" + "src/wayland/kywc-toplevel-v1-protocol.c" "src/wayland/*.cpp" "src/wayland/*.h") # Find the library @@ -45,29 +45,30 @@ find_package(Qt6Concurrent REQUIRED) find_package(Qt6PrintSupport REQUIRED) find_package(Qt6Gui REQUIRED) find_package(Qt6WaylandClient REQUIRED) -find_package(Dtk2widget REQUIRED) +find_package(Dtk6Widget REQUIRED) find_package(Dtk6Core REQUIRED) find_package(KF6SyntaxHighlighting REQUIRED) find_package(KF6Codecs REQUIRED) # 尝试查找WebEngine模块 -find_package(Qt6WebEngine QUIET) +find_package(Qt6WebEngineCore QUIET) find_package(Qt6WebEngineWidgets QUIET) +find_package(Qt6WebChannel QUIET) include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS}) # Tell CMake to create the executable add_executable(gxde-editor ${SRCS} src/gxde-editor.qrc) -target_include_directories(gxde-editor PUBLIC - /usr/include/dtk2/DWidget - /usr/include/dtk6/DCore - /usr/include/dtk6/DLog +target_include_directories(gxde-editor PUBLIC + /usr/include/dtk6/DWidget + /usr/include/dtk6/DCore + /usr/include/dtk6/DLog /usr/include/dtk6/DGui - ${OBJECT_BINARY_DIR} + ${OBJECT_BINARY_DIR} ${CMARK_INCLUDE_DIRS} ) target_link_libraries(gxde-editor - ${Dtk2widget_LIBRARIES} + Dtk6::Widget Dtk6::Core Qt6::Widgets Qt6::DBus @@ -83,11 +84,12 @@ target_link_libraries(gxde-editor ) # 判断 Qt6WebEngine 模块是否存在 -if(Qt6WebEngine_FOUND AND Qt6WebEngineWidgets_FOUND) +if(Qt6WebEngineCore_FOUND AND Qt6WebEngineWidgets_FOUND AND Qt6WebChannel_FOUND) # 添加模块链接和宏定义 target_link_libraries(gxde-editor - Qt6::WebEngine + Qt6::WebEngineCore Qt6::WebEngineWidgets + Qt6::WebChannel ) target_compile_definitions(gxde-editor PRIVATE diff --git a/debian/control b/debian/control index 6fc596a..abc1d83 100644 --- a/debian/control +++ b/debian/control @@ -4,19 +4,19 @@ Priority: optional Maintainer: gfdgd xi <3025613752@qq.com>, Deepin Packages Builder Build-Depends: debhelper (>= 9), - cmake, + cmake, pkg-config, - libdtk6core-dev, - libdtk2widget6-dev, - qt6-base-dev, - libpolkit-qt6-1-dev, - libkf6syntaxhighlighting-dev, - libkf6codecs-dev, + libdtk6core-dev, + libdtk6widget-dev, + qt6-base-dev, + libpolkit-qt6-1-dev, + libkf6syntaxhighlighting-dev, + libkf6codecs-dev, qt6-tools-dev-tools, qt6-base-private-dev, qt6-5compat-dev, - qt6-wayland-dev -# qt6-webengine-dev [i386 amd64 armhf arm64 mips64el loong64] + qt6-wayland-dev, + qt6-webengine-dev, Standards-Version: 3.9.8 Homepage: https://www.gxde.top diff --git a/src/editwrapper.cpp b/src/editwrapper.cpp index e73a12a..58a0248 100644 --- a/src/editwrapper.cpp +++ b/src/editwrapper.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "drecentmanager.h" @@ -101,7 +102,7 @@ EditWrapper::EditWrapper(QWidget *parent) connect(m_toast, &Toast::saveAsBtnClicked, this, &EditWrapper::requestSaveAs); connect(m_pendingLoadTimer, &QTimer::timeout, this, &EditWrapper::appendPendingTextLoadChunk); - setDarkTheme(DThemeManager::instance()->theme() == "dark"); + setDarkTheme(Dtk::Gui::DGuiApplicationHelper::instance()->themeType() == Dtk::Gui::DGuiApplicationHelper::DarkType); } EditWrapper::~EditWrapper() diff --git a/src/findbar.cpp b/src/findbar.cpp index 83e7cef..a542e81 100644 --- a/src/findbar.cpp +++ b/src/findbar.cpp @@ -25,6 +25,7 @@ #include #include +#include FindBar::FindBar(QWidget *parent) : QWidget(parent) @@ -39,7 +40,7 @@ FindBar::FindBar(QWidget *parent) m_editLine = new LineBar(); m_findNextButton = new QPushButton(tr("Next")); m_findPrevButton = new QPushButton(tr("Previous")); - m_closeButton = new DImageButton(); + m_closeButton = new QPushButton(); m_closeButton->setFixedSize(16, 16); m_layout->addWidget(m_findLabel); @@ -61,7 +62,7 @@ FindBar::FindBar(QWidget *parent) connect(m_findNextButton, &QPushButton::clicked, this, &FindBar::findNext, Qt::QueuedConnection); connect(m_findPrevButton, &QPushButton::clicked, this, &FindBar::findPrev, Qt::QueuedConnection); - connect(m_closeButton, &DImageButton::clicked, this, &FindBar::findCancel, Qt::QueuedConnection); + connect(m_closeButton, &QPushButton::clicked, this, &FindBar::findCancel, Qt::QueuedConnection); } bool FindBar::isFocus() @@ -72,15 +73,15 @@ bool FindBar::isFocus() void FindBar::focus() { m_editLine->setFocus(); - m_editLine->selectAll(); + m_editLine->lineEdit()->selectAll(); } void FindBar::activeInput(QString text, QString file, int row, int column, int scrollOffset) { // Try fill keyword with select text. m_editLine->clear(); - m_editLine->insert(text); - m_editLine->selectAll(); + m_editLine->lineEdit()->insert(text); + m_editLine->lineEdit()->selectAll(); // Show. QWidget::show(); @@ -143,15 +144,13 @@ void FindBar::setBackground(QString color) if (QColor(m_backgroundColor).lightness() < 128) { m_findLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); - m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_dark.svg")); - m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_dark.svg")); - m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_dark.svg")); + m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_dark.svg"))); + m_closeButton->setStyleSheet("QPushButton { border: none; }"); } else { m_findLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); - m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_light.svg")); - m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_light.svg")); - m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_light.svg")); + m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_light.svg"))); + m_closeButton->setStyleSheet("QPushButton { border: none; }"); } update(); diff --git a/src/findbar.h b/src/findbar.h index ae0bd7e..b2d39f8 100644 --- a/src/findbar.h +++ b/src/findbar.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include DWIDGET_USE_NAMESPACE @@ -67,7 +67,7 @@ protected: private: QPushButton *m_findNextButton; QPushButton *m_findPrevButton; - DImageButton *m_closeButton; + QPushButton *m_closeButton; LineBar *m_editLine; QHBoxLayout *m_layout; QLabel *m_findLabel; diff --git a/src/jumplinebar.cpp b/src/jumplinebar.cpp index a5eb7cd..d9b94cc 100644 --- a/src/jumplinebar.cpp +++ b/src/jumplinebar.cpp @@ -41,7 +41,7 @@ JumpLineBar::JumpLineBar(QWidget *parent) m_editLine = new LineBar(); m_lineValidator = new QIntValidator; - m_editLine->setValidator(m_lineValidator); + m_editLine->lineEdit()->setValidator(m_lineValidator); m_layout->addWidget(m_label); m_layout->addWidget(m_editLine); diff --git a/src/main.cpp b/src/main.cpp index 70e73e6..7962593 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,8 +44,6 @@ DTK_USE_NAMESPACE int main(int argc, char *argv[]) { - // Init DTK. - DApplication::loadDXcbPlugin(); const char *descriptionText = QT_TRANSLATE_NOOP("MainWindow", "GXDE Editor is a desktop text editor that supports common text editing features."); diff --git a/src/replacebar.cpp b/src/replacebar.cpp index bec03fc..f96f044 100644 --- a/src/replacebar.cpp +++ b/src/replacebar.cpp @@ -25,6 +25,7 @@ #include #include +#include ReplaceBar::ReplaceBar(QWidget *parent) : QWidget(parent) @@ -43,7 +44,7 @@ ReplaceBar::ReplaceBar(QWidget *parent) m_replaceSkipButton = new QPushButton(tr("Skip")); m_replaceRestButton = new QPushButton(tr("Replace Rest")); m_replaceAllButton = new QPushButton(tr("Replace All")); - m_closeButton = new DImageButton(); + m_closeButton = new QPushButton(); m_closeButton->setFixedSize(16, 16); m_layout->addWidget(m_replaceLabel); @@ -85,7 +86,7 @@ ReplaceBar::ReplaceBar(QWidget *parent) connect(m_replaceRestButton, &QPushButton::clicked, this, &ReplaceBar::handleReplaceRest, Qt::QueuedConnection); connect(m_replaceAllButton, &QPushButton::clicked, this, &ReplaceBar::handleReplaceAll, Qt::QueuedConnection); - connect(m_closeButton, &DImageButton::clicked, this, &ReplaceBar::replaceCancel, Qt::QueuedConnection); + connect(m_closeButton, &QPushButton::clicked, this, &ReplaceBar::replaceCancel, Qt::QueuedConnection); } bool ReplaceBar::isFocus() @@ -102,8 +103,8 @@ void ReplaceBar::activeInput(QString text, QString file, int row, int column, in { // Try fill keyword with select text. m_replaceLine->clear(); - m_replaceLine->insert(text); - m_replaceLine->selectAll(); + m_replaceLine->lineEdit()->insert(text); + m_replaceLine->lineEdit()->selectAll(); // Show. show(); @@ -176,16 +177,14 @@ void ReplaceBar::setBackground(QString color) m_replaceLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); m_withLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); - m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_dark.svg")); - m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_dark.svg")); - m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_dark.svg")); + m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_dark.svg"))); + m_closeButton->setStyleSheet("QPushButton { border: none; }"); } else { m_replaceLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); m_withLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); - m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_light.svg")); - m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_light.svg")); - m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_light.svg")); + m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_light.svg"))); + m_closeButton->setStyleSheet("QPushButton { border: none; }"); } repaint(); diff --git a/src/replacebar.h b/src/replacebar.h index 32c17ff..2c63c03 100644 --- a/src/replacebar.h +++ b/src/replacebar.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include DWIDGET_USE_NAMESPACE @@ -77,7 +77,7 @@ private: QPushButton *m_replaceButton; QPushButton *m_replaceRestButton; QPushButton *m_replaceSkipButton; - DImageButton *m_closeButton; + QPushButton *m_closeButton; LineBar *m_replaceLine; LineBar *m_withLine; QHBoxLayout *m_layout; diff --git a/src/settings.cpp b/src/settings.cpp index 2cbb2d6..842deb5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -144,7 +145,13 @@ QWidget *Settings::createFontComBoBoxHandle(QObject *obj) auto option = qobject_cast(obj); QComboBox *comboBox = new QComboBox; - QWidget *optionWidget = DSettingsWidgetFactory::createTwoColumWidget(option, comboBox); + const auto pair = DSettingsWidgetFactory::createStandardItem(QByteArray(), option, comboBox); + QWidget *optionWidget = new QWidget; + QHBoxLayout *layout = new QHBoxLayout(optionWidget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(pair.first); + layout->addWidget(pair.second); QFontDatabase fontDatabase; comboBox->addItems(fontDatabase.families()); diff --git a/src/widgets/bottombar.cpp b/src/widgets/bottombar.cpp index 66600b6..df13d22 100644 --- a/src/widgets/bottombar.cpp +++ b/src/widgets/bottombar.cpp @@ -22,6 +22,7 @@ #include "../editwrapper.h" #include #include +#include #include #include diff --git a/src/widgets/markdownpreviewwidget.cpp b/src/widgets/markdownpreviewwidget.cpp index 5ea6b6c..f27d9ce 100644 --- a/src/widgets/markdownpreviewwidget.cpp +++ b/src/widgets/markdownpreviewwidget.cpp @@ -4,15 +4,13 @@ #include #include #include +#include #include -#include -#include #include #include #include - -#include -#include +#include +#include // 使用GitHub风格的Markdown样式 const QString STYLE_LIGHT = R"( @@ -43,19 +41,18 @@ const QString STYLE_DARK = R"( )"; -// JavaScript 同步代码 -const QString SCROLL_SYNC_JS = R"( -let lastSentPosition = 0; -const SYNC_THRESHOLD = 5; // 像素阈值避免微小抖动 +MarkdownPreviewBridge::MarkdownPreviewBridge(MarkdownPreviewWidget* widget) + : QObject(widget) + , m_widget(widget) +{ +} -window.addEventListener('scroll', function(e) { - const current = window.scrollY || document.documentElement.scrollTop; - if (Math.abs(current - lastSentPosition) > SYNC_THRESHOLD) { - lastSentPosition = current; - window.external.syncPreviewScroll(current); +void MarkdownPreviewBridge::syncPreviewToEditor(double ratio) +{ + if (m_widget) { + m_widget->syncPreviewToEditor(ratio); } -}); -)"; +} MarkdownPreviewWidget::MarkdownPreviewWidget(QWidget* parent) : QWidget(parent) @@ -65,6 +62,10 @@ MarkdownPreviewWidget::MarkdownPreviewWidget(QWidget* parent) m_webView = new QWebEngineView(this); m_webPage = new MarkdownWebPage(this); + m_webChannel = new QWebChannel(this); + m_bridge = new MarkdownPreviewBridge(this); + m_webChannel->registerObject(QStringLiteral("markdownPreview"), m_bridge); + m_webPage->setWebChannel(m_webChannel); m_webView->setPage(m_webPage); m_webView->settings()->setAttribute(QWebEngineSettings::WebAttribute::LocalContentCanAccessRemoteUrls, true); @@ -78,132 +79,177 @@ MarkdownPreviewWidget::MarkdownPreviewWidget(QWidget* parent) m_updateTimer->setSingleShot(true); connect(m_updateTimer, &QTimer::timeout, this, &MarkdownPreviewWidget::performUpdate); + // 定时轮询预览区滚动位置,用于预览 -> 编辑器的同步 m_scrollSyncTimer = new QTimer(this); - m_scrollSyncTimer->setSingleShot(true); - //connect(m_scrollSyncTimer, &QTimer::timeout, this, &MarkdownPreviewWidget::setupScrollSync); + m_scrollSyncTimer->setInterval(50); + connect(m_scrollSyncTimer, &QTimer::timeout, this, &MarkdownPreviewWidget::pollPreviewScroll); + + // 页面加载完成后恢复滚动比例并开始监听预览区滚动 + connect(m_webView, &QWebEngineView::loadFinished, + this, &MarkdownPreviewWidget::onPreviewLoadFinished); } bool MarkdownPreviewWidget::isSupport() { - // 检测是否是 root 用户且有设置 QTWEBENGINE_DISABLE_SANDBOX - if (QProcessEnvironment::systemEnvironment().value("USER") != "root" || - QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_DISABLE_SANDBOX") == "1") { - QDBusMessage dbus = QDBusMessage::createMethodCall("com.gxde.daemon.system.info", - "/com/gxde/daemon/system/info", - "com.gxde.daemon.system.info", - "IsInChroot"); - auto result = QDBusConnection::sessionBus().call(dbus); - bool isInChroot = false; - if (result.arguments().count() > 0) { - isInChroot = result.arguments().at(0).toBool(); - } - if (!isInChroot) { - return 1; - } - } - return 0; + // 只要编译期启用了 WebEngine,就启用 Markdown 预览。 + // 之前还会根据 root/chroot 环境禁用,导致部分环境下看不到预览界面。 + return true; } -void MarkdownPreviewWidget::setSourceEditor(QTextEdit* editor) { - if (m_sourceEditor == editor) return; +void MarkdownPreviewWidget::setSourceEditor(QTextEdit* editor) +{ + if (m_sourceEditor == editor) + return; if (m_sourceEditor) { disconnect(m_sourceEditor, &QTextEdit::textChanged, this, &MarkdownPreviewWidget::scheduleUpdate); - } - - if (m_sourceEditor) { disconnect(m_sourceEditor->verticalScrollBar(), &QScrollBar::valueChanged, - this, &MarkdownPreviewWidget::syncEditorToPreview); + this, &MarkdownPreviewWidget::syncEditorToPreview); } m_sourceEditor = editor; + ++m_loadGeneration; + m_syncing = false; + m_lastScrollRatio = 0.0; + m_lastPreviewRatio = 0.0; - if (m_sourceEditor) { - connect(m_sourceEditor, &QTextEdit::textChanged, - this, &MarkdownPreviewWidget::scheduleUpdate); - scheduleUpdate(); - } + if (m_scrollSyncTimer) + m_scrollSyncTimer->stop(); - // 初始化滚动同步 - if (m_sourceEditor) { - connect(m_sourceEditor->verticalScrollBar(), &QScrollBar::valueChanged, - this, &MarkdownPreviewWidget::syncEditorToPreview); - // 初始化内容 - m_webView->setHtml(generateHtml(m_sourceEditor->toPlainText())); + if (!m_sourceEditor) { + m_webView->setHtml(QString()); + return; } -} -void MarkdownPreviewWidget::setupScrollSync() { - // 注入JavaScript同步处理器 - QWebEngineScript script; - script.setSourceCode(SCROLL_SYNC_JS); - script.setName("ScrollSync"); - script.setWorldId(QWebEngineScript::MainWorld); - script.setInjectionPoint(QWebEngineScript::DocumentReady); - m_webView->page()->scripts().insert(script); + connect(m_sourceEditor, &QTextEdit::textChanged, + this, &MarkdownPreviewWidget::scheduleUpdate); + connect(m_sourceEditor->verticalScrollBar(), &QScrollBar::valueChanged, + this, &MarkdownPreviewWidget::syncEditorToPreview); + + // 初始化内容,页面加载完成后会恢复滚动位置 + m_webView->setHtml(generateHtml(m_sourceEditor->toPlainText()), QUrl("qrc:/")); } -double MarkdownPreviewWidget::calculateSyncRatio() { - const int editorHeight = m_sourceEditor->height(); - const int editorContentHeight = m_sourceEditor->document()->size().height(); - const double editorVisibleRatio = static_cast(editorHeight) / editorContentHeight; +void MarkdownPreviewWidget::syncEditorToPreview() +{ + if (!m_sourceEditor || m_syncing) + return; - // 获取预览内容高度 - int previewContentHeight = 0; - m_webView->page()->runJavaScript("document.documentElement.scrollHeight", - [&](const QVariant& result) { - previewContentHeight = result.toInt(); - }); + QScrollBar* scrollBar = m_sourceEditor->verticalScrollBar(); + if (scrollBar->maximum() <= 0) + return; - const int previewHeight = m_webView->height(); - const double previewVisibleRatio = static_cast(previewHeight) / previewContentHeight; + const double ratio = static_cast(scrollBar->value()) / scrollBar->maximum(); + m_lastPreviewRatio = ratio; + + m_syncing = true; + setPreviewScrollRatio(ratio); - // 计算比例修正系数(考虑padding等因素) - return (1.0 - previewVisibleRatio) / (1.0 - editorVisibleRatio); + // 短暂锁定,避免 JS 侧回读滚动位置再次触发编辑器滚动 + QTimer::singleShot(100, this, [this] { + m_syncing = false; + }); } -void MarkdownPreviewWidget::syncEditorToPreview() { - //if (m_syncing) return; - m_syncing = true; +void MarkdownPreviewWidget::syncPreviewToEditor(double ratio) +{ + if (!m_sourceEditor || m_syncing) + return; - // 计算滚动比例 QScrollBar* scrollBar = m_sourceEditor->verticalScrollBar(); - const double ratio = static_cast(scrollBar->value()) / scrollBar->maximum(); + if (scrollBar->maximum() <= 0) + return; - // 异步执行滚动操作 - m_webView->page()->runJavaScript( - QString("window.scrollTo(0, document.documentElement.scrollHeight * %1);") - .arg(ratio) - ); + m_lastPreviewRatio = ratio; + m_syncing = true; + scrollBar->setValue(qRound(ratio * scrollBar->maximum())); + + QTimer::singleShot(100, this, [this] { + m_syncing = false; + }); +} + +void MarkdownPreviewWidget::pollPreviewScroll() +{ + if (!m_sourceEditor || m_syncing || !m_webView) + return; - // 200ms后解除同步锁定 - //QTimer::singleShot(200, this, [this](){ m_syncing = false; }); + const int generation = m_loadGeneration; + m_webView->page()->runJavaScript( + "var e = document.documentElement;" + "var h = e.scrollHeight - e.clientHeight;" + "h > 0 ? (window.scrollY || e.scrollTop || 0) / h : 0;", + [this, generation](const QVariant &result) { + if (generation != m_loadGeneration || !m_sourceEditor || m_syncing) + return; + + const double ratio = result.toDouble(); + if (qAbs(ratio - m_lastPreviewRatio) < 0.0005) + return; + + m_lastPreviewRatio = ratio; + + QScrollBar* scrollBar = m_sourceEditor->verticalScrollBar(); + if (scrollBar->maximum() <= 0) + return; + + m_syncing = true; + scrollBar->setValue(qRound(ratio * scrollBar->maximum())); + QTimer::singleShot(100, this, [this] { + m_syncing = false; + }); + }); } +void MarkdownPreviewWidget::onPreviewLoadFinished(bool ok) +{ + if (!ok || !m_sourceEditor) + return; + + // 在 JS 真正执行 scrollTo 后再记录比例并启动轮询, + // 避免轮询读到恢复前的旧位置导致滚动被拉回去。 + const double ratio = m_lastScrollRatio; + const int generation = m_loadGeneration; + m_webView->page()->runJavaScript( + QString("var e = document.documentElement;" + "var h = e.scrollHeight - e.clientHeight;" + "window.scrollTo(0, %1 * (h > 0 ? h : 0));").arg(ratio), + [this, ratio, generation](const QVariant &) { + if (generation != m_loadGeneration) + return; + m_lastPreviewRatio = ratio; + if (m_sourceEditor && m_scrollSyncTimer && !m_scrollSyncTimer->isActive()) + m_scrollSyncTimer->start(); + } + ); +} -void MarkdownPreviewWidget::scheduleUpdate() { - m_lastScrollPosition = m_webView->page()->scrollPosition().y(); +void MarkdownPreviewWidget::scheduleUpdate() +{ + // 记录当前预览比例,内容刷新后尽量维持阅读位置 + m_lastScrollRatio = m_lastPreviewRatio; m_updateTimer->start(200); // 200ms防抖 } -void MarkdownPreviewWidget::performUpdate() { - if (!m_sourceEditor) return; +void MarkdownPreviewWidget::performUpdate() +{ + if (!m_sourceEditor) + return; + + // 刷新期间暂停轮询,避免在页面重新加载过程中误同步 + if (m_scrollSyncTimer) + m_scrollSyncTimer->stop(); const QString content = m_sourceEditor->toPlainText(); const QString html = generateHtml(content); + ++m_loadGeneration; m_webView->setHtml(html, QUrl("qrc:/")); - - // 延迟恢复滚动位置 - QTimer::singleShot(50, this, [this] { - m_webView->page()->runJavaScript( - QString("window.scrollTo(0, %1);").arg(m_lastScrollPosition) - ); - }); } -QString MarkdownPreviewWidget::generateHtml(const QString& markdown) { +QString MarkdownPreviewWidget::generateHtml(const QString& markdown) +{ // 使用 QJsonDocument 正确序列化字符串 QJsonArray a; a.append(QJsonValue(markdown)); @@ -223,6 +269,7 @@ QString MarkdownPreviewWidget::generateHtml(const QString& markdown) { %1 +
@@ -237,6 +284,19 @@ QString MarkdownPreviewWidget::generateHtml(const QString& markdown) { img.style.maxWidth = '100%'; img.style.height = 'auto'; }); + + // 通过 QWebChannel 将预览区滚动事件实时通知给 C++ 侧 + new QWebChannel(qt.webChannelTransport, function(channel) { + const bridge = channel.objects.markdownPreview; + if (!bridge) return; + window.addEventListener('scroll', function() { + const e = document.documentElement; + const h = e.scrollHeight - e.clientHeight; + if (h > 0) { + bridge.syncPreviewToEditor((window.scrollY || e.scrollTop || 0) / h); + } + }); + }); @@ -244,12 +304,27 @@ QString MarkdownPreviewWidget::generateHtml(const QString& markdown) { .arg(escapedMarkdown); // 直接插入已转义内容 } -void MarkdownPreviewWidget::setDarkTheme(bool enabled) { +void MarkdownPreviewWidget::setDarkTheme(bool enabled) +{ m_darkMode = enabled; scheduleUpdate(); } -void MarkdownPreviewWidget::initWebView() { +void MarkdownPreviewWidget::setPreviewScrollRatio(double ratio) +{ + if (!m_webView || !m_sourceEditor) + return; + + m_lastPreviewRatio = ratio; + m_webView->page()->runJavaScript( + QString("var e = document.documentElement;" + "var h = e.scrollHeight - e.clientHeight;" + "window.scrollTo(0, %1 * (h > 0 ? h : 0));").arg(ratio) + ); +} + +void MarkdownPreviewWidget::initWebView() +{ // 禁用上下文菜单 //m_webView->setContextMenuPolicy(Qt::NoContextMenu); diff --git a/src/widgets/markdownpreviewwidget.h b/src/widgets/markdownpreviewwidget.h index c795e91..c82537c 100644 --- a/src/widgets/markdownpreviewwidget.h +++ b/src/widgets/markdownpreviewwidget.h @@ -12,6 +12,22 @@ #include // 用于实现调用浏览器打开超链接 +class QWebChannel; +class MarkdownPreviewWidget; + +// 仅暴露滚动同步接口给 QWebChannel,避免把整个 QWidget 暴露给页面 +class MarkdownPreviewBridge : public QObject { + Q_OBJECT +public: + explicit MarkdownPreviewBridge(MarkdownPreviewWidget* widget); + +public slots: + void syncPreviewToEditor(double ratio); + +private: + MarkdownPreviewWidget* m_widget; +}; + class MarkdownWebPage : public QWebEnginePage { Q_OBJECT public: @@ -34,35 +50,35 @@ public: void setSourceEditor(QTextEdit* editor); void setDarkTheme(bool enabled); - static bool isSupport()/* { - // 检测是否有设置 QTWEBENGINE_DISABLE_SANDBOX - if (QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_DISABLE_SANDBOX") == "1") { - return true; - } - return QProcessEnvironment::systemEnvironment().value("USER") != "root"; - }*/; + static bool isSupport(); + + void syncPreviewToEditor(double ratio); private slots: void scheduleUpdate(); void performUpdate(); - void setupScrollSync(); void syncEditorToPreview(); + void pollPreviewScroll(); + void onPreviewLoadFinished(bool ok); private: void initWebView(); - void updateScrollPosition(); QString generateHtml(const QString& markdown); - double calculateSyncRatio(); + void setPreviewScrollRatio(double ratio); - QWebEngineView* m_webView; - MarkdownWebPage* m_webPage; + QWebEngineView* m_webView = nullptr; + MarkdownWebPage* m_webPage = nullptr; + QWebChannel* m_webChannel = nullptr; + MarkdownPreviewBridge* m_bridge = nullptr; QTextEdit* m_sourceEditor = nullptr; - QTimer* m_scrollSyncTimer; - QTimer* m_updateTimer; + QTimer* m_updateTimer = nullptr; + QTimer* m_scrollSyncTimer = nullptr; bool m_syncing = false; - int m_lastScrollPosition = 0; bool m_darkMode = false; + double m_lastScrollRatio = 0.0; + double m_lastPreviewRatio = 0.0; + int m_loadGeneration = 0; }; #endif #endif diff --git a/src/widgets/toast.cpp b/src/widgets/toast.cpp index fbb389b..6f6e56c 100644 --- a/src/widgets/toast.cpp +++ b/src/widgets/toast.cpp @@ -19,6 +19,13 @@ #include "toast.h" #include +#include +#include + +static QString currentThemeName() +{ + return Dtk::Gui::DGuiApplicationHelper::instance()->themeType() == Dtk::Gui::DGuiApplicationHelper::DarkType ? "dark" : "light"; +} Toast::Toast(QWidget *parent) : QFrame(parent) @@ -49,8 +56,10 @@ Toast::Toast(QWidget *parent) setGraphicsEffect(m_effect); hide(); - setTheme(DThemeManager::instance()->theme()); - connect(DThemeManager::instance(), &DThemeManager::themeChanged, this, &Toast::setTheme); + setTheme(currentThemeName()); + connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, this, [this](Dtk::Gui::DGuiApplicationHelper::ColorType) { + setTheme(currentThemeName()); + }); connect(m_reloadBtn, &QPushButton::clicked, this, [=] { hideAnimation(); @@ -108,16 +117,18 @@ void Toast::setOnlyShow(bool onlyshow) m_onlyshow = onlyshow; if (m_onlyshow) { - m_closeBtn = new DImageButton; + m_closeBtn = new QPushButton; m_closeBtn->setFixedSize(14, 14); m_layout->addWidget(m_reloadBtn); m_layout->addWidget(m_saveAsBtn); m_layout->addWidget(m_closeBtn); - initCloseBtn(DThemeManager::instance()->theme()); + initCloseBtn(currentThemeName()); - connect(DThemeManager::instance(), &DThemeManager::themeChanged, this, &Toast::initCloseBtn); - connect(m_closeBtn, &DImageButton::clicked, this, &Toast::hideAnimation); - connect(m_closeBtn, &DImageButton::clicked, this, &Toast::closeBtnClicked); + connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, this, [this](Dtk::Gui::DGuiApplicationHelper::ColorType) { + initCloseBtn(currentThemeName()); + }); + connect(m_closeBtn, &QPushButton::clicked, this, &Toast::hideAnimation); + connect(m_closeBtn, &QPushButton::clicked, this, &Toast::closeBtnClicked); } } @@ -208,9 +219,8 @@ void Toast::setOpacity(qreal opacity) void Toast::initCloseBtn(QString theme) { - m_closeBtn->setNormalPic(QString(":/images/close_%1_%2.svg").arg("normal", theme)); - m_closeBtn->setHoverPic(QString(":/images/close_%1_%2.svg").arg("hover", theme)); - m_closeBtn->setPressPic(QString(":/images/close_%1_%2.svg").arg("press", theme)); + m_closeBtn->setIcon(QIcon(QString(":/images/close_%1_%2.svg").arg("normal", theme))); + m_closeBtn->setStyleSheet("QPushButton { border: none; }"); } void Toast::showEvent(QShowEvent *e) diff --git a/src/widgets/toast.h b/src/widgets/toast.h index 3912f15..e47a7d7 100644 --- a/src/widgets/toast.h +++ b/src/widgets/toast.h @@ -28,8 +28,6 @@ #include #include -#include -#include #include #include @@ -82,7 +80,7 @@ private: QPropertyAnimation *m_animation = nullptr; DGraphicsGlowEffect *m_effect = nullptr; - DImageButton *m_closeBtn; + QPushButton *m_closeBtn; QPushButton *m_reloadBtn; QPushButton *m_saveAsBtn; }; diff --git a/src/window.cpp b/src/window.cpp index ab2b7c5..1541fa4 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -23,8 +23,8 @@ #include "window.h" #include "toolbar.h" #include -#include -#include +#include +#include #include "utils.h" #include @@ -166,8 +166,6 @@ Window::Window(DMainWindow *parent) connect(qApp, &QGuiApplication::focusWindowChanged, this, &Window::handleFocusWindowChanged); - // 允许设置背景 - setEnableWindowBackground(1); WaylandActivator::instance(); } @@ -209,7 +207,7 @@ void Window::initTitlebar() ToolBar *toolBar = new ToolBar; toolBar->setTabbar(m_tabbar); - titlebar()->setCustomWidget(toolBar, Qt::AlignVCenter, false); + titlebar()->setCustomWidget(toolBar, false); titlebar()->setAutoHideOnFullscreen(true); titlebar()->setSeparatorVisible(true); titlebar()->setFixedHeight(40); @@ -778,7 +776,6 @@ void Window::popupSettingsDialog() dialog->widgetFactory()->registerWidget("fontcombobox", Settings::createFontComBoBoxHandle); dialog->setProperty("_d_dtk_theme", "dark"); dialog->setProperty("_d_QSSFilename", "DSettingsDialog"); - DThemeManager::instance()->registerWidget(dialog); dialog->updateSettings(m_settings->settings); m_settings->dtkThemeWorkaround(dialog, "dlight"); @@ -1258,9 +1255,9 @@ void Window::loadTheme(const QString &path) bool isDark = (QColor(backgroundColor).lightness() < 128); if (isDark) { - DThemeManager::instance()->setTheme("dark"); + Dtk::Gui::DGuiApplicationHelper::instance()->setPaletteType(Dtk::Gui::DGuiApplicationHelper::DarkType); } else { - DThemeManager::instance()->setTheme("light"); + Dtk::Gui::DGuiApplicationHelper::instance()->setPaletteType(Dtk::Gui::DGuiApplicationHelper::LightType); } changeTitlebarBackground(tabbarStartColor, tabbarEndColor); -- Gitee From 6c164358657403cc304a0c86a02411d75f9e59bf Mon Sep 17 00:00:00 2001 From: Meteorology Platform Date: Thu, 20 Aug 2026 02:21:59 +0800 Subject: [PATCH 2/2] =?UTF-8?q?revert:=20=E6=94=B9=E5=9B=9E=20Dtk2=20?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CMake 恢复使用 Dtk2widget - 恢复 DImageButton、DThemeManager、DApplication::loadDXcbPlugin 等 Dtk2 API - 保留 Markdown 预览与滚动条双向同步功能 --- CMakeLists.txt | 6 +++--- debian/control | 2 +- src/editwrapper.cpp | 3 +-- src/findbar.cpp | 21 +++++++++++---------- src/findbar.h | 4 ++-- src/jumplinebar.cpp | 2 +- src/main.cpp | 2 ++ src/replacebar.cpp | 19 ++++++++++--------- src/replacebar.h | 4 ++-- src/settings.cpp | 9 +-------- src/widgets/bottombar.cpp | 1 - src/widgets/toast.cpp | 30 ++++++++++-------------------- src/widgets/toast.h | 4 +++- src/window.cpp | 13 ++++++++----- 14 files changed, 55 insertions(+), 65 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6563bc4..b898699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ find_package(Qt6Concurrent REQUIRED) find_package(Qt6PrintSupport REQUIRED) find_package(Qt6Gui REQUIRED) find_package(Qt6WaylandClient REQUIRED) -find_package(Dtk6Widget REQUIRED) +find_package(Dtk2widget REQUIRED) find_package(Dtk6Core REQUIRED) find_package(KF6SyntaxHighlighting REQUIRED) find_package(KF6Codecs REQUIRED) @@ -60,7 +60,7 @@ include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS}) # Tell CMake to create the executable add_executable(gxde-editor ${SRCS} src/gxde-editor.qrc) target_include_directories(gxde-editor PUBLIC - /usr/include/dtk6/DWidget + /usr/include/dtk2/DWidget /usr/include/dtk6/DCore /usr/include/dtk6/DLog /usr/include/dtk6/DGui @@ -68,7 +68,7 @@ target_include_directories(gxde-editor PUBLIC ${CMARK_INCLUDE_DIRS} ) target_link_libraries(gxde-editor - Dtk6::Widget + ${Dtk2widget_LIBRARIES} Dtk6::Core Qt6::Widgets Qt6::DBus diff --git a/debian/control b/debian/control index abc1d83..90f1289 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Build-Depends: cmake, pkg-config, libdtk6core-dev, - libdtk6widget-dev, + libdtk2widget6-dev, qt6-base-dev, libpolkit-qt6-1-dev, libkf6syntaxhighlighting-dev, diff --git a/src/editwrapper.cpp b/src/editwrapper.cpp index 58a0248..e73a12a 100644 --- a/src/editwrapper.cpp +++ b/src/editwrapper.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include "drecentmanager.h" @@ -102,7 +101,7 @@ EditWrapper::EditWrapper(QWidget *parent) connect(m_toast, &Toast::saveAsBtnClicked, this, &EditWrapper::requestSaveAs); connect(m_pendingLoadTimer, &QTimer::timeout, this, &EditWrapper::appendPendingTextLoadChunk); - setDarkTheme(Dtk::Gui::DGuiApplicationHelper::instance()->themeType() == Dtk::Gui::DGuiApplicationHelper::DarkType); + setDarkTheme(DThemeManager::instance()->theme() == "dark"); } EditWrapper::~EditWrapper() diff --git a/src/findbar.cpp b/src/findbar.cpp index a542e81..83e7cef 100644 --- a/src/findbar.cpp +++ b/src/findbar.cpp @@ -25,7 +25,6 @@ #include #include -#include FindBar::FindBar(QWidget *parent) : QWidget(parent) @@ -40,7 +39,7 @@ FindBar::FindBar(QWidget *parent) m_editLine = new LineBar(); m_findNextButton = new QPushButton(tr("Next")); m_findPrevButton = new QPushButton(tr("Previous")); - m_closeButton = new QPushButton(); + m_closeButton = new DImageButton(); m_closeButton->setFixedSize(16, 16); m_layout->addWidget(m_findLabel); @@ -62,7 +61,7 @@ FindBar::FindBar(QWidget *parent) connect(m_findNextButton, &QPushButton::clicked, this, &FindBar::findNext, Qt::QueuedConnection); connect(m_findPrevButton, &QPushButton::clicked, this, &FindBar::findPrev, Qt::QueuedConnection); - connect(m_closeButton, &QPushButton::clicked, this, &FindBar::findCancel, Qt::QueuedConnection); + connect(m_closeButton, &DImageButton::clicked, this, &FindBar::findCancel, Qt::QueuedConnection); } bool FindBar::isFocus() @@ -73,15 +72,15 @@ bool FindBar::isFocus() void FindBar::focus() { m_editLine->setFocus(); - m_editLine->lineEdit()->selectAll(); + m_editLine->selectAll(); } void FindBar::activeInput(QString text, QString file, int row, int column, int scrollOffset) { // Try fill keyword with select text. m_editLine->clear(); - m_editLine->lineEdit()->insert(text); - m_editLine->lineEdit()->selectAll(); + m_editLine->insert(text); + m_editLine->selectAll(); // Show. QWidget::show(); @@ -144,13 +143,15 @@ void FindBar::setBackground(QString color) if (QColor(m_backgroundColor).lightness() < 128) { m_findLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); - m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_dark.svg"))); - m_closeButton->setStyleSheet("QPushButton { border: none; }"); + m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_dark.svg")); + m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_dark.svg")); + m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_dark.svg")); } else { m_findLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); - m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_light.svg"))); - m_closeButton->setStyleSheet("QPushButton { border: none; }"); + m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_light.svg")); + m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_light.svg")); + m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_light.svg")); } update(); diff --git a/src/findbar.h b/src/findbar.h index b2d39f8..ae0bd7e 100644 --- a/src/findbar.h +++ b/src/findbar.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include DWIDGET_USE_NAMESPACE @@ -67,7 +67,7 @@ protected: private: QPushButton *m_findNextButton; QPushButton *m_findPrevButton; - QPushButton *m_closeButton; + DImageButton *m_closeButton; LineBar *m_editLine; QHBoxLayout *m_layout; QLabel *m_findLabel; diff --git a/src/jumplinebar.cpp b/src/jumplinebar.cpp index d9b94cc..a5eb7cd 100644 --- a/src/jumplinebar.cpp +++ b/src/jumplinebar.cpp @@ -41,7 +41,7 @@ JumpLineBar::JumpLineBar(QWidget *parent) m_editLine = new LineBar(); m_lineValidator = new QIntValidator; - m_editLine->lineEdit()->setValidator(m_lineValidator); + m_editLine->setValidator(m_lineValidator); m_layout->addWidget(m_label); m_layout->addWidget(m_editLine); diff --git a/src/main.cpp b/src/main.cpp index 7962593..70e73e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,8 @@ DTK_USE_NAMESPACE int main(int argc, char *argv[]) { + // Init DTK. + DApplication::loadDXcbPlugin(); const char *descriptionText = QT_TRANSLATE_NOOP("MainWindow", "GXDE Editor is a desktop text editor that supports common text editing features."); diff --git a/src/replacebar.cpp b/src/replacebar.cpp index f96f044..bec03fc 100644 --- a/src/replacebar.cpp +++ b/src/replacebar.cpp @@ -25,7 +25,6 @@ #include #include -#include ReplaceBar::ReplaceBar(QWidget *parent) : QWidget(parent) @@ -44,7 +43,7 @@ ReplaceBar::ReplaceBar(QWidget *parent) m_replaceSkipButton = new QPushButton(tr("Skip")); m_replaceRestButton = new QPushButton(tr("Replace Rest")); m_replaceAllButton = new QPushButton(tr("Replace All")); - m_closeButton = new QPushButton(); + m_closeButton = new DImageButton(); m_closeButton->setFixedSize(16, 16); m_layout->addWidget(m_replaceLabel); @@ -86,7 +85,7 @@ ReplaceBar::ReplaceBar(QWidget *parent) connect(m_replaceRestButton, &QPushButton::clicked, this, &ReplaceBar::handleReplaceRest, Qt::QueuedConnection); connect(m_replaceAllButton, &QPushButton::clicked, this, &ReplaceBar::handleReplaceAll, Qt::QueuedConnection); - connect(m_closeButton, &QPushButton::clicked, this, &ReplaceBar::replaceCancel, Qt::QueuedConnection); + connect(m_closeButton, &DImageButton::clicked, this, &ReplaceBar::replaceCancel, Qt::QueuedConnection); } bool ReplaceBar::isFocus() @@ -103,8 +102,8 @@ void ReplaceBar::activeInput(QString text, QString file, int row, int column, in { // Try fill keyword with select text. m_replaceLine->clear(); - m_replaceLine->lineEdit()->insert(text); - m_replaceLine->lineEdit()->selectAll(); + m_replaceLine->insert(text); + m_replaceLine->selectAll(); // Show. show(); @@ -177,14 +176,16 @@ void ReplaceBar::setBackground(QString color) m_replaceLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); m_withLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#AAAAAA")); - m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_dark.svg"))); - m_closeButton->setStyleSheet("QPushButton { border: none; }"); + m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_dark.svg")); + m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_dark.svg")); + m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_dark.svg")); } else { m_replaceLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); m_withLabel->setStyleSheet(QString("QLabel { background-color: %1; color: %2; }").arg(color).arg("#000000")); - m_closeButton->setIcon(QIcon(Utils::getQrcPath("bar_close_normal_light.svg"))); - m_closeButton->setStyleSheet("QPushButton { border: none; }"); + m_closeButton->setNormalPic(Utils::getQrcPath("bar_close_normal_light.svg")); + m_closeButton->setHoverPic(Utils::getQrcPath("bar_close_hover_light.svg")); + m_closeButton->setPressPic(Utils::getQrcPath("bar_close_press_light.svg")); } repaint(); diff --git a/src/replacebar.h b/src/replacebar.h index 2c63c03..32c17ff 100644 --- a/src/replacebar.h +++ b/src/replacebar.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include DWIDGET_USE_NAMESPACE @@ -77,7 +77,7 @@ private: QPushButton *m_replaceButton; QPushButton *m_replaceRestButton; QPushButton *m_replaceSkipButton; - QPushButton *m_closeButton; + DImageButton *m_closeButton; LineBar *m_replaceLine; LineBar *m_withLine; QHBoxLayout *m_layout; diff --git a/src/settings.cpp b/src/settings.cpp index 842deb5..2cbb2d6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -145,13 +144,7 @@ QWidget *Settings::createFontComBoBoxHandle(QObject *obj) auto option = qobject_cast(obj); QComboBox *comboBox = new QComboBox; - const auto pair = DSettingsWidgetFactory::createStandardItem(QByteArray(), option, comboBox); - QWidget *optionWidget = new QWidget; - QHBoxLayout *layout = new QHBoxLayout(optionWidget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(pair.first); - layout->addWidget(pair.second); + QWidget *optionWidget = DSettingsWidgetFactory::createTwoColumWidget(option, comboBox); QFontDatabase fontDatabase; comboBox->addItems(fontDatabase.families()); diff --git a/src/widgets/bottombar.cpp b/src/widgets/bottombar.cpp index df13d22..66600b6 100644 --- a/src/widgets/bottombar.cpp +++ b/src/widgets/bottombar.cpp @@ -22,7 +22,6 @@ #include "../editwrapper.h" #include #include -#include #include #include diff --git a/src/widgets/toast.cpp b/src/widgets/toast.cpp index 6f6e56c..fbb389b 100644 --- a/src/widgets/toast.cpp +++ b/src/widgets/toast.cpp @@ -19,13 +19,6 @@ #include "toast.h" #include -#include -#include - -static QString currentThemeName() -{ - return Dtk::Gui::DGuiApplicationHelper::instance()->themeType() == Dtk::Gui::DGuiApplicationHelper::DarkType ? "dark" : "light"; -} Toast::Toast(QWidget *parent) : QFrame(parent) @@ -56,10 +49,8 @@ Toast::Toast(QWidget *parent) setGraphicsEffect(m_effect); hide(); - setTheme(currentThemeName()); - connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, this, [this](Dtk::Gui::DGuiApplicationHelper::ColorType) { - setTheme(currentThemeName()); - }); + setTheme(DThemeManager::instance()->theme()); + connect(DThemeManager::instance(), &DThemeManager::themeChanged, this, &Toast::setTheme); connect(m_reloadBtn, &QPushButton::clicked, this, [=] { hideAnimation(); @@ -117,18 +108,16 @@ void Toast::setOnlyShow(bool onlyshow) m_onlyshow = onlyshow; if (m_onlyshow) { - m_closeBtn = new QPushButton; + m_closeBtn = new DImageButton; m_closeBtn->setFixedSize(14, 14); m_layout->addWidget(m_reloadBtn); m_layout->addWidget(m_saveAsBtn); m_layout->addWidget(m_closeBtn); - initCloseBtn(currentThemeName()); + initCloseBtn(DThemeManager::instance()->theme()); - connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, this, [this](Dtk::Gui::DGuiApplicationHelper::ColorType) { - initCloseBtn(currentThemeName()); - }); - connect(m_closeBtn, &QPushButton::clicked, this, &Toast::hideAnimation); - connect(m_closeBtn, &QPushButton::clicked, this, &Toast::closeBtnClicked); + connect(DThemeManager::instance(), &DThemeManager::themeChanged, this, &Toast::initCloseBtn); + connect(m_closeBtn, &DImageButton::clicked, this, &Toast::hideAnimation); + connect(m_closeBtn, &DImageButton::clicked, this, &Toast::closeBtnClicked); } } @@ -219,8 +208,9 @@ void Toast::setOpacity(qreal opacity) void Toast::initCloseBtn(QString theme) { - m_closeBtn->setIcon(QIcon(QString(":/images/close_%1_%2.svg").arg("normal", theme))); - m_closeBtn->setStyleSheet("QPushButton { border: none; }"); + m_closeBtn->setNormalPic(QString(":/images/close_%1_%2.svg").arg("normal", theme)); + m_closeBtn->setHoverPic(QString(":/images/close_%1_%2.svg").arg("hover", theme)); + m_closeBtn->setPressPic(QString(":/images/close_%1_%2.svg").arg("press", theme)); } void Toast::showEvent(QShowEvent *e) diff --git a/src/widgets/toast.h b/src/widgets/toast.h index e47a7d7..3912f15 100644 --- a/src/widgets/toast.h +++ b/src/widgets/toast.h @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -80,7 +82,7 @@ private: QPropertyAnimation *m_animation = nullptr; DGraphicsGlowEffect *m_effect = nullptr; - QPushButton *m_closeBtn; + DImageButton *m_closeBtn; QPushButton *m_reloadBtn; QPushButton *m_saveAsBtn; }; diff --git a/src/window.cpp b/src/window.cpp index 1541fa4..ab2b7c5 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -23,8 +23,8 @@ #include "window.h" #include "toolbar.h" #include -#include -#include +#include +#include #include "utils.h" #include @@ -166,6 +166,8 @@ Window::Window(DMainWindow *parent) connect(qApp, &QGuiApplication::focusWindowChanged, this, &Window::handleFocusWindowChanged); + // 允许设置背景 + setEnableWindowBackground(1); WaylandActivator::instance(); } @@ -207,7 +209,7 @@ void Window::initTitlebar() ToolBar *toolBar = new ToolBar; toolBar->setTabbar(m_tabbar); - titlebar()->setCustomWidget(toolBar, false); + titlebar()->setCustomWidget(toolBar, Qt::AlignVCenter, false); titlebar()->setAutoHideOnFullscreen(true); titlebar()->setSeparatorVisible(true); titlebar()->setFixedHeight(40); @@ -776,6 +778,7 @@ void Window::popupSettingsDialog() dialog->widgetFactory()->registerWidget("fontcombobox", Settings::createFontComBoBoxHandle); dialog->setProperty("_d_dtk_theme", "dark"); dialog->setProperty("_d_QSSFilename", "DSettingsDialog"); + DThemeManager::instance()->registerWidget(dialog); dialog->updateSettings(m_settings->settings); m_settings->dtkThemeWorkaround(dialog, "dlight"); @@ -1255,9 +1258,9 @@ void Window::loadTheme(const QString &path) bool isDark = (QColor(backgroundColor).lightness() < 128); if (isDark) { - Dtk::Gui::DGuiApplicationHelper::instance()->setPaletteType(Dtk::Gui::DGuiApplicationHelper::DarkType); + DThemeManager::instance()->setTheme("dark"); } else { - Dtk::Gui::DGuiApplicationHelper::instance()->setPaletteType(Dtk::Gui::DGuiApplicationHelper::LightType); + DThemeManager::instance()->setTheme("light"); } changeTitlebarBackground(tabbarStartColor, tabbarEndColor); -- Gitee