From 0386852043c5767c134f22cd2dcde68dc8d5d6c4 Mon Sep 17 00:00:00 2001 From: ck Date: Tue, 10 Jan 2023 10:36:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=87=AA=E5=AE=9A=E4=B9=89=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 显示自定义工具栏; 2. 工具栏上调整控件位置; 3. 工具栏上添加和移除控件; 4. 恢复默认工具栏控件; 5. 工具栏生效。 Log: 添加自定义工具栏功能 Influence: none Change-Id: Icb1d12d4c8881ace309e9b5cd5508bbca965c5fc --- .../collections/mainwindow.cpp | 106 +- .../collections/resources.qrc | 1 + .../resources/data/titlebar-settings.json | 18 + include/dtkwidget/widgets/dtitlebar.h | 5 + include/dtkwidget/widgets/dtitlebarsettings.h | 62 + .../assets/icons/dark/texts/fold_14px.svg | 1 + .../icons/dark/texts/spacer_fixed_34px.svg | 1 + .../icons/dark/texts/spacer_stretch_34px.svg | 1 + src/widgets/assets/icons/dtk-icon-theme.qrc | 6 + .../assets/icons/light/texts/fold_14px.svg | 1 + .../icons/light/texts/spacer_fixed_34px.svg | 1 + .../icons/light/texts/spacer_stretch_34px.svg | 1 + src/widgets/dtitlebar.cpp | 76 ++ src/widgets/dtitlebarsettings.cpp | 65 + src/widgets/private/dtitlebareditpanel.cpp | 946 +++++++++++++++ src/widgets/private/dtitlebareditpanel.h | 226 ++++ src/widgets/private/dtitlebarsettingsimpl.cpp | 1041 +++++++++++++++++ src/widgets/private/dtitlebarsettingsimpl.h | 176 +++ tests/CMakeLists.txt | 5 + tests/data.qrc | 5 + tests/data/titlebar-settings.json | 11 + .../widgets/ut_dtitlebarsettings.cpp | 175 +++ 22 files changed, 2928 insertions(+), 2 deletions(-) create mode 100644 examples/dwidget-examples/collections/resources/data/titlebar-settings.json create mode 100644 include/dtkwidget/widgets/dtitlebarsettings.h create mode 100644 src/widgets/assets/icons/dark/texts/fold_14px.svg create mode 100644 src/widgets/assets/icons/dark/texts/spacer_fixed_34px.svg create mode 100644 src/widgets/assets/icons/dark/texts/spacer_stretch_34px.svg create mode 100644 src/widgets/assets/icons/light/texts/fold_14px.svg create mode 100644 src/widgets/assets/icons/light/texts/spacer_fixed_34px.svg create mode 100644 src/widgets/assets/icons/light/texts/spacer_stretch_34px.svg create mode 100644 src/widgets/dtitlebarsettings.cpp create mode 100644 src/widgets/private/dtitlebareditpanel.cpp create mode 100644 src/widgets/private/dtitlebareditpanel.h create mode 100644 src/widgets/private/dtitlebarsettingsimpl.cpp create mode 100644 src/widgets/private/dtitlebarsettingsimpl.h create mode 100644 tests/data.qrc create mode 100644 tests/data/titlebar-settings.json create mode 100644 tests/testcases/widgets/ut_dtitlebarsettings.cpp diff --git a/examples/dwidget-examples/collections/mainwindow.cpp b/examples/dwidget-examples/collections/mainwindow.cpp index 6c6269897..6f818f0cf 100644 --- a/examples/dwidget-examples/collections/mainwindow.cpp +++ b/examples/dwidget-examples/collections/mainwindow.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,6 @@ #include #include #include -#include #include "buttonexample.h" #include "editexample.h" @@ -48,15 +48,109 @@ #include "dsettingsoption.h" #include "dsettings.h" #include "dfeaturedisplaydialog.h" +#include "dtitlebarsettings.h" DCORE_USE_NAMESPACE DWIDGET_USE_NAMESPACE +class DTitleBarToolCut : public DTitleBarToolInterface { +public: + virtual QString id() const override + { + return "builtin/edit-cut"; + } + virtual QString description() override + { + return "edit-cut"; + } + virtual QString iconName() override + { + return "edit-cut"; + } + virtual QWidget *createView() override + { + auto view = new DIconButton(); + view->setFixedSize(82, 36); + view->setIconSize(QSize(36, 36)); + view->setIcon(QIcon::fromTheme("edit-cut")); + connect(this, &DTitleBarToolInterface::triggered, this, [this](){ + qInfo() << "edit-cut executed"; + }); + connect(view, &DIconButton::clicked, this, [this](){ + qInfo() << "edit-cut executed"; + }); + return view; + } +}; + +class DTitleBarToolDelete : public DTitleBarToolInterface { +public: + virtual QString id() const override + { + return "builtin/edit-delete"; + } + virtual QString description() override + { + return "edit-delete"; + } + virtual QString iconName() override + { + return "edit-delete"; + } + virtual QWidget *createView() override + { + auto view = new DIconButton(); + view->setFixedSize(82, 36); + view->setIconSize(QSize(36, 36)); + view->setIcon(QIcon::fromTheme("edit-delete")); + + connect(this, &DTitleBarToolInterface::triggered, this, [this](){ + qInfo() << "edit-delete executed"; + }); + connect(view, &DIconButton::clicked, this, [this](){ + qInfo() << "edit-delete executed"; + }); + return view; + } +}; + +class DTitleBarToolFind : public DTitleBarToolInterface { +public: + virtual QString id() const override + { + return "builtin/edit-find"; + } + virtual QString description() override + { + return "edit-find"; + } + virtual QString iconName() override + { + return "edit-find"; + } + virtual QWidget *createView() override + { + auto view = new DIconButton(); + view->setFixedSize(36, 36); + view->setIconSize(QSize(36, 36)); + view->setIcon(QIcon::fromTheme("edit-find")); + + connect(this, &DTitleBarToolInterface::triggered,this, [this](){ + qInfo() << "edit-find executed"; + }); + connect(view, &DIconButton::clicked, this, [this](){ + qInfo() << "edit-find executed"; + }); + return view; + } +}; + + MainWindow::MainWindow(QWidget *parent) : DMainWindow(parent) { setWindowIcon(QIcon(":/images/logo_icon.svg")); - setMinimumSize(qApp->primaryScreen()->availableSize() / 5 * 3); + setMinimumSize(qApp->primaryScreen()->availableSize() / 5); QHBoxLayout *mainLayout = new QHBoxLayout(); mainLayout->setMargin(0); @@ -122,6 +216,14 @@ MainWindow::MainWindow(QWidget *parent) | Qt::WindowMaximizeButtonHint | Qt::WindowSystemMenuHint); titlebar->setAutoHideOnFullscreen(true); + + QList tools; + tools << new DTitleBarToolCut() + << new DTitleBarToolDelete() + << new DTitleBarToolFind(); + auto settings = titlebar->settings(); + settings->initilize(tools, ":/resources/data/titlebar-settings.json"); + settings->toolsEditPanel()->setMinimumWidth(this->width()); } DButtonBox *buttonBox = new DButtonBox(titlebar); diff --git a/examples/dwidget-examples/collections/resources.qrc b/examples/dwidget-examples/collections/resources.qrc index ecd3cd4ac..ef50497f7 100644 --- a/examples/dwidget-examples/collections/resources.qrc +++ b/examples/dwidget-examples/collections/resources.qrc @@ -2,5 +2,6 @@ resources/data/dfm-settings.json resources/data/dt-settings.json + resources/data/titlebar-settings.json diff --git a/examples/dwidget-examples/collections/resources/data/titlebar-settings.json b/examples/dwidget-examples/collections/resources/data/titlebar-settings.json new file mode 100644 index 000000000..567acae18 --- /dev/null +++ b/examples/dwidget-examples/collections/resources/data/titlebar-settings.json @@ -0,0 +1,18 @@ +{ + "spacingSize": 40, + "alignment": "left", + "tools": [ + { + "key": "builtin/edit-cut" + }, + { + "key": "builtin/spacer", + "fixed": true, + "count": 2 + }, + { + "key": "builtin/edit-delete", + "fixed": true + } + ] +} diff --git a/include/dtkwidget/widgets/dtitlebar.h b/include/dtkwidget/widgets/dtitlebar.h index 117bcfc6d..6d6180949 100644 --- a/include/dtkwidget/widgets/dtitlebar.h +++ b/include/dtkwidget/widgets/dtitlebar.h @@ -14,7 +14,9 @@ DGUI_USE_NAMESPACE DWIDGET_BEGIN_NAMESPACE + class DSidebarHelper; +class DTitlebarSettings; class DTitlebarPrivate; class LIBDTKWIDGETSHARED_EXPORT DTitlebar : public QFrame, public DTK_CORE_NAMESPACE::DObject { @@ -71,6 +73,8 @@ class LIBDTKWIDGETSHARED_EXPORT DTitlebar : public QFrame, public DTK_CORE_NAMES bool blurBackground() const; void setFullScreenButtonVisible(bool enabled); + DTitlebarSettings *settings(); + Q_SIGNALS: void optionClicked(); void doubleClicked(); @@ -121,6 +125,7 @@ private Q_SLOTS: D_PRIVATE_SLOT(void _q_aboutActionTriggered()) D_PRIVATE_SLOT(void _q_quitActionTriggered()) D_PRIVATE_SLOT(void _q_switchThemeActionTriggered(QAction*)) + D_PRIVATE_SLOT(void _q_toolBarActionTriggerd()) #endif }; diff --git a/include/dtkwidget/widgets/dtitlebarsettings.h b/include/dtkwidget/widgets/dtitlebarsettings.h new file mode 100644 index 000000000..2160f5ab9 --- /dev/null +++ b/include/dtkwidget/widgets/dtitlebarsettings.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +DWIDGET_BEGIN_NAMESPACE + +class LIBDTKWIDGETSHARED_EXPORT DTitlebarToolBaseInterface : public QObject +{ + Q_OBJECT +public: + explicit DTitlebarToolBaseInterface(QObject *parent = nullptr) : QObject(parent) {} + virtual ~DTitlebarToolBaseInterface(){} + + virtual QString id() const = 0; + virtual QString description() = 0; + virtual QString iconName() = 0; +}; + +class LIBDTKWIDGETSHARED_EXPORT DTitleBarToolInterface : public DTitlebarToolBaseInterface { + Q_OBJECT +public: + explicit DTitleBarToolInterface(QObject *parent = nullptr) : DTitlebarToolBaseInterface(parent) {} + virtual ~DTitleBarToolInterface(){} + + virtual QWidget *createView() = 0; +Q_SIGNALS: + void triggered(); +}; + +class LIBDTKWIDGETSHARED_EXPORT DTitleBarSpacerInterface : public DTitlebarToolBaseInterface { + Q_OBJECT +public: + explicit DTitleBarSpacerInterface(QObject *parent = nullptr) : DTitlebarToolBaseInterface(parent) {} + virtual ~DTitleBarSpacerInterface(){} + + virtual QWidget *createPlaceholderView() = 0; + virtual int size() const = 0; +}; + +class DTitlebarSettingsPrivate; +class DTitlebarSettingsImpl; +class DTitlebar; +class LIBDTKWIDGETSHARED_EXPORT DTitlebarSettings : public DCORE_NAMESPACE::DObject +{ +public: + explicit DTitlebarSettings(DTitlebar *titlebar); + bool initilize(QList &tools, const QString &path); + + QWidget *toolsEditPanel() const; + +private: + D_DECLARE_PRIVATE(DTitlebarSettings) + DTitlebarSettingsImpl *impl(); + friend class DTitlebar; +}; + +DWIDGET_END_NAMESPACE diff --git a/src/widgets/assets/icons/dark/texts/fold_14px.svg b/src/widgets/assets/icons/dark/texts/fold_14px.svg new file mode 100644 index 000000000..2f889bba3 --- /dev/null +++ b/src/widgets/assets/icons/dark/texts/fold_14px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/assets/icons/dark/texts/spacer_fixed_34px.svg b/src/widgets/assets/icons/dark/texts/spacer_fixed_34px.svg new file mode 100644 index 000000000..4548bd0a5 --- /dev/null +++ b/src/widgets/assets/icons/dark/texts/spacer_fixed_34px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/assets/icons/dark/texts/spacer_stretch_34px.svg b/src/widgets/assets/icons/dark/texts/spacer_stretch_34px.svg new file mode 100644 index 000000000..6c206c478 --- /dev/null +++ b/src/widgets/assets/icons/dark/texts/spacer_stretch_34px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/assets/icons/dtk-icon-theme.qrc b/src/widgets/assets/icons/dtk-icon-theme.qrc index 08b391015..6cb43ac24 100644 --- a/src/widgets/assets/icons/dtk-icon-theme.qrc +++ b/src/widgets/assets/icons/dtk-icon-theme.qrc @@ -41,6 +41,12 @@ dark/actions/print_previewscale_18px.svg dark/actions/printer_dropdown_14px.svg dark/actions/printer_dropup_14px.svg + light/texts/spacer_fixed_34px.svg + light/texts/spacer_stretch_34px.svg + light/texts/fold_14px.svg + dark/texts/spacer_fixed_34px.svg + dark/texts/spacer_stretch_34px.svg + dark/texts/fold_14px.svg dark/actions/printer_lrtb_1_24px.svg dark/actions/printer_lrtb_2_24px.svg dark/actions/printer_lrtb_3_24px.svg diff --git a/src/widgets/assets/icons/light/texts/fold_14px.svg b/src/widgets/assets/icons/light/texts/fold_14px.svg new file mode 100644 index 000000000..2f889bba3 --- /dev/null +++ b/src/widgets/assets/icons/light/texts/fold_14px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/assets/icons/light/texts/spacer_fixed_34px.svg b/src/widgets/assets/icons/light/texts/spacer_fixed_34px.svg new file mode 100644 index 000000000..4548bd0a5 --- /dev/null +++ b/src/widgets/assets/icons/light/texts/spacer_fixed_34px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/assets/icons/light/texts/spacer_stretch_34px.svg b/src/widgets/assets/icons/light/texts/spacer_stretch_34px.svg new file mode 100644 index 000000000..6c206c478 --- /dev/null +++ b/src/widgets/assets/icons/light/texts/spacer_stretch_34px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/widgets/dtitlebar.cpp b/src/widgets/dtitlebar.cpp index 61ae8a693..bcb9526bf 100644 --- a/src/widgets/dtitlebar.cpp +++ b/src/widgets/dtitlebar.cpp @@ -43,6 +43,8 @@ #include "dwidgetstype.h" #include "dlabel.h" #include "dsizemode.h" +#include "private/dtitlebarsettingsimpl.h" +#include "dtitlebarsettings.h" DWIDGET_BEGIN_NAMESPACE @@ -82,6 +84,7 @@ class DTitlebarPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate void _q_addDefaultMenuItems(); void _q_helpActionTriggered(); void _q_feedbackActionTriggerd(); + void _q_toolBarActionTriggerd(); void _q_aboutActionTriggered(); void _q_quitActionTriggered(); void _q_switchThemeActionTriggered(QAction*action); @@ -113,6 +116,8 @@ class DTitlebarPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate q->setMinimumHeight(DefaultTitlebarHeight()); } + void setFixedButtonsEnabled(bool isEnabled); + QHBoxLayout *mainLayout; QWidget *leftArea; QHBoxLayout *leftLayout; @@ -142,6 +147,7 @@ class DTitlebarPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate QMenu *menu = Q_NULLPTR; QAction *helpAction = Q_NULLPTR; QAction *feedbackAction = Q_NULLPTR; + QAction *toolbarAction = Q_NULLPTR; QAction *aboutAction = Q_NULLPTR; QAction *quitAction = Q_NULLPTR; bool canSwitchTheme = true; @@ -162,6 +168,8 @@ class DTitlebarPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate bool splitScreenWidgetEnable = true; QTimer *maxButtonPressAndHoldTimer = nullptr; QWidget *sidebarBackgroundWidget = nullptr; + DTitlebarSettingsImpl *titlebarSettingsImpl = nullptr; + DTitlebarSettings *titlebarSettings = nullptr; Q_DECLARE_PUBLIC(DTitlebar) }; @@ -657,6 +665,14 @@ void DTitlebarPrivate::_q_addDefaultMenuItems() menu->addAction(feedbackAction); } + // add toolbarAction menu item for deepin or uos application + if (titlebarSettingsImpl->isValid() && !toolbarAction) { + toolbarAction = new QAction(qApp->translate("TitleBarMenu", "TitlebarSettings"), menu); + toolbarAction->setObjectName("TitlebarSettings"); + QObject::connect(toolbarAction, SIGNAL(triggered(bool)), q, SLOT(_q_toolBarActionTriggerd())); + menu->addAction(toolbarAction); + } + // add about menu item. if (!aboutAction) { aboutAction = new QAction(qApp->translate("TitleBarMenu", "About"), menu); @@ -686,6 +702,25 @@ void DTitlebarPrivate::_q_feedbackActionTriggerd() { QProcess::startDetached("deepin-feedback", { qApp->applicationName() }); } +void DTitlebarPrivate::_q_toolBarActionTriggerd() +{ + D_Q(DTitlebar); + + auto toolBarEditPanel = titlebarSettingsImpl->toolsEditPanel(); + if (toolBarEditPanel->minimumWidth() >= q->width()) { + toolBarEditPanel->setParent(nullptr); + int x = q->mapToGlobal(q->pos()).x() - (toolBarEditPanel->width()- q->width()) / 2 ; + toolBarEditPanel->move(x, q->mapToGlobal(q->pos()).y() + q->height()); + } else { + toolBarEditPanel->setParent(q->parentWidget()); + toolBarEditPanel->move(0, q->height()); + toolBarEditPanel->resize(q->width(), q->parentWidget()->height() * 70 / 100); + } + toolBarEditPanel->installEventFilter(q); + + titlebarSettingsImpl->showEditPanel(); +} + void DTitlebarPrivate::_q_aboutActionTriggered() { DApplication *dapp = qobject_cast(qApp); @@ -822,6 +857,14 @@ void DTitlebarPrivate::hideSplitScreenWidget() #endif +void DTitlebarPrivate::setFixedButtonsEnabled(bool isEnabled) +{ + maxButton->setEnabled(isEnabled); + minButton->setEnabled(isEnabled); + closeButton->setEnabled(isEnabled); + optionButton->setEnabled(isEnabled); +} + /*! @~english \class Dtk::Widget::DTitlebar @@ -1073,6 +1116,13 @@ bool DTitlebar::eventFilter(QObject *obj, QEvent *event) } } + if (d->titlebarSettings && d->titlebarSettingsImpl->hasEditPanel() && obj == d->titlebarSettingsImpl->toolsEditPanel()) { + if (event->type() == QEvent::Show) { + d->setFixedButtonsEnabled(false); + } else if ((event->type() == QEvent::Close)) { + d->setFixedButtonsEnabled(true); + } + } return QWidget::eventFilter(obj, event); } @@ -1117,6 +1167,20 @@ void DTitlebar::resizeEvent(QResizeEvent *event) if (d->sidebarBackgroundWidget) d->sidebarBackgroundWidget->setFixedHeight(event->size().height()); + if (d->titlebarSettingsImpl && d->titlebarSettingsImpl->hasEditPanel() && d->titlebarSettingsImpl->toolsEditPanel()->isVisible()) { + if (d->titlebarSettingsImpl->toolsEditPanel()->minimumWidth() >= this->width()) { + d->titlebarSettingsImpl->toolsEditPanel()->setWindowFlag(Qt::Dialog); + d->titlebarSettingsImpl->toolsEditPanel()->show(); + int x = this->mapToGlobal(this->pos()).x() - (d->titlebarSettingsImpl->toolsEditPanel()->width()- this->width()) / 2 ; + d->titlebarSettingsImpl->toolsEditPanel()->move(x, this->mapToGlobal(this->pos()).y() + this->height()); + } else { + d->titlebarSettingsImpl->toolsEditPanel()->setWindowFlag(Qt::Dialog, false); + d->titlebarSettingsImpl->toolsEditPanel()->show(); + d->titlebarSettingsImpl->toolsEditPanel()->move(0, this->height()); + d->titlebarSettingsImpl->toolsEditPanel()->resize(width(), parentWidget()->height() * 70 / 100); + } + } + return QWidget::resizeEvent(event); } @@ -1635,6 +1699,18 @@ void DTitlebar::setFullScreenButtonVisible(bool visible) d->fullScreenButtonVisible = visible; } +DTitlebarSettings *DTitlebar::settings() +{ + D_D(DTitlebar); + + if (!d->titlebarSettings) { + auto settings = new DTitlebarSettings(this); + d->titlebarSettingsImpl = settings->impl(); + d->titlebarSettings = settings; + } + return d->titlebarSettings; +} + void DTitlebar::mouseMoveEvent(QMouseEvent *event) { D_D(DTitlebar); diff --git a/src/widgets/dtitlebarsettings.cpp b/src/widgets/dtitlebarsettings.cpp new file mode 100644 index 000000000..f0e59edf5 --- /dev/null +++ b/src/widgets/dtitlebarsettings.cpp @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtitlebarsettings.h" +#include "dtitlebar.h" +#include "private/dtitlebarsettingsimpl.h" +#include + +DWIDGET_BEGIN_NAMESPACE + +class DTitlebarSettingsPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(DTitlebarSettings) + + DTitlebarSettingsPrivate(DTitlebarSettings *qq); + + DTitlebarSettingsImpl* impl; + DTitlebar *titlebar; +}; + +DTitlebarSettingsPrivate::DTitlebarSettingsPrivate(DTitlebarSettings *qq) + : DObjectPrivate(qq) + , impl(new DTitlebarSettingsImpl()) +{ +} + +DTitlebarSettings::DTitlebarSettings(DTitlebar *titlebar) + : DObject( *new DTitlebarSettingsPrivate(this)) +{ + D_D(DTitlebarSettings); + d->titlebar = titlebar; +} + +bool DTitlebarSettings::initilize(QList &tools, const QString &path) +{ + D_D(DTitlebarSettings); + d->impl->setTools(tools); + if (!d->impl->load(path)) { + return false; + } + + auto titleBarEditPanel = d->impl->toolsView(); + titleBarEditPanel->setParent(d->titlebar->parentWidget()); + titleBarEditPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + d->titlebar->setCustomWidget(titleBarEditPanel); + return true; +} + +QWidget *DTitlebarSettings::toolsEditPanel() const +{ + D_DC(DTitlebarSettings); + return d->impl->toolsEditPanel(); +} + +DTitlebarSettingsImpl *DTitlebarSettings::impl() +{ + D_D(DTitlebarSettings); + return d->impl; +} + +#include "moc_dtitlebarsettings.cpp" + +DWIDGET_END_NAMESPACE diff --git a/src/widgets/private/dtitlebareditpanel.cpp b/src/widgets/private/dtitlebareditpanel.cpp new file mode 100644 index 000000000..b06494e34 --- /dev/null +++ b/src/widgets/private/dtitlebareditpanel.cpp @@ -0,0 +1,946 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtitlebareditpanel.h" +#include "dtitlebarsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "dtitlebar.h" + +DWIDGET_BEGIN_NAMESPACE +DGUI_USE_NAMESPACE + +#define SPACING 10 + +static const char* TitlebarZoneDataFormat = "titlebarZoneWidget"; +static const char* SelectionZoneDataFormat = "selectionZoneWidget"; +static const char* DefaultZoneDataFormat = "defaultZoneWidget"; + +static QBitmap bitmapOfMask(const QSize &size, const qreal radius) +{ + QBitmap bitMap(size); + bitMap.fill(Qt::color0); + + QPainter painter(&bitMap); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::color1); + painter.drawRoundedRect(bitMap.rect(), radius, radius); + + return bitMap; +} + +PlaceHoderWidget::PlaceHoderWidget(QWidget *parent) + : QWidget(parent) +{ + +} + +void PlaceHoderWidget::paintEvent(QPaintEvent *event) +{ + QColor color; + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) { + color = QColor(0, 0, 0, qRound(255 * 0.06)); + } else { + color = QColor(255, 255, 255, qRound(255 * 0.06)); + } + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing, true); + QPen pen(color); + painter.setPen(Qt::NoPen); + painter.setBrush(color); + painter.drawRoundedRect(this->rect().adjusted(1, 1, -1, -1), 8.0, 8.0); + + QWidget::paintEvent(event); +} + +DragDropWidget::DragDropWidget(const QString &id, QWidget *parent) + : DIconButton(parent) + , m_id(id) +{ +} + +DragDropWidget::~DragDropWidget() +{ + +} + +void DragDropWidget::setButtonIcon(const QIcon &icon, const QSize &size) +{ + this->setIcon(icon); + this->setIconSize(size); +} + +QString DragDropWidget::id() const +{ + return m_id; +} + +void DragDropWidget::setScreenShotedView(QWidget *view) +{ + m_view = view; +} + +void DragDropWidget::screenShot() +{ + if (m_view && m_view->size().width() > 0) { + this->setFixedSize(m_view->size()); + auto pixmap = m_view->grab(m_view->rect()); + pixmap.setMask(bitmapOfMask(pixmap.size(), 8)); + this->setButtonIcon(pixmap, m_view->size()); + } +} + +void DragDropWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + m_isClicked = true; + } +} + +void DragDropWidget::mouseMoveEvent(QMouseEvent *event) +{ + if (m_isClicked) + startDrag(event->pos()); +} + +void DragDropWidget::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + m_isClicked = false; +} + +void DragDropWidget::startDrag(const QPoint &pos) +{ + m_startDrag = mapToGlobal(this->pos()); + + QPoint hotSpot = pos; + QPixmap pixmap(this->grab()); + pixmap.setMask(bitmapOfMask(pixmap.size(), 8)); + m_pixmap = pixmap; + int index = -1; + if (DTitlebarEditPanel *panel = qobject_cast(this->parentWidget())) { + index = panel->layout()->indexOf(this); + m_titleBarEditPanel = panel; + m_index = index; + if (panel->isFixedTool(index)) { + return; + } + } + + QMimeData *mimeData = new QMimeData; + QByteArray itemData; + QDataStream dataStream(&itemData, QIODevice::WriteOnly); + dataStream << m_id << hotSpot << this->size() << index; + + mimeData->setData(m_mimeDataFormat, itemData); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pixmap); + drag->setHotSpot(hotSpot); + + Qt::DropAction dropAction = drag->exec(Qt::MoveAction); + if (dropAction == Qt::IgnoreAction) { + onIgnoreAction(); + } +} + +void DragDropWidget::onIgnoreAction() +{ + gobackDrag(m_pixmap, QCursor::pos()); +} + +void DragDropWidget::gobackDrag(const QPixmap &pixmap, const QPoint &pos) +{ + QLabel *widget = new QLabel(); + widget->setAttribute(Qt::WA_TranslucentBackground); + widget->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); + widget->setFixedSize(pixmap.size()); + widget->setPixmap(pixmap); + widget->move(pos); + widget->show(); + + auto currentXAni = new QPropertyAnimation(widget, "pos"); + const int AnimationTime = 250; + currentXAni->setEasingCurve(QEasingCurve::OutCubic); + currentXAni->setDuration(AnimationTime); + currentXAni->setStartValue(pos); + currentXAni->setEndValue(m_startDrag); + currentXAni->setDirection(QAbstractAnimation::Forward); + currentXAni->start(); + connect(currentXAni, &QPropertyAnimation::finished, currentXAni, &QWidget::deleteLater); + connect(currentXAni, &QPropertyAnimation::finished, widget, &QWidget::deleteLater); +} + +TitlebarZoneWidget::TitlebarZoneWidget(const QString &id, QWidget *parent) + : DragDropWidget(id, parent) +{ + m_mimeDataFormat = TitlebarZoneDataFormat; +} + +void TitlebarZoneWidget::onIgnoreAction() +{ + if (!m_titleBarEditPanel->dropped()) { + Q_EMIT m_titleBarEditPanel->removedToolView(m_id, m_index); + m_titleBarEditPanel->removePlaceHolder(); + m_titleBarEditPanel->updateCustomWidget(); + m_titleBarEditPanel->updateScreenShotedViews(); + m_titleBarEditPanel->setDropped(true); + this->deleteLater(); + } +} + +SelectionZoneWidget::SelectionZoneWidget(const QString &id, QWidget *parent) + : DragDropWidget(id, parent) +{ + m_mimeDataFormat = SelectionZoneDataFormat; +} + +DefaultZoneWidget::DefaultZoneWidget(const QString &id, QWidget *parent) + : DragDropWidget(id, parent) +{ + m_mimeDataFormat = DefaultZoneDataFormat; +} + +IconTextWidget::IconTextWidget(DragDropWidget *iconWidget, const QString &toolId, QWidget *parent) + : QWidget(parent) + , m_toolId(toolId) + , m_iconWidget(iconWidget) + , m_titleLabel(new QLabel) +{ +} + +IconTextWidget::~IconTextWidget() +{ + +} + +void IconTextWidget::setContent(const QIcon &icon, const QString &text, const QSize &size) +{ + m_iconWidget->setFixedSize(size); + m_iconWidget->setButtonIcon(icon, size); + m_titleLabel->setText(text); + m_titleLabel->setAlignment(Qt::AlignHCenter); + DFontSizeManager *fontManager = DFontSizeManager::instance(); + fontManager->bind(m_titleLabel, DFontSizeManager::T10, QFont::Medium); + + if (layout()) + return; + + QVBoxLayout *mainVLayout = new QVBoxLayout(this); + mainVLayout->setSpacing(0); + mainVLayout->setMargin(0); + mainVLayout->addWidget(m_iconWidget, 0, Qt::AlignHCenter); + mainVLayout->addSpacing(6); + mainVLayout->addWidget(m_titleLabel, 0, Qt::AlignHCenter); +} + +void IconTextWidget::setIconSize(const QSize &size) +{ + m_iconWidget->setFixedSize(size); +} + +DCollapseWidget::DCollapseWidget(DTitlebarSettingsImpl *settings, QWidget *parent) + : QWidget(parent) + , m_settingsImpl(settings) + , m_mainHLayout(new QHBoxLayout(this)) + , m_placeHolder(new PlaceHoderWidget) +{ + m_placeHolder->setObjectName("placeHolder"); + m_mainHLayout->setSpacing(SPACING); +} + +DCollapseWidget::~DCollapseWidget() +{ + +} + +void DCollapseWidget::removeAll() +{ + QLayoutItem *item = nullptr; + while ((item = m_mainHLayout->takeAt(0)) != nullptr) { + if (auto w = item->widget()) { + if (w->objectName() != "placeHolder") { + delete item->widget(); + delete item; + } + } + } + removePlaceHolder(); +} + +void DCollapseWidget::removePlaceHolder() +{ + if (m_placeHolder && m_placeHolder->isVisible()) { + m_mainHLayout->removeWidget(m_placeHolder); + m_placeHolder->hide(); + } +} + +void DCollapseWidget::reloadWidgets() +{ + removeAll(); + for (auto key : m_settingsImpl->keys()) { + addWidget(key, -1); + } +} + +void DCollapseWidget::addWidget(const QString &key, int index) +{ + Q_UNUSED(key) + Q_UNUSED(index) +} + +void DCollapseWidget::removeWidget(int index) +{ + if (auto item = m_mainHLayout->takeAt(index)) { + if (auto w = item->widget()) { + w->hide(); + } + } +} + +void DCollapseWidget::resizeEvent(QResizeEvent *event) +{ + updateMinimumValue(); + + if (width() < m_minimumWidth) { + collapse(); + } else { + expand(); + } + + QWidget::resizeEvent(event); +} + +void DCollapseWidget::updateMinimumValue() +{ + int minimum = 0; + + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto item = m_mainHLayout->itemAt(i); + if (auto spacerItem = item->spacerItem()) { + if (spacerItem->sizePolicy().horizontalPolicy() == QSizePolicy::Fixed) { + auto baseInterface = m_settingsImpl->tool(m_settingsImpl->findKeyByPos(i)); + if (auto spacerInterface = qobject_cast(baseInterface)) { + minimum += spacerInterface->size() + SPACING; + qDebug() << "+" << spacerInterface->size() + SPACING; + } + } + } else { + auto w = item->widget(); + if (!w || w->sizePolicy().horizontalPolicy() == QSizePolicy::Expanding) + continue; + minimum += w->width(); + qDebug() << "+" << w->width(); + if (auto dragDropWidget = qobject_cast(w)) { + if (m_settingsImpl->isSpacerTool(m_settingsImpl->findKeyByPos(i)) && !m_settingsImpl->isStrecherTool(m_settingsImpl->findKeyByPos(i))) { + minimum += SPACING; + qDebug() << "+" << SPACING; + } + } + } + } + + minimum += 2 * m_mainHLayout->margin(); + qDebug() << "+" << 2 * m_mainHLayout->margin(); + + m_minimumWidth = minimum; + qDebug() << "minimum:" << m_minimumWidth << "width:" << this->width() << "count:" << m_mainHLayout->count(); +} + +void DCollapseWidget::initExpandButton() +{ + m_expandButton = new DIconButton; + m_expandButton->setObjectName("expandButton"); + m_expandButton->setFixedSize(36, 36); + m_expandButton->setIconSize(QSize(36, 36)); + m_expandButton->setIcon(QIcon::fromTheme("fold")); + m_expandButton->setFlat(false); + m_mainHLayout->insertWidget(m_mainHLayout->count(), m_expandButton); + connect(m_expandButton, &QPushButton::clicked, this, [this] { + QMenu menu(m_expandButton); + for (auto view : m_viewsInMenu) { + DTitlebarToolBaseInterface* i = m_settingsImpl->tool(view.first); + auto interface = qobject_cast(i); + if (!interface) continue; + QAction *action = new QAction(interface->description()); + connect(action, &QAction::triggered, interface, &DTitleBarToolInterface::triggered); + menu.addAction(action); + } + menu.move(this->mapToGlobal(m_expandButton->pos()).x(), this->mapToGlobal(m_expandButton->pos()).y() + m_expandButton->height()); + menu.exec(); + }); +} + +void DCollapseWidget::collapse() +{ + if (m_mainHLayout->count() == 0) + return; + + int index = m_mainHLayout->count() - 1; + if (m_expandButton && m_expandButton->isVisible()) { + --index; + } + + if (auto item = m_mainHLayout->takeAt(index)) { + if (auto spacerItem = item->spacerItem()) { // 如果是spacer,只存数据,不处理expand按钮 + QPair tmp{m_settingsImpl->findKeyByPos(index), nullptr}; + m_viewsInMenu.append(tmp); + qDebug() << "collapse:" << m_viewsInMenu; + return; + } else { + if (auto w = item->widget()) { + w->hide(); + QPair tmp{m_settingsImpl->findKeyByPos(index), w}; + m_viewsInMenu.append(tmp); + } + } + qDebug() << "collapse:" << m_viewsInMenu; + } + + if (!m_expandButton) { + initExpandButton(); + } + + if (!m_expandButton->isVisible() && m_mainHLayout->indexOf(m_expandButton) == -1) { + m_mainHLayout->insertWidget(m_mainHLayout->count(), m_expandButton); + m_expandButton->show(); + } +} + +void DCollapseWidget::expand() +{ + if (m_viewsInMenu.isEmpty()) + return; + auto view = m_viewsInMenu.constLast(); + if (!view.second) { + if (this->width() >= m_minimumWidth + SPACING) { + m_viewsInMenu.takeLast(); + int index = m_mainHLayout->indexOf(m_expandButton); + if (m_settingsImpl->isStrecherTool(view.first)) { + m_mainHLayout->insertStretch(index, 0); + } else { + auto tool = m_settingsImpl->tool(view.first); + if (auto spacerInter = qobject_cast(tool)) { + auto spacingSize = spacerInter->size(); + m_mainHLayout->insertSpacing(index, spacingSize); + } + } + } + } else { + if (this->width() >= m_minimumWidth + view.second->width() + SPACING) { + qDebug() << "expand" << m_viewsInMenu.count(); + auto w = m_viewsInMenu.takeLast(); + int index = m_mainHLayout->indexOf(m_expandButton); + m_mainHLayout->insertWidget(index, view.second); + view.second->show(); + } + } + qDebug() << "expand:" << m_viewsInMenu; + + if (m_viewsInMenu.isEmpty()) { + m_mainHLayout->removeWidget(m_expandButton); + m_expandButton->hide(); + } +} + +DTitlebarCustomWidget::DTitlebarCustomWidget(DTitlebarSettingsImpl *settings, QWidget *parent) + : DCollapseWidget(settings, parent) +{ +} + +bool DTitlebarCustomWidget::editMode() const +{ + return m_isEditMode; +} + +void DTitlebarCustomWidget::setEditMode(bool isEditMode) +{ + m_isEditMode = isEditMode; +} + +QWidget *DTitlebarCustomWidget::widget(const int index) const +{ + if (auto item = m_mainHLayout->itemAt(index)) + return item->widget(); + return nullptr; +} + +void DTitlebarCustomWidget::addWidget(const QString &key, int index) +{ + auto tool = m_settingsImpl->tool(key); + if (!tool) { + return; + } + const bool isSpacer = DTitlebarSettingsImpl::isSpacerTool(tool); + if (isSpacer) { + auto spacerInterface = qobject_cast(tool); + if (!spacerInterface) { + return; + } + if (m_isEditMode) { + auto view = spacerInterface->createPlaceholderView(); + m_mainHLayout->insertWidget(index, view); + } else { + const auto spacingSize = spacerInterface->size(); + if (spacingSize < 0) { + m_mainHLayout->insertStretch(index, 0); + } else { + m_mainHLayout->insertSpacing(index, spacingSize + SPACING); + } + } + } else { + auto toolInterface = qobject_cast(tool); + if (!toolInterface) { + return; + } + QWidget *view = toolInterface->createView(); + m_mainHLayout->insertWidget(index, view); + } +} + +void DTitlebarCustomWidget::appendDefaultWidget(const QString &toolId) +{ + auto tool = m_settingsImpl->toolById(toolId); + if (!tool) + return; + + const bool isSpacer = DTitlebarSettingsImpl::isSpacerTool(tool); + if (isSpacer) { + auto spacerInterface = qobject_cast(tool); + if (!spacerInterface) { + return; + } + auto spacingSize = spacerInterface->size(); + if (spacingSize < 0) { + m_mainHLayout->insertStretch(-1, 1); + } else { + m_mainHLayout->insertSpacing(-1, spacingSize + SPACING); + } + } else { + auto toolInterface = qobject_cast(tool); + if (!toolInterface) { + return; + } + QWidget *view = toolInterface->createView(); + m_mainHLayout->insertWidget(-1, view); + } +} + +void DTitlebarCustomWidget::insertPlaceHolder(int index, const QSize &size) +{ + m_placeHolder->setFixedSize(size); + m_mainHLayout->insertWidget(index, m_placeHolder); + m_placeHolder->show(); +} + +void DTitlebarCustomWidget::resizeEvent(QResizeEvent *event) +{ + if (event->size() != event->oldSize() && m_isEditMode) + m_settingsImpl->adjustDisplayView(); +} + +DTitlebarEditPanel::DTitlebarEditPanel(DTitlebarSettingsImpl *settings, DTitlebarCustomWidget *customWidget, QWidget *parent) + : DCollapseWidget(settings, parent) + , m_customWidget(customWidget) +{ + setAcceptDrops(true); + setFocusPolicy(Qt::StrongFocus); + connect(this, &DTitlebarEditPanel::startScreenShot, this, &DTitlebarEditPanel::doStartScreenShot, Qt::QueuedConnection); +} + +void DTitlebarEditPanel::updateCustomWidget(bool isEditMode) +{ + m_customWidget->setEditMode(isEditMode); + m_customWidget->reloadWidgets(); +} + +void DTitlebarEditPanel::updateScreenShotedViews() +{ + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto w = m_mainHLayout->itemAt(i)->widget(); + auto btn = qobject_cast (w); + if (btn) { + btn->setScreenShotedView(m_customWidget->widget(i)); + } + } + Q_EMIT startScreenShot(); +} + +void DTitlebarEditPanel::addWidget(const QString &key, int index) +{ + DragDropWidget *w = new TitlebarZoneWidget(key); + if (m_settingsImpl->isSpacerTool(key)) { + auto spacerInterface = qobject_cast(m_settingsImpl->tool(key)); + if (!spacerInterface) { + return; + } + if (spacerInterface->size() == -1) { + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + } else { + qDebug() << "size" << spacerInterface->size(); + w->setFixedWidth(spacerInterface->size()); + } + } + m_mainHLayout->insertWidget(index, w); +} + +bool DTitlebarEditPanel::isFixedTool(const int index) +{ + return m_settingsImpl->isFixedTool(index); +} + +bool DTitlebarEditPanel::dropped() const +{ + return m_isDropped; +} + +void DTitlebarEditPanel::setDropped(bool isDropped) +{ + m_isDropped = isDropped; +} + +void DTitlebarEditPanel::doStartScreenShot() +{ + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto w = m_mainHLayout->itemAt(i)->widget(); + auto btn = qobject_cast (w); + if (btn) { + btn->screenShot(); + } + } +} + +void DTitlebarEditPanel::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); + if (event->mimeData()->hasFormat(TitlebarZoneDataFormat)) { + if (!m_isDropped) { + return; + } + QByteArray itemData = event->mimeData()->data(TitlebarZoneDataFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + QString key; + QPoint hotSpot; + QSize size; + int index; + dataStream >> key >> hotSpot >> size >> index; + if (auto item = m_mainHLayout->takeAt(index)) { + if (auto w = qobject_cast(item->widget())) { + w->hide(); + m_customWidget->removeWidget(index); + m_isDropped = false; + Q_EMIT startScreenShot(); + } + } + } +} + +void DTitlebarEditPanel::dragMoveEvent(QDragMoveEvent *event) +{ + if (event->mimeData()->hasFormat(TitlebarZoneDataFormat)) { + handleTitlebarZoneWidgetMoveEvent(event); + } else if (event->mimeData()->hasFormat(SelectionZoneDataFormat)) { + handleSelectionZoneWidgetMoveEvent(event); + } else { + event->accept(); // default zone + } +} + +void DTitlebarEditPanel::dragLeaveEvent(QDragLeaveEvent *event) +{ + removePlaceHolder(); + m_customWidget->removePlaceHolder(); + QWidget::dragLeaveEvent(event); +} + +void DTitlebarEditPanel::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasFormat(DefaultZoneDataFormat)) { + handleDefaultWidgetDropEvent(event); + } else if (event->mimeData()->hasFormat(TitlebarZoneDataFormat)) { + handleTitlebarZoneWidgetDropEvent(event); + } else { + handleSelectionZoneWidgetDropEvent(event); + } +} + +bool DTitlebarEditPanel::containsTool(const QString &toolId) +{ + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto w = m_mainHLayout->itemAt(i)->widget(); + auto view = qobject_cast(w); + if (view && m_settingsImpl->toolId(view->id()) == toolId) { + return true; + } + } + return false; +} + +void DTitlebarEditPanel::replaceOldView(const QString &toolId) +{ + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto w = m_mainHLayout->itemAt(i)->widget(); + auto view = qobject_cast(w); + if (view && m_settingsImpl->toolId(view->id()) == toolId) { + m_mainHLayout->takeAt(i); + int index = m_mainHLayout->indexOf(m_placeHolder); + m_mainHLayout->insertWidget(index, w); + removePlaceHolder(); + Q_EMIT movedToolView(view->id(), index); + updateCustomWidget(); + updateScreenShotedViews(); + break; + } + } +} + +void DTitlebarEditPanel::handleTitlebarZoneWidgetMoveEvent(QDropEvent *event) +{ + removePlaceHolder(); + m_customWidget->removePlaceHolder(); + + QByteArray itemData = event->mimeData()->data(TitlebarZoneDataFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + QString key; + QPoint hotSpot; + QSize size; + dataStream >> key >> hotSpot >> size; + + positionPlaceHolder(event->pos(), hotSpot, size); + Q_EMIT startScreenShot(); +} + +void DTitlebarEditPanel::handleSelectionZoneWidgetMoveEvent(QDropEvent *event) +{ + removePlaceHolder(); + m_customWidget->removePlaceHolder(); + + QByteArray itemData = event->mimeData()->data(SelectionZoneDataFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + QString key; + QPoint hotSpot; + QSize size; + dataStream >> key >> hotSpot >> size; + + positionPlaceHolder(event->pos(), hotSpot, size); + Q_EMIT startScreenShot(); +} + +void DTitlebarEditPanel::handleDefaultWidgetDropEvent(QDropEvent *event) +{ + Q_EMIT resetToolView(); + updateCustomWidget(); + reloadWidgets(); + updateScreenShotedViews(); + event->accept(); +} + +void DTitlebarEditPanel::handleTitlebarZoneWidgetDropEvent(QDropEvent *event) +{ + QByteArray itemData = event->mimeData()->data(TitlebarZoneDataFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + QString id; + QPoint hotSpot; + QSize size; + int type; + int index; + dataStream >> id >> hotSpot >> size >> type >> index; + if (m_mainHLayout->indexOf(m_placeHolder) != -1) { // 调整位置 + auto w = qobject_cast (event->source()); + if (m_settingsImpl->isFixedTool(id)) { + event->ignore(); + return; + } + if (w) { + m_mainHLayout->replaceWidget(m_placeHolder, w); + auto tool = m_settingsImpl->tool(id); + const bool isSpacer = DTitlebarSettingsImpl::isSpacerTool(tool); + if (isSpacer && qobject_cast(tool)->size() == -1) { + m_mainHLayout->setStretchFactor(w, 1); + } + m_placeHolder->hide(); + w->show(); + Q_EMIT movedToolView(id, m_mainHLayout->indexOf(w)); + updateCustomWidget(); + updateScreenShotedViews(); + m_isDropped = true; + event->accept(); + } + } +} + +void DTitlebarEditPanel::handleSelectionZoneWidgetDropEvent(QDropEvent *event) +{ + QByteArray itemData = event->mimeData()->data(SelectionZoneDataFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + QString id; + dataStream >> id; + + if (!m_settingsImpl->isSpacerToolById(id) && containsTool(id)) { + replaceOldView(id); + } else { + int index = m_mainHLayout->indexOf(m_placeHolder); + Q_EMIT addingToolView(id, index); + updateCustomWidget(); + this->addWidget(m_settingsImpl->findKeyByPos(index), index); + removePlaceHolder(); + updateScreenShotedViews(); + } + event->accept(); +} + +void DTitlebarEditPanel::positionPlaceHolder(const QPoint &pos, const QPoint &hotSpot, const QSize &size) +{ + int newIndex = -1; + QWidget *child = childAt(pos); + if (!child) { + for (int i = 0; i < m_mainHLayout->count(); ++i) { + auto w = m_mainHLayout->itemAt(i)->widget(); + if (pos.x() < w->pos().x()) { + if (qobject_cast(w)) { + newIndex = i; + break; + } + } + } + } else if (qobject_cast(child)) { + newIndex = m_mainHLayout->indexOf(child); + if (pos.x() - hotSpot.x() + size.width() / 2 > child->pos().x() + (child->width() / 2)) { + ++newIndex; + } + } else if (qobject_cast(child)) { + return; + } else { // strech + newIndex = m_mainHLayout->count(); + } + + if (newIndex == -1) { + newIndex = m_mainHLayout->count(); + } + if (newIndex != -1) { + m_mainHLayout->insertWidget(newIndex, m_placeHolder); + m_customWidget->insertPlaceHolder(newIndex, size); + m_placeHolder->setFixedSize(size); + m_placeHolder->show(); + } +} + +bool DTitlebarEditPanel::eventFilter(QObject *obj, QEvent *event) +{ + return QWidget::eventFilter(obj, event); +} + +void DTitlebarEditPanel::resizeEvent(QResizeEvent *event) +{ + if (event->size() != event->oldSize()) + Q_EMIT startScreenShot(); +} + +DToolbarEditPanel::DToolbarEditPanel(DTitlebarSettingsImpl *settingsImpl, QWidget *parent) + : DBlurEffectWidget(parent), + m_settingsImpl(settingsImpl), + m_selectZoneView(new QWidget), + m_flowLayout(new DFlowLayout(m_selectZoneView)), + m_defaultToolBarWidget(new IconTextWidget(new DefaultZoneWidget, "default")), + m_confirmBtn(new QPushButton) +{ + init(); +} + +void DToolbarEditPanel::addWidgetToSelectionZone(const QString &id) +{ + auto tool = m_settingsImpl->toolById(id); + Q_ASSERT(tool); + + IconTextWidget * customWidget = new IconTextWidget(new SelectionZoneWidget(id), id, m_selectZoneView); + customWidget->setContent(QIcon::fromTheme(tool->iconName()), tool->description()); + m_flowLayout->addWidget(customWidget); +} + +void DToolbarEditPanel::setDefaultView(const QPixmap &pixmap, const QSize &size) +{ + m_defaultToolBarWidget->setContent(QIcon(pixmap), tr("Default toolset"), size); + m_defaultToolBarWidget->setIconSize(size); +} + +void DToolbarEditPanel::removeAll() +{ + QLayoutItem *item = nullptr; + while ((item = m_flowLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } +} + +void DToolbarEditPanel::onConfirmBtnClicked() +{ + Q_EMIT confirmBtnClicked(); + this->close(); +} + +void DToolbarEditPanel::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + onConfirmBtnClicked(); + } + DBlurEffectWidget::keyPressEvent(event); +} + +void DToolbarEditPanel::init() +{ + QVBoxLayout *mainVLayout = new QVBoxLayout(this); + + QLabel *selectZoneToolTipLabel = new QLabel(tr("Drag your favorite items into the toolbar")); + QLabel *defaultZoneToolTipLabel = new QLabel(tr("Drag below items into the toolbar to restore defaults")); + m_selectZoneView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_flowLayout->setSpacing(30); + + m_confirmBtn->setFixedSize(126, 36); + m_confirmBtn->setText(tr("Confirm")); + + mainVLayout->setSpacing(0); + mainVLayout->setContentsMargins(10, 0, 10, 0); + mainVLayout->addSpacing(21); + mainVLayout->addWidget(selectZoneToolTipLabel, 0, Qt::AlignCenter); + mainVLayout->addSpacing(12); + mainVLayout->addWidget(m_selectZoneView); + mainVLayout->addSpacing(20); + mainVLayout->addWidget(new DHorizontalLine); + mainVLayout->addSpacing(12); + mainVLayout->addWidget(defaultZoneToolTipLabel, 0, Qt::AlignCenter); + mainVLayout->addSpacing(12); + mainVLayout->addWidget(m_defaultToolBarWidget, 0, Qt::AlignLeft); + mainVLayout->addSpacing(10); + mainVLayout->addWidget(new DHorizontalLine); + mainVLayout->addSpacing(10); + mainVLayout->addWidget(m_confirmBtn, 0, Qt::AlignRight); + mainVLayout->addSpacing(10); + + setMouseTracking(true); + + connect(m_confirmBtn, &QPushButton::clicked, this, &DToolbarEditPanel::onConfirmBtnClicked); +} + +DWIDGET_END_NAMESPACE diff --git a/src/widgets/private/dtitlebareditpanel.h b/src/widgets/private/dtitlebareditpanel.h new file mode 100644 index 000000000..545104826 --- /dev/null +++ b/src/widgets/private/dtitlebareditpanel.h @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtitlebarsettingsimpl.h" + +#include +#include +#include +#include + +#include +#include + +class QPushButton; +class QLabel; + +DWIDGET_BEGIN_NAMESPACE + +class DTitlebarEditPanel; +class DFlowLayout; +class DIconButton; + +class PlaceHoderWidget: public QWidget +{ + Q_OBJECT +public: + explicit PlaceHoderWidget(QWidget *parent = nullptr); +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; +}; + +class DragDropWidget : public DIconButton +{ + Q_OBJECT +public: + explicit DragDropWidget(const QString &id = "", QWidget *parent = nullptr); + virtual~DragDropWidget() Q_DECL_OVERRIDE; + + void setButtonIcon(const QIcon &icon, const QSize &size = QSize(36, 36)); + QString id() const; + void setScreenShotedView(QWidget *view); + void screenShot(); + +protected: + virtual void onIgnoreAction(); + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +private: + void startDrag(const QPoint &pos); + void gobackDrag(const QPixmap &pixmap, const QPoint &pos); + +protected: + QString m_mimeDataFormat; + DTitlebarEditPanel *m_titleBarEditPanel = nullptr; + int m_index = -1; + QPixmap m_pixmap; + QString m_id; // m_id is key when Type is TITLEBAR_TYPE, m_id is toolId when Type is SELECTZONE_TYPE + +private: + QPoint m_startDrag; + bool m_isClicked = false; + QPointer m_view = nullptr; +}; + +class TitlebarZoneWidget : public DragDropWidget +{ +public: + explicit TitlebarZoneWidget(const QString &id = "", QWidget *parent = nullptr); + void onIgnoreAction() Q_DECL_OVERRIDE; +}; + +class SelectionZoneWidget : public DragDropWidget +{ +public: + explicit SelectionZoneWidget(const QString &id = "", QWidget *parent = nullptr); +}; + +class DefaultZoneWidget : public DragDropWidget +{ +public: + explicit DefaultZoneWidget(const QString &id = "", QWidget *parent = nullptr); +}; + +class IconTextWidget : public QWidget { + Q_OBJECT +public: + explicit IconTextWidget(DragDropWidget *m_iconWidget, const QString &toolId, QWidget *parent = nullptr); + ~IconTextWidget(); + + void setContent(const QIcon &icon, const QString &text, const QSize &size = QSize(36, 36)); + void setIconSize(const QSize &size); + +private: + QString m_toolId; + DragDropWidget *m_iconWidget; + QLabel *m_titleLabel; +}; + +class DCollapseWidget : public QWidget +{ + Q_OBJECT +public: + explicit DCollapseWidget(DTitlebarSettingsImpl *settings, QWidget *parent = nullptr); + virtual~DCollapseWidget() Q_DECL_OVERRIDE; + + void removeAll(); + void reloadWidgets(); + void removePlaceHolder(); + virtual void addWidget(const QString &key, int index); + void removeWidget(int index); + +protected: + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + void collapse(); + void expand(); + void updateMinimumValue(); + void initExpandButton(); + +protected: + DTitlebarSettingsImpl *m_settingsImpl = nullptr; + QHBoxLayout *m_mainHLayout; + QVector> m_viewsInMenu; + DIconButton *m_expandButton = nullptr; + QPointer m_placeHolder = nullptr; + +private: + int m_minimumWidth = 0; +}; + +class DTitlebarCustomWidget: public DCollapseWidget +{ + Q_OBJECT +public: + explicit DTitlebarCustomWidget(DTitlebarSettingsImpl *settings, QWidget *parent = nullptr); + bool editMode() const; + void setEditMode(bool isEditMode); + QWidget *widget(const int index) const; + void addWidget(const QString &key, int index) Q_DECL_OVERRIDE; + void appendDefaultWidget(const QString &toolId); + void insertPlaceHolder(int index, const QSize &size); + +protected: + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + +private: + bool m_isEditMode = false; +}; + +class DTitlebarEditPanel : public DCollapseWidget +{ + Q_OBJECT +public: + explicit DTitlebarEditPanel(DTitlebarSettingsImpl *settings, DTitlebarCustomWidget *customWidget, QWidget *parent = nullptr); + void updateCustomWidget(bool isEditMode = true); + void updateScreenShotedViews(); + void addWidget(const QString &key, int index) Q_DECL_OVERRIDE; + bool isFixedTool(const int index); + bool dropped() const; + void setDropped(bool isDropped); + void doStartScreenShot(); + void replaceOldView(const QString &toolId); + +Q_SIGNALS: + void addingToolView(const QString &key, const int pos); + void removedToolView(const QString &key, const int pos); + void movedToolView(const QString &key, const int pos); + void resetToolView(); + void startScreenShot(); + +protected: + void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; + void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE; + void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE; + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + +private: + bool containsTool(const QString &toolId); + void handleTitlebarZoneWidgetMoveEvent(QDropEvent *event); + void handleSelectionZoneWidgetMoveEvent(QDropEvent *event); + void handleDefaultWidgetDropEvent(QDropEvent *event); + void handleTitlebarZoneWidgetDropEvent(QDropEvent *event); + void handleSelectionZoneWidgetDropEvent(QDropEvent *event); + void positionPlaceHolder(const QPoint &pos, const QPoint &hotSpot, const QSize &size); + +private: + bool m_isDropped = true; + DTitlebarCustomWidget *m_customWidget; +}; + +class DToolbarEditPanel : public DBlurEffectWidget +{ + Q_OBJECT +public: + explicit DToolbarEditPanel(DTitlebarSettingsImpl *settingsImpl, QWidget *parent = Q_NULLPTR); + void addWidgetToSelectionZone(const QString &id); + void setDefaultView(const QPixmap &pixmap, const QSize &size); + void removeAll(); + +Q_SIGNALS: + void confirmBtnClicked(); + +private Q_SLOTS: + void onConfirmBtnClicked(); + +protected: + void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; + +private: + void init(); + +private: + DTitlebarSettingsImpl *m_settingsImpl = nullptr; + QWidget *m_selectZoneView; + DFlowLayout *m_flowLayout; + IconTextWidget *m_defaultToolBarWidget; + QPushButton *m_confirmBtn; +}; + +DWIDGET_END_NAMESPACE diff --git a/src/widgets/private/dtitlebarsettingsimpl.cpp b/src/widgets/private/dtitlebarsettingsimpl.cpp new file mode 100644 index 000000000..476a0d67d --- /dev/null +++ b/src/widgets/private/dtitlebarsettingsimpl.cpp @@ -0,0 +1,1041 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtitlebarsettingsimpl.h" +#include "ddialog.h" +#include "dtitlebareditpanel.h" +#include "diconbutton.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dflowlayout.h" +#include "dtitlebar.h" + +DWIDGET_BEGIN_NAMESPACE +DCORE_USE_NAMESPACE + +static const QString SettingsTools(u8"tools"); +static const QString SettingsAlignment(u8"alignment"); +static const QString SettingsKey(u8"key"); +static const QString SettingsFixed(u8"fixed"); +static const QString SettingsCount(u8"count"); +static const QString SettingsSpacingSize(u8"spacingSize"); +static const QString SettingsSpacerId(u8"builtin/spacer"); +static const QString SettingsStretchId(u8"builtin/stretch"); + +DTitlebarDataStore::DTitlebarDataStore(QObject *parent) + : QObject(parent) + , m_settingsGroupName("dtitlebar-settings") + , m_settingsGroupNameSubGroup(QString("%1/%2").arg(m_settingsGroupName)) +{ +} + +DTitlebarDataStore::~DTitlebarDataStore() +{ + save(); + qDeleteAll(m_instances); +} + +DTitlebarDataStore *DTitlebarDataStore::instance() +{ + static DTitlebarDataStore *dataStore = nullptr; + if (!dataStore) { + dataStore = new DTitlebarDataStore; + } + return dataStore; +} + +bool DTitlebarDataStore::load() +{ + const auto root = metaRoot(); + if (root.isEmpty()) + return false; + + m_isValid = true; + + if (root.contains(SettingsSpacingSize)) { + m_spacingSize = root[SettingsSpacingSize].toInt(); + } + + const auto cachePos = positionsFromCache(); + if (!cachePos.isEmpty()) { + for (auto item: cachePos) { + const auto data = item.toMap(); + const auto &id = data["toolId"].toString(); + const auto &key = data["key"].toString(); + const auto &fixed = data["fixed"].toBool(); + const auto instance = createInstance(id, key); + instance->isFixed = fixed; + m_instances << instance; + } + } else { + const auto metaPos = toolInstancesFromToolMeta(root); + for (int i = 0; i < metaPos.count(); i++) { + const auto item = metaPos[i]; + const auto instance = createInstance(item.toolId); + instance->isFixed = item.isFixed; + m_instances << instance; + } + } + return true; +} + +ToolInstance *DTitlebarDataStore::createInstance(const QString &id) +{ + return createInstance(id, QUuid::createUuid().toString()); +} + +ToolInstance *DTitlebarDataStore::createInstance(const QString &id, const QString &key) +{ + auto instance = new ToolInstance(); + instance->key = key; + instance->toolId = id; + return instance; +} + +ToolInstance *DTitlebarDataStore::getInstance(const QString &key) const +{ + if (isInvalid()) + return nullptr; + for (int i = 0; i < m_instances.count(); i++) { + if (m_instances[i]->key == key) + return m_instances[i]; + } + return nullptr; +} + +bool DTitlebarDataStore::load(const QString &path) +{ + m_filePath = path; + return load(); +} + +void DTitlebarDataStore::save() +{ + if (m_isValid) { + savePositionsToCache(); + } +} + +void DTitlebarDataStore::clear() +{ + clearCache(); + qDeleteAll(m_instances); + m_instances.clear(); +} + +void DTitlebarDataStore::reset() +{ + clear(); + load(); +} + +bool DTitlebarDataStore::isValid() const +{ + return m_isValid; +} + +QString DTitlebarDataStore::findKeyByPos(const int pos) const +{ + if (isInvalid()) + return QString(); + + if (m_instances.count() <= pos || pos < 0) + return QString(); + + return m_instances[pos]->key; +} + +QStringList DTitlebarDataStore::defaultIds() const +{ + return positionsFromToolMeta(); +} + +QStringList DTitlebarDataStore::keys() const +{ + if (isInvalid()) + return QStringList(); + QStringList positions; + for (auto item: m_instances) { + positions << item->key; + } + return positions; +} + +QString DTitlebarDataStore::key(const int pos) +{ + if (isInvalid()) + return QString(); + + if (m_instances.count() <= pos || pos < 0) + return QString(); + + return m_instances[pos]->key; +} + +QStringList DTitlebarDataStore::toolIds() const +{ + QStringList positions; + for (auto item: m_instances) { + positions << item->toolId; + } + return positions; +} + +QString DTitlebarDataStore::toolId(const QString &key) const +{ + for (int i = 0; i < m_instances.count(); i++) { + if (m_instances[i]->key == key) + return m_instances[i]->toolId; + } + return QString(); +} + +void DTitlebarDataStore::removeAllNotExistIds(const QStringList &ids) +{ + for (int i = m_instances.count() - 1; i >= 0; i--) { + auto instance = m_instances[i]; + if (ids.contains(instance->toolId)) + continue; + + qDebug() << QString("Don't exit the id for %1.").arg(instance->toolId); + m_instances.remove(i); + delete instance; + } +} + +int DTitlebarDataStore::position(const QString &key) const +{ + const auto instance = getInstance(key); + if (!instance) + return -1; + + return m_instances.indexOf(instance); +} + +bool DTitlebarDataStore::contains(const QString &key) const +{ + return getInstance(key); +} + +bool DTitlebarDataStore::isExistTheId(const QString &id) const +{ + if (isInvalid()) + return false; + for (const auto item: m_instances) { + if (item->toolId == id) + return true; + } + return false; +} + +int DTitlebarDataStore::spacingSize() const +{ + return m_spacingSize; +} + +QString DTitlebarDataStore::insert(const QString &id, const int pos) +{ + if (isInvalid()) + return QString(); + + const int index = pos == -1 ? m_instances.count() : pos; + + const auto instance = createInstance(id); + m_instances.insert(index, instance); + return instance->key; +} + +void DTitlebarDataStore::remove(const QString &key) +{ + if (!contains(key)) + return; + + remove(position(key)); +} + +void DTitlebarDataStore::remove(const int pos) +{ + if (isInvalid()) + return; + + if (pos < 0 || pos >= m_instances.count()) + return; + + auto instance = m_instances.takeAt(pos); + delete instance; +} + +bool DTitlebarDataStore::isFixed(const QString &key) const +{ + if (auto instance = getInstance(key)) { + return instance->isFixed; + } + return false; +} + +bool DTitlebarDataStore::isFixed(const int pos) const +{ + if (pos < 0 || pos >= m_instances.count()) + return false; + + return m_instances[pos]->isFixed; +} + +bool DTitlebarDataStore::isInvalid() const +{ + if (!m_isValid) + qWarning() << "TitleBarDataStore is invalid."; + return !m_isValid; +} + +QStringList DTitlebarDataStore::positionsFromToolMeta(const QJsonObject &root) const +{ + QStringList metaPos; + for (auto item : toolInstancesFromToolMeta(root)) { + metaPos << item.toolId; + } + + return metaPos; +} + +QList DTitlebarDataStore::toolInstancesFromToolMeta(const QJsonObject &root) const +{ + QList results; + const auto &tools = root[SettingsTools].toArray(); + for (int i = 0; i < tools.size(); i++) { + const auto item = tools[i]; + const auto id = item[SettingsKey].toString(); + int count = acceptCountField(id) ? countFromToolMeta(root, i) : 1; + + for (int j = 0; j < count; j++) { + ToolInstance ins; + ins.toolId = id; + ins.isFixed = fixedFromToolMeta(root, i); + results << ins; + } + } + + ToolInstance stretchInstance; + stretchInstance.toolId = SettingsStretchId; + stretchInstance.isFixed = true; + const QString &aligment = alignmentFromToolMeta(root); + if (aligment == "right") { + results.prepend(stretchInstance); + } else { + results << stretchInstance; + } + + return results; +} + +bool DTitlebarDataStore::fixedFromToolMeta(const QJsonObject &root, const int index) const +{ + const auto &tools = root[SettingsTools].toArray(); + if (index < 0 || index >= tools.count()) + return false; + + const QJsonObject &item = tools[index].toObject(); + if (!item.contains(SettingsFixed)) + return false; + + return item[SettingsFixed].toBool(); +} + +int DTitlebarDataStore::countFromToolMeta(const QJsonObject &root, const int index) const +{ + const auto &tools = root[SettingsTools].toArray(); + if (index < 0 || index >= tools.count()) + return 0; + + const QJsonObject &item = tools[index].toObject(); + if (!item.contains(SettingsCount)) + return 1; + + return item[SettingsCount].toInt(); +} + +QString DTitlebarDataStore::alignmentFromToolMeta(const QJsonObject &root) const +{ + if (!root.contains(SettingsAlignment)) + return "left"; + return root[SettingsAlignment].toString(); +} + +QStringList DTitlebarDataStore::positionsFromToolMeta() const +{ + const auto root = metaRoot(); + return positionsFromToolMeta(root); +} + +QJsonObject DTitlebarDataStore::metaRoot() const +{ + QFile file(m_filePath); + if (!file.open(QIODevice::ReadOnly)) { + qWarning("Failed on open file: \"%s\", error message: \"%s\"", + qPrintable(file.fileName()), qPrintable(file.errorString())); + return QJsonObject(); + } + + QJsonParseError error; + auto document = QJsonDocument::fromJson(file.readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning("Failed on parse file: %s", qPrintable(error.errorString())); + return QJsonObject(); + } + + return document.object(); +} + +QVariantList DTitlebarDataStore::positionsFromCache() +{ + QVariantList positions; + QSettings settings; + const int size = settings.beginReadArray(m_settingsGroupNameSubGroup.arg("positions")); + for (int i = 0; i < size; i++) { + settings.setArrayIndex(i); + QVariantMap data; + data["key"] = settings.value("key"); + data["toolId"] = settings.value("toolId"); + data["fixed"] = settings.value("fixed"); + positions << data; + } + settings.endArray(); + return positions; +} + +void DTitlebarDataStore::savePositionsToCache() +{ + QSettings settings; + settings.beginWriteArray(m_settingsGroupNameSubGroup.arg("positions")); + for (int i = 0; i < m_instances.size(); i++) { + const auto item = m_instances[i]; + settings.setArrayIndex(i); + settings.setValue("key", item->key); + settings.setValue("toolId", item->toolId); + settings.setValue("fixed", item->isFixed); + } + settings.endArray(); +} + +void DTitlebarDataStore::clearCache() +{ + QSettings settings; + settings.beginGroup(m_settingsGroupName); + settings.remove(""); + settings.endGroup(); +} + +bool DTitlebarDataStore::acceptCountField(const QString &id) const +{ + const QStringList countToolIds { + SettingsSpacerId + }; + return countToolIds.contains(id); +} + +void DTitlebarDataStore::move(const QString &key, const int pos) +{ + if (isInvalid()) + return; + + if (!contains(key)) + return; + + m_instances.move(position(key), pos); +} + +QString DTitlebarDataStore::add(const QString &id) +{ + return insert(id, -1); +} + +DTitlebarToolFactory::DTitlebarToolFactory(QObject *parent) + : QObject(parent) +{ +} + +DTitlebarToolFactory::~DTitlebarToolFactory() +{ + m_tools.clear(); +} + +void DTitlebarToolFactory::add(DTitlebarToolBaseInterface *tool) +{ + bool exist = false; + for (const auto item : qAsConst(m_tools)) { + if (item.tool->id() == tool->id()) { + exist = true; + break; + } + } + if (exist) { + qWarning() << "The tool already exist in factory, tool key: " << tool->id(); + return; + } + m_tools[tool->id()] = ToolWrapper{tool}; +} + +void DTitlebarToolFactory::remove(const QString &id) +{ + m_tools.remove(id); +} + +void DTitlebarToolFactory::setTools(const QList &tools) +{ + m_tools.clear(); + for (auto tool : qAsConst(tools)) + m_tools[tool->id()] = ToolWrapper{tool}; +} + +DTitlebarToolBaseInterface *DTitlebarToolFactory::tool(const QString &id) const +{ + if (!contains(id)) + return nullptr; + + return m_tools[id].tool.data(); +} + +QList DTitlebarToolFactory::tools() const +{ + QList result; + for (auto item : m_tools.values()) + result << item.tool.data(); + + return result; +} + +bool DTitlebarToolFactory::contains(const QString &id) const +{ + return m_tools.contains(id); +} + +QStringList DTitlebarToolFactory::toolIds() const +{ + return m_tools.keys(); +} + +ReloadSignal *ReloadSignal::instance() +{ + static ReloadSignal *reloadSignal = nullptr; + if (!reloadSignal) { + reloadSignal = new ReloadSignal; + } + return reloadSignal; +} + +class ToolSpacer: public QWidget { +public: + explicit ToolSpacer(QWidget *parent = nullptr) : QWidget(parent) + { + } + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; +}; + +void ToolSpacer::paintEvent(QPaintEvent *event) +{ + QColor color; + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) { + color = QColor(65, 77, 104); + } else { + color = QColor(192, 198, 212); + } + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing, true); + painter.setPen(QColor(213, 217, 221)); + painter.drawRoundedRect(this->rect().adjusted(1, 1, -1, -1), 8.0, 8.0); + + painter.setRenderHints(QPainter::Antialiasing, false); + + QPen pen(color); + painter.setPen(color); + painter.setBrush(color); + painter.drawLine(QPoint(rect().x() + 4, height() / 2 - 4), QPoint(rect().x() + 4, height() / 2 + 4)); + painter.drawLine(QPoint(width() - 4 - 1, height() / 2 - 4), QPoint(width() - 4 -1, height() / 2 + 4)); + + pen.setStyle(Qt::DashLine); + painter.setPen(pen); + painter.drawLine(QPoint(rect().x() + 4 + 2, height() / 2), QPoint(width() - 4 - 2, height() / 2)); + + QWidget::paintEvent(event); +} + +class DTitleBarToolSpacer : public DTitleBarSpacerInterface +{ +public: + DTitleBarToolSpacer(DTitlebarDataStore *dataStore) + : m_dataStore(dataStore) + { + } + inline virtual QString id() const override { return SettingsSpacerId; } + virtual QString description() override + { + return "builtin/spacer"; + } + virtual QString iconName() override + { + return "spacer_fixed"; + } + virtual QWidget *createPlaceholderView() override + { + auto view = new ToolSpacer(); + view->setFixedWidth(size()); + return view; + } + virtual int size() const override; +private: + const DTitlebarDataStore *m_dataStore = nullptr; +}; + +int DTitleBarToolSpacer::size() const +{ + if (!m_dataStore || m_dataStore->spacingSize() == -1) + return 30; + return m_dataStore->spacingSize(); +} + +class ToolStretch: public QWidget { +public: + explicit ToolStretch(QWidget *parent = nullptr) : QWidget(parent) + { + } + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; +}; + +void ToolStretch::paintEvent(QPaintEvent *event) +{ + QColor color; + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) { + color = QColor(65, 77, 104); + } else { + color = QColor(192, 198, 212); + } + QPainter painter(this); + painter.setPen(QColor(213, 217, 221)); + painter.setRenderHints(QPainter::Antialiasing, true); + painter.drawRoundedRect(this->rect().adjusted(1, 1, -1, -1), 8.0, 8.0); + + painter.setRenderHints(QPainter::Antialiasing, false); + + QPen pen(color); + painter.setPen(color); + painter.setBrush(color); + QPolygon leftTriangle; + leftTriangle.setPoints(3, rect().x() + 4, height() / 2, rect().x() + 4 + 4, height() / 2 - 4, rect().x() + 4 + 4, height() / 2 + 4); + painter.drawPolygon(leftTriangle); + + QPolygon rightTriangle; + rightTriangle.setPoints(3, width() - 4, height() / 2, width() - 4 - 4, height() / 2 - 4, width() - 4 - 4, height() / 2 + 4); + painter.drawPolygon(rightTriangle); + + pen.setStyle(Qt::DashLine); + painter.setPen(pen); + painter.drawLine(QPoint(rect().x() + 4 + 6, height() / 2), QPoint(width() - 4 - 6, height() / 2)); + + QWidget::paintEvent(event); +} + +class DTitleBarToolStretch : public DTitleBarSpacerInterface +{ +public: + inline virtual QString id() const override { return SettingsStretchId; } + virtual QString description() override + { + return "builtin/stretch"; + } + virtual QString iconName() override + { + return "spacer_stretch"; + } + virtual QWidget *createPlaceholderView() override + { + auto view = new ToolStretch(); + view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + return view; + } + virtual int size() const override; +}; + +int DTitleBarToolStretch::size() const +{ + return -1; +} + +class DTitlebarSettingsImplPrivate: public DCORE_NAMESPACE::DObjectPrivate { + D_DECLARE_PUBLIC(DTitlebarSettingsImpl) +public: + DTitlebarSettingsImplPrivate(DTitlebarSettingsImpl *qq) + : DObjectPrivate(qq) + , dataStore(DTitlebarDataStore::instance()) + { + } + + void initDisplayView() + { + D_Q(DTitlebarSettingsImpl); + + displayView = new DTitlebarEditPanel(q, customView); + displayView->setAutoFillBackground(true); + displayView->setBackgroundRole(QPalette::Base); + QObject::connect(displayView, SIGNAL(addingToolView(const QString &, const int)), + q, SLOT(_q_addingToolView(const QString &, const int))); + QObject::connect(displayView, SIGNAL(removedToolView(const QString &, const int)), + q, SLOT(_q_removedToolView(const QString &, const int))); + QObject::connect(displayView, SIGNAL(resetToolView()), + q, SLOT(_q_resetToolView())); + QObject::connect(displayView, SIGNAL(movedToolView(const QString &, const int)), + q, SLOT(_q_movedToolView(const QString &, const int))); + } + + bool load(const QString &path) + { + if (!dataStore->isValid()) { + if (!dataStore->load(path)) + return false; + } + + // remove not existed tool. + dataStore->removeAllNotExistIds(factory.toolIds()); + + D_Q(DTitlebarSettingsImpl); + QObject::connect(ReloadSignal::instance(), SIGNAL(reload()), q, SLOT(_q_onReload())); + loadCustomView(false); + return true; + } + + void loadCustomView(bool isEditMode) + { + D_Q(DTitlebarSettingsImpl); + + if (!customView) { + customView = new DTitlebarCustomWidget(q); + } + + customView->setEditMode(isEditMode); + customView->removeAll(); + + // load tool from cache. + for (auto key : dataStore->keys()) { + customView->addWidget(key, -1); + } + customView->show(); + } + + void loadSelectZoneView() + { + toolsEditPanel->removeAll(); + for (auto id : factory.toolIds()) { + toolsEditPanel->addWidgetToSelectionZone(id); + } + } + + void loadDefaultZoneView() + { + D_Q(DTitlebarSettingsImpl); + + DTitlebarCustomWidget *defaultTitleBarEditPanel = new DTitlebarCustomWidget(q); + const QSize &size = QSize(toolsEditPanel->minimumWidth(), customView->height()); + defaultTitleBarEditPanel->setFixedSize(size); + for (auto id : dataStore->defaultIds()) { + defaultTitleBarEditPanel->appendDefaultWidget(id); + } + const QPixmap &pixmap = defaultTitleBarEditPanel->grab().scaled(size.width(), size.height() * 70 / 100); + toolsEditPanel->setDefaultView(pixmap, QSize(size.width(), size.height() * 70 / 100)); + defaultTitleBarEditPanel->deleteLater(); + } + + void loadDisplayView() + { + if (!displayView) { + initDisplayView(); + } + + displayView->removeAll(); + for (auto key : dataStore->keys()) { + displayView->addWidget(key, -1); + } + + displayView->updateScreenShotedViews(); + adjustDisplayView(); + } + + void adjustDisplayView() + { + if (displayView) { + displayView->setParent(customView->parentWidget()); + displayView->setFixedSize(customView->size()); + displayView->move(customView->pos()); + displayView->raise(); + Q_EMIT displayView->startScreenShot(); + displayView->show(); + } + } + + void addBuiltinTools() + { + auto spacer = new DTitleBarToolSpacer(dataStore); + factory.add(spacer); + auto stretch = new DTitleBarToolStretch(); + factory.add(stretch); + } + + void showEditPanel() + { + loadCustomView(true); // load the real view in title bar + loadDisplayView(); // load the editable view in title bar + loadSelectZoneView(); + loadDefaultZoneView(); + + toolsEditPanel->show(); + toolsEditPanel->setFocus(); + } + + void removeTool(const QString &key) + { + factory.remove(key); + if (!dataStore->contains(key)) { + qDebug() << "The tool doesn't exist in factory, tool key: " << key; + return; + } + dataStore->remove(key); + } + + void _q_addingToolView(const QString &id, const int pos) + { + D_QC(DTitlebarSettingsImpl); + qDebug() << Q_FUNC_INFO << id << pos; + if (!factory.contains(id)) + return; + + if (!q->isSpacerToolById(id)) { + if (dataStore->isExistTheId(id)) + return; + } + dataStore->insert(id, pos); + dataStore->save(); + Q_EMIT ReloadSignal::instance()->reload(); + } + + void _q_removedToolView(const QString &key, const int pos) + { + qDebug() << Q_FUNC_INFO << key << pos; + dataStore->remove(key); + dataStore->save(); + Q_EMIT ReloadSignal::instance()->reload(); + } + + void _q_movedToolView(const QString &key, const int pos) + { + qDebug() << Q_FUNC_INFO << key << pos; + dataStore->move(key, pos); + dataStore->save(); + Q_EMIT ReloadSignal::instance()->reload(); + } + + void _q_resetToolView() + { + qDebug() << Q_FUNC_INFO; + dataStore->reset(); + Q_EMIT ReloadSignal::instance()->reload(); + } + + void _q_confirmBtnClicked() + { + qDebug() << Q_FUNC_INFO << this; + dataStore->save(); + customView->setEditMode(false); + customView->reloadWidgets(); + displayView->setVisible(false); + } + + void _q_onReload() + { + qDebug() << Q_FUNC_INFO << this; + loadCustomView(customView->editMode()); + } + + QWidget *tryCreateToolsEditPanel() + { + D_Q(DTitlebarSettingsImpl); + if (!toolsEditPanel) { + toolsEditPanel = new DToolbarEditPanel(q); + QObject::connect(toolsEditPanel.data(), SIGNAL(confirmBtnClicked()), + q, SLOT(_q_confirmBtnClicked())); + } + return toolsEditPanel; + } + + DTitlebarToolFactory factory; + DTitlebarDataStore *dataStore = nullptr; + DTitlebarCustomWidget *customView = nullptr; + DTitlebarEditPanel *displayView = nullptr; + + QPointer toolsEditPanel = nullptr; +}; + +DTitlebarSettingsImpl::DTitlebarSettingsImpl(QObject *parent) + : QObject(parent) + , DObject(*new DTitlebarSettingsImplPrivate(this)) +{ +} + +DTitlebarSettingsImpl::~DTitlebarSettingsImpl() +{ +} + +void DTitlebarSettingsImpl::setTools(const QList &tools) +{ + D_D(DTitlebarSettingsImpl); + d->factory.setTools(tools); + d->addBuiltinTools(); +} + +void DTitlebarSettingsImpl::addTool(DTitlebarToolBaseInterface *tool) +{ + D_D(DTitlebarSettingsImpl); + d->factory.add(tool); +} + +DTitlebarToolBaseInterface *DTitlebarSettingsImpl::tool(const QString &key) const +{ + D_DC(DTitlebarSettingsImpl); + auto id = d->dataStore->toolId(key); + return d->factory.tool(id); +} + +DTitlebarToolBaseInterface *DTitlebarSettingsImpl::toolById(const QString &id) const +{ + D_DC(DTitlebarSettingsImpl); + return d->factory.tool(id); +} + +QStringList DTitlebarSettingsImpl::keys() const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->keys(); +} + +QString DTitlebarSettingsImpl::findKeyByPos(const int pos) const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->findKeyByPos(pos); +} + +QString DTitlebarSettingsImpl::toolId(const QString &key) const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->toolId(key); +} + +void DTitlebarSettingsImpl::removeTool(const QString &key) +{ + D_D(DTitlebarSettingsImpl); + d->removeTool(key); +} + +bool DTitlebarSettingsImpl::isSpacerTool(const DTitlebarToolBaseInterface *tool) +{ + return qobject_cast(tool); +} + +bool DTitlebarSettingsImpl::isSpacerTool(const QString &key) const +{ + D_DC(DTitlebarSettingsImpl); + const auto id = d->dataStore->toolId(key); + return isSpacerToolById(id); +} + +bool DTitlebarSettingsImpl::isStrecherTool(const QString &key) const +{ + D_DC(DTitlebarSettingsImpl); + const auto id = d->dataStore->toolId(key); + if (auto tool = qobject_cast(d->factory.tool(id))) + return tool->size() < 0; + + return false; +} + +bool DTitlebarSettingsImpl::isSpacerToolById(const QString &id) const +{ + D_DC(DTitlebarSettingsImpl); + return isSpacerTool(d->factory.tool(id)); +} + +bool DTitlebarSettingsImpl::isFixedTool(const QString &key) const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->isFixed(key); +} + +bool DTitlebarSettingsImpl::isFixedTool(const int pos) const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->isFixed(pos); +} + +bool DTitlebarSettingsImpl::load(const QString &path) +{ + D_D(DTitlebarSettingsImpl); + return d->load(path); +} + +QWidget *DTitlebarSettingsImpl::toolsView() const +{ + D_DC(DTitlebarSettingsImpl); + return d->customView; +} + +QWidget *DTitlebarSettingsImpl::toolsEditPanel() const +{ + D_DC(DTitlebarSettingsImpl); + return const_cast(d)->tryCreateToolsEditPanel(); +} + +bool DTitlebarSettingsImpl::hasEditPanel() const +{ + D_DC(DTitlebarSettingsImpl); + return d->toolsEditPanel != nullptr; +} + +void DTitlebarSettingsImpl::adjustDisplayView() +{ + D_D(DTitlebarSettingsImpl); + d->adjustDisplayView(); +} + +void DTitlebarSettingsImpl::showEditPanel() +{ + D_D(DTitlebarSettingsImpl); + d->showEditPanel(); +} + +bool DTitlebarSettingsImpl::isValid() const +{ + D_DC(DTitlebarSettingsImpl); + return d->dataStore->isValid(); +} + +void DTitlebarSettingsImpl::clearCache() +{ + D_D(DTitlebarSettingsImpl); + d->dataStore->clear(); +} + +DWIDGET_END_NAMESPACE + +#include "moc_dtitlebarsettingsimpl.cpp" diff --git a/src/widgets/private/dtitlebarsettingsimpl.h b/src/widgets/private/dtitlebarsettingsimpl.h new file mode 100644 index 000000000..adb94dbf3 --- /dev/null +++ b/src/widgets/private/dtitlebarsettingsimpl.h @@ -0,0 +1,176 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtkwidget_global.h" +#include "dtitlebarsettings.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +DWIDGET_BEGIN_NAMESPACE + +class DTitlebarToolBaseInterface; + +struct ToolInstance +{ + QString key; + QString toolId; + bool isFixed = false; +}; +class DTitlebarDataStore : public QObject +{ + Q_OBJECT + +public: + explicit DTitlebarDataStore(QObject *parent = nullptr); + virtual ~DTitlebarDataStore() override; + + static DTitlebarDataStore *instance(); + bool load(const QString &path); + void save(); + void clear(); + void reset(); + bool isValid() const; + + QString findKeyByPos(const int pos) const; + QStringList defaultIds() const; + QStringList keys() const; + QString key(const int pos); + QStringList toolIds() const; + QString toolId(const QString &key) const; + void removeAllNotExistIds(const QStringList &ids); + int position(const QString &key) const; + bool contains(const QString &key) const; + bool isExistTheId(const QString &id) const; + int spacingSize() const; + + void move(const QString &key, const int pos); + QString add(const QString &id); + QString insert(const QString &id, const int pos); + void remove(const QString &key); + void remove(const int pos); + + bool isFixed(const QString &key) const; + bool isFixed(const int pos) const; + +private: + bool load(); + ToolInstance *createInstance(const QString &id); + ToolInstance *createInstance(const QString &id, const QString &key); + ToolInstance *getInstance(const QString &key) const; + bool isInvalid() const; + QStringList positionsFromToolMeta(const QJsonObject &root) const; + QList toolInstancesFromToolMeta(const QJsonObject &root) const; + bool fixedFromToolMeta(const QJsonObject &root, const int index) const; + int countFromToolMeta(const QJsonObject &root, const int index) const; + QString alignmentFromToolMeta(const QJsonObject &root) const; + QStringList positionsFromToolMeta() const; + QJsonObject metaRoot() const; + QVariantList positionsFromCache(); + void savePositionsToCache(); + void clearCache(); + bool acceptCountField(const QString &id) const; + +private: + QString m_settingsGroupName; + QString m_settingsGroupNameSubGroup; + QVector m_instances; + int m_spacingSize = -1; + bool m_isValid = false; + QString m_filePath; +}; + +struct ToolWrapper +{ + explicit ToolWrapper(DTitlebarToolBaseInterface *t = nullptr) + : tool(t) + { + } + QSharedPointer tool = nullptr; +}; + +class DTitlebarToolFactory : public QObject +{ + Q_OBJECT +public: + explicit DTitlebarToolFactory(QObject *parent = nullptr); + virtual ~DTitlebarToolFactory() override; + + void add(DTitlebarToolBaseInterface *tool); + void remove(const QString &id); + void setTools(const QList &tools); + DTitlebarToolBaseInterface *tool(const QString &id) const; + QList tools() const; + bool contains(const QString &id) const; + + QStringList toolIds() const; + +private: + QMap m_tools; +}; + +class ReloadSignal : public QObject +{ + Q_OBJECT +public: + static ReloadSignal *instance(); +Q_SIGNALS: + void reload(); +private: + ReloadSignal() = default; + ~ReloadSignal() = default; +}; + +class DTitlebarSettingsImplPrivate; + +class DTitlebarSettingsImpl : public QObject, public DCORE_NAMESPACE::DObject +{ + Q_OBJECT +public: + explicit DTitlebarSettingsImpl(QObject *parent = nullptr); + virtual ~DTitlebarSettingsImpl() override; + + void setTools(const QList &tools); + void addTool(DTitlebarToolBaseInterface *tool); + DTitlebarToolBaseInterface *tool(const QString &key) const; + DTitlebarToolBaseInterface *toolById(const QString &id) const; + QStringList keys() const; + QString findKeyByPos(const int pos) const; + QString toolId(const QString &key) const; + void removeTool(const QString &key); + static bool isSpacerTool(const DTitlebarToolBaseInterface *tool); + bool isSpacerTool(const QString &key) const; + bool isStrecherTool(const QString &key) const; + bool isSpacerToolById(const QString &id) const; + bool isFixedTool(const QString &key) const; + bool isFixedTool(const int pos) const; + bool load(const QString &path); + + QWidget *toolsView() const; + QWidget *toolsEditPanel() const; + bool hasEditPanel() const; + void adjustDisplayView(); + void showEditPanel(); + bool isValid() const; + void clearCache(); + +private: + D_DECLARE_PRIVATE(DTitlebarSettingsImpl) + + D_PRIVATE_SLOT(void _q_addingToolView(const QString &, const int)) + D_PRIVATE_SLOT(void _q_removedToolView(const QString &, const int)) + D_PRIVATE_SLOT(void _q_movedToolView(const QString &, const int)) + D_PRIVATE_SLOT(void _q_resetToolView()) + D_PRIVATE_SLOT(void _q_confirmBtnClicked()) + D_PRIVATE_SLOT(void _q_onReload()) +}; + +DWIDGET_END_NAMESPACE diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc81256d9..abc2b54f3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,7 @@ set(BIN_NAME "ut-${PROJECT_NAME}") set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_FLAGS "-fno-access-control") @@ -123,6 +124,7 @@ set(WIDGET_TEST testcases/widgets/ut_dtickeffect.cpp testcases/widgets/ut_dtiplabel.cpp testcases/widgets/ut_dtitlebar.cpp + testcases/widgets/ut_dtitlebarsettings.cpp testcases/widgets/ut_dtoolbutton.cpp testcases/widgets/ut_dtooltip.cpp testcases/widgets/ut_dwarningbutton.cpp @@ -138,10 +140,13 @@ set(WIDGET_TEST include(../src/util/util.cmake) include(../src/widgets/widgets.cmake) +set(RESCOUCES data.qrc) + add_executable(${BIN_NAME} main.cpp ${util_SRC} ${widgets_SRC} + ${RESCOUCES} ${WIDGET_TEST} ) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/tests/data.qrc b/tests/data.qrc new file mode 100644 index 000000000..68b15b181 --- /dev/null +++ b/tests/data.qrc @@ -0,0 +1,5 @@ + + + data/titlebar-settings.json + + diff --git a/tests/data/titlebar-settings.json b/tests/data/titlebar-settings.json new file mode 100644 index 000000000..ea8478345 --- /dev/null +++ b/tests/data/titlebar-settings.json @@ -0,0 +1,11 @@ +{ + "spacingSize": 20, + "tools": [ + { + "key": "builtin/search-tool" + }, + { + "key": "test-tool" + } + ] +} diff --git a/tests/testcases/widgets/ut_dtitlebarsettings.cpp b/tests/testcases/widgets/ut_dtitlebarsettings.cpp new file mode 100644 index 000000000..9a4fa3d78 --- /dev/null +++ b/tests/testcases/widgets/ut_dtitlebarsettings.cpp @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include + +#include "dtitlebar.h" +#include "private/dtitlebarsettingsimpl.h" + +DWIDGET_USE_NAMESPACE + +static const QString dataFilePath = ":/data/titlebar-settings.json"; + +class ut_DTitlebarDataStore : public testing::Test +{ +protected: + virtual void TearDown() override; +}; + +void ut_DTitlebarDataStore::TearDown() +{ + // clear cache. + DTitlebarDataStore dataStore; + dataStore.clear(); +} + +TEST_F(ut_DTitlebarDataStore, loadAndSave) +{ + QStringList settingsTools({"builtin/search-tool", + "test-tool"}); + { + DTitlebarDataStore dataStore; + dataStore.clear(); + ASSERT_TRUE(dataStore.load(dataFilePath)); + ASSERT_EQ(dataStore.toolIds(), settingsTools << "builtin/stretch"); + } + { + DTitlebarDataStore dataStore; + dataStore.load(dataFilePath); + dataStore.move("test-tool", 0); + ASSERT_EQ(dataStore.defaultIds(), settingsTools); + } +} + +TEST_F(ut_DTitlebarDataStore, addAndRemove) +{ + { + QStringList settingsTools({"builtin/search-tool", + "test-tool", + "builtin/stretch", + "test-tool2"}); + DTitlebarDataStore dataStore; + dataStore.load(dataFilePath); + dataStore.add("test-tool2"); + ASSERT_EQ(dataStore.toolIds(), settingsTools); + } + { + QStringList settingsTools({"builtin/search-tool", + "test-tool", + "builtin/stretch", + "test-tool2"}); + DTitlebarDataStore dataStore; + dataStore.load(dataFilePath); + ASSERT_EQ(dataStore.toolIds(), settingsTools); + } + { + QStringList settingsTools({"builtin/search-tool", + "test-tool", + "builtin/stretch", + "test-tool2"}); + DTitlebarDataStore dataStore; + dataStore.load(dataFilePath); + const auto key = dataStore.keys().at(0); + const auto toolId = dataStore.toolId(key); + dataStore.remove(key); + ASSERT_EQ(toolId, "builtin/search-tool"); + settingsTools.removeAll(toolId); + ASSERT_EQ(dataStore.toolIds().size(), settingsTools.size()); + } +} + +class TitleBarToolTest : public DTitleBarToolInterface { +public: + virtual QWidget *createView() override + { + auto view = new DLineEdit(); + connect(view, &DLineEdit::textEdited, this, [this](const QString &) { + actionExecuted = true; + }); + return view; + } + virtual QString id() const override + { + return "test-tool"; + } + virtual QString description() override + { + return "test tool"; + } + virtual QString iconName() override + { + return "test-icon"; + } + bool actionExecuted = false; +}; + +class TitleBarToolTest2 : public TitleBarToolTest { +public: + virtual QString id() const override + { + return "test-tool2"; + } + virtual QString description() override + { + return "test tool2"; + } +}; + +class ut_DTitleBarToolInterface : public testing::Test +{ +}; + +TEST_F(ut_DTitleBarToolInterface, base) +{ + auto tool = new TitleBarToolTest(); + auto view = qobject_cast(tool->createView()); + ASSERT_TRUE(view); + ASSERT_FALSE(tool->actionExecuted); + view->textEdited("text"); + ASSERT_TRUE(tool->actionExecuted); + + ASSERT_EQ(tool->id(), QString("test-tool")); + ASSERT_EQ(tool->description(), QString("test tool")); + ASSERT_EQ(tool->iconName(), QString("test-icon")); + + tool->deleteLater(); + view->deleteLater(); +} + +class ut_DTitlebarToolFactory : public testing::Test +{ +}; + +TEST_F(ut_DTitlebarToolFactory, base) +{ + DTitlebarToolFactory factory; + auto tool = new TitleBarToolTest(); + factory.add(tool); + ASSERT_EQ(factory.toolIds(), QStringList{"test-tool"}); + ASSERT_TRUE(factory.contains("test-tool")); + ASSERT_EQ(factory.tool("test-tool"), tool); +} + +class ut_DTitleBarSettings : public testing::Test +{ +protected: + virtual void TearDown() override; +}; + +void ut_DTitleBarSettings::TearDown() +{ + // clear cache. + DTitlebarSettingsImpl settings; + settings.clearCache(); +} + +TEST_F(ut_DTitleBarSettings, base) +{ + DTitlebarSettingsImpl settings; + settings.addTool(new TitleBarToolTest()); + settings.addTool(new TitleBarToolTest2()); + settings.load(dataFilePath); +}