From abed88310d76e98f184f1cbd8137c52972795ad0 Mon Sep 17 00:00:00 2001 From: czyt1988 Date: Wed, 27 Nov 2024 23:20:38 +0800 Subject: [PATCH] =?UTF-8?q?=E9=81=8D=E5=8E=86=E6=95=B4=E4=B8=AA=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E6=8A=8ALF=E6=8D=A2=E8=A1=8C=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E4=B8=BACRLF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/MainWindowExample/mainwindow.cpp | 3462 +- example/MainWindowExample/mainwindow.h | 242 +- example/MdiAreaWindowExample/main.cpp | 128 +- .../MdiAreaWindowExample/mdimainwindow.cpp | 162 +- example/MdiAreaWindowExample/mdimainwindow.h | 66 +- example/NormalMenuBarExample/MainWindow.cpp | 30 +- example/NormalMenuBarExample/MainWindow.h | 48 +- example/NormalMenuBarExample/main.cpp | 132 +- example/StaticExample/MainWindow.cpp | 86 +- example/StaticExample/MainWindow.h | 30 +- example/StaticExample/main.cpp | 28 +- example/WidgetWithRibbon/InnerWidget.cpp | 34 +- example/WidgetWithRibbon/InnerWidget.h | 50 +- example/WidgetWithRibbon/MainWidget.cpp | 30 +- example/WidgetWithRibbon/MainWidget.h | 44 +- example/WidgetWithRibbon/RibbonWidget.cpp | 170 +- example/WidgetWithRibbon/RibbonWidget.h | 50 +- example/WidgetWithRibbon/main.cpp | 22 +- src/SARibbon.cpp | 32444 ++++++++-------- src/SARibbon.h | 7556 ++-- src/SARibbonBar/SARibbonActionsManager.cpp | 1276 +- src/SARibbonBar/SARibbonActionsManager.h | 294 +- src/SARibbonBar/SARibbonBarVersionInfo.h | 66 +- src/SARibbonBar/SARibbonCategoryLayout.cpp | 1446 +- src/SARibbonBar/SARibbonCategoryLayout.h | 190 +- src/SARibbonBar/SARibbonColorToolButton.cpp | 416 +- src/SARibbonBar/SARibbonColorToolButton.h | 110 +- src/SARibbonBar/SARibbonComboBox.cpp | 22 +- src/SARibbonBar/SARibbonComboBox.h | 38 +- src/SARibbonBar/SARibbonContextCategory.cpp | 424 +- src/SARibbonBar/SARibbonContextCategory.h | 160 +- src/SARibbonBar/SARibbonControlButton.cpp | 32 +- src/SARibbonBar/SARibbonControlButton.h | 66 +- src/SARibbonBar/SARibbonCustomizeData.cpp | 1408 +- src/SARibbonBar/SARibbonCustomizeData.h | 304 +- src/SARibbonBar/SARibbonCustomizeDialog.cpp | 374 +- src/SARibbonBar/SARibbonCustomizeDialog.h | 116 +- src/SARibbonBar/SARibbonCustomizeWidget.cpp | 3026 +- src/SARibbonBar/SARibbonCustomizeWidget.h | 418 +- src/SARibbonBar/SARibbonElementFactory.cpp | 236 +- src/SARibbonBar/SARibbonElementFactory.h | 118 +- src/SARibbonBar/SARibbonElementManager.cpp | 54 +- src/SARibbonBar/SARibbonElementManager.h | 104 +- src/SARibbonBar/SARibbonGallery.cpp | 842 +- src/SARibbonBar/SARibbonGallery.h | 250 +- src/SARibbonBar/SARibbonGlobal.h | 298 +- .../SARibbonLineWidgetContainer.cpp | 114 +- src/SARibbonBar/SARibbonLineWidgetContainer.h | 88 +- src/SARibbonBar/SARibbonMenu.cpp | 100 +- src/SARibbonBar/SARibbonMenu.h | 46 +- src/SARibbonBar/SARibbonPannel.cpp | 2026 +- src/SARibbonBar/SARibbonPannel.h | 474 +- src/SARibbonBar/SARibbonPannelItem.cpp | 30 +- src/SARibbonBar/SARibbonPannelItem.h | 100 +- src/SARibbonBar/SARibbonPannelLayout.cpp | 1904 +- src/SARibbonBar/SARibbonPannelLayout.h | 226 +- .../SARibbonPannelOptionButton.cpp | 32 +- src/SARibbonBar/SARibbonPannelOptionButton.h | 48 +- src/SARibbonBar/SARibbonSeparatorWidget.cpp | 52 +- src/SARibbonBar/SARibbonSeparatorWidget.h | 36 +- src/SARibbonBar/SARibbonSystemButtonBar.h | 176 +- src/SARibbonBar/SARibbonToolButton.cpp | 2458 +- src/SARibbonBar/SARibbonToolButton.h | 184 +- src/SARibbonBar/SARibbonWidget.cpp | 454 +- src/SARibbonBar/SARibbonWidget.h | 90 +- .../colorWidgets/SAColorGridWidget.cpp | 1008 +- .../colorWidgets/SAColorGridWidget.h | 186 +- src/SARibbonBar/colorWidgets/SAColorMenu.cpp | 520 +- src/SARibbonBar/colorWidgets/SAColorMenu.h | 106 +- .../colorWidgets/SAColorPaletteGridWidget.cpp | 342 +- .../colorWidgets/SAColorPaletteGridWidget.h | 90 +- .../colorWidgets/SAColorToolButton.cpp | 1262 +- .../colorWidgets/SAColorToolButton.h | 210 +- .../colorWidgets/SAColorWidgetsGlobal.h | 116 +- src/SARibbonBar/colorWidgets/tst/Widget.cpp | 268 +- src/SARibbonBar/colorWidgets/tst/Widget.h | 58 +- src/SARibbonBar/colorWidgets/tst/main.cpp | 22 +- tools/amalgamate/SARibbonAmalgamTemplate.cpp | 138 +- tools/amalgamate/SARibbonAmalgamTemplate.h | 26 +- .../SARibbonAmalgamTemplateHeaderGlue.h | 4 +- .../SARibbonAmalgamTemplatePublicHeaders.h | 94 +- 81 files changed, 34995 insertions(+), 34995 deletions(-) diff --git a/example/MainWindowExample/mainwindow.cpp b/example/MainWindowExample/mainwindow.cpp index f11df211..8d2bd790 100644 --- a/example/MainWindowExample/mainwindow.cpp +++ b/example/MainWindowExample/mainwindow.cpp @@ -1,1731 +1,1731 @@ -#include "mainwindow.h" -#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER -#include "SAFramelessHelper.h" -#endif -#include "SARibbonApplicationButton.h" -#include "SARibbonBar.h" -#include "SARibbonButtonGroupWidget.h" -#include "SARibbonCategory.h" -#include "SARibbonCheckBox.h" -#include "SARibbonColorToolButton.h" -#include "SARibbonComboBox.h" -#include "SARibbonCustomizeDialog.h" -#include "SARibbonCustomizeWidget.h" -#include "SARibbonGallery.h" -#include "SARibbonLineEdit.h" -#include "SARibbonMenu.h" -#include "SARibbonPannel.h" -#include "SARibbonQuickAccessBar.h" -#include "SARibbonToolButton.h" -#include "colorWidgets/SAColorGridWidget.h" -#include "colorWidgets/SAColorPaletteGridWidget.h" -#include "SARibbonSystemButtonBar.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define PRINT_COST_START() \ - QElapsedTimer __TMP_COST; \ - __TMP_COST.start(); \ - int __TMP_LASTTIMES = 0 - -#define PRINT_COST(STR) \ - do { \ - int ___TMP_INT = __TMP_COST.elapsed(); \ - qDebug() << STR << " cost " << ___TMP_INT - __TMP_LASTTIMES << " ms (" << ___TMP_INT << ")"; \ - mTextedit->append(QString("%1 cost %2 ms(%3)").arg(STR).arg(___TMP_INT - __TMP_LASTTIMES).arg(___TMP_INT)); \ - __TMP_LASTTIMES = ___TMP_INT; \ - } while (0) - -MainWindow::MainWindow(QWidget* par) - : SARibbonMainWindow(par), mWidgetForCustomize(nullptr), mMenuApplicationBtn(nullptr) -{ - - PRINT_COST_START(); - setWindowTitle(("ribbon mainwindow test")); - mTextedit = new QTextEdit(this); - setCentralWidget(mTextedit); - setStatusBar(new QStatusBar()); - - SARibbonBar* ribbon = ribbonBar(); - //! 通过setContentsMargins设置ribbon四周的间距 - ribbon->setContentsMargins(5, 0, 5, 0); - - connect(ribbon, &SARibbonBar::actionTriggered, this, [ this ](QAction* action) { - mTextedit->append(QString("action object name=%1 triggered").arg(action->objectName())); - }); - - //! cn: - //! 这里示例的是如何设置和修改applicationButton,默认情况下SARibbonBar会创建一个SARibbonApplicationButton, - //! SARibbonApplicationButton的父类是QToolButton,用户也可以创建自己的applicationButton, - //! 通过SARibbonBar::setApplicationButton函数设置自己定义的按钮,如果传入一个空指针,将取消掉applicationButton - //! en: - //! Here is an example of how to set and modify the applicationButton. By default, - //! SARibbonBar will create a SARibbonApplicationButton. The parent class of SARibbonApplicationButton is QToolButton. - //! Users can also create their own applicationButton, and set their own buttons through the SARibbonBar::setApplicationButton function. - //! If a null pointer is passed in, the applicationButton will be canceled - createRibbonApplicationButton(); - - //! cn: - //! 添加主标签页,这里演示通过SARibbonBar::addCategoryPage函数添加一个标签页 - //! en: - //! Add the main tab. Here we show how to add a tab through the SARibbonBar::addCategoryPage function - SARibbonCategory* categoryMain = ribbon->addCategoryPage(tr("&Main")); - //! cn: SARibbonBar的Category和Pannel,以及对应的Action都应该设置ObjectName,因为如果要自定义action,这些ObjectName是必不可少的 - //! en: The category , pannel and actions of SARibbonBar, should be set with Object Names, as these Object Names are essential for customizing actions - categoryMain->setObjectName(("categoryMain")); - createCategoryMain(categoryMain); - PRINT_COST("new main page"); - - //! cn:这里演示了另外一种添加标签页的方式,先创建SARibbonCategory指针,并通过SARibbonBar::addCategoryPage函数把SARibbonCategory指针传递给SARibbonBar - //! en:Here is another way to add a tab: first create a SARibbonCategory pointer and pass it to SARibbonBar through the SARibbonBar::addCategoryPage function - SARibbonCategory* categoryOther = new SARibbonCategory(); - categoryOther->setCategoryName(tr("Other")); - categoryOther->setObjectName(("categoryOther")); - createCategoryOther(categoryOther); - ribbon->addCategoryPage(categoryOther); - PRINT_COST("add other page"); - - // 添加删除标签页 - SARibbonCategory* categoryDelete = new SARibbonCategory(); - categoryDelete->setCategoryName(("Delete")); - categoryDelete->setObjectName(("categoryDelete")); - ribbon->addCategoryPage(categoryDelete); - createCategoryDelete(categoryDelete); - PRINT_COST("add category delete page"); - - // 添加尺寸标签页 - SARibbonCategory* categorySize = new SARibbonCategory(); - categorySize->setCategoryName(("Size(example long category)")); - categorySize->setObjectName(("categorySize")); - ribbon->addCategoryPage(categorySize); - createCategorySize(categorySize); - PRINT_COST("add category size page"); - - // 添加颜色标签页 - SARibbonCategory* categoryColor = new SARibbonCategory(); - categoryColor->setCategoryName(("Color")); - categoryColor->setObjectName(("categoryColor")); - ribbon->addCategoryPage(categoryColor); - createCategoryColor(categoryColor); - PRINT_COST("add category color page"); - - createContextCategory1(); - PRINT_COST("add context1 category page"); - - createContextCategory2(); - PRINT_COST("add context2 category page"); - - createQuickAccessBar(); - PRINT_COST("add quick access bar"); - - //! cn: - //! 创建RightButtonGroup,RightButtonGroup类似一个在右上角的工具栏,给用户放置一些快捷图标,例如关于、帮助等图标, - //! RightButtonGroup在SARibbonBar创建时就会构建一个默认的SARibbonButtonGroupWidget,可以通过SARibbonBar::rightButtonGroup函数获取 - //! en: - //! Create a RightButtonGroup, which is similar to a toolbar in the upper right corner, - //! providing users with shortcut icons such as About, Help, etc. RightButtonGroup will build a default SARibbonButtonGroupWidget when creating SARibbonBar, - //! which can be obtained through the SARibbonBar::rightButtonGroup function - createRightButtonGroup(); - PRINT_COST("add right bar"); - - //! cn: - //! 这里演示了如何在系统窗口最小化最大化关闭按钮旁边添加其他按钮 - createWindowButtonGroupBar(); - - //! cn: - //! actionManager可以管理所有的action,并给SARibbon的自定义窗口使用, - //! actionManager必须在ribbon的action都创建完成后创建,如果在之前就创建好,后加入ribbon的action需要手动管理到actionManager里, - //! actionManager也可以管理不在ribbonBar里的action - //! en: - //! The ActionManager can manage all actions and be used for SARibbon's custom window. - //! The ActionManager must be created after all the actions in the ribbon are created. - //! If the actions are created before, the actions added to the ribbon need to be manually managed in the ActionManager. - //! The ActionManager can also manage actions not in the ribbon bar - createActionsManager(); - - setMinimumWidth(500); - setWindowIcon(QIcon(":/icon/icon/SA.svg")); - - connect(ribbon, &SARibbonBar::currentRibbonTabChanged, this, [ this ](int v) { - mTextedit->append(QString("SARibbonBar::currentRibbonTabChanged(%1)").arg(v)); - }); - - showMaximized(); -} - -/** - * @brief 创建ApplicationButton - */ -void MainWindow::createRibbonApplicationButton() -{ - SARibbonBar* ribbon = ribbonBar(); - if (!ribbon) { - return; - } - QAbstractButton* btn = ribbon->applicationButton(); - if (!btn) { - //! cn: SARibbonBar默认就会创建一个SARibbonApplicationButton,因此,在正常情况下,这个位置不会进入 - //! en: SARibbonBar creates a SARibbonApplicationButton by default. Therefore, under normal circumstances, this location will not enter - btn = new SARibbonApplicationButton(this); - ribbon->setApplicationButton(btn); - } - ribbon->applicationButton()->setText((" &File ")); // 文字两边留有间距,好看一点 - //! cn: SARibbonMenu和QMenu的操作是一样的 - //! en: The operations of SARibbonMenu and QMenu are the same - if (!mMenuApplicationBtn) { - mMenuApplicationBtn = new SARibbonMenu(this); - mMenuApplicationBtn->addAction(createAction("test1", ":/icon/icon/action.svg")); - mMenuApplicationBtn->addAction(createAction("test2", ":/icon/icon/action2.svg")); - mMenuApplicationBtn->addAction(createAction("test3", ":/icon/icon/action3.svg")); - mMenuApplicationBtn->addAction(createAction("test4", ":/icon/icon/action4.svg")); - } - SARibbonApplicationButton* appBtn = qobject_cast< SARibbonApplicationButton* >(btn); - if (!appBtn) { - return; - } - appBtn->setMenu(mMenuApplicationBtn); -} - -/** - * @brief 显示ContextCategory1 - * @param on - */ -void MainWindow::onShowContextCategory(bool on) -{ - if (mContextCategory == nullptr) { - createContextCategory1(); - } - if (on) { - this->ribbonBar()->showContextCategory(mContextCategory); - } else { - this->ribbonBar()->hideContextCategory(mContextCategory); - } -} - -void MainWindow::onStyleClicked(int id) -{ - - SARibbonBar::RibbonStyles ribbonStyle = static_cast< SARibbonBar::RibbonStyles >(id); - ribbonBar()->setRibbonStyle(ribbonStyle); - mActionWordWrap->setChecked(SARibbonToolButton::isEnableWordWrap()); - switch (ribbonStyle) { - case SARibbonBar::RibbonStyleLooseThreeRow: - // cn:"LooseThreeRow"样式的文字显示是换行的,同时也会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, - // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 - mTextedit->append( - tr("\nThe text display of the \"LooseThreeRow\" style is word wrap and also displays the title bar. " - "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," - "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); - mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleLooseThreeRow);")); - break; - case SARibbonBar::RibbonStyleLooseTwoRow: - // cn:"LooseThreeRow"样式的文字显示是不换行的,同时也会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, - // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 - mTextedit->append( - tr("\nThe text display of the \"LooseTwoRow\" style is not word wrap and also displays the title bar. " - "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," - "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); - mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleLooseTwoRow);")); - break; - case SARibbonBar::RibbonStyleCompactThreeRow: - // cn:"CompactThreeRow"样式的文字显示是换行的,不会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, - // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 - mTextedit->append( - tr("\nThe text display of the \"LooseThreeRow\" style is word wrap and not displays the title bar. " - "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," - "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); - mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleCompactThreeRow);")); - break; - case SARibbonBar::RibbonStyleCompactTwoRow: - // cn:"CompactTwoRow"样式的文字显示是不换行的,不会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, - // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 - mTextedit->append( - tr("\nThe text display of the \"CompactTwoRow\" style is not word wrap and not displays the title bar. " - "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," - "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); - mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleCompactTwoRow);")); - break; - default: - break; - } -} - -/** - * @brief 自定义按钮槽 - * - * 这里演示了如何使用@ref SARibbonCustomizeWidget 来实现界面的自定义 - */ -void MainWindow::onActionCustomizeTriggered() -{ - if (nullptr == mWidgetForCustomize) { - mWidgetForCustomize = - new SARibbonCustomizeWidget(this, this, Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::Dialog); - mWidgetForCustomize->setWindowModality(Qt::ApplicationModal); // 设置阻塞类型 - mWidgetForCustomize->setAttribute(Qt::WA_ShowModal, true); // 属性设置 true:模态 false:非模态 - mWidgetForCustomize->setupActionsManager(mActionsManager); - } - mWidgetForCustomize->show(); - mWidgetForCustomize->applys(); -} - -/** - * @brief 自定义按钮槽 - * - * 这里演示了如何使用@ref SARibbonCustomizeDialog 来实现界面的自定义,同时演示了如何把自定义的界面保存为xml文件 - */ -void MainWindow::onActionCustomizeAndSaveTriggered() -{ - SARibbonCustomizeDialog dlg(this); - dlg.setupActionsManager(mActionsManager); - // 如果启动时未应用上次修改,先应用再读取,保持本地数据和ui一致 - if (!mHasApplyCustomizeXmlFile) { - auto res = QMessageBox::question( - this, tr("question"), tr("Apply the last modification?\nIf not, local data will be reset")); - if (res == QMessageBox::Yes) { - onActionLoadCustomizeXmlFileTriggered(); - return; - } else { - QFile::remove("customize.xml"); - dlg.clear(); - - mHasApplyCustomizeXmlFile = true; - } - } - - dlg.fromXml("customize.xml"); - if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { - // 先apply - if (dlg.isCached()) - dlg.applys(); - - // 无更改直接退出 - if (!dlg.isApplied()) { - mTextedit->append("no change to save"); - return; - } - - QByteArray str; - QXmlStreamWriter xml(&str); - xml.setAutoFormatting(true); - xml.setAutoFormattingIndent(2); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. - xml.setCodec("utf-8"); -#endif - xml.writeStartDocument(); - bool isok = dlg.toXml(&xml); - xml.writeEndDocument(); - if (isok) { - QFile f("customize.xml"); - if (f.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { - QTextStream s(&f); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QTextStream always encodes XML in UTF-8. - s.setCodec("utf-8"); -#endif - s << str; - s.flush(); - } - mTextedit->append("write xml:"); - mTextedit->append(str); - } - } -} - -/** - * @brief 自定义按钮槽 - * - * 这里演示了如何使用@ref SARibbonCustomizeWidget 嵌入到QDialog中实现界面的自定义, - * 这样是为了让@ref SARibbonCustomizeWidget更好的和自己的软件设置页面结合 - */ -void MainWindow::onActionCustomizeAndSaveWithApplyTriggered() -{ - // 如果启动时未应用上次修改,先应用再读取,保持本地数据和ui一致 - if (!mHasApplyCustomizeXmlFile) { - auto res = QMessageBox::question( - this, tr("question"), tr("Apply the last modification?\nIf not, local data will be reset")); - if (res == QMessageBox::Yes) { - onActionLoadCustomizeXmlFileTriggered(); - return; - } else { - QFile::remove("customize.xml"); - mHasApplyCustomizeXmlFile = true; - } - } - - QDialog dlg; - QVBoxLayout* main = new QVBoxLayout; - dlg.setLayout(main); - SARibbonCustomizeWidget* widgetForCustomize = new SARibbonCustomizeWidget(this, &dlg); - widgetForCustomize->setupActionsManager(mActionsManager); - - main->addWidget(widgetForCustomize, 1); - - QDialogButtonBox* buttonBox = - new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); - - main->addWidget(buttonBox); - - connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); - connect(buttonBox, &QDialogButtonBox::clicked, &dlg, [ = ](QAbstractButton* button) { - auto role = buttonBox->buttonRole(button); - if (role == QDialogButtonBox::ApplyRole) // apply - { - if (widgetForCustomize->isCached()) { - widgetForCustomize->applys(); - mTextedit->append("change applied"); - } else { - mTextedit->append("no change to apply"); - } - } - }); - - widgetForCustomize->fromXml("customize.xml"); - if (QDialog::Accepted == dlg.exec()) { - // 先apply - if (widgetForCustomize->isCached()) - widgetForCustomize->applys(); - - // 无更改直接退出 - if (!widgetForCustomize->isApplied()) { - mTextedit->append("no change to save"); - return; - } - - QByteArray str; - QXmlStreamWriter xml(&str); - xml.setAutoFormatting(true); - xml.setAutoFormattingIndent(2); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. - xml.setCodec("utf-8"); -#endif - xml.writeStartDocument(); - bool isok = widgetForCustomize->toXml(&xml); - xml.writeEndDocument(); - if (isok) { - QFile f("customize.xml"); - if (f.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { - QTextStream s(&f); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QTextStream always encodes XML in UTF-8. - s.setCodec("utf-8"); -#endif - s << str; - s.flush(); - } - mTextedit->append("write xml:"); - mTextedit->append(str); - } - } else { - // 清除所有动作 - widgetForCustomize->clear(); - mTextedit->append("all changes clear, the applied changes will take no effect afer restart"); - } -} - -void MainWindow::onActionHelpTriggered() -{ - QMessageBox::information(this, - tr("infomation"), - tr("\n ===============" - "\n SARibbonBar version:%1" - "\n Author:czy" - "\n Email:czy.t@163.com" - "\n ===============") - .arg(SARibbonBar::versionString())); -} - -/** - * @brief 移除ApplicationButton - * @param b - */ -void MainWindow::onActionRemoveAppBtnTriggered(bool b) -{ - if (b) { - ribbonBar()->setApplicationButton(nullptr); - } else { - SARibbonApplicationButton* actionRemoveAppBtn = new SARibbonApplicationButton(); - actionRemoveAppBtn->setText(tr(" &File ")); - this->ribbonBar()->setApplicationButton(actionRemoveAppBtn); - createRibbonApplicationButton(); - } -} - -void MainWindow::onActionUseQssTriggered() -{ - QFile f("ribbon.qss"); - if (!f.exists()) { - QString fdir = QFileDialog::getOpenFileName(this, tr("select qss file")); - if (fdir.isEmpty()) { - return; - } - f.setFileName(fdir); - } - if (!f.open(QIODevice::ReadWrite)) { - return; - } - QString qss(f.readAll()); - mTextedit->setText(qss); - this->ribbonBar()->setStyleSheet(qss); -} - -void MainWindow::onActionLoadCustomizeXmlFileTriggered() -{ - // 只能调用一次 - if (!mHasApplyCustomizeXmlFile) { - if (!QFile::exists("customize.xml")) { - mHasApplyCustomizeXmlFile = true; - return; - } - QFile f("customize.xml"); - qDebug() << "size of customize.xml : " << f.size(); - if (f.size() <= 0) { - mHasApplyCustomizeXmlFile = true; - return; - } - mHasApplyCustomizeXmlFile = sa_apply_customize_from_xml_file("customize.xml", ribbonBar(), mActionsManager); - } -} - -void MainWindow::onActionWindowFlagNormalButtonTriggered(bool b) -{ - Qt::WindowFlags f = windowFlags(); - f.setFlag(Qt::WindowCloseButtonHint, b); - f.setFlag(Qt::WindowMinMaxButtonsHint, b); - setWindowFlags(f); -} - -void MainWindow::onFontComWidgetCurrentFontChanged(const QFont& f) -{ - qDebug() << "set font:" << f; - ribbonBar()->setFont(f); - update(); -} - -void MainWindow::onActionFontLargerTriggered() -{ - QFont f = font(); - f.setPointSize(f.pointSize() + 1); - qDebug() << "before set ribbonBar font:" << ribbonBar()->font(); - ribbonBar()->setFont(f); - update(); - qDebug() << "set font:" << f; -} - -void MainWindow::onActionFontSmallerTriggered() -{ - QFont f = font(); - f.setPointSize(f.pointSize() - 1); - qDebug() << "before set ribbonBar font:" << ribbonBar()->font(); - ribbonBar()->setFont(f); - update(); - qDebug() << "set font:" << f; -} - -void MainWindow::onActionWordWrapTriggered(bool b) -{ - ribbonBar()->setEnableWordWrap(b); - mTextedit->append(tr("By using the SARibbonBar::setEnableWordWrap function, " - "you can set whether text breaks or not.\n" - "By default, the two line mode will not wrap, the three line mode will wrap.\n" - "You can force the two line mode to wrap, or the three line mode to not wrap")); - // cn:通过SARibbonBar::setEnableWordWrap函数可以设置文字是否换行。\n - // 默认情况下,两行模式都不会换行,三行模式下都会换行。\n - // 可以强制设置两行模式也换行,或者三行模式不换行 -} - -/** - * @brief 测试SARibbonButtonGroupWidget和标题对齐 - * @param act - */ -void MainWindow::onButtonGroupActionTriggered(QAction* act) -{ - QVariant v = act->property("align"); - if (v.isValid()) { - Qt::Alignment al = static_cast< Qt::Alignment >(v.toInt()); - if (!ribbonBar()) { - return; - } - ribbonBar()->setWindowTitleAligment(al); - ribbonBar()->repaint(); - } -} - -/** - * @brief 颜色按钮点击 - * @param c - * @param on - */ -void MainWindow::onColorButtonColorClicked(const QColor& c, bool on) -{ - Q_UNUSED(on); - mTextedit->append(QString("color click %1").arg(c.name())); -} - -/** - * @brief 样式改变 - * @param index - */ -void MainWindow::onRibbonThemeComboBoxCurrentIndexChanged(int index) -{ - SARibbonTheme t = static_cast< SARibbonTheme >(mComboboxRibbonTheme->itemData(index).toInt()); - setRibbonTheme(t); -} - -/** - * @brief 隐藏action - * @param on - */ -void MainWindow::onActionHideActionTriggered(bool on) -{ - mActionWordWrap->setVisible(on); - mActionDisable->setVisible(on); - mActionUnlock->setVisible(on); - mActionSetTextTest->setVisible(on); - mActionShowTest->setVisible(on); - mActionHideAction2->setVisible(on); - mActionHideAction4->setVisible(on); - mActionHideShowTextAct2->setVisible(on); - mActionHideShowTextAct3->setVisible(on); - mActionHideShowTextAct4->setVisible(on); - ribbonBar()->updateRibbonGeometry(); -} - -/** - * @brief 切换所有action是否可见 - * @param on - */ -void MainWindow::onActionVisibleAllTriggered(bool on) -{ - QList< QAction* > acts = mActionsManager->allActions(); - for (QAction* a : acts) { - if (a != mActionVisibleAll) { - a->setVisible(on); - } - } - ribbonBar()->updateRibbonGeometry(); -} - -/** - @brief 居中对齐checkbox的槽 - @param checked - */ -void MainWindow::onCheckBoxAlignmentCenterClicked(bool checked) -{ - if (checked) { - ribbonBar()->setRibbonAlignment(SARibbonAlignment::AlignCenter); - } else { - ribbonBar()->setRibbonAlignment(SARibbonAlignment::AlignLeft); - } -} - -void MainWindow::onSpinBoxRibbonTitleHeightChanged(int h) -{ - ribbonBar()->setTitleBarHeight(h); -} - -void MainWindow::onSpinBoxRibbonTabHeightChanged(int h) -{ - ribbonBar()->setTabBarHeight(h); -} - -void MainWindow::onSpinBoxRibbonCategoryHeightChanged(int h) -{ - ribbonBar()->setCategoryHeight(h); -} - -void MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged(int h) -{ - ribbonBar()->setPannelTitleHeight(h); -} - -void MainWindow::onSpinBoxRibbonPannelSpacingChanged(int h) -{ - ribbonBar()->setPannelSpacing(h); -} - -void MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged(int h) -{ - ribbonBar()->setPannelToolButtonIconSize(QSize(h, h)); -} - -/** - * @brief 创建其它actions,这些actions并不在SARibbonBar管理 - */ -void MainWindow::createOtherActions() -{ - mOtherAction1 = new QAction(("text action1"), this); - mOtherAction2 = new QAction(("text action2"), this); - mOtherAction3 = new QAction(("text action3"), this); - mOtherAction4 = new QAction(("text action4"), this); - mOtherAction5 = new QAction(("text action5"), this); - mOtherActionIcon1 = new QAction(QIcon(":/icon/icon/layout.svg"), ("action with icon"), this); -} - -void MainWindow::closeEvent(QCloseEvent* e) -{ - auto res = QMessageBox::question(this, tr("question"), tr("Confirm whether to exit")); - if (res == QMessageBox::Yes) { - e->accept(); - } else { - e->ignore(); - } -} - -void MainWindow::createCategoryMain(SARibbonCategory* page) -{ - //! 1 - //! pannel 1 start - //! - - // 使用addPannel函数来创建SARibbonPannel,效果和new SARibbonPannel再addPannel一样 - SARibbonPannel* pannelStyle = page->addPannel(("ribbon style")); - - QAction* actSave = createAction(tr("Save"), ":/icon/icon/save.svg"); - // 这样设置快捷键 - QShortcut* shortCut = new QShortcut(QKeySequence(QLatin1String("Ctrl+S")), this); - connect(shortCut, &QShortcut::activated, this, [ actSave ]() { actSave->trigger(); }); - // 这样设置是无效的 - // actSave->setShortcut(QKeySequence(QLatin1String("Ctrl+S"))); - - connect(actSave, &QAction::triggered, this, [ this ](bool b) { - Q_UNUSED(b); - this->mTextedit->append("actSaveion clicked"); - }); - pannelStyle->addLargeAction(actSave); - - QAction* actHideRibbon = createAction(tr("hide ribbon"), ":/icon/icon/hideRibbon.svg", "actHideRibbon"); - actHideRibbon->setCheckable(true); - pannelStyle->addSmallAction(actHideRibbon); - connect(actHideRibbon, &QAction::triggered, this, [ this ](bool b) { this->ribbonBar()->setMinimumMode(b); }); - connect(ribbonBar(), &SARibbonBar::ribbonModeChanged, this, [ actHideRibbon ](SARibbonBar::RibbonMode nowNode) { - actHideRibbon->setChecked(nowNode == SARibbonBar::MinimumRibbonMode); - }); - - QAction* actShowHideButton = - createAction(tr("show \nhide button"), ":/icon/icon/showHideButton.svg", "show hide button"); - actShowHideButton->setCheckable(true); - pannelStyle->addSmallAction(actShowHideButton); // wrod wrap was not effect in small button - connect(actShowHideButton, &QAction::triggered, this, [ this ](bool b) { - this->ribbonBar()->showMinimumModeButton(b); // 显示ribbon最小化按钮 - }); - actShowHideButton->trigger(); - - mActionWordWrap = createAction(tr("word wrap"), ":/icon/icon/wordwrap.svg"); - mActionWordWrap->setCheckable(ribbonBar()->isEnableWordWrap()); - pannelStyle->addSmallAction(mActionWordWrap); - connect(mActionWordWrap, &QAction::triggered, this, &MainWindow::onActionWordWrapTriggered); - - QButtonGroup* g = new QButtonGroup(page); - - QRadioButton* r = new QRadioButton(); - r->setText(tr("use office style")); - r->setObjectName(("use office style")); - r->setWindowTitle(r->text()); - r->setChecked(true); - pannelStyle->addSmallWidget(r); - g->addButton(r, SARibbonBar::RibbonStyleLooseThreeRow); - - r = new QRadioButton(); - r->setObjectName(("use wps style")); - r->setText(tr("use wps style")); - r->setWindowTitle(r->text()); - r->setChecked(false); - pannelStyle->addSmallWidget(r); - g->addButton(r, SARibbonBar::RibbonStyleCompactThreeRow); - - r = new QRadioButton(); - r->setObjectName(("use office 2row style")); - r->setText(tr("use office 2 row style")); - r->setWindowTitle(r->text()); - r->setChecked(false); - pannelStyle->addSmallWidget(r); - g->addButton(r, SARibbonBar::RibbonStyleLooseTwoRow); - - r = new QRadioButton(); - r->setObjectName(("use wps 2row style")); - r->setText(tr("use wps 2row style")); - r->setWindowTitle(r->text()); - r->setChecked(false); - pannelStyle->addSmallWidget(r); - g->addButton(r, SARibbonBar::RibbonStyleCompactTwoRow); - -// connect(g, QOverload::of(&QButtonGroup::buttonClicked), this, &MainWindow::onStyleClicked); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(g, static_cast< void (QButtonGroup::*)(int) >(&QButtonGroup::idClicked), this, &MainWindow::onStyleClicked); -#else - connect(g, static_cast< void (QButtonGroup::*)(int) >(&QButtonGroup::buttonClicked), this, &MainWindow::onStyleClicked); -#endif - - mComboboxRibbonTheme = new SARibbonComboBox(); - mComboboxRibbonTheme->setWindowTitle(tr("RibbonTheme")); - mComboboxRibbonTheme->setObjectName("RibbonTheme"); - mComboboxRibbonTheme->addItem("Theme Win7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); - mComboboxRibbonTheme->addItem("Theme Office2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); - mComboboxRibbonTheme->addItem("Theme Office2016 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); - mComboboxRibbonTheme->addItem("Theme Office2021 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); - mComboboxRibbonTheme->addItem("Theme Dark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); - mComboboxRibbonTheme->addItem("Theme Dark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); - mComboboxRibbonTheme->setCurrentIndex(mComboboxRibbonTheme->findData(static_cast< int >(ribbonTheme()))); - connect(mComboboxRibbonTheme, - QOverload< int >::of(&SARibbonComboBox::currentIndexChanged), - this, - &MainWindow::onRibbonThemeComboBoxCurrentIndexChanged); - pannelStyle->addSmallWidget(mComboboxRibbonTheme); - - SARibbonCheckBox* checkBox = new SARibbonCheckBox(this); - - checkBox->setText(tr("Alignment Center")); - checkBox->setObjectName("checkBoxAlignmentCenter"); - checkBox->setWindowTitle(checkBox->text()); - connect(checkBox, &SARibbonCheckBox::clicked, this, &MainWindow::onCheckBoxAlignmentCenterClicked); - pannelStyle->addSmallWidget(checkBox); - - SARibbonPannel* pannelToolButtonStyle = page->addPannel(("sa ribbon toolbutton style")); - - SARibbonMenu* menu = new SARibbonMenu(this); - QAction* a = nullptr; - { - QIcon itemicon = QIcon(":/icon/icon/item.svg"); - for (int i = 0; i < 5; ++i) { - a = menu->addAction(itemicon, tr("item %1").arg(i + 1)); - a->setObjectName(QStringLiteral("item %1").arg(i + 1)); - } - } - - QAction* act = createAction(tr("test 1"), ":/icon/icon/test1.svg"); - QVariant temp("Test"); - act->setData(temp); - act->setMenu(menu); - act->setToolTip(tr("use QToolButton::MenuButtonPopup mode")); - pannelToolButtonStyle->addSmallAction(act, QToolButton::MenuButtonPopup); - - act = createAction(tr("test 2"), ":/icon/icon/test2.svg"); - act->setMenu(menu); - act->setToolTip(tr("use QToolButton::InstantPopup mode")); - pannelToolButtonStyle->addSmallAction(act, QToolButton::InstantPopup); - - pannelToolButtonStyle->addSeparator(); - - act = createAction(tr("Delayed\nPopup"), ":/icon/icon/folder-cog.svg"); - act->setMenu(menu); - pannelToolButtonStyle->addLargeAction(act, QToolButton::DelayedPopup); - - connect(act, &QAction::triggered, this, &MainWindow::onDelayedPopupCheckabletriggered); - - act = createAction(tr("Menu Button Popup"), ":/icon/icon/folder-star.svg"); - act->setMenu(menu); - pannelToolButtonStyle->addLargeAction(act, QToolButton::MenuButtonPopup); - connect(act, &QAction::triggered, this, &MainWindow::onMenuButtonPopupCheckabletriggered); - - act = createAction(tr("Instant Popup"), ":/icon/icon/folder-stats.svg"); - act->setMenu(menu); - pannelToolButtonStyle->addLargeAction(act, QToolButton::InstantPopup); - connect(act, &QAction::triggered, this, &MainWindow::onInstantPopupCheckabletriggered); - - act = createAction(tr("Delayed Popup checkable"), ":/icon/icon/folder-table.svg"); - act->setCheckable(true); - act->setMenu(menu); - pannelToolButtonStyle->addLargeAction(act, QToolButton::DelayedPopup); - connect(act, &QAction::triggered, this, &MainWindow::onDelayedPopupCheckableTest); - - act = createAction(tr("Menu Button Popup checkable"), ":/icon/icon/folder-checkmark.svg"); - act->setCheckable(true); - act->setMenu(menu); - pannelToolButtonStyle->addLargeAction(act, QToolButton::MenuButtonPopup); - connect(act, &QAction::triggered, this, &MainWindow::onMenuButtonPopupCheckableTest); - - act = createAction(tr("disable action"), ":/icon/icon/disable.svg"); - act->setCheckable(true); - act->setMenu(menu); - act->setEnabled(false); - pannelToolButtonStyle->addLargeAction(act); - - QAction* optAct = new QAction(this); - connect(optAct, &QAction::triggered, this, [ this ](bool on) { - Q_UNUSED(on); - QMessageBox::information(this, tr("Option Action Triggered"), tr("Option Action Triggered")); - }); - pannelToolButtonStyle->setOptionAction(optAct); - //! 2 - //! pannel 2 start - //! - SARibbonPannel* pannel2 = page->addPannel(("pannel 2")); - - QAction* actShowContext = createAction(tr("show Context"), ":/icon/icon/showContext.svg"); - actShowContext->setCheckable(true); - pannel2->addLargeAction(actShowContext); - connect(actShowContext, &QAction::triggered, this, &MainWindow::onShowContextCategory); - - QAction* actDeleteContext = createAction(tr("delete Context"), ":/icon/icon/deleteContext.svg"); - pannel2->addLargeAction(actDeleteContext); - connect(actDeleteContext, &QAction::triggered, this, [ this, act ](bool on) { - Q_UNUSED(on); - if (this->mContextCategory) { - this->ribbonBar()->destroyContextCategory(this->mContextCategory); - this->mContextCategory = nullptr; - act->setDisabled(true); - } - }); - - act = createAction(tr("Word\nWrap"), ":/icon/icon/setText.svg"); - pannel2->addLargeAction(act); - connect(act, &QAction::triggered, this, [ this ](bool on) { - Q_UNUSED(on); - this->mTextedit->append(tr("Text can be manually wrapped(use \\n), and will appear as 1 line in the case of " - "SARibbonBar::setEnableWordWrap (false)")); // cn:文本中手动换行 - }); - - act = createAction(tr("Word \nWrap"), ":/icon/icon/setText.svg"); - act->setMenu(menu); - pannel2->addLargeAction(act); - connect(act, &QAction::triggered, this, [ this ](bool on) { - Q_UNUSED(on); - this->mTextedit->append(tr("Text can be manually wrapped(use \\n), and will appear as 1 line in the case of " - "SARibbonBar::setEnableWordWrap (false)")); // cn:文本中手动换行 - }); - //! 3 - //! pannel 3 start -> widget test - //! - - SARibbonPannel* pannelWidgetTest = page->addPannel(tr("widget test")); - pannelWidgetTest->setObjectName(QStringLiteral(u"pannelWidgetTest")); - - SARibbonComboBox* com = new SARibbonComboBox(this); - com->setObjectName("SARibbonComboBox test"); - com->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - com->setWindowTitle(tr("SARibbonComboBox test")); - for (int i = 0; i < 40; ++i) { - com->addItem(QString("SARibbonComboBox test%1").arg(i + 1)); - } - com->setEditable(true); - pannelWidgetTest->addSmallWidget(com); - - com = new SARibbonComboBox(this); - com->setObjectName("ComboBox Editable"); - com->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - com->setWindowTitle("ComboBox Editable"); - for (int i = 0; i < 40; ++i) { - com->addItem(QString("item %1").arg(i + 1)); - } - pannelWidgetTest->addSmallWidget(com); - - SARibbonLineEdit* lineEdit = new SARibbonLineEdit(this); - - lineEdit->setObjectName("Line Edit"); - lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - lineEdit->setWindowTitle("Line Edit"); - lineEdit->setText("SARibbonLineEdit"); - pannelWidgetTest->addSmallWidget(lineEdit); - QWidget* w = lineEdit->parentWidget(); - - while (w) { - qDebug() << w->metaObject()->className(); - w = w->parentWidget(); - } - - pannelWidgetTest->addSeparator(); - - QCalendarWidget* calendarWidget = new QCalendarWidget(this); - calendarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - calendarWidget->setObjectName(("calendarWidget")); - calendarWidget->setWindowTitle("calendarWidget"); - pannelWidgetTest->addLargeWidget(calendarWidget); - optAct = new QAction(this); - connect(optAct, &QAction::triggered, this, [ this ](bool on) { - Q_UNUSED(on); - QMessageBox::information(this, tr("Option Action Triggered"), tr("Option Action Triggered")); - }); - pannelWidgetTest->setOptionAction(optAct); - - pannelWidgetTest->setVisible(true); -} - -void MainWindow::createCategoryOther(SARibbonCategory* page) -{ - - SARibbonPannel* pannel1 = new SARibbonPannel(tr("pannel one")); - pannel1->setObjectName("CategoryOther-pannel1"); - page->addPannel(pannel1); - // 按钮组 - SARibbonButtonGroupWidget* btnGroup1 = new SARibbonButtonGroupWidget(pannel1); - btnGroup1->setObjectName("SARibbonButtonGroupWidget1"); - btnGroup1->setWindowTitle("SARibbonButtonGroupWidget1"); - - btnGroup1->addAction(createAction(tr("Decrease Margin"), ":/icon/icon/Decrease-Margin.svg")); - btnGroup1->addAction(createAction(tr("Decrease Indent"), ":/icon/icon/Decrease-Indent.svg")); - btnGroup1->addAction(createAction(tr("Wrap Image Left"), ":/icon/icon/Wrap-Image Left.svg")); - btnGroup1->addAction(createAction(tr("Wrap Image Right"), ":/icon/icon/Wrap-Image Right.svg")); - pannel1->addWidget(btnGroup1, SARibbonPannelItem::Medium); - SARibbonButtonGroupWidget* btnGroup2 = new SARibbonButtonGroupWidget(pannel1); - btnGroup2->setObjectName("SARibbonButtonGroupWidget2"); - btnGroup2->setWindowTitle("SARibbonButtonGroupWidget2"); - - QAction* titleAlgnment = createAction(tr("Align Right"), ":/icon/icon/Align-Right.svg"); - titleAlgnment->setProperty("align", (int)Qt::AlignRight | Qt::AlignVCenter); - btnGroup2->addAction(titleAlgnment); - titleAlgnment = createAction(tr("Align Left"), ":/icon/icon/Align-Left.svg"); - titleAlgnment->setProperty("align", (int)Qt::AlignLeft | Qt::AlignVCenter); - btnGroup2->addAction(titleAlgnment); - titleAlgnment = createAction(tr("Align Center"), ":/icon/icon/Align-Center.svg"); - titleAlgnment->setProperty("align", (int)Qt::AlignCenter); - btnGroup2->addAction(titleAlgnment); - pannel1->addWidget(btnGroup2, SARibbonPannelItem::Medium); - connect(btnGroup2, &SARibbonButtonGroupWidget::actionTriggered, this, &MainWindow::onButtonGroupActionTriggered); - // Gallery - SARibbonGallery* gallery = pannel1->addGallery(); - QList< QAction* > galleryActions; - auto lambdaCreateGalleryAction = [ this ](const QString& text, const QString& iconurl) -> QAction* { - QAction* act = this->createAction(text, iconurl); - this->connect(act, &QAction::triggered, this, [ this, text ]() { - if (this->mTextedit) { - this->mTextedit->append(QString("%1 triggered").arg(text)); - } - }); - return act; - }; - galleryActions.append(lambdaCreateGalleryAction("Document File", ":/gallery-icon/icon/gallery/Document-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Download File", ":/gallery-icon/icon/gallery/Download-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Drive File Four Word", ":/gallery-icon/icon/gallery/Drive-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Dropbox File", ":/gallery-icon/icon/gallery/Dropbox-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Email File", ":/gallery-icon/icon/gallery/Email-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Encode File", ":/gallery-icon/icon/gallery/Encode-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("Favorit File", ":/gallery-icon/icon/gallery/Favorit-File.svg")); - galleryActions.append(lambdaCreateGalleryAction("File Error", ":/gallery-icon/icon/gallery/File-Error.svg")); - galleryActions.append(lambdaCreateGalleryAction("File Read Only", ":/gallery-icon/icon/gallery/File-Readonly.svg")); - galleryActions.append(lambdaCreateGalleryAction("File Settings", ":/gallery-icon/icon/gallery/File-Settings.svg")); - galleryActions.append( - lambdaCreateGalleryAction("Presentation File", ":/gallery-icon/icon/gallery/Presentation-File.svg")); - SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); - group1->setGalleryGroupStyle(SARibbonGalleryGroup::IconWithWordWrapText); - group1->setGridMinimumWidth(80); - galleryActions.clear(); - galleryActions.append(lambdaCreateGalleryAction("Photoshop", ":/gallery-icon/icon/gallery/Photoshop.svg")); - galleryActions.append( - lambdaCreateGalleryAction("Internet-Explorer", ":/gallery-icon/icon/gallery/Internet-Explorer.svg")); - galleryActions.append(lambdaCreateGalleryAction("Illustrator", ":/gallery-icon/icon/gallery/Illustrator.svg")); - galleryActions.append(lambdaCreateGalleryAction("Google-Maps", ":/gallery-icon/icon/gallery/Google-Maps.svg")); - galleryActions.append(lambdaCreateGalleryAction("Adobe", ":/gallery-icon/icon/gallery/Adobe.svg")); - galleryActions.append(lambdaCreateGalleryAction("Word", ":/gallery-icon/icon/gallery/Word.svg")); - SARibbonGalleryGroup* group2 = gallery->addCategoryActions(tr("Apps"), galleryActions); - group2->setGridMinimumWidth(80); - gallery->setCurrentViewGroup(group1); - - QAction* optAct = new QAction(this); - - optAct->setObjectName(("debug")); - pannel1->setOptionAction(optAct); - - SARibbonPannel* pannel2 = new SARibbonPannel(tr("pannel two")); - pannel2->setObjectName("CategoryOther-pannel2"); - page->addPannel(pannel2); - QAction* actionRemoveAppBtn = createAction(tr("remove application button"), ":/icon/icon/remove-app-btn.svg"); - actionRemoveAppBtn->setCheckable(true); - connect(actionRemoveAppBtn, &QAction::toggled, this, &MainWindow::onActionRemoveAppBtnTriggered); - pannel2->addLargeAction(actionRemoveAppBtn); - - QAction* actionLongText = - createAction(tr("show very long text in a button,balabalabala etc"), ":/icon/icon/long-text.svg", "long-text"); - pannel2->addLargeAction(actionLongText); - - SARibbonPannel* pannelStyle = new SARibbonPannel(tr("style")); - pannelStyle->setObjectName("CategoryOther-pannelStyle"); - page->addPannel(pannelStyle); - - QAction* actionUseQss = createAction(tr("use qss"), ":/icon/icon/useqss.svg"); - connect(actionUseQss, &QAction::triggered, this, &MainWindow::onActionUseQssTriggered); - pannelStyle->addSmallAction(actionUseQss); - - QAction* actionLoadCustomizeXmlFile = createAction(tr("load customize from xml file"), ":/icon/icon/useCustomize.svg"); - connect(actionLoadCustomizeXmlFile, &QAction::triggered, this, &MainWindow::onActionLoadCustomizeXmlFileTriggered); - pannelStyle->addSmallAction(actionLoadCustomizeXmlFile); - - QAction* actionWindowFlagNormalButton = createAction(tr("window normal button"), ":/icon/icon/windowsflag-normal.svg"); - actionWindowFlagNormalButton->setCheckable(true); - actionWindowFlagNormalButton->setChecked(true); - pannelStyle->addSmallAction(actionWindowFlagNormalButton); - connect(actionWindowFlagNormalButton, &QAction::triggered, this, &MainWindow::onActionWindowFlagNormalButtonTriggered); - - SARibbonPannel* pannelUtf8 = new SARibbonPannel(QStringLiteral(u"中文显示测试")); - pannelUtf8->setObjectName("CategoryOther-pannelUtf8"); - page->addPannel(pannelUtf8); - - QAction* actionChangeText = - createAction(QStringLiteral(u"改变显示为英文"), ":/icon/icon/chinese-char.svg", "actionChangeText"); - actionChangeText->setCheckable(true); - actionChangeText->setChecked(false); - pannelUtf8->addLargeAction(actionChangeText); - connect(actionChangeText, &QAction::triggered, this, [ pannelUtf8, actionChangeText ](bool on) { - if (on) { - pannelUtf8->setPannelName(QStringLiteral(u"show chinese char")); - actionChangeText->setText(QStringLiteral(u"show in chinese")); - } else { - pannelUtf8->setPannelName(QStringLiteral(u"中文显示测试")); - actionChangeText->setText(QStringLiteral(u"改变显示为英文")); - } - }); - - SARibbonPannel* pannelContextCategory = new SARibbonPannel(("Context Category")); - page->addPannel(pannelContextCategory); - QAction* a1 = createAction("Context Category 1", ":/icon/icon/ContextCategory.svg"); - a1->setCheckable(true); - connect(a1, &QAction::triggered, this, [ this ](bool c) { - this->ribbonBar()->setContextCategoryVisible(mContextCategory, c); - }); - - QAction* a2 = createAction("Context Category 2", ":/icon/icon/ContextCategory.svg"); - a2->setCheckable(true); - connect(a2, &QAction::triggered, this, [ this ](bool c) { - this->ribbonBar()->setContextCategoryVisible(mContextCategory2, c); - }); - pannelContextCategory->addLargeAction(a1); - pannelContextCategory->addLargeAction(a2); -} - -void MainWindow::createCategoryDelete(SARibbonCategory* page) -{ - SARibbonPannel* pannel1 = new SARibbonPannel(("pannel 1")); - SARibbonPannel* pannel2 = new SARibbonPannel(("pannel 2")); - - QAction* actionRemovePanne = createAction(tr("remove pannel"), ":/icon/icon/remove.svg"); - - connect(actionRemovePanne, &QAction::triggered, this, [ page, pannel2 ]() { page->removePannel(pannel2); }); - pannel1->addLargeAction(actionRemovePanne); - - QAction* actionRemoveCategory = createAction(tr("remove this category"), ":/icon/icon/removePage.svg"); - - connect(actionRemoveCategory, &QAction::triggered, this, [ this, page, actionRemoveCategory ]() { - this->ribbonBar()->removeCategory(page); - page->hide(); - page->deleteLater(); - actionRemoveCategory->setDisabled(true); - }); - pannel2->addLargeAction(actionRemoveCategory); - - page->addPannel(pannel1); - page->addPannel(pannel2); -} - -/** - * @brief 构建尺寸页 - * @param page - */ -void MainWindow::createCategorySize(SARibbonCategory* page) -{ - //========================= - // pannel : Font - //========================= - SARibbonPannel* pannel = page->addPannel(tr("Font")); - SARibbonButtonGroupWidget* groupFontFamile = new SARibbonButtonGroupWidget(pannel); - groupFontFamile->setObjectName(QStringLiteral(u"group1")); - QLabel* labelFontSize = new QLabel(groupFontFamile); - labelFontSize->setText(tr("select font")); - labelFontSize->setObjectName(QStringLiteral(u"labelFontSize")); - groupFontFamile->addWidget(labelFontSize); - QFontComboBox* fontComWidget = new QFontComboBox(groupFontFamile); - fontComWidget->setObjectName(QStringLiteral(u"fontComboBox")); - connect(fontComWidget, &QFontComboBox::currentFontChanged, this, &MainWindow::onFontComWidgetCurrentFontChanged); - //! 给窗口设置WindowTitle,这样在自定义窗口中,也能显示这个action对应的名字 - //! 你也可以给group1设置一个icon - //! setWindowTitle应该在addWidget之前 - groupFontFamile->setWindowTitle(tr("Font ComboBox")); - groupFontFamile->addWidget(fontComWidget); - QAction* actWidgetFontFamile = pannel->addWidget(groupFontFamile, SARibbonPannelItem::Medium); - //! 设置objectname是为了让actionManager正确管理action,如果不设置这个action对应的objectname, - //! SARibbonPannel::addWidget会自动生成一个objectname,action.{widgetobjectName},因此你要保证你添加的widget有objectName - actWidgetFontFamile->setObjectName(QStringLiteral("Size.Font.FontFamile")); - - SARibbonButtonGroupWidget* groupFontTools = new SARibbonButtonGroupWidget(pannel); - groupFontTools->setObjectName(QStringLiteral(u"group2")); - groupFontTools->addAction(createAction("Bold", ":/icon/icon/bold.svg")); - groupFontTools->addAction(createAction("Italic", ":/icon/icon/Italic.svg")); - groupFontTools->addSeparator(); - groupFontTools->addAction(createAction("left alignment", ":/icon/icon/al-left.svg")); - groupFontTools->addAction(createAction("center alignment", ":/icon/icon/al-center.svg")); - groupFontTools->addAction(createAction("right alignment", ":/icon/icon/al-right.svg")); - groupFontTools->addAction(createAction("line up on both sides", ":/icon/icon/al-bothside.svg")); - //! 给窗口设置WindowTitle,这样在自定义窗口中,也能显示这个action对应的名字 - //! 你也可以给group1设置一个icon - //! setWindowTitle应该在addWidget之前 - groupFontTools->setWindowTitle(tr("Font Tools")); - QAction* actWidgetFontTools = pannel->addWidget(groupFontTools, SARibbonPannelItem::Medium); - //! 设置objectname是为了让actionManager正确管理action,如果不设置这个action对应的objectname, - //! SARibbonPannel::addWidget会自动生成一个objectname,action.{widgetobjectName},因此你要保证你添加的widget有objectName - actWidgetFontTools->setObjectName(QStringLiteral("Size.Font.FontTools")); - - pannel->addSeparator(); - - QAction* actLargerFontSize = createAction(tr("Larger"), ":/icon/icon/largerFont.svg", "actLargerFontSize"); - pannel->addLargeAction(actLargerFontSize); - - QAction* actSmallFontSize = createAction(tr("Smaller"), ":/icon/icon/smallFont.svg", "actSmallFontSize"); - pannel->addLargeAction(actSmallFontSize); - - connect(actLargerFontSize, &QAction::triggered, this, &MainWindow::onActionFontLargerTriggered); - connect(actSmallFontSize, &QAction::triggered, this, &MainWindow::onActionFontSmallerTriggered); - - //========================= - // pannel : Font Size - //========================= - pannel = new SARibbonPannel(tr("Font Size")); - QFont f = font(); - f.setPointSize(14); - pannel->setFont(f); - QAction* actLargeFontText = createAction(tr("Larger Font Text"), ":/icon/icon/file.svg", "actLargeFontText"); - pannel->addLargeAction(actLargeFontText); - page->addPannel(pannel); - - // pannel 3 - // 这里演示了一个窗口,带有gridlayoout - pannel = new SARibbonPannel(tr("Grid")); - QWidget* w = new QWidget(); - QGridLayout* g = new QGridLayout(w); - - QLabel* lab0 = new QLabel("Ribbon Title Height:"); - QSpinBox* spinboxRibbonTitleHeight = new QSpinBox(w); - spinboxRibbonTitleHeight->setRange(10, 99); - - QLabel* lab1 = new QLabel("Ribbon Tab Height:"); - QSpinBox* spinboxRibbonTabHeight = new QSpinBox(w); - spinboxRibbonTabHeight->setRange(10, 99); - - QLabel* lab2 = new QLabel("Ribbon Category Height:"); - QSpinBox* spinboxRibbonCategoryHeight = new QSpinBox(w); - spinboxRibbonCategoryHeight->setRange(30, 300); - - QLabel* lab3 = new QLabel("Ribbon Pannel Title:"); - QSpinBox* spinboxRibbonPannelTtitleHeight = new QSpinBox(w); - spinboxRibbonPannelTtitleHeight->setRange(5, 99); - - QLabel* lab4 = new QLabel("Ribbon Pannel Spacing:"); - QSpinBox* spinboxRibbonPannelSpacing = new QSpinBox(w); - spinboxRibbonPannelSpacing->setRange(0, 25); - - QLabel* lab5 = new QLabel("Pannel ToolButton Icon Size:"); - QSpinBox* spinboxRibbonPannelToolBtnIconSize = new QSpinBox(w); - spinboxRibbonPannelToolBtnIconSize->setRange(1, 99); - - g->addWidget(lab0, 0, 0); - g->addWidget(spinboxRibbonTitleHeight, 0, 1); - g->addWidget(lab1, 1, 0); - g->addWidget(spinboxRibbonTabHeight, 1, 1); - g->addWidget(lab2, 0, 2); - g->addWidget(spinboxRibbonCategoryHeight, 0, 3); - g->addWidget(lab3, 1, 2); - g->addWidget(spinboxRibbonPannelTtitleHeight, 1, 3); - g->addWidget(lab4, 0, 4); - g->addWidget(spinboxRibbonPannelSpacing, 0, 5); - g->addWidget(lab5, 1, 4); - g->addWidget(spinboxRibbonPannelToolBtnIconSize, 1, 5); - spinboxRibbonTitleHeight->setValue(ribbonBar()->titleBarHeight()); - spinboxRibbonTabHeight->setValue(ribbonBar()->tabBarHeight()); - spinboxRibbonCategoryHeight->setValue(ribbonBar()->categoryHeight()); - spinboxRibbonPannelTtitleHeight->setValue(ribbonBar()->pannelTitleHeight()); - spinboxRibbonPannelSpacing->setValue(ribbonBar()->pannelSpacing()); - spinboxRibbonPannelToolBtnIconSize->setValue(ribbonBar()->pannelToolButtonIconSize().width()); -#if QT_VERSION_MAJOR >= 6 - connect(spinboxRibbonTitleHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonTitleHeightChanged); - connect(spinboxRibbonTabHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonTabHeightChanged); - connect(spinboxRibbonCategoryHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonCategoryHeightChanged); - connect(spinboxRibbonPannelTtitleHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged); - connect(spinboxRibbonPannelSpacing, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonPannelSpacingChanged); - connect(spinboxRibbonPannelToolBtnIconSize, - &QSpinBox::valueChanged, - this, - &MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged); -#else - connect(spinboxRibbonTitleHeight, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonTitleHeightChanged); - connect(spinboxRibbonTabHeight, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonTabHeightChanged); - connect(spinboxRibbonCategoryHeight, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonCategoryHeightChanged); - connect(spinboxRibbonPannelTtitleHeight, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged); - connect(spinboxRibbonPannelSpacing, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonPannelSpacingChanged); - connect(spinboxRibbonPannelToolBtnIconSize, - QOverload< int >::of(&QSpinBox::valueChanged), - this, - &MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged); -#endif - pannel->addLargeWidget(w); - page->addPannel(pannel); -} - -void MainWindow::createCategoryColor(SARibbonCategory* page) -{ - SARibbonPannel* pannel = page->addPannel(tr("color")); - - auto fpCreateBtn = [ this, pannel ](const QColor& defaultColor = Qt::red) -> SARibbonColorToolButton* { - SARibbonColorToolButton* colorButton = new SARibbonColorToolButton(pannel); - colorButton->setColor(defaultColor); - colorButton->setupStandardColorMenu(); - this->connect(colorButton, &SARibbonColorToolButton::colorClicked, this, &MainWindow::onColorButtonColorClicked); - return colorButton; - }; - // No Icon No text - SARibbonColorToolButton* colorButton = fpCreateBtn(); - colorButton->setObjectName("ColorFillToIcon-NoIconNoText"); - colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); - pannel->addSmallWidget(colorButton); - - // No Icon have text - colorButton = fpCreateBtn(Qt::blue); - colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); - colorButton->setObjectName("ColorFillToIcon-NoIconHaveText"); - colorButton->setText("No Icon have text"); - pannel->addSmallWidget(colorButton); - - // have Icon No text - colorButton = fpCreateBtn(QColor()); - colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); - pannel->addSmallWidget(colorButton); - - // have Icon have text - colorButton = fpCreateBtn(Qt::red); - colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); - colorButton->setText("have Icon have text"); - pannel->addSmallWidget(colorButton); - - colorButton = fpCreateBtn(QColor()); - colorButton->setButtonType(SARibbonToolButton::LargeButton); - colorButton->setObjectName("ColorFillToIcon-LargeColorButton"); - colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); - colorButton->setText("Large Color Button"); - pannel->addLargeWidget(colorButton); - - colorButton = fpCreateBtn(QColor()); - colorButton->setButtonType(SARibbonToolButton::LargeButton); - colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); - colorButton->setObjectName("ColorUnderIcon-LargeColorButton"); - colorButton->setText("Large Color Button"); - pannel->addLargeWidget(colorButton); - // - pannel->addSeparator(); - SAColorPaletteGridWidget* pw = new SAColorPaletteGridWidget(SA::getStandardColorList(), pannel); - pw->setFactor({ 75, 120 }); - pannel->addLargeWidget(pw); -} - -/** - * @brief 创建上下文标签 - */ -void MainWindow::createContextCategory1() -{ - // 上下文标签 - SARibbonBar* ribbon = ribbonBar(); - mContextCategory = ribbon->addContextCategory(tr("context"), QColor(), 1); - - SARibbonCategory* contextCategoryPage1 = mContextCategory->addCategoryPage(tr("Page1")); - createContextCategoryPage1(contextCategoryPage1); - - SARibbonCategory* contextCategoryPage2 = mContextCategory->addCategoryPage(tr("Page2")); - createContextCategoryPage2(contextCategoryPage2); -} - -void MainWindow::createContextCategory2() -{ - SARibbonBar* ribbon = ribbonBar(); - mContextCategory2 = ribbon->addContextCategory(("context2"), QColor(), 2); - mContextCategory2->addCategoryPage(("context2 Page1")); - mContextCategory2->addCategoryPage(("context2 Page2")); -} - -void MainWindow::createContextCategoryPage1(SARibbonCategory* page) -{ - SARibbonPannel* pannel = page->addPannel(tr("show and hide test")); - - QAction* actionHideActions = createAction("hide action", ":/icon/icon/hidePannel.svg"); - actionHideActions->setCheckable(true); - actionHideActions->setChecked(true); - pannel->addLargeAction(actionHideActions); - connect(actionHideActions, &QAction::triggered, this, &MainWindow::onActionHideActionTriggered); - - mActionDisable = createAction(tr("Disable"), ":/icon/icon/enableTest.svg"); - - mActionDisable->setDisabled(true); - pannel->addLargeAction(mActionDisable); - connect(mActionDisable, &QAction::triggered, this, [ this ](bool b) { - Q_UNUSED(b); - mActionDisable->setDisabled(true); - }); - - mActionUnlock = createAction(tr("unlock"), ":/icon/icon/unlock.svg"); - mActionUnlock->setShortcut(QKeySequence(QLatin1String("Ctrl+E"))); - mActionUnlock->setShortcutContext(Qt::ApplicationShortcut); - pannel->addLargeAction(mActionUnlock); - connect(mActionUnlock, &QAction::triggered, this, [ this ](bool b) { - Q_UNUSED(b); - mActionDisable->setEnabled(true); - mActionDisable->setText(("Enabled")); - }); - auto act = createAction(tr("1"), ":/icon/icon/unlock.svg"); - act->setToolTip(tr("very short string")); - pannel->addLargeAction(act); - - mActionSetTextTest = createAction("set text", ":/icon/icon/setText.svg"); - - mActionSetTextTest->setCheckable(true); - mActionSetTextTest->setShortcut(QKeySequence(QLatin1String("Ctrl+D"))); - mActionSetTextTest->setShortcutContext(Qt::ApplicationShortcut); - pannel->addLargeAction(mActionSetTextTest); - - connect(mActionSetTextTest, &QAction::toggled, this, [ this ](bool b) { - if (b) { - mActionSetTextTest->setText(QStringLiteral(u"setText测试")); - } else { - mActionSetTextTest->setText(QStringLiteral(u"set text")); - } - }); - // 隐藏pannel - mActionShowTest = createAction("show beside pannel", ":/icon/icon/show.svg"); - - mActionShowTest->setCheckable(true); - pannel->addLargeAction(mActionShowTest); - - mPannelVisbileExample = page->addPannel(tr("show/hide")); - // 重复添加 - mPannelVisbileExample->addLargeAction(mActionSetTextTest); - - connect(mActionShowTest, &QAction::toggled, this, [ this ](bool b) { - mPannelVisbileExample->setVisible(!b); - if (b) { - mActionShowTest->setText(tr("hide beside pannel")); - } else { - mActionShowTest->setText(tr("show beside pannel")); - } - ribbonBar()->repaint(); - }); - - SARibbonPannel* pannel3 = page->addPannel(("show/hide action test")); - - mActionHideAction2 = createAction("hide action 2", ":/icon/icon/action.svg"); - mActionHideAction4 = createAction("hide action 4", ":/icon/icon/action.svg"); - mActionHideAction2->setCheckable(true); - mActionHideAction2->setChecked(true); - mActionHideAction4->setCheckable(true); - mActionHideAction4->setChecked(true); - mActionHideShowTextAct2 = createAction("action2", ":/icon/icon/action2.svg"); - mActionHideShowTextAct3 = createAction("action3", ":/icon/icon/action3.svg"); - mActionHideShowTextAct4 = createAction("action4", ":/icon/icon/action4.svg"); - pannel3->addLargeAction(mActionHideAction2); - pannel3->addLargeAction(mActionHideAction4); - pannel3->addSmallAction(mActionHideShowTextAct2); - pannel3->addSmallAction(mActionHideShowTextAct3); - pannel3->addSmallAction(mActionHideShowTextAct4); - connect(mActionHideAction2, &QAction::triggered, this, [ this ](bool b) { - if (b) { - mActionHideShowTextAct2->setVisible(true); - mActionHideAction2->setText(tr("hide action2")); - } else { - mActionHideShowTextAct2->setVisible(false); - mActionHideAction2->setText(tr("show action2")); - } - }); - connect(mActionHideAction4, &QAction::triggered, this, [ this ](bool b) { - if (b) { - mActionHideShowTextAct4->setVisible(true); - mActionHideAction4->setText(tr("hide action4")); - } else { - mActionHideShowTextAct4->setVisible(false); - mActionHideAction4->setText(tr("show action4")); - } - }); - // - SARibbonPannel* pannel4 = page->addPannel(("widget")); - QSpinBox* spinbox = new QSpinBox(this); - SARibbonCtrlContainer* ctrlContainer1 = new SARibbonCtrlContainer(pannel4); - ctrlContainer1->setContainerWidget(spinbox); - ctrlContainer1->setText(tr("spinbox:")); - ctrlContainer1->setEnableShowIcon(false); - ctrlContainer1->setMaximumHeight(fontMetrics().lineSpacing() * 1.5); - pannel4->addMediumWidget(ctrlContainer1); - - QLineEdit* linedit = new QLineEdit(this); - SARibbonCtrlContainer* ctrlContainer2 = new SARibbonCtrlContainer(pannel4); - ctrlContainer2->setContainerWidget(linedit); - ctrlContainer2->setText(tr("linedit:")); - ctrlContainer2->setEnableShowIcon(false); - ctrlContainer2->setMaximumHeight(fontMetrics().lineSpacing() * 1.5); - pannel4->addMediumWidget(ctrlContainer2); -} - -void MainWindow::createContextCategoryPage2(SARibbonCategory* page) -{ - SARibbonPannel* pannel1 = page->addPannel(("删除CategoryPage测试")); - - QAction* actionDeleteThisCategory = createAction("delete this category", ":/icon/icon/delete.svg"); - pannel1->addLargeAction(actionDeleteThisCategory); - connect(actionDeleteThisCategory, &QAction::triggered, this, [ this, page ]() { - this->ribbonBar()->removeCategory(page); - page->deleteLater(); - }); - SARibbonPannel* pannelLayout = page->addPannel(("特殊布局")); - - pannelLayout->addAction( - createAction("Large", ":/icon/icon/layout.svg", "@Large1"), QToolButton::InstantPopup, SARibbonPannelItem::Large); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small1"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small2"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addSeparator(); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small3"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small4"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small5"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small6"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addSeparator(); - pannelLayout->addAction( - createAction("Large", ":/icon/icon/layout.svg", "@Large2"), QToolButton::InstantPopup, SARibbonPannelItem::Large); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium1"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium2"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small7"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium3"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction( - createAction("Large", ":/icon/icon/layout.svg", "@Large3"), QToolButton::InstantPopup, SARibbonPannelItem::Large); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium4"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium5"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction( - createAction("Large", ":/icon/icon/layout.svg", "@Large4"), QToolButton::InstantPopup, SARibbonPannelItem::Large); - pannelLayout->addSeparator(); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium6"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction( - createAction("Large", ":/icon/icon/layout.svg", "@Large5"), QToolButton::InstantPopup, SARibbonPannelItem::Large); - pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium7"), - QToolButton::InstantPopup, - SARibbonPannelItem::Medium); - pannelLayout->addAction( - createAction("Small", ":/icon/icon/layout.svg", "@Small8"), QToolButton::InstantPopup, SARibbonPannelItem::Small); -} - -void MainWindow::createQuickAccessBar() -{ - SARibbonQuickAccessBar* quickAccessBar = ribbonBar()->quickAccessBar(); - - quickAccessBar->addAction(createAction("save", ":/icon/icon/save.svg", "save-quickbar")); - quickAccessBar->addSeparator(); - - QAction* actionUndo = createAction("undo", ":/icon/icon/undo.svg"); - actionUndo->setShortcut(QKeySequence("Ctrl+Shift+z")); - actionUndo->setShortcutContext(Qt::ApplicationShortcut); - quickAccessBar->addAction(actionUndo); - - QAction* actionRedo = createAction("redo", ":/icon/icon/redo.svg"); - actionRedo->setShortcut(QKeySequence("Ctrl+z")); - actionRedo->setShortcutContext(Qt::ApplicationShortcut); - quickAccessBar->addAction(actionRedo); - - connect(actionUndo, &QAction::triggered, this, [ this ]() { mTextedit->append("undo"); }); - connect(actionRedo, &QAction::triggered, this, [ this ]() { mTextedit->append("redo"); }); - - quickAccessBar->addSeparator(); - - QMenu* m1 = new QMenu("Presentation File 1", this); - m1->setIcon(QIcon(":/icon/icon/presentationFile.svg")); - for (int i = 0; i < 10; ++i) { - m1->addAction(createAction(QString("file%1 - 1").arg(i + 1), ":/icon/icon/file.svg")); - } - quickAccessBar->addMenu(m1, Qt::ToolButtonIconOnly, QToolButton::InstantPopup); - - QMenu* m2 = new QMenu("Presentation File 2", this); - m2->setIcon(QIcon(":/icon/icon/presentationFile.svg")); - for (int i = 0; i < 10; ++i) { - m2->addAction(createAction(QString("file%1 - 2").arg(i + 1), ":/icon/icon/file.svg")); - } - QAction* actiontestmenu = new QAction("Presentation File 2"); - actiontestmenu->setMenu(m2); - quickAccessBar->addAction(actiontestmenu, Qt::ToolButtonIconOnly, QToolButton::MenuButtonPopup); - - QAction* customize = createAction("customize", ":/icon/icon/customize0.svg", "customize2"); - quickAccessBar->addAction(customize); - connect(customize, &QAction::triggered, this, &MainWindow::onActionCustomizeTriggered); - - QAction* actionCustomizeAndSave = createAction("customize and save", ":/icon/icon/customize.svg"); - quickAccessBar->addAction(actionCustomizeAndSave); - connect(actionCustomizeAndSave, &QAction::triggered, this, &MainWindow::onActionCustomizeAndSaveTriggered); - - QAction* actionCustomizeAndSaveWithApply = createAction("customize and save with apply", ":/icon/icon/customize.svg"); - quickAccessBar->addAction(actionCustomizeAndSaveWithApply); - connect(actionCustomizeAndSaveWithApply, &QAction::triggered, this, &MainWindow::onActionCustomizeAndSaveWithApplyTriggered); - - // - mSearchEditor = new SARibbonLineEdit(this); - mSearchEditor->setFixedWidth(100); - mSearchEditor->setPlaceholderText("Search"); - quickAccessBar->addWidget(mSearchEditor); - connect(mSearchEditor, &QLineEdit::editingFinished, this, [ this ]() { - this->mTextedit->append(this->mSearchEditor->text()); - }); -} - -/** - * @brief 创建RightButtonGroup - * - * RightButtonGroup实在ribbonbar右边的工具栏,可以放置一些快捷图标 - * @param rightBar - */ -void MainWindow::createRightButtonGroup() -{ - SARibbonBar* ribbon = ribbonBar(); - if (!ribbon) { - return; - } - SARibbonButtonGroupWidget* rightBar = ribbon->rightButtonGroup(); - QAction* actionHelp = createAction(tr("help"), ":/icon/icon/help.svg"); - mActionVisibleAll = createAction(tr("Visible"), ":/icon/icon/visible-true.svg"); - mActionVisibleAll->setCheckable(true); - mActionVisibleAll->setChecked(true); - connect(actionHelp, &QAction::triggered, this, &MainWindow::onActionHelpTriggered); - connect(mActionVisibleAll, &QAction::triggered, this, &MainWindow::onActionVisibleAllTriggered); - rightBar->addAction(actionHelp); - rightBar->addAction(mActionVisibleAll); -} - -/** - * @brief 创建右上角系统最大、最小化按钮栏的图标工具 - */ -void MainWindow::createWindowButtonGroupBar() -{ - SARibbonSystemButtonBar* wbar = windowButtonBar(); - if (!wbar) { - return; - } - QAction* a = wbar->addAction(tr("Login"), QIcon(), Qt::ToolButtonTextOnly); - connect(a, &QAction::triggered, this, [ this ]() { this->mTextedit->append("Login triggered"); }); -} - -/** - * @brief 创建ActionsManager(Create ActionsManager) - * - * 创建ActionsManager,实现actions的管理以及SARibbonBar的自定义 - * - * Create ActionsManager to manage actions and customize SARibbonBar - * - * - */ -void MainWindow::createActionsManager() -{ - // 添加其他的action,这些action并不在ribbon管理范围,主要用于SARibbonCustomizeWidget自定义用 - createOtherActions(); - mTagForActionText = SARibbonActionsManager::UserDefineActionTag + 1; - mTagForActionIcon = SARibbonActionsManager::UserDefineActionTag + 2; - - mActionsManager = new SARibbonActionsManager(ribbonBar()); // 申明过程已经自动注册所有action - - // 以下注册特别的action - mActionsManager->registeAction(mOtherAction1, SARibbonActionsManager::CommonlyUsedActionTag); - mActionsManager->registeAction(mOtherAction3, SARibbonActionsManager::CommonlyUsedActionTag); - mActionsManager->registeAction(mOtherAction5, SARibbonActionsManager::CommonlyUsedActionTag); - mActionsManager->registeAction(mOtherActionIcon1, SARibbonActionsManager::CommonlyUsedActionTag); - - mActionsManager->registeAction(mOtherAction1, mTagForActionText); - mActionsManager->registeAction(mOtherAction2, mTagForActionText); - mActionsManager->registeAction(mOtherAction3, mTagForActionText); - mActionsManager->registeAction(mOtherAction4, mTagForActionText); - mActionsManager->registeAction(mOtherAction5, mTagForActionText); - - mActionsManager->registeAction(mOtherActionIcon1, mTagForActionIcon); - - mActionsManager->setTagName(SARibbonActionsManager::CommonlyUsedActionTag, tr("in common use")); // - mActionsManager->setTagName(mTagForActionText, tr("no icon action")); - mActionsManager->setTagName(mTagForActionIcon, tr("have icon action")); -} - -QAction* MainWindow::createAction(const QString& text, const QString& iconurl, const QString& objName) -{ - QAction* act = new QAction(this); - act->setText(text); - act->setIcon(QIcon(iconurl)); - act->setObjectName(objName); - return act; -} - -QAction* MainWindow::createAction(const QString& text, const QString& iconurl) -{ - QAction* act = new QAction(this); - act->setText(text); - act->setIcon(QIcon(iconurl)); - act->setObjectName(text); - return act; -} - -void MainWindow::onMenuButtonPopupCheckableTest(bool b) -{ - mTextedit->append(QString("MenuButtonPopupCheckableTest : %1").arg(b)); -} - -void MainWindow::onInstantPopupCheckableTest(bool b) -{ - mTextedit->append(QString("InstantPopupCheckableTest : %1").arg(b)); -} - -void MainWindow::onDelayedPopupCheckableTest(bool b) -{ - mTextedit->append(QString("DelayedPopupCheckableTest : %1").arg(b)); -} - -void MainWindow::onMenuButtonPopupCheckabletriggered(bool b) -{ - mTextedit->append(QString("MenuButtonPopupCheckabletriggered : %1").arg(b)); -} - -void MainWindow::onInstantPopupCheckabletriggered(bool b) -{ - mTextedit->append(QString("InstantPopupCheckabletriggered : %1").arg(b)); -} - -void MainWindow::onDelayedPopupCheckabletriggered(bool b) -{ - Q_UNUSED(b); - mTextedit->append(tr("The SARibbonToolButton::setPopupMode(QToolButton::DelayedPopup) method " - "can be used to set the menu pop-up method to delayed pop-up. " - "This also demonstrates manually setting text wrapping")); - // cn:使用SARibbonToolButton::setPopupMode(QToolButton::DelayedPopup)方法可以设置菜单弹出方式为延迟弹出,这里也演示了手动设置文本的换行 -} +#include "mainwindow.h" +#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#include "SAFramelessHelper.h" +#endif +#include "SARibbonApplicationButton.h" +#include "SARibbonBar.h" +#include "SARibbonButtonGroupWidget.h" +#include "SARibbonCategory.h" +#include "SARibbonCheckBox.h" +#include "SARibbonColorToolButton.h" +#include "SARibbonComboBox.h" +#include "SARibbonCustomizeDialog.h" +#include "SARibbonCustomizeWidget.h" +#include "SARibbonGallery.h" +#include "SARibbonLineEdit.h" +#include "SARibbonMenu.h" +#include "SARibbonPannel.h" +#include "SARibbonQuickAccessBar.h" +#include "SARibbonToolButton.h" +#include "colorWidgets/SAColorGridWidget.h" +#include "colorWidgets/SAColorPaletteGridWidget.h" +#include "SARibbonSystemButtonBar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PRINT_COST_START() \ + QElapsedTimer __TMP_COST; \ + __TMP_COST.start(); \ + int __TMP_LASTTIMES = 0 + +#define PRINT_COST(STR) \ + do { \ + int ___TMP_INT = __TMP_COST.elapsed(); \ + qDebug() << STR << " cost " << ___TMP_INT - __TMP_LASTTIMES << " ms (" << ___TMP_INT << ")"; \ + mTextedit->append(QString("%1 cost %2 ms(%3)").arg(STR).arg(___TMP_INT - __TMP_LASTTIMES).arg(___TMP_INT)); \ + __TMP_LASTTIMES = ___TMP_INT; \ + } while (0) + +MainWindow::MainWindow(QWidget* par) + : SARibbonMainWindow(par), mWidgetForCustomize(nullptr), mMenuApplicationBtn(nullptr) +{ + + PRINT_COST_START(); + setWindowTitle(("ribbon mainwindow test")); + mTextedit = new QTextEdit(this); + setCentralWidget(mTextedit); + setStatusBar(new QStatusBar()); + + SARibbonBar* ribbon = ribbonBar(); + //! 通过setContentsMargins设置ribbon四周的间距 + ribbon->setContentsMargins(5, 0, 5, 0); + + connect(ribbon, &SARibbonBar::actionTriggered, this, [ this ](QAction* action) { + mTextedit->append(QString("action object name=%1 triggered").arg(action->objectName())); + }); + + //! cn: + //! 这里示例的是如何设置和修改applicationButton,默认情况下SARibbonBar会创建一个SARibbonApplicationButton, + //! SARibbonApplicationButton的父类是QToolButton,用户也可以创建自己的applicationButton, + //! 通过SARibbonBar::setApplicationButton函数设置自己定义的按钮,如果传入一个空指针,将取消掉applicationButton + //! en: + //! Here is an example of how to set and modify the applicationButton. By default, + //! SARibbonBar will create a SARibbonApplicationButton. The parent class of SARibbonApplicationButton is QToolButton. + //! Users can also create their own applicationButton, and set their own buttons through the SARibbonBar::setApplicationButton function. + //! If a null pointer is passed in, the applicationButton will be canceled + createRibbonApplicationButton(); + + //! cn: + //! 添加主标签页,这里演示通过SARibbonBar::addCategoryPage函数添加一个标签页 + //! en: + //! Add the main tab. Here we show how to add a tab through the SARibbonBar::addCategoryPage function + SARibbonCategory* categoryMain = ribbon->addCategoryPage(tr("&Main")); + //! cn: SARibbonBar的Category和Pannel,以及对应的Action都应该设置ObjectName,因为如果要自定义action,这些ObjectName是必不可少的 + //! en: The category , pannel and actions of SARibbonBar, should be set with Object Names, as these Object Names are essential for customizing actions + categoryMain->setObjectName(("categoryMain")); + createCategoryMain(categoryMain); + PRINT_COST("new main page"); + + //! cn:这里演示了另外一种添加标签页的方式,先创建SARibbonCategory指针,并通过SARibbonBar::addCategoryPage函数把SARibbonCategory指针传递给SARibbonBar + //! en:Here is another way to add a tab: first create a SARibbonCategory pointer and pass it to SARibbonBar through the SARibbonBar::addCategoryPage function + SARibbonCategory* categoryOther = new SARibbonCategory(); + categoryOther->setCategoryName(tr("Other")); + categoryOther->setObjectName(("categoryOther")); + createCategoryOther(categoryOther); + ribbon->addCategoryPage(categoryOther); + PRINT_COST("add other page"); + + // 添加删除标签页 + SARibbonCategory* categoryDelete = new SARibbonCategory(); + categoryDelete->setCategoryName(("Delete")); + categoryDelete->setObjectName(("categoryDelete")); + ribbon->addCategoryPage(categoryDelete); + createCategoryDelete(categoryDelete); + PRINT_COST("add category delete page"); + + // 添加尺寸标签页 + SARibbonCategory* categorySize = new SARibbonCategory(); + categorySize->setCategoryName(("Size(example long category)")); + categorySize->setObjectName(("categorySize")); + ribbon->addCategoryPage(categorySize); + createCategorySize(categorySize); + PRINT_COST("add category size page"); + + // 添加颜色标签页 + SARibbonCategory* categoryColor = new SARibbonCategory(); + categoryColor->setCategoryName(("Color")); + categoryColor->setObjectName(("categoryColor")); + ribbon->addCategoryPage(categoryColor); + createCategoryColor(categoryColor); + PRINT_COST("add category color page"); + + createContextCategory1(); + PRINT_COST("add context1 category page"); + + createContextCategory2(); + PRINT_COST("add context2 category page"); + + createQuickAccessBar(); + PRINT_COST("add quick access bar"); + + //! cn: + //! 创建RightButtonGroup,RightButtonGroup类似一个在右上角的工具栏,给用户放置一些快捷图标,例如关于、帮助等图标, + //! RightButtonGroup在SARibbonBar创建时就会构建一个默认的SARibbonButtonGroupWidget,可以通过SARibbonBar::rightButtonGroup函数获取 + //! en: + //! Create a RightButtonGroup, which is similar to a toolbar in the upper right corner, + //! providing users with shortcut icons such as About, Help, etc. RightButtonGroup will build a default SARibbonButtonGroupWidget when creating SARibbonBar, + //! which can be obtained through the SARibbonBar::rightButtonGroup function + createRightButtonGroup(); + PRINT_COST("add right bar"); + + //! cn: + //! 这里演示了如何在系统窗口最小化最大化关闭按钮旁边添加其他按钮 + createWindowButtonGroupBar(); + + //! cn: + //! actionManager可以管理所有的action,并给SARibbon的自定义窗口使用, + //! actionManager必须在ribbon的action都创建完成后创建,如果在之前就创建好,后加入ribbon的action需要手动管理到actionManager里, + //! actionManager也可以管理不在ribbonBar里的action + //! en: + //! The ActionManager can manage all actions and be used for SARibbon's custom window. + //! The ActionManager must be created after all the actions in the ribbon are created. + //! If the actions are created before, the actions added to the ribbon need to be manually managed in the ActionManager. + //! The ActionManager can also manage actions not in the ribbon bar + createActionsManager(); + + setMinimumWidth(500); + setWindowIcon(QIcon(":/icon/icon/SA.svg")); + + connect(ribbon, &SARibbonBar::currentRibbonTabChanged, this, [ this ](int v) { + mTextedit->append(QString("SARibbonBar::currentRibbonTabChanged(%1)").arg(v)); + }); + + showMaximized(); +} + +/** + * @brief 创建ApplicationButton + */ +void MainWindow::createRibbonApplicationButton() +{ + SARibbonBar* ribbon = ribbonBar(); + if (!ribbon) { + return; + } + QAbstractButton* btn = ribbon->applicationButton(); + if (!btn) { + //! cn: SARibbonBar默认就会创建一个SARibbonApplicationButton,因此,在正常情况下,这个位置不会进入 + //! en: SARibbonBar creates a SARibbonApplicationButton by default. Therefore, under normal circumstances, this location will not enter + btn = new SARibbonApplicationButton(this); + ribbon->setApplicationButton(btn); + } + ribbon->applicationButton()->setText((" &File ")); // 文字两边留有间距,好看一点 + //! cn: SARibbonMenu和QMenu的操作是一样的 + //! en: The operations of SARibbonMenu and QMenu are the same + if (!mMenuApplicationBtn) { + mMenuApplicationBtn = new SARibbonMenu(this); + mMenuApplicationBtn->addAction(createAction("test1", ":/icon/icon/action.svg")); + mMenuApplicationBtn->addAction(createAction("test2", ":/icon/icon/action2.svg")); + mMenuApplicationBtn->addAction(createAction("test3", ":/icon/icon/action3.svg")); + mMenuApplicationBtn->addAction(createAction("test4", ":/icon/icon/action4.svg")); + } + SARibbonApplicationButton* appBtn = qobject_cast< SARibbonApplicationButton* >(btn); + if (!appBtn) { + return; + } + appBtn->setMenu(mMenuApplicationBtn); +} + +/** + * @brief 显示ContextCategory1 + * @param on + */ +void MainWindow::onShowContextCategory(bool on) +{ + if (mContextCategory == nullptr) { + createContextCategory1(); + } + if (on) { + this->ribbonBar()->showContextCategory(mContextCategory); + } else { + this->ribbonBar()->hideContextCategory(mContextCategory); + } +} + +void MainWindow::onStyleClicked(int id) +{ + + SARibbonBar::RibbonStyles ribbonStyle = static_cast< SARibbonBar::RibbonStyles >(id); + ribbonBar()->setRibbonStyle(ribbonStyle); + mActionWordWrap->setChecked(SARibbonToolButton::isEnableWordWrap()); + switch (ribbonStyle) { + case SARibbonBar::RibbonStyleLooseThreeRow: + // cn:"LooseThreeRow"样式的文字显示是换行的,同时也会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, + // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 + mTextedit->append( + tr("\nThe text display of the \"LooseThreeRow\" style is word wrap and also displays the title bar. " + "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," + "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); + mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleLooseThreeRow);")); + break; + case SARibbonBar::RibbonStyleLooseTwoRow: + // cn:"LooseThreeRow"样式的文字显示是不换行的,同时也会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, + // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 + mTextedit->append( + tr("\nThe text display of the \"LooseTwoRow\" style is not word wrap and also displays the title bar. " + "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," + "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); + mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleLooseTwoRow);")); + break; + case SARibbonBar::RibbonStyleCompactThreeRow: + // cn:"CompactThreeRow"样式的文字显示是换行的,不会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, + // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 + mTextedit->append( + tr("\nThe text display of the \"LooseThreeRow\" style is word wrap and not displays the title bar. " + "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," + "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); + mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleCompactThreeRow);")); + break; + case SARibbonBar::RibbonStyleCompactTwoRow: + // cn:"CompactTwoRow"样式的文字显示是不换行的,不会显示标题栏,你也可以通过SARibbonBar::setEnableWordWrap来控制按钮是否换行显示, + // 可以通过SARibbonBar::setEnableShowPannelTitle控制标题栏是否显示 + mTextedit->append( + tr("\nThe text display of the \"CompactTwoRow\" style is not word wrap and not displays the title bar. " + "You can also control whether the button is line breaking through SARibbonBar::setEnableWordWrap," + "and whether the title bar is displayed through SARibbonBar::setEnableShowPannelTitle")); + mTextedit->append(tr("ribbonBar()->setRibbonStyle(SARibbonBar::RibbonStyleCompactTwoRow);")); + break; + default: + break; + } +} + +/** + * @brief 自定义按钮槽 + * + * 这里演示了如何使用@ref SARibbonCustomizeWidget 来实现界面的自定义 + */ +void MainWindow::onActionCustomizeTriggered() +{ + if (nullptr == mWidgetForCustomize) { + mWidgetForCustomize = + new SARibbonCustomizeWidget(this, this, Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::Dialog); + mWidgetForCustomize->setWindowModality(Qt::ApplicationModal); // 设置阻塞类型 + mWidgetForCustomize->setAttribute(Qt::WA_ShowModal, true); // 属性设置 true:模态 false:非模态 + mWidgetForCustomize->setupActionsManager(mActionsManager); + } + mWidgetForCustomize->show(); + mWidgetForCustomize->applys(); +} + +/** + * @brief 自定义按钮槽 + * + * 这里演示了如何使用@ref SARibbonCustomizeDialog 来实现界面的自定义,同时演示了如何把自定义的界面保存为xml文件 + */ +void MainWindow::onActionCustomizeAndSaveTriggered() +{ + SARibbonCustomizeDialog dlg(this); + dlg.setupActionsManager(mActionsManager); + // 如果启动时未应用上次修改,先应用再读取,保持本地数据和ui一致 + if (!mHasApplyCustomizeXmlFile) { + auto res = QMessageBox::question( + this, tr("question"), tr("Apply the last modification?\nIf not, local data will be reset")); + if (res == QMessageBox::Yes) { + onActionLoadCustomizeXmlFileTriggered(); + return; + } else { + QFile::remove("customize.xml"); + dlg.clear(); + + mHasApplyCustomizeXmlFile = true; + } + } + + dlg.fromXml("customize.xml"); + if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { + // 先apply + if (dlg.isCached()) + dlg.applys(); + + // 无更改直接退出 + if (!dlg.isApplied()) { + mTextedit->append("no change to save"); + return; + } + + QByteArray str; + QXmlStreamWriter xml(&str); + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(2); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. + xml.setCodec("utf-8"); +#endif + xml.writeStartDocument(); + bool isok = dlg.toXml(&xml); + xml.writeEndDocument(); + if (isok) { + QFile f("customize.xml"); + if (f.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + QTextStream s(&f); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QTextStream always encodes XML in UTF-8. + s.setCodec("utf-8"); +#endif + s << str; + s.flush(); + } + mTextedit->append("write xml:"); + mTextedit->append(str); + } + } +} + +/** + * @brief 自定义按钮槽 + * + * 这里演示了如何使用@ref SARibbonCustomizeWidget 嵌入到QDialog中实现界面的自定义, + * 这样是为了让@ref SARibbonCustomizeWidget更好的和自己的软件设置页面结合 + */ +void MainWindow::onActionCustomizeAndSaveWithApplyTriggered() +{ + // 如果启动时未应用上次修改,先应用再读取,保持本地数据和ui一致 + if (!mHasApplyCustomizeXmlFile) { + auto res = QMessageBox::question( + this, tr("question"), tr("Apply the last modification?\nIf not, local data will be reset")); + if (res == QMessageBox::Yes) { + onActionLoadCustomizeXmlFileTriggered(); + return; + } else { + QFile::remove("customize.xml"); + mHasApplyCustomizeXmlFile = true; + } + } + + QDialog dlg; + QVBoxLayout* main = new QVBoxLayout; + dlg.setLayout(main); + SARibbonCustomizeWidget* widgetForCustomize = new SARibbonCustomizeWidget(this, &dlg); + widgetForCustomize->setupActionsManager(mActionsManager); + + main->addWidget(widgetForCustomize, 1); + + QDialogButtonBox* buttonBox = + new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); + + main->addWidget(buttonBox); + + connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); + connect(buttonBox, &QDialogButtonBox::clicked, &dlg, [ = ](QAbstractButton* button) { + auto role = buttonBox->buttonRole(button); + if (role == QDialogButtonBox::ApplyRole) // apply + { + if (widgetForCustomize->isCached()) { + widgetForCustomize->applys(); + mTextedit->append("change applied"); + } else { + mTextedit->append("no change to apply"); + } + } + }); + + widgetForCustomize->fromXml("customize.xml"); + if (QDialog::Accepted == dlg.exec()) { + // 先apply + if (widgetForCustomize->isCached()) + widgetForCustomize->applys(); + + // 无更改直接退出 + if (!widgetForCustomize->isApplied()) { + mTextedit->append("no change to save"); + return; + } + + QByteArray str; + QXmlStreamWriter xml(&str); + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(2); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. + xml.setCodec("utf-8"); +#endif + xml.writeStartDocument(); + bool isok = widgetForCustomize->toXml(&xml); + xml.writeEndDocument(); + if (isok) { + QFile f("customize.xml"); + if (f.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + QTextStream s(&f); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QTextStream always encodes XML in UTF-8. + s.setCodec("utf-8"); +#endif + s << str; + s.flush(); + } + mTextedit->append("write xml:"); + mTextedit->append(str); + } + } else { + // 清除所有动作 + widgetForCustomize->clear(); + mTextedit->append("all changes clear, the applied changes will take no effect afer restart"); + } +} + +void MainWindow::onActionHelpTriggered() +{ + QMessageBox::information(this, + tr("infomation"), + tr("\n ===============" + "\n SARibbonBar version:%1" + "\n Author:czy" + "\n Email:czy.t@163.com" + "\n ===============") + .arg(SARibbonBar::versionString())); +} + +/** + * @brief 移除ApplicationButton + * @param b + */ +void MainWindow::onActionRemoveAppBtnTriggered(bool b) +{ + if (b) { + ribbonBar()->setApplicationButton(nullptr); + } else { + SARibbonApplicationButton* actionRemoveAppBtn = new SARibbonApplicationButton(); + actionRemoveAppBtn->setText(tr(" &File ")); + this->ribbonBar()->setApplicationButton(actionRemoveAppBtn); + createRibbonApplicationButton(); + } +} + +void MainWindow::onActionUseQssTriggered() +{ + QFile f("ribbon.qss"); + if (!f.exists()) { + QString fdir = QFileDialog::getOpenFileName(this, tr("select qss file")); + if (fdir.isEmpty()) { + return; + } + f.setFileName(fdir); + } + if (!f.open(QIODevice::ReadWrite)) { + return; + } + QString qss(f.readAll()); + mTextedit->setText(qss); + this->ribbonBar()->setStyleSheet(qss); +} + +void MainWindow::onActionLoadCustomizeXmlFileTriggered() +{ + // 只能调用一次 + if (!mHasApplyCustomizeXmlFile) { + if (!QFile::exists("customize.xml")) { + mHasApplyCustomizeXmlFile = true; + return; + } + QFile f("customize.xml"); + qDebug() << "size of customize.xml : " << f.size(); + if (f.size() <= 0) { + mHasApplyCustomizeXmlFile = true; + return; + } + mHasApplyCustomizeXmlFile = sa_apply_customize_from_xml_file("customize.xml", ribbonBar(), mActionsManager); + } +} + +void MainWindow::onActionWindowFlagNormalButtonTriggered(bool b) +{ + Qt::WindowFlags f = windowFlags(); + f.setFlag(Qt::WindowCloseButtonHint, b); + f.setFlag(Qt::WindowMinMaxButtonsHint, b); + setWindowFlags(f); +} + +void MainWindow::onFontComWidgetCurrentFontChanged(const QFont& f) +{ + qDebug() << "set font:" << f; + ribbonBar()->setFont(f); + update(); +} + +void MainWindow::onActionFontLargerTriggered() +{ + QFont f = font(); + f.setPointSize(f.pointSize() + 1); + qDebug() << "before set ribbonBar font:" << ribbonBar()->font(); + ribbonBar()->setFont(f); + update(); + qDebug() << "set font:" << f; +} + +void MainWindow::onActionFontSmallerTriggered() +{ + QFont f = font(); + f.setPointSize(f.pointSize() - 1); + qDebug() << "before set ribbonBar font:" << ribbonBar()->font(); + ribbonBar()->setFont(f); + update(); + qDebug() << "set font:" << f; +} + +void MainWindow::onActionWordWrapTriggered(bool b) +{ + ribbonBar()->setEnableWordWrap(b); + mTextedit->append(tr("By using the SARibbonBar::setEnableWordWrap function, " + "you can set whether text breaks or not.\n" + "By default, the two line mode will not wrap, the three line mode will wrap.\n" + "You can force the two line mode to wrap, or the three line mode to not wrap")); + // cn:通过SARibbonBar::setEnableWordWrap函数可以设置文字是否换行。\n + // 默认情况下,两行模式都不会换行,三行模式下都会换行。\n + // 可以强制设置两行模式也换行,或者三行模式不换行 +} + +/** + * @brief 测试SARibbonButtonGroupWidget和标题对齐 + * @param act + */ +void MainWindow::onButtonGroupActionTriggered(QAction* act) +{ + QVariant v = act->property("align"); + if (v.isValid()) { + Qt::Alignment al = static_cast< Qt::Alignment >(v.toInt()); + if (!ribbonBar()) { + return; + } + ribbonBar()->setWindowTitleAligment(al); + ribbonBar()->repaint(); + } +} + +/** + * @brief 颜色按钮点击 + * @param c + * @param on + */ +void MainWindow::onColorButtonColorClicked(const QColor& c, bool on) +{ + Q_UNUSED(on); + mTextedit->append(QString("color click %1").arg(c.name())); +} + +/** + * @brief 样式改变 + * @param index + */ +void MainWindow::onRibbonThemeComboBoxCurrentIndexChanged(int index) +{ + SARibbonTheme t = static_cast< SARibbonTheme >(mComboboxRibbonTheme->itemData(index).toInt()); + setRibbonTheme(t); +} + +/** + * @brief 隐藏action + * @param on + */ +void MainWindow::onActionHideActionTriggered(bool on) +{ + mActionWordWrap->setVisible(on); + mActionDisable->setVisible(on); + mActionUnlock->setVisible(on); + mActionSetTextTest->setVisible(on); + mActionShowTest->setVisible(on); + mActionHideAction2->setVisible(on); + mActionHideAction4->setVisible(on); + mActionHideShowTextAct2->setVisible(on); + mActionHideShowTextAct3->setVisible(on); + mActionHideShowTextAct4->setVisible(on); + ribbonBar()->updateRibbonGeometry(); +} + +/** + * @brief 切换所有action是否可见 + * @param on + */ +void MainWindow::onActionVisibleAllTriggered(bool on) +{ + QList< QAction* > acts = mActionsManager->allActions(); + for (QAction* a : acts) { + if (a != mActionVisibleAll) { + a->setVisible(on); + } + } + ribbonBar()->updateRibbonGeometry(); +} + +/** + @brief 居中对齐checkbox的槽 + @param checked + */ +void MainWindow::onCheckBoxAlignmentCenterClicked(bool checked) +{ + if (checked) { + ribbonBar()->setRibbonAlignment(SARibbonAlignment::AlignCenter); + } else { + ribbonBar()->setRibbonAlignment(SARibbonAlignment::AlignLeft); + } +} + +void MainWindow::onSpinBoxRibbonTitleHeightChanged(int h) +{ + ribbonBar()->setTitleBarHeight(h); +} + +void MainWindow::onSpinBoxRibbonTabHeightChanged(int h) +{ + ribbonBar()->setTabBarHeight(h); +} + +void MainWindow::onSpinBoxRibbonCategoryHeightChanged(int h) +{ + ribbonBar()->setCategoryHeight(h); +} + +void MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged(int h) +{ + ribbonBar()->setPannelTitleHeight(h); +} + +void MainWindow::onSpinBoxRibbonPannelSpacingChanged(int h) +{ + ribbonBar()->setPannelSpacing(h); +} + +void MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged(int h) +{ + ribbonBar()->setPannelToolButtonIconSize(QSize(h, h)); +} + +/** + * @brief 创建其它actions,这些actions并不在SARibbonBar管理 + */ +void MainWindow::createOtherActions() +{ + mOtherAction1 = new QAction(("text action1"), this); + mOtherAction2 = new QAction(("text action2"), this); + mOtherAction3 = new QAction(("text action3"), this); + mOtherAction4 = new QAction(("text action4"), this); + mOtherAction5 = new QAction(("text action5"), this); + mOtherActionIcon1 = new QAction(QIcon(":/icon/icon/layout.svg"), ("action with icon"), this); +} + +void MainWindow::closeEvent(QCloseEvent* e) +{ + auto res = QMessageBox::question(this, tr("question"), tr("Confirm whether to exit")); + if (res == QMessageBox::Yes) { + e->accept(); + } else { + e->ignore(); + } +} + +void MainWindow::createCategoryMain(SARibbonCategory* page) +{ + //! 1 + //! pannel 1 start + //! + + // 使用addPannel函数来创建SARibbonPannel,效果和new SARibbonPannel再addPannel一样 + SARibbonPannel* pannelStyle = page->addPannel(("ribbon style")); + + QAction* actSave = createAction(tr("Save"), ":/icon/icon/save.svg"); + // 这样设置快捷键 + QShortcut* shortCut = new QShortcut(QKeySequence(QLatin1String("Ctrl+S")), this); + connect(shortCut, &QShortcut::activated, this, [ actSave ]() { actSave->trigger(); }); + // 这样设置是无效的 + // actSave->setShortcut(QKeySequence(QLatin1String("Ctrl+S"))); + + connect(actSave, &QAction::triggered, this, [ this ](bool b) { + Q_UNUSED(b); + this->mTextedit->append("actSaveion clicked"); + }); + pannelStyle->addLargeAction(actSave); + + QAction* actHideRibbon = createAction(tr("hide ribbon"), ":/icon/icon/hideRibbon.svg", "actHideRibbon"); + actHideRibbon->setCheckable(true); + pannelStyle->addSmallAction(actHideRibbon); + connect(actHideRibbon, &QAction::triggered, this, [ this ](bool b) { this->ribbonBar()->setMinimumMode(b); }); + connect(ribbonBar(), &SARibbonBar::ribbonModeChanged, this, [ actHideRibbon ](SARibbonBar::RibbonMode nowNode) { + actHideRibbon->setChecked(nowNode == SARibbonBar::MinimumRibbonMode); + }); + + QAction* actShowHideButton = + createAction(tr("show \nhide button"), ":/icon/icon/showHideButton.svg", "show hide button"); + actShowHideButton->setCheckable(true); + pannelStyle->addSmallAction(actShowHideButton); // wrod wrap was not effect in small button + connect(actShowHideButton, &QAction::triggered, this, [ this ](bool b) { + this->ribbonBar()->showMinimumModeButton(b); // 显示ribbon最小化按钮 + }); + actShowHideButton->trigger(); + + mActionWordWrap = createAction(tr("word wrap"), ":/icon/icon/wordwrap.svg"); + mActionWordWrap->setCheckable(ribbonBar()->isEnableWordWrap()); + pannelStyle->addSmallAction(mActionWordWrap); + connect(mActionWordWrap, &QAction::triggered, this, &MainWindow::onActionWordWrapTriggered); + + QButtonGroup* g = new QButtonGroup(page); + + QRadioButton* r = new QRadioButton(); + r->setText(tr("use office style")); + r->setObjectName(("use office style")); + r->setWindowTitle(r->text()); + r->setChecked(true); + pannelStyle->addSmallWidget(r); + g->addButton(r, SARibbonBar::RibbonStyleLooseThreeRow); + + r = new QRadioButton(); + r->setObjectName(("use wps style")); + r->setText(tr("use wps style")); + r->setWindowTitle(r->text()); + r->setChecked(false); + pannelStyle->addSmallWidget(r); + g->addButton(r, SARibbonBar::RibbonStyleCompactThreeRow); + + r = new QRadioButton(); + r->setObjectName(("use office 2row style")); + r->setText(tr("use office 2 row style")); + r->setWindowTitle(r->text()); + r->setChecked(false); + pannelStyle->addSmallWidget(r); + g->addButton(r, SARibbonBar::RibbonStyleLooseTwoRow); + + r = new QRadioButton(); + r->setObjectName(("use wps 2row style")); + r->setText(tr("use wps 2row style")); + r->setWindowTitle(r->text()); + r->setChecked(false); + pannelStyle->addSmallWidget(r); + g->addButton(r, SARibbonBar::RibbonStyleCompactTwoRow); + +// connect(g, QOverload::of(&QButtonGroup::buttonClicked), this, &MainWindow::onStyleClicked); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + connect(g, static_cast< void (QButtonGroup::*)(int) >(&QButtonGroup::idClicked), this, &MainWindow::onStyleClicked); +#else + connect(g, static_cast< void (QButtonGroup::*)(int) >(&QButtonGroup::buttonClicked), this, &MainWindow::onStyleClicked); +#endif + + mComboboxRibbonTheme = new SARibbonComboBox(); + mComboboxRibbonTheme->setWindowTitle(tr("RibbonTheme")); + mComboboxRibbonTheme->setObjectName("RibbonTheme"); + mComboboxRibbonTheme->addItem("Theme Win7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); + mComboboxRibbonTheme->addItem("Theme Office2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); + mComboboxRibbonTheme->addItem("Theme Office2016 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); + mComboboxRibbonTheme->addItem("Theme Office2021 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); + mComboboxRibbonTheme->addItem("Theme Dark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); + mComboboxRibbonTheme->addItem("Theme Dark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); + mComboboxRibbonTheme->setCurrentIndex(mComboboxRibbonTheme->findData(static_cast< int >(ribbonTheme()))); + connect(mComboboxRibbonTheme, + QOverload< int >::of(&SARibbonComboBox::currentIndexChanged), + this, + &MainWindow::onRibbonThemeComboBoxCurrentIndexChanged); + pannelStyle->addSmallWidget(mComboboxRibbonTheme); + + SARibbonCheckBox* checkBox = new SARibbonCheckBox(this); + + checkBox->setText(tr("Alignment Center")); + checkBox->setObjectName("checkBoxAlignmentCenter"); + checkBox->setWindowTitle(checkBox->text()); + connect(checkBox, &SARibbonCheckBox::clicked, this, &MainWindow::onCheckBoxAlignmentCenterClicked); + pannelStyle->addSmallWidget(checkBox); + + SARibbonPannel* pannelToolButtonStyle = page->addPannel(("sa ribbon toolbutton style")); + + SARibbonMenu* menu = new SARibbonMenu(this); + QAction* a = nullptr; + { + QIcon itemicon = QIcon(":/icon/icon/item.svg"); + for (int i = 0; i < 5; ++i) { + a = menu->addAction(itemicon, tr("item %1").arg(i + 1)); + a->setObjectName(QStringLiteral("item %1").arg(i + 1)); + } + } + + QAction* act = createAction(tr("test 1"), ":/icon/icon/test1.svg"); + QVariant temp("Test"); + act->setData(temp); + act->setMenu(menu); + act->setToolTip(tr("use QToolButton::MenuButtonPopup mode")); + pannelToolButtonStyle->addSmallAction(act, QToolButton::MenuButtonPopup); + + act = createAction(tr("test 2"), ":/icon/icon/test2.svg"); + act->setMenu(menu); + act->setToolTip(tr("use QToolButton::InstantPopup mode")); + pannelToolButtonStyle->addSmallAction(act, QToolButton::InstantPopup); + + pannelToolButtonStyle->addSeparator(); + + act = createAction(tr("Delayed\nPopup"), ":/icon/icon/folder-cog.svg"); + act->setMenu(menu); + pannelToolButtonStyle->addLargeAction(act, QToolButton::DelayedPopup); + + connect(act, &QAction::triggered, this, &MainWindow::onDelayedPopupCheckabletriggered); + + act = createAction(tr("Menu Button Popup"), ":/icon/icon/folder-star.svg"); + act->setMenu(menu); + pannelToolButtonStyle->addLargeAction(act, QToolButton::MenuButtonPopup); + connect(act, &QAction::triggered, this, &MainWindow::onMenuButtonPopupCheckabletriggered); + + act = createAction(tr("Instant Popup"), ":/icon/icon/folder-stats.svg"); + act->setMenu(menu); + pannelToolButtonStyle->addLargeAction(act, QToolButton::InstantPopup); + connect(act, &QAction::triggered, this, &MainWindow::onInstantPopupCheckabletriggered); + + act = createAction(tr("Delayed Popup checkable"), ":/icon/icon/folder-table.svg"); + act->setCheckable(true); + act->setMenu(menu); + pannelToolButtonStyle->addLargeAction(act, QToolButton::DelayedPopup); + connect(act, &QAction::triggered, this, &MainWindow::onDelayedPopupCheckableTest); + + act = createAction(tr("Menu Button Popup checkable"), ":/icon/icon/folder-checkmark.svg"); + act->setCheckable(true); + act->setMenu(menu); + pannelToolButtonStyle->addLargeAction(act, QToolButton::MenuButtonPopup); + connect(act, &QAction::triggered, this, &MainWindow::onMenuButtonPopupCheckableTest); + + act = createAction(tr("disable action"), ":/icon/icon/disable.svg"); + act->setCheckable(true); + act->setMenu(menu); + act->setEnabled(false); + pannelToolButtonStyle->addLargeAction(act); + + QAction* optAct = new QAction(this); + connect(optAct, &QAction::triggered, this, [ this ](bool on) { + Q_UNUSED(on); + QMessageBox::information(this, tr("Option Action Triggered"), tr("Option Action Triggered")); + }); + pannelToolButtonStyle->setOptionAction(optAct); + //! 2 + //! pannel 2 start + //! + SARibbonPannel* pannel2 = page->addPannel(("pannel 2")); + + QAction* actShowContext = createAction(tr("show Context"), ":/icon/icon/showContext.svg"); + actShowContext->setCheckable(true); + pannel2->addLargeAction(actShowContext); + connect(actShowContext, &QAction::triggered, this, &MainWindow::onShowContextCategory); + + QAction* actDeleteContext = createAction(tr("delete Context"), ":/icon/icon/deleteContext.svg"); + pannel2->addLargeAction(actDeleteContext); + connect(actDeleteContext, &QAction::triggered, this, [ this, act ](bool on) { + Q_UNUSED(on); + if (this->mContextCategory) { + this->ribbonBar()->destroyContextCategory(this->mContextCategory); + this->mContextCategory = nullptr; + act->setDisabled(true); + } + }); + + act = createAction(tr("Word\nWrap"), ":/icon/icon/setText.svg"); + pannel2->addLargeAction(act); + connect(act, &QAction::triggered, this, [ this ](bool on) { + Q_UNUSED(on); + this->mTextedit->append(tr("Text can be manually wrapped(use \\n), and will appear as 1 line in the case of " + "SARibbonBar::setEnableWordWrap (false)")); // cn:文本中手动换行 + }); + + act = createAction(tr("Word \nWrap"), ":/icon/icon/setText.svg"); + act->setMenu(menu); + pannel2->addLargeAction(act); + connect(act, &QAction::triggered, this, [ this ](bool on) { + Q_UNUSED(on); + this->mTextedit->append(tr("Text can be manually wrapped(use \\n), and will appear as 1 line in the case of " + "SARibbonBar::setEnableWordWrap (false)")); // cn:文本中手动换行 + }); + //! 3 + //! pannel 3 start -> widget test + //! + + SARibbonPannel* pannelWidgetTest = page->addPannel(tr("widget test")); + pannelWidgetTest->setObjectName(QStringLiteral(u"pannelWidgetTest")); + + SARibbonComboBox* com = new SARibbonComboBox(this); + com->setObjectName("SARibbonComboBox test"); + com->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + com->setWindowTitle(tr("SARibbonComboBox test")); + for (int i = 0; i < 40; ++i) { + com->addItem(QString("SARibbonComboBox test%1").arg(i + 1)); + } + com->setEditable(true); + pannelWidgetTest->addSmallWidget(com); + + com = new SARibbonComboBox(this); + com->setObjectName("ComboBox Editable"); + com->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + com->setWindowTitle("ComboBox Editable"); + for (int i = 0; i < 40; ++i) { + com->addItem(QString("item %1").arg(i + 1)); + } + pannelWidgetTest->addSmallWidget(com); + + SARibbonLineEdit* lineEdit = new SARibbonLineEdit(this); + + lineEdit->setObjectName("Line Edit"); + lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + lineEdit->setWindowTitle("Line Edit"); + lineEdit->setText("SARibbonLineEdit"); + pannelWidgetTest->addSmallWidget(lineEdit); + QWidget* w = lineEdit->parentWidget(); + + while (w) { + qDebug() << w->metaObject()->className(); + w = w->parentWidget(); + } + + pannelWidgetTest->addSeparator(); + + QCalendarWidget* calendarWidget = new QCalendarWidget(this); + calendarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + calendarWidget->setObjectName(("calendarWidget")); + calendarWidget->setWindowTitle("calendarWidget"); + pannelWidgetTest->addLargeWidget(calendarWidget); + optAct = new QAction(this); + connect(optAct, &QAction::triggered, this, [ this ](bool on) { + Q_UNUSED(on); + QMessageBox::information(this, tr("Option Action Triggered"), tr("Option Action Triggered")); + }); + pannelWidgetTest->setOptionAction(optAct); + + pannelWidgetTest->setVisible(true); +} + +void MainWindow::createCategoryOther(SARibbonCategory* page) +{ + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("pannel one")); + pannel1->setObjectName("CategoryOther-pannel1"); + page->addPannel(pannel1); + // 按钮组 + SARibbonButtonGroupWidget* btnGroup1 = new SARibbonButtonGroupWidget(pannel1); + btnGroup1->setObjectName("SARibbonButtonGroupWidget1"); + btnGroup1->setWindowTitle("SARibbonButtonGroupWidget1"); + + btnGroup1->addAction(createAction(tr("Decrease Margin"), ":/icon/icon/Decrease-Margin.svg")); + btnGroup1->addAction(createAction(tr("Decrease Indent"), ":/icon/icon/Decrease-Indent.svg")); + btnGroup1->addAction(createAction(tr("Wrap Image Left"), ":/icon/icon/Wrap-Image Left.svg")); + btnGroup1->addAction(createAction(tr("Wrap Image Right"), ":/icon/icon/Wrap-Image Right.svg")); + pannel1->addWidget(btnGroup1, SARibbonPannelItem::Medium); + SARibbonButtonGroupWidget* btnGroup2 = new SARibbonButtonGroupWidget(pannel1); + btnGroup2->setObjectName("SARibbonButtonGroupWidget2"); + btnGroup2->setWindowTitle("SARibbonButtonGroupWidget2"); + + QAction* titleAlgnment = createAction(tr("Align Right"), ":/icon/icon/Align-Right.svg"); + titleAlgnment->setProperty("align", (int)Qt::AlignRight | Qt::AlignVCenter); + btnGroup2->addAction(titleAlgnment); + titleAlgnment = createAction(tr("Align Left"), ":/icon/icon/Align-Left.svg"); + titleAlgnment->setProperty("align", (int)Qt::AlignLeft | Qt::AlignVCenter); + btnGroup2->addAction(titleAlgnment); + titleAlgnment = createAction(tr("Align Center"), ":/icon/icon/Align-Center.svg"); + titleAlgnment->setProperty("align", (int)Qt::AlignCenter); + btnGroup2->addAction(titleAlgnment); + pannel1->addWidget(btnGroup2, SARibbonPannelItem::Medium); + connect(btnGroup2, &SARibbonButtonGroupWidget::actionTriggered, this, &MainWindow::onButtonGroupActionTriggered); + // Gallery + SARibbonGallery* gallery = pannel1->addGallery(); + QList< QAction* > galleryActions; + auto lambdaCreateGalleryAction = [ this ](const QString& text, const QString& iconurl) -> QAction* { + QAction* act = this->createAction(text, iconurl); + this->connect(act, &QAction::triggered, this, [ this, text ]() { + if (this->mTextedit) { + this->mTextedit->append(QString("%1 triggered").arg(text)); + } + }); + return act; + }; + galleryActions.append(lambdaCreateGalleryAction("Document File", ":/gallery-icon/icon/gallery/Document-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Download File", ":/gallery-icon/icon/gallery/Download-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Drive File Four Word", ":/gallery-icon/icon/gallery/Drive-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Dropbox File", ":/gallery-icon/icon/gallery/Dropbox-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Email File", ":/gallery-icon/icon/gallery/Email-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Encode File", ":/gallery-icon/icon/gallery/Encode-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("Favorit File", ":/gallery-icon/icon/gallery/Favorit-File.svg")); + galleryActions.append(lambdaCreateGalleryAction("File Error", ":/gallery-icon/icon/gallery/File-Error.svg")); + galleryActions.append(lambdaCreateGalleryAction("File Read Only", ":/gallery-icon/icon/gallery/File-Readonly.svg")); + galleryActions.append(lambdaCreateGalleryAction("File Settings", ":/gallery-icon/icon/gallery/File-Settings.svg")); + galleryActions.append( + lambdaCreateGalleryAction("Presentation File", ":/gallery-icon/icon/gallery/Presentation-File.svg")); + SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); + group1->setGalleryGroupStyle(SARibbonGalleryGroup::IconWithWordWrapText); + group1->setGridMinimumWidth(80); + galleryActions.clear(); + galleryActions.append(lambdaCreateGalleryAction("Photoshop", ":/gallery-icon/icon/gallery/Photoshop.svg")); + galleryActions.append( + lambdaCreateGalleryAction("Internet-Explorer", ":/gallery-icon/icon/gallery/Internet-Explorer.svg")); + galleryActions.append(lambdaCreateGalleryAction("Illustrator", ":/gallery-icon/icon/gallery/Illustrator.svg")); + galleryActions.append(lambdaCreateGalleryAction("Google-Maps", ":/gallery-icon/icon/gallery/Google-Maps.svg")); + galleryActions.append(lambdaCreateGalleryAction("Adobe", ":/gallery-icon/icon/gallery/Adobe.svg")); + galleryActions.append(lambdaCreateGalleryAction("Word", ":/gallery-icon/icon/gallery/Word.svg")); + SARibbonGalleryGroup* group2 = gallery->addCategoryActions(tr("Apps"), galleryActions); + group2->setGridMinimumWidth(80); + gallery->setCurrentViewGroup(group1); + + QAction* optAct = new QAction(this); + + optAct->setObjectName(("debug")); + pannel1->setOptionAction(optAct); + + SARibbonPannel* pannel2 = new SARibbonPannel(tr("pannel two")); + pannel2->setObjectName("CategoryOther-pannel2"); + page->addPannel(pannel2); + QAction* actionRemoveAppBtn = createAction(tr("remove application button"), ":/icon/icon/remove-app-btn.svg"); + actionRemoveAppBtn->setCheckable(true); + connect(actionRemoveAppBtn, &QAction::toggled, this, &MainWindow::onActionRemoveAppBtnTriggered); + pannel2->addLargeAction(actionRemoveAppBtn); + + QAction* actionLongText = + createAction(tr("show very long text in a button,balabalabala etc"), ":/icon/icon/long-text.svg", "long-text"); + pannel2->addLargeAction(actionLongText); + + SARibbonPannel* pannelStyle = new SARibbonPannel(tr("style")); + pannelStyle->setObjectName("CategoryOther-pannelStyle"); + page->addPannel(pannelStyle); + + QAction* actionUseQss = createAction(tr("use qss"), ":/icon/icon/useqss.svg"); + connect(actionUseQss, &QAction::triggered, this, &MainWindow::onActionUseQssTriggered); + pannelStyle->addSmallAction(actionUseQss); + + QAction* actionLoadCustomizeXmlFile = createAction(tr("load customize from xml file"), ":/icon/icon/useCustomize.svg"); + connect(actionLoadCustomizeXmlFile, &QAction::triggered, this, &MainWindow::onActionLoadCustomizeXmlFileTriggered); + pannelStyle->addSmallAction(actionLoadCustomizeXmlFile); + + QAction* actionWindowFlagNormalButton = createAction(tr("window normal button"), ":/icon/icon/windowsflag-normal.svg"); + actionWindowFlagNormalButton->setCheckable(true); + actionWindowFlagNormalButton->setChecked(true); + pannelStyle->addSmallAction(actionWindowFlagNormalButton); + connect(actionWindowFlagNormalButton, &QAction::triggered, this, &MainWindow::onActionWindowFlagNormalButtonTriggered); + + SARibbonPannel* pannelUtf8 = new SARibbonPannel(QStringLiteral(u"中文显示测试")); + pannelUtf8->setObjectName("CategoryOther-pannelUtf8"); + page->addPannel(pannelUtf8); + + QAction* actionChangeText = + createAction(QStringLiteral(u"改变显示为英文"), ":/icon/icon/chinese-char.svg", "actionChangeText"); + actionChangeText->setCheckable(true); + actionChangeText->setChecked(false); + pannelUtf8->addLargeAction(actionChangeText); + connect(actionChangeText, &QAction::triggered, this, [ pannelUtf8, actionChangeText ](bool on) { + if (on) { + pannelUtf8->setPannelName(QStringLiteral(u"show chinese char")); + actionChangeText->setText(QStringLiteral(u"show in chinese")); + } else { + pannelUtf8->setPannelName(QStringLiteral(u"中文显示测试")); + actionChangeText->setText(QStringLiteral(u"改变显示为英文")); + } + }); + + SARibbonPannel* pannelContextCategory = new SARibbonPannel(("Context Category")); + page->addPannel(pannelContextCategory); + QAction* a1 = createAction("Context Category 1", ":/icon/icon/ContextCategory.svg"); + a1->setCheckable(true); + connect(a1, &QAction::triggered, this, [ this ](bool c) { + this->ribbonBar()->setContextCategoryVisible(mContextCategory, c); + }); + + QAction* a2 = createAction("Context Category 2", ":/icon/icon/ContextCategory.svg"); + a2->setCheckable(true); + connect(a2, &QAction::triggered, this, [ this ](bool c) { + this->ribbonBar()->setContextCategoryVisible(mContextCategory2, c); + }); + pannelContextCategory->addLargeAction(a1); + pannelContextCategory->addLargeAction(a2); +} + +void MainWindow::createCategoryDelete(SARibbonCategory* page) +{ + SARibbonPannel* pannel1 = new SARibbonPannel(("pannel 1")); + SARibbonPannel* pannel2 = new SARibbonPannel(("pannel 2")); + + QAction* actionRemovePanne = createAction(tr("remove pannel"), ":/icon/icon/remove.svg"); + + connect(actionRemovePanne, &QAction::triggered, this, [ page, pannel2 ]() { page->removePannel(pannel2); }); + pannel1->addLargeAction(actionRemovePanne); + + QAction* actionRemoveCategory = createAction(tr("remove this category"), ":/icon/icon/removePage.svg"); + + connect(actionRemoveCategory, &QAction::triggered, this, [ this, page, actionRemoveCategory ]() { + this->ribbonBar()->removeCategory(page); + page->hide(); + page->deleteLater(); + actionRemoveCategory->setDisabled(true); + }); + pannel2->addLargeAction(actionRemoveCategory); + + page->addPannel(pannel1); + page->addPannel(pannel2); +} + +/** + * @brief 构建尺寸页 + * @param page + */ +void MainWindow::createCategorySize(SARibbonCategory* page) +{ + //========================= + // pannel : Font + //========================= + SARibbonPannel* pannel = page->addPannel(tr("Font")); + SARibbonButtonGroupWidget* groupFontFamile = new SARibbonButtonGroupWidget(pannel); + groupFontFamile->setObjectName(QStringLiteral(u"group1")); + QLabel* labelFontSize = new QLabel(groupFontFamile); + labelFontSize->setText(tr("select font")); + labelFontSize->setObjectName(QStringLiteral(u"labelFontSize")); + groupFontFamile->addWidget(labelFontSize); + QFontComboBox* fontComWidget = new QFontComboBox(groupFontFamile); + fontComWidget->setObjectName(QStringLiteral(u"fontComboBox")); + connect(fontComWidget, &QFontComboBox::currentFontChanged, this, &MainWindow::onFontComWidgetCurrentFontChanged); + //! 给窗口设置WindowTitle,这样在自定义窗口中,也能显示这个action对应的名字 + //! 你也可以给group1设置一个icon + //! setWindowTitle应该在addWidget之前 + groupFontFamile->setWindowTitle(tr("Font ComboBox")); + groupFontFamile->addWidget(fontComWidget); + QAction* actWidgetFontFamile = pannel->addWidget(groupFontFamile, SARibbonPannelItem::Medium); + //! 设置objectname是为了让actionManager正确管理action,如果不设置这个action对应的objectname, + //! SARibbonPannel::addWidget会自动生成一个objectname,action.{widgetobjectName},因此你要保证你添加的widget有objectName + actWidgetFontFamile->setObjectName(QStringLiteral("Size.Font.FontFamile")); + + SARibbonButtonGroupWidget* groupFontTools = new SARibbonButtonGroupWidget(pannel); + groupFontTools->setObjectName(QStringLiteral(u"group2")); + groupFontTools->addAction(createAction("Bold", ":/icon/icon/bold.svg")); + groupFontTools->addAction(createAction("Italic", ":/icon/icon/Italic.svg")); + groupFontTools->addSeparator(); + groupFontTools->addAction(createAction("left alignment", ":/icon/icon/al-left.svg")); + groupFontTools->addAction(createAction("center alignment", ":/icon/icon/al-center.svg")); + groupFontTools->addAction(createAction("right alignment", ":/icon/icon/al-right.svg")); + groupFontTools->addAction(createAction("line up on both sides", ":/icon/icon/al-bothside.svg")); + //! 给窗口设置WindowTitle,这样在自定义窗口中,也能显示这个action对应的名字 + //! 你也可以给group1设置一个icon + //! setWindowTitle应该在addWidget之前 + groupFontTools->setWindowTitle(tr("Font Tools")); + QAction* actWidgetFontTools = pannel->addWidget(groupFontTools, SARibbonPannelItem::Medium); + //! 设置objectname是为了让actionManager正确管理action,如果不设置这个action对应的objectname, + //! SARibbonPannel::addWidget会自动生成一个objectname,action.{widgetobjectName},因此你要保证你添加的widget有objectName + actWidgetFontTools->setObjectName(QStringLiteral("Size.Font.FontTools")); + + pannel->addSeparator(); + + QAction* actLargerFontSize = createAction(tr("Larger"), ":/icon/icon/largerFont.svg", "actLargerFontSize"); + pannel->addLargeAction(actLargerFontSize); + + QAction* actSmallFontSize = createAction(tr("Smaller"), ":/icon/icon/smallFont.svg", "actSmallFontSize"); + pannel->addLargeAction(actSmallFontSize); + + connect(actLargerFontSize, &QAction::triggered, this, &MainWindow::onActionFontLargerTriggered); + connect(actSmallFontSize, &QAction::triggered, this, &MainWindow::onActionFontSmallerTriggered); + + //========================= + // pannel : Font Size + //========================= + pannel = new SARibbonPannel(tr("Font Size")); + QFont f = font(); + f.setPointSize(14); + pannel->setFont(f); + QAction* actLargeFontText = createAction(tr("Larger Font Text"), ":/icon/icon/file.svg", "actLargeFontText"); + pannel->addLargeAction(actLargeFontText); + page->addPannel(pannel); + + // pannel 3 + // 这里演示了一个窗口,带有gridlayoout + pannel = new SARibbonPannel(tr("Grid")); + QWidget* w = new QWidget(); + QGridLayout* g = new QGridLayout(w); + + QLabel* lab0 = new QLabel("Ribbon Title Height:"); + QSpinBox* spinboxRibbonTitleHeight = new QSpinBox(w); + spinboxRibbonTitleHeight->setRange(10, 99); + + QLabel* lab1 = new QLabel("Ribbon Tab Height:"); + QSpinBox* spinboxRibbonTabHeight = new QSpinBox(w); + spinboxRibbonTabHeight->setRange(10, 99); + + QLabel* lab2 = new QLabel("Ribbon Category Height:"); + QSpinBox* spinboxRibbonCategoryHeight = new QSpinBox(w); + spinboxRibbonCategoryHeight->setRange(30, 300); + + QLabel* lab3 = new QLabel("Ribbon Pannel Title:"); + QSpinBox* spinboxRibbonPannelTtitleHeight = new QSpinBox(w); + spinboxRibbonPannelTtitleHeight->setRange(5, 99); + + QLabel* lab4 = new QLabel("Ribbon Pannel Spacing:"); + QSpinBox* spinboxRibbonPannelSpacing = new QSpinBox(w); + spinboxRibbonPannelSpacing->setRange(0, 25); + + QLabel* lab5 = new QLabel("Pannel ToolButton Icon Size:"); + QSpinBox* spinboxRibbonPannelToolBtnIconSize = new QSpinBox(w); + spinboxRibbonPannelToolBtnIconSize->setRange(1, 99); + + g->addWidget(lab0, 0, 0); + g->addWidget(spinboxRibbonTitleHeight, 0, 1); + g->addWidget(lab1, 1, 0); + g->addWidget(spinboxRibbonTabHeight, 1, 1); + g->addWidget(lab2, 0, 2); + g->addWidget(spinboxRibbonCategoryHeight, 0, 3); + g->addWidget(lab3, 1, 2); + g->addWidget(spinboxRibbonPannelTtitleHeight, 1, 3); + g->addWidget(lab4, 0, 4); + g->addWidget(spinboxRibbonPannelSpacing, 0, 5); + g->addWidget(lab5, 1, 4); + g->addWidget(spinboxRibbonPannelToolBtnIconSize, 1, 5); + spinboxRibbonTitleHeight->setValue(ribbonBar()->titleBarHeight()); + spinboxRibbonTabHeight->setValue(ribbonBar()->tabBarHeight()); + spinboxRibbonCategoryHeight->setValue(ribbonBar()->categoryHeight()); + spinboxRibbonPannelTtitleHeight->setValue(ribbonBar()->pannelTitleHeight()); + spinboxRibbonPannelSpacing->setValue(ribbonBar()->pannelSpacing()); + spinboxRibbonPannelToolBtnIconSize->setValue(ribbonBar()->pannelToolButtonIconSize().width()); +#if QT_VERSION_MAJOR >= 6 + connect(spinboxRibbonTitleHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonTitleHeightChanged); + connect(spinboxRibbonTabHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonTabHeightChanged); + connect(spinboxRibbonCategoryHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonCategoryHeightChanged); + connect(spinboxRibbonPannelTtitleHeight, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged); + connect(spinboxRibbonPannelSpacing, &QSpinBox::valueChanged, this, &MainWindow::onSpinBoxRibbonPannelSpacingChanged); + connect(spinboxRibbonPannelToolBtnIconSize, + &QSpinBox::valueChanged, + this, + &MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged); +#else + connect(spinboxRibbonTitleHeight, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonTitleHeightChanged); + connect(spinboxRibbonTabHeight, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonTabHeightChanged); + connect(spinboxRibbonCategoryHeight, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonCategoryHeightChanged); + connect(spinboxRibbonPannelTtitleHeight, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonPannelTtitleHeightChanged); + connect(spinboxRibbonPannelSpacing, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonPannelSpacingChanged); + connect(spinboxRibbonPannelToolBtnIconSize, + QOverload< int >::of(&QSpinBox::valueChanged), + this, + &MainWindow::onSpinBoxRibbonPannelToolBtnIconSizeChanged); +#endif + pannel->addLargeWidget(w); + page->addPannel(pannel); +} + +void MainWindow::createCategoryColor(SARibbonCategory* page) +{ + SARibbonPannel* pannel = page->addPannel(tr("color")); + + auto fpCreateBtn = [ this, pannel ](const QColor& defaultColor = Qt::red) -> SARibbonColorToolButton* { + SARibbonColorToolButton* colorButton = new SARibbonColorToolButton(pannel); + colorButton->setColor(defaultColor); + colorButton->setupStandardColorMenu(); + this->connect(colorButton, &SARibbonColorToolButton::colorClicked, this, &MainWindow::onColorButtonColorClicked); + return colorButton; + }; + // No Icon No text + SARibbonColorToolButton* colorButton = fpCreateBtn(); + colorButton->setObjectName("ColorFillToIcon-NoIconNoText"); + colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); + pannel->addSmallWidget(colorButton); + + // No Icon have text + colorButton = fpCreateBtn(Qt::blue); + colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); + colorButton->setObjectName("ColorFillToIcon-NoIconHaveText"); + colorButton->setText("No Icon have text"); + pannel->addSmallWidget(colorButton); + + // have Icon No text + colorButton = fpCreateBtn(QColor()); + colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); + pannel->addSmallWidget(colorButton); + + // have Icon have text + colorButton = fpCreateBtn(Qt::red); + colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); + colorButton->setText("have Icon have text"); + pannel->addSmallWidget(colorButton); + + colorButton = fpCreateBtn(QColor()); + colorButton->setButtonType(SARibbonToolButton::LargeButton); + colorButton->setObjectName("ColorFillToIcon-LargeColorButton"); + colorButton->setColorStyle(SARibbonColorToolButton::ColorFillToIcon); + colorButton->setText("Large Color Button"); + pannel->addLargeWidget(colorButton); + + colorButton = fpCreateBtn(QColor()); + colorButton->setButtonType(SARibbonToolButton::LargeButton); + colorButton->setIcon(QIcon(":/icon/icon/long-text.svg")); + colorButton->setObjectName("ColorUnderIcon-LargeColorButton"); + colorButton->setText("Large Color Button"); + pannel->addLargeWidget(colorButton); + // + pannel->addSeparator(); + SAColorPaletteGridWidget* pw = new SAColorPaletteGridWidget(SA::getStandardColorList(), pannel); + pw->setFactor({ 75, 120 }); + pannel->addLargeWidget(pw); +} + +/** + * @brief 创建上下文标签 + */ +void MainWindow::createContextCategory1() +{ + // 上下文标签 + SARibbonBar* ribbon = ribbonBar(); + mContextCategory = ribbon->addContextCategory(tr("context"), QColor(), 1); + + SARibbonCategory* contextCategoryPage1 = mContextCategory->addCategoryPage(tr("Page1")); + createContextCategoryPage1(contextCategoryPage1); + + SARibbonCategory* contextCategoryPage2 = mContextCategory->addCategoryPage(tr("Page2")); + createContextCategoryPage2(contextCategoryPage2); +} + +void MainWindow::createContextCategory2() +{ + SARibbonBar* ribbon = ribbonBar(); + mContextCategory2 = ribbon->addContextCategory(("context2"), QColor(), 2); + mContextCategory2->addCategoryPage(("context2 Page1")); + mContextCategory2->addCategoryPage(("context2 Page2")); +} + +void MainWindow::createContextCategoryPage1(SARibbonCategory* page) +{ + SARibbonPannel* pannel = page->addPannel(tr("show and hide test")); + + QAction* actionHideActions = createAction("hide action", ":/icon/icon/hidePannel.svg"); + actionHideActions->setCheckable(true); + actionHideActions->setChecked(true); + pannel->addLargeAction(actionHideActions); + connect(actionHideActions, &QAction::triggered, this, &MainWindow::onActionHideActionTriggered); + + mActionDisable = createAction(tr("Disable"), ":/icon/icon/enableTest.svg"); + + mActionDisable->setDisabled(true); + pannel->addLargeAction(mActionDisable); + connect(mActionDisable, &QAction::triggered, this, [ this ](bool b) { + Q_UNUSED(b); + mActionDisable->setDisabled(true); + }); + + mActionUnlock = createAction(tr("unlock"), ":/icon/icon/unlock.svg"); + mActionUnlock->setShortcut(QKeySequence(QLatin1String("Ctrl+E"))); + mActionUnlock->setShortcutContext(Qt::ApplicationShortcut); + pannel->addLargeAction(mActionUnlock); + connect(mActionUnlock, &QAction::triggered, this, [ this ](bool b) { + Q_UNUSED(b); + mActionDisable->setEnabled(true); + mActionDisable->setText(("Enabled")); + }); + auto act = createAction(tr("1"), ":/icon/icon/unlock.svg"); + act->setToolTip(tr("very short string")); + pannel->addLargeAction(act); + + mActionSetTextTest = createAction("set text", ":/icon/icon/setText.svg"); + + mActionSetTextTest->setCheckable(true); + mActionSetTextTest->setShortcut(QKeySequence(QLatin1String("Ctrl+D"))); + mActionSetTextTest->setShortcutContext(Qt::ApplicationShortcut); + pannel->addLargeAction(mActionSetTextTest); + + connect(mActionSetTextTest, &QAction::toggled, this, [ this ](bool b) { + if (b) { + mActionSetTextTest->setText(QStringLiteral(u"setText测试")); + } else { + mActionSetTextTest->setText(QStringLiteral(u"set text")); + } + }); + // 隐藏pannel + mActionShowTest = createAction("show beside pannel", ":/icon/icon/show.svg"); + + mActionShowTest->setCheckable(true); + pannel->addLargeAction(mActionShowTest); + + mPannelVisbileExample = page->addPannel(tr("show/hide")); + // 重复添加 + mPannelVisbileExample->addLargeAction(mActionSetTextTest); + + connect(mActionShowTest, &QAction::toggled, this, [ this ](bool b) { + mPannelVisbileExample->setVisible(!b); + if (b) { + mActionShowTest->setText(tr("hide beside pannel")); + } else { + mActionShowTest->setText(tr("show beside pannel")); + } + ribbonBar()->repaint(); + }); + + SARibbonPannel* pannel3 = page->addPannel(("show/hide action test")); + + mActionHideAction2 = createAction("hide action 2", ":/icon/icon/action.svg"); + mActionHideAction4 = createAction("hide action 4", ":/icon/icon/action.svg"); + mActionHideAction2->setCheckable(true); + mActionHideAction2->setChecked(true); + mActionHideAction4->setCheckable(true); + mActionHideAction4->setChecked(true); + mActionHideShowTextAct2 = createAction("action2", ":/icon/icon/action2.svg"); + mActionHideShowTextAct3 = createAction("action3", ":/icon/icon/action3.svg"); + mActionHideShowTextAct4 = createAction("action4", ":/icon/icon/action4.svg"); + pannel3->addLargeAction(mActionHideAction2); + pannel3->addLargeAction(mActionHideAction4); + pannel3->addSmallAction(mActionHideShowTextAct2); + pannel3->addSmallAction(mActionHideShowTextAct3); + pannel3->addSmallAction(mActionHideShowTextAct4); + connect(mActionHideAction2, &QAction::triggered, this, [ this ](bool b) { + if (b) { + mActionHideShowTextAct2->setVisible(true); + mActionHideAction2->setText(tr("hide action2")); + } else { + mActionHideShowTextAct2->setVisible(false); + mActionHideAction2->setText(tr("show action2")); + } + }); + connect(mActionHideAction4, &QAction::triggered, this, [ this ](bool b) { + if (b) { + mActionHideShowTextAct4->setVisible(true); + mActionHideAction4->setText(tr("hide action4")); + } else { + mActionHideShowTextAct4->setVisible(false); + mActionHideAction4->setText(tr("show action4")); + } + }); + // + SARibbonPannel* pannel4 = page->addPannel(("widget")); + QSpinBox* spinbox = new QSpinBox(this); + SARibbonCtrlContainer* ctrlContainer1 = new SARibbonCtrlContainer(pannel4); + ctrlContainer1->setContainerWidget(spinbox); + ctrlContainer1->setText(tr("spinbox:")); + ctrlContainer1->setEnableShowIcon(false); + ctrlContainer1->setMaximumHeight(fontMetrics().lineSpacing() * 1.5); + pannel4->addMediumWidget(ctrlContainer1); + + QLineEdit* linedit = new QLineEdit(this); + SARibbonCtrlContainer* ctrlContainer2 = new SARibbonCtrlContainer(pannel4); + ctrlContainer2->setContainerWidget(linedit); + ctrlContainer2->setText(tr("linedit:")); + ctrlContainer2->setEnableShowIcon(false); + ctrlContainer2->setMaximumHeight(fontMetrics().lineSpacing() * 1.5); + pannel4->addMediumWidget(ctrlContainer2); +} + +void MainWindow::createContextCategoryPage2(SARibbonCategory* page) +{ + SARibbonPannel* pannel1 = page->addPannel(("删除CategoryPage测试")); + + QAction* actionDeleteThisCategory = createAction("delete this category", ":/icon/icon/delete.svg"); + pannel1->addLargeAction(actionDeleteThisCategory); + connect(actionDeleteThisCategory, &QAction::triggered, this, [ this, page ]() { + this->ribbonBar()->removeCategory(page); + page->deleteLater(); + }); + SARibbonPannel* pannelLayout = page->addPannel(("特殊布局")); + + pannelLayout->addAction( + createAction("Large", ":/icon/icon/layout.svg", "@Large1"), QToolButton::InstantPopup, SARibbonPannelItem::Large); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small1"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small2"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addSeparator(); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small3"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small4"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small5"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small6"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addSeparator(); + pannelLayout->addAction( + createAction("Large", ":/icon/icon/layout.svg", "@Large2"), QToolButton::InstantPopup, SARibbonPannelItem::Large); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium1"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium2"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small7"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium3"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction( + createAction("Large", ":/icon/icon/layout.svg", "@Large3"), QToolButton::InstantPopup, SARibbonPannelItem::Large); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium4"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium5"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction( + createAction("Large", ":/icon/icon/layout.svg", "@Large4"), QToolButton::InstantPopup, SARibbonPannelItem::Large); + pannelLayout->addSeparator(); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium6"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction( + createAction("Large", ":/icon/icon/layout.svg", "@Large5"), QToolButton::InstantPopup, SARibbonPannelItem::Large); + pannelLayout->addAction(createAction("Medium", ":/icon/icon/layout.svg", "@Medium7"), + QToolButton::InstantPopup, + SARibbonPannelItem::Medium); + pannelLayout->addAction( + createAction("Small", ":/icon/icon/layout.svg", "@Small8"), QToolButton::InstantPopup, SARibbonPannelItem::Small); +} + +void MainWindow::createQuickAccessBar() +{ + SARibbonQuickAccessBar* quickAccessBar = ribbonBar()->quickAccessBar(); + + quickAccessBar->addAction(createAction("save", ":/icon/icon/save.svg", "save-quickbar")); + quickAccessBar->addSeparator(); + + QAction* actionUndo = createAction("undo", ":/icon/icon/undo.svg"); + actionUndo->setShortcut(QKeySequence("Ctrl+Shift+z")); + actionUndo->setShortcutContext(Qt::ApplicationShortcut); + quickAccessBar->addAction(actionUndo); + + QAction* actionRedo = createAction("redo", ":/icon/icon/redo.svg"); + actionRedo->setShortcut(QKeySequence("Ctrl+z")); + actionRedo->setShortcutContext(Qt::ApplicationShortcut); + quickAccessBar->addAction(actionRedo); + + connect(actionUndo, &QAction::triggered, this, [ this ]() { mTextedit->append("undo"); }); + connect(actionRedo, &QAction::triggered, this, [ this ]() { mTextedit->append("redo"); }); + + quickAccessBar->addSeparator(); + + QMenu* m1 = new QMenu("Presentation File 1", this); + m1->setIcon(QIcon(":/icon/icon/presentationFile.svg")); + for (int i = 0; i < 10; ++i) { + m1->addAction(createAction(QString("file%1 - 1").arg(i + 1), ":/icon/icon/file.svg")); + } + quickAccessBar->addMenu(m1, Qt::ToolButtonIconOnly, QToolButton::InstantPopup); + + QMenu* m2 = new QMenu("Presentation File 2", this); + m2->setIcon(QIcon(":/icon/icon/presentationFile.svg")); + for (int i = 0; i < 10; ++i) { + m2->addAction(createAction(QString("file%1 - 2").arg(i + 1), ":/icon/icon/file.svg")); + } + QAction* actiontestmenu = new QAction("Presentation File 2"); + actiontestmenu->setMenu(m2); + quickAccessBar->addAction(actiontestmenu, Qt::ToolButtonIconOnly, QToolButton::MenuButtonPopup); + + QAction* customize = createAction("customize", ":/icon/icon/customize0.svg", "customize2"); + quickAccessBar->addAction(customize); + connect(customize, &QAction::triggered, this, &MainWindow::onActionCustomizeTriggered); + + QAction* actionCustomizeAndSave = createAction("customize and save", ":/icon/icon/customize.svg"); + quickAccessBar->addAction(actionCustomizeAndSave); + connect(actionCustomizeAndSave, &QAction::triggered, this, &MainWindow::onActionCustomizeAndSaveTriggered); + + QAction* actionCustomizeAndSaveWithApply = createAction("customize and save with apply", ":/icon/icon/customize.svg"); + quickAccessBar->addAction(actionCustomizeAndSaveWithApply); + connect(actionCustomizeAndSaveWithApply, &QAction::triggered, this, &MainWindow::onActionCustomizeAndSaveWithApplyTriggered); + + // + mSearchEditor = new SARibbonLineEdit(this); + mSearchEditor->setFixedWidth(100); + mSearchEditor->setPlaceholderText("Search"); + quickAccessBar->addWidget(mSearchEditor); + connect(mSearchEditor, &QLineEdit::editingFinished, this, [ this ]() { + this->mTextedit->append(this->mSearchEditor->text()); + }); +} + +/** + * @brief 创建RightButtonGroup + * + * RightButtonGroup实在ribbonbar右边的工具栏,可以放置一些快捷图标 + * @param rightBar + */ +void MainWindow::createRightButtonGroup() +{ + SARibbonBar* ribbon = ribbonBar(); + if (!ribbon) { + return; + } + SARibbonButtonGroupWidget* rightBar = ribbon->rightButtonGroup(); + QAction* actionHelp = createAction(tr("help"), ":/icon/icon/help.svg"); + mActionVisibleAll = createAction(tr("Visible"), ":/icon/icon/visible-true.svg"); + mActionVisibleAll->setCheckable(true); + mActionVisibleAll->setChecked(true); + connect(actionHelp, &QAction::triggered, this, &MainWindow::onActionHelpTriggered); + connect(mActionVisibleAll, &QAction::triggered, this, &MainWindow::onActionVisibleAllTriggered); + rightBar->addAction(actionHelp); + rightBar->addAction(mActionVisibleAll); +} + +/** + * @brief 创建右上角系统最大、最小化按钮栏的图标工具 + */ +void MainWindow::createWindowButtonGroupBar() +{ + SARibbonSystemButtonBar* wbar = windowButtonBar(); + if (!wbar) { + return; + } + QAction* a = wbar->addAction(tr("Login"), QIcon(), Qt::ToolButtonTextOnly); + connect(a, &QAction::triggered, this, [ this ]() { this->mTextedit->append("Login triggered"); }); +} + +/** + * @brief 创建ActionsManager(Create ActionsManager) + * + * 创建ActionsManager,实现actions的管理以及SARibbonBar的自定义 + * + * Create ActionsManager to manage actions and customize SARibbonBar + * + * + */ +void MainWindow::createActionsManager() +{ + // 添加其他的action,这些action并不在ribbon管理范围,主要用于SARibbonCustomizeWidget自定义用 + createOtherActions(); + mTagForActionText = SARibbonActionsManager::UserDefineActionTag + 1; + mTagForActionIcon = SARibbonActionsManager::UserDefineActionTag + 2; + + mActionsManager = new SARibbonActionsManager(ribbonBar()); // 申明过程已经自动注册所有action + + // 以下注册特别的action + mActionsManager->registeAction(mOtherAction1, SARibbonActionsManager::CommonlyUsedActionTag); + mActionsManager->registeAction(mOtherAction3, SARibbonActionsManager::CommonlyUsedActionTag); + mActionsManager->registeAction(mOtherAction5, SARibbonActionsManager::CommonlyUsedActionTag); + mActionsManager->registeAction(mOtherActionIcon1, SARibbonActionsManager::CommonlyUsedActionTag); + + mActionsManager->registeAction(mOtherAction1, mTagForActionText); + mActionsManager->registeAction(mOtherAction2, mTagForActionText); + mActionsManager->registeAction(mOtherAction3, mTagForActionText); + mActionsManager->registeAction(mOtherAction4, mTagForActionText); + mActionsManager->registeAction(mOtherAction5, mTagForActionText); + + mActionsManager->registeAction(mOtherActionIcon1, mTagForActionIcon); + + mActionsManager->setTagName(SARibbonActionsManager::CommonlyUsedActionTag, tr("in common use")); // + mActionsManager->setTagName(mTagForActionText, tr("no icon action")); + mActionsManager->setTagName(mTagForActionIcon, tr("have icon action")); +} + +QAction* MainWindow::createAction(const QString& text, const QString& iconurl, const QString& objName) +{ + QAction* act = new QAction(this); + act->setText(text); + act->setIcon(QIcon(iconurl)); + act->setObjectName(objName); + return act; +} + +QAction* MainWindow::createAction(const QString& text, const QString& iconurl) +{ + QAction* act = new QAction(this); + act->setText(text); + act->setIcon(QIcon(iconurl)); + act->setObjectName(text); + return act; +} + +void MainWindow::onMenuButtonPopupCheckableTest(bool b) +{ + mTextedit->append(QString("MenuButtonPopupCheckableTest : %1").arg(b)); +} + +void MainWindow::onInstantPopupCheckableTest(bool b) +{ + mTextedit->append(QString("InstantPopupCheckableTest : %1").arg(b)); +} + +void MainWindow::onDelayedPopupCheckableTest(bool b) +{ + mTextedit->append(QString("DelayedPopupCheckableTest : %1").arg(b)); +} + +void MainWindow::onMenuButtonPopupCheckabletriggered(bool b) +{ + mTextedit->append(QString("MenuButtonPopupCheckabletriggered : %1").arg(b)); +} + +void MainWindow::onInstantPopupCheckabletriggered(bool b) +{ + mTextedit->append(QString("InstantPopupCheckabletriggered : %1").arg(b)); +} + +void MainWindow::onDelayedPopupCheckabletriggered(bool b) +{ + Q_UNUSED(b); + mTextedit->append(tr("The SARibbonToolButton::setPopupMode(QToolButton::DelayedPopup) method " + "can be used to set the menu pop-up method to delayed pop-up. " + "This also demonstrates manually setting text wrapping")); + // cn:使用SARibbonToolButton::setPopupMode(QToolButton::DelayedPopup)方法可以设置菜单弹出方式为延迟弹出,这里也演示了手动设置文本的换行 +} diff --git a/example/MainWindowExample/mainwindow.h b/example/MainWindowExample/mainwindow.h index 501aa4d0..e5f9b19e 100644 --- a/example/MainWindowExample/mainwindow.h +++ b/example/MainWindowExample/mainwindow.h @@ -1,121 +1,121 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H -#include "SARibbonMainWindow.h" -class SARibbonCategory; -class SARibbonContextCategory; -class SARibbonCustomizeWidget; -class SARibbonActionsManager; -class SARibbonQuickAccessBar; -class SARibbonButtonGroupWidget; -class SARibbonPannel; -class QTextEdit; -class QComboBox; -class QCloseEvent; -class QLineEdit; - -class MainWindow : public SARibbonMainWindow -{ - Q_OBJECT -public: - MainWindow(QWidget* par = nullptr); - -private: - // 创建ribbon-application菜单示例 - void createRibbonApplicationButton(); - // 创建main菜单示例 - void createCategoryMain(SARibbonCategory* page); - void createCategoryOther(SARibbonCategory* page); - void createCategoryDelete(SARibbonCategory* page); - void createCategorySize(SARibbonCategory* page); - void createCategoryColor(SARibbonCategory* page); - void createContextCategory1(); - void createContextCategory2(); - void createContextCategoryPage1(SARibbonCategory* page); - void createContextCategoryPage2(SARibbonCategory* page); - void createQuickAccessBar(); - void createRightButtonGroup(); - void createWindowButtonGroupBar(); - // 创建ActionsManager,实现actions的管理以及SARibbonBar的自定义 - void createActionsManager(); - QAction* createAction(const QString& text, const QString& iconurl, const QString& objName); - QAction* createAction(const QString& text, const QString& iconurl); -private Q_SLOTS: - void onMenuButtonPopupCheckableTest(bool b); - void onInstantPopupCheckableTest(bool b); - void onDelayedPopupCheckableTest(bool b); - void onMenuButtonPopupCheckabletriggered(bool b); - void onInstantPopupCheckabletriggered(bool b); - void onDelayedPopupCheckabletriggered(bool b); - void onShowContextCategory(bool on); - void onStyleClicked(int id); - void onActionCustomizeTriggered(); - void onActionCustomizeAndSaveTriggered(); - void onActionCustomizeAndSaveWithApplyTriggered(); - void onActionHelpTriggered(); - void onActionRemoveAppBtnTriggered(bool b); - void onActionUseQssTriggered(); - void onActionLoadCustomizeXmlFileTriggered(); - void onActionWindowFlagNormalButtonTriggered(bool b); - void onFontComWidgetCurrentFontChanged(const QFont& f); - void onActionFontLargerTriggered(); - void onActionFontSmallerTriggered(); - void onActionWordWrapTriggered(bool b); - void onButtonGroupActionTriggered(QAction* act); - void onColorButtonColorClicked(const QColor& c, bool on); - void onRibbonThemeComboBoxCurrentIndexChanged(int index); - void onActionHideActionTriggered(bool on); - // 切换所有action是否可见 - void onActionVisibleAllTriggered(bool on); - // 居中对齐checkbox的槽 - void onCheckBoxAlignmentCenterClicked(bool checked); - // - void onSpinBoxRibbonTitleHeightChanged(int h); - void onSpinBoxRibbonTabHeightChanged(int h); - void onSpinBoxRibbonCategoryHeightChanged(int h); - void onSpinBoxRibbonPannelTtitleHeightChanged(int h); - void onSpinBoxRibbonPannelSpacingChanged(int h); - void onSpinBoxRibbonPannelToolBtnIconSizeChanged(int h); - -private: - // 创建其它actions,这些actions并不在SARibbonBar管理 - void createOtherActions(); - -protected: - void closeEvent(QCloseEvent* e) override; - -private: - SARibbonContextCategory* mContextCategory; - SARibbonContextCategory* mContextCategory2; - SARibbonCustomizeWidget* mWidgetForCustomize; - QTextEdit* mTextedit; - SARibbonActionsManager* mActionsManager; - int mTagForActionText; - int mTagForActionIcon; - QMenu* mMenuApplicationBtn; - QComboBox* mComboboxRibbonTheme; - QLineEdit* mSearchEditor { nullptr }; - // - QAction* mActionWordWrap { nullptr }; - QAction* mActionDisable { nullptr }; - QAction* mActionUnlock { nullptr }; - QAction* mActionSetTextTest { nullptr }; - QAction* mActionShowTest { nullptr }; - QAction* mActionHideAction2 { nullptr }; - QAction* mActionHideAction4 { nullptr }; - QAction* mActionHideShowTextAct2 { nullptr }; - QAction* mActionHideShowTextAct3 { nullptr }; - QAction* mActionHideShowTextAct4 { nullptr }; - // - QAction* mOtherAction1 { nullptr }; - QAction* mOtherAction2 { nullptr }; - QAction* mOtherAction3 { nullptr }; - QAction* mOtherAction4 { nullptr }; - QAction* mOtherAction5 { nullptr }; - QAction* mOtherActionIcon1 { nullptr }; - SARibbonPannel* mPannelVisbileExample { nullptr }; - // - QAction* mActionVisibleAll { nullptr }; ///< 控制所有action是否可见的开关 - bool mHasApplyCustomizeXmlFile { false }; /// 程序启动后是否应用上次修改 -}; - -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H +#include "SARibbonMainWindow.h" +class SARibbonCategory; +class SARibbonContextCategory; +class SARibbonCustomizeWidget; +class SARibbonActionsManager; +class SARibbonQuickAccessBar; +class SARibbonButtonGroupWidget; +class SARibbonPannel; +class QTextEdit; +class QComboBox; +class QCloseEvent; +class QLineEdit; + +class MainWindow : public SARibbonMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* par = nullptr); + +private: + // 创建ribbon-application菜单示例 + void createRibbonApplicationButton(); + // 创建main菜单示例 + void createCategoryMain(SARibbonCategory* page); + void createCategoryOther(SARibbonCategory* page); + void createCategoryDelete(SARibbonCategory* page); + void createCategorySize(SARibbonCategory* page); + void createCategoryColor(SARibbonCategory* page); + void createContextCategory1(); + void createContextCategory2(); + void createContextCategoryPage1(SARibbonCategory* page); + void createContextCategoryPage2(SARibbonCategory* page); + void createQuickAccessBar(); + void createRightButtonGroup(); + void createWindowButtonGroupBar(); + // 创建ActionsManager,实现actions的管理以及SARibbonBar的自定义 + void createActionsManager(); + QAction* createAction(const QString& text, const QString& iconurl, const QString& objName); + QAction* createAction(const QString& text, const QString& iconurl); +private Q_SLOTS: + void onMenuButtonPopupCheckableTest(bool b); + void onInstantPopupCheckableTest(bool b); + void onDelayedPopupCheckableTest(bool b); + void onMenuButtonPopupCheckabletriggered(bool b); + void onInstantPopupCheckabletriggered(bool b); + void onDelayedPopupCheckabletriggered(bool b); + void onShowContextCategory(bool on); + void onStyleClicked(int id); + void onActionCustomizeTriggered(); + void onActionCustomizeAndSaveTriggered(); + void onActionCustomizeAndSaveWithApplyTriggered(); + void onActionHelpTriggered(); + void onActionRemoveAppBtnTriggered(bool b); + void onActionUseQssTriggered(); + void onActionLoadCustomizeXmlFileTriggered(); + void onActionWindowFlagNormalButtonTriggered(bool b); + void onFontComWidgetCurrentFontChanged(const QFont& f); + void onActionFontLargerTriggered(); + void onActionFontSmallerTriggered(); + void onActionWordWrapTriggered(bool b); + void onButtonGroupActionTriggered(QAction* act); + void onColorButtonColorClicked(const QColor& c, bool on); + void onRibbonThemeComboBoxCurrentIndexChanged(int index); + void onActionHideActionTriggered(bool on); + // 切换所有action是否可见 + void onActionVisibleAllTriggered(bool on); + // 居中对齐checkbox的槽 + void onCheckBoxAlignmentCenterClicked(bool checked); + // + void onSpinBoxRibbonTitleHeightChanged(int h); + void onSpinBoxRibbonTabHeightChanged(int h); + void onSpinBoxRibbonCategoryHeightChanged(int h); + void onSpinBoxRibbonPannelTtitleHeightChanged(int h); + void onSpinBoxRibbonPannelSpacingChanged(int h); + void onSpinBoxRibbonPannelToolBtnIconSizeChanged(int h); + +private: + // 创建其它actions,这些actions并不在SARibbonBar管理 + void createOtherActions(); + +protected: + void closeEvent(QCloseEvent* e) override; + +private: + SARibbonContextCategory* mContextCategory; + SARibbonContextCategory* mContextCategory2; + SARibbonCustomizeWidget* mWidgetForCustomize; + QTextEdit* mTextedit; + SARibbonActionsManager* mActionsManager; + int mTagForActionText; + int mTagForActionIcon; + QMenu* mMenuApplicationBtn; + QComboBox* mComboboxRibbonTheme; + QLineEdit* mSearchEditor { nullptr }; + // + QAction* mActionWordWrap { nullptr }; + QAction* mActionDisable { nullptr }; + QAction* mActionUnlock { nullptr }; + QAction* mActionSetTextTest { nullptr }; + QAction* mActionShowTest { nullptr }; + QAction* mActionHideAction2 { nullptr }; + QAction* mActionHideAction4 { nullptr }; + QAction* mActionHideShowTextAct2 { nullptr }; + QAction* mActionHideShowTextAct3 { nullptr }; + QAction* mActionHideShowTextAct4 { nullptr }; + // + QAction* mOtherAction1 { nullptr }; + QAction* mOtherAction2 { nullptr }; + QAction* mOtherAction3 { nullptr }; + QAction* mOtherAction4 { nullptr }; + QAction* mOtherAction5 { nullptr }; + QAction* mOtherActionIcon1 { nullptr }; + SARibbonPannel* mPannelVisbileExample { nullptr }; + // + QAction* mActionVisibleAll { nullptr }; ///< 控制所有action是否可见的开关 + bool mHasApplyCustomizeXmlFile { false }; /// 程序启动后是否应用上次修改 +}; + +#endif // MAINWINDOW_H diff --git a/example/MdiAreaWindowExample/main.cpp b/example/MdiAreaWindowExample/main.cpp index f3375d43..fa063d73 100644 --- a/example/MdiAreaWindowExample/main.cpp +++ b/example/MdiAreaWindowExample/main.cpp @@ -1,64 +1,64 @@ -#include "mdimainwindow.h" -#include -#include -#include -#include "SARibbonBar.h" -// 重定向qdebug的打印 -void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg); - -/** - * @brief 重定向qdebug的打印 - * @param type - * @param context - * @param msg - */ -void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ - QByteArray localMsg = msg.toLocal8Bit(); - - switch (type) { - case QtDebugMsg: - fprintf(stdout, "%s |[Debug] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); - break; - - case QtWarningMsg: - fprintf(stdout, "%s |[Warning] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); - break; - - case QtCriticalMsg: - fprintf(stdout, "%s |[Critical] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); - break; - - case QtFatalMsg: - fprintf(stdout, "%s |[Fatal] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); - abort(); - break; - - default: - fprintf(stdout, "%s |[Debug](%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); - break; - } -#ifndef QT_NO_DEBUG_OUTPUT - fflush(stdout); -#endif -} - -int main(int argc, char* argv[]) -{ - // 以下是针对高分屏的设置,有高分屏需求都需要按照下面进行设置 - SARibbonBar::initHighDpi(); - - QApplication a(argc, argv); - qInstallMessageHandler(log_out_put); - QFont f = a.font(); - f.setFamily(u8"微软雅黑"); - a.setFont(f); - QElapsedTimer cost; - - cost.start(); - MainWindow w; - qDebug() << "window build cost:" << cost.elapsed() << " ms"; - w.show(); - - return (a.exec()); -} +#include "mdimainwindow.h" +#include +#include +#include +#include "SARibbonBar.h" +// 重定向qdebug的打印 +void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg); + +/** + * @brief 重定向qdebug的打印 + * @param type + * @param context + * @param msg + */ +void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + + switch (type) { + case QtDebugMsg: + fprintf(stdout, "%s |[Debug] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); + break; + + case QtWarningMsg: + fprintf(stdout, "%s |[Warning] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); + break; + + case QtCriticalMsg: + fprintf(stdout, "%s |[Critical] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); + break; + + case QtFatalMsg: + fprintf(stdout, "%s |[Fatal] (%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); + abort(); + break; + + default: + fprintf(stdout, "%s |[Debug](%s[%u],%s)\n", localMsg.constData(), context.function, context.line, context.file); + break; + } +#ifndef QT_NO_DEBUG_OUTPUT + fflush(stdout); +#endif +} + +int main(int argc, char* argv[]) +{ + // 以下是针对高分屏的设置,有高分屏需求都需要按照下面进行设置 + SARibbonBar::initHighDpi(); + + QApplication a(argc, argv); + qInstallMessageHandler(log_out_put); + QFont f = a.font(); + f.setFamily(u8"微软雅黑"); + a.setFont(f); + QElapsedTimer cost; + + cost.start(); + MainWindow w; + qDebug() << "window build cost:" << cost.elapsed() << " ms"; + w.show(); + + return (a.exec()); +} diff --git a/example/MdiAreaWindowExample/mdimainwindow.cpp b/example/MdiAreaWindowExample/mdimainwindow.cpp index 0ac95e3e..d7a49654 100644 --- a/example/MdiAreaWindowExample/mdimainwindow.cpp +++ b/example/MdiAreaWindowExample/mdimainwindow.cpp @@ -1,81 +1,81 @@ -#include "mdimainwindow.h" -#include "ui_MainWindow.h" -#include -#include -#include "SARibbonBar.h" -#include "SARibbonCategory.h" -#include "SARibbonPannel.h" -MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent), ui(new Ui::MainWindow) -{ - // 注意,你必须在ui文件中删除菜单栏,否则会拿到空的ribbon指针 - ui->setupUi(this); - SARibbonBar* ribbon = ribbonBar(); - SARibbonCategory* mainPage = new SARibbonCategory("Main"); - ribbon->addCategoryPage(mainPage); - buildMainPage(mainPage); - // 如果你用qwk,你需要安装mdi窗口的最大最小化按钮到frameless框架中,你需要绑定此槽函数 - connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &MainWindow::onMdiAreaSubWindowActivated); -} - -MainWindow::~MainWindow() -{ - delete ui; -} - -void MainWindow::buildMainPage(SARibbonCategory* mainPage) -{ - SARibbonPannel* mainPannel = new SARibbonPannel("operate"); - mainPannel->addAction(ui->actionadd_window); - mainPage->addPannel(mainPannel); -} - -void MainWindow::on_actionadd_window_triggered() -{ - QMdiSubWindow* sub = ui->mdiArea->addSubWindow(new QTextEdit()); - sub->setAttribute(Qt::WA_DeleteOnClose); - sub->show(); - // 如果你用qwk,你需要安装mdi窗口的最大最小化按钮到frameless框架中,你需要绑定此槽函数 - connect(sub, &QMdiSubWindow::windowStateChanged, this, &MainWindow::onSubWindowStateChanged); -} - -/** - * @brief 窗口激活后对cornerwidget的绑定 - * - * @note mdi窗口的状态变化会导致原来的cornerwidget删除,因此,subWindowActivated和windowStateChanged这两个都要绑定, - * 防止任意一种状态变化没有监测到,例如最大化状态关闭所有窗口,再新建一个窗口,这时候它不会触发windowStateChanged,但会触发subWindowActivated - * @param window - */ -void MainWindow::onMdiAreaSubWindowActivated(QMdiSubWindow* window) -{ -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - if (window) { - SARibbonBar* ribbon = ribbonBar(); - QWidget* cw = ribbon->cornerWidget(Qt::TopRightCorner); - if (cw && cw != m_mdiCornerWidget) { - qDebug() << "set mdi corner widget in frameless|onMdiAreaSubWindowActivated"; - setFramelessHitTestVisible(cw); - m_mdiCornerWidget = cw; - } - } -#endif -} - -/** - * @brief 子窗口变化对cornerwidget的绑定 - * @param oldState - * @param newState - */ -void MainWindow::onSubWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState) -{ -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - if (newState.testFlag(Qt::WindowMaximized)) { - SARibbonBar* ribbon = ribbonBar(); - QWidget* cw = ribbon->cornerWidget(Qt::TopRightCorner); - if (cw && cw != m_mdiCornerWidget) { - qDebug() << "set mdi corner widget in frameless|onSubWindowStateChanged"; - setFramelessHitTestVisible(cw); - m_mdiCornerWidget = cw; - } - } -#endif -} +#include "mdimainwindow.h" +#include "ui_MainWindow.h" +#include +#include +#include "SARibbonBar.h" +#include "SARibbonCategory.h" +#include "SARibbonPannel.h" +MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent), ui(new Ui::MainWindow) +{ + // 注意,你必须在ui文件中删除菜单栏,否则会拿到空的ribbon指针 + ui->setupUi(this); + SARibbonBar* ribbon = ribbonBar(); + SARibbonCategory* mainPage = new SARibbonCategory("Main"); + ribbon->addCategoryPage(mainPage); + buildMainPage(mainPage); + // 如果你用qwk,你需要安装mdi窗口的最大最小化按钮到frameless框架中,你需要绑定此槽函数 + connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &MainWindow::onMdiAreaSubWindowActivated); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::buildMainPage(SARibbonCategory* mainPage) +{ + SARibbonPannel* mainPannel = new SARibbonPannel("operate"); + mainPannel->addAction(ui->actionadd_window); + mainPage->addPannel(mainPannel); +} + +void MainWindow::on_actionadd_window_triggered() +{ + QMdiSubWindow* sub = ui->mdiArea->addSubWindow(new QTextEdit()); + sub->setAttribute(Qt::WA_DeleteOnClose); + sub->show(); + // 如果你用qwk,你需要安装mdi窗口的最大最小化按钮到frameless框架中,你需要绑定此槽函数 + connect(sub, &QMdiSubWindow::windowStateChanged, this, &MainWindow::onSubWindowStateChanged); +} + +/** + * @brief 窗口激活后对cornerwidget的绑定 + * + * @note mdi窗口的状态变化会导致原来的cornerwidget删除,因此,subWindowActivated和windowStateChanged这两个都要绑定, + * 防止任意一种状态变化没有监测到,例如最大化状态关闭所有窗口,再新建一个窗口,这时候它不会触发windowStateChanged,但会触发subWindowActivated + * @param window + */ +void MainWindow::onMdiAreaSubWindowActivated(QMdiSubWindow* window) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + if (window) { + SARibbonBar* ribbon = ribbonBar(); + QWidget* cw = ribbon->cornerWidget(Qt::TopRightCorner); + if (cw && cw != m_mdiCornerWidget) { + qDebug() << "set mdi corner widget in frameless|onMdiAreaSubWindowActivated"; + setFramelessHitTestVisible(cw); + m_mdiCornerWidget = cw; + } + } +#endif +} + +/** + * @brief 子窗口变化对cornerwidget的绑定 + * @param oldState + * @param newState + */ +void MainWindow::onSubWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + if (newState.testFlag(Qt::WindowMaximized)) { + SARibbonBar* ribbon = ribbonBar(); + QWidget* cw = ribbon->cornerWidget(Qt::TopRightCorner); + if (cw && cw != m_mdiCornerWidget) { + qDebug() << "set mdi corner widget in frameless|onSubWindowStateChanged"; + setFramelessHitTestVisible(cw); + m_mdiCornerWidget = cw; + } + } +#endif +} diff --git a/example/MdiAreaWindowExample/mdimainwindow.h b/example/MdiAreaWindowExample/mdimainwindow.h index 1e8e140e..b36a8d0b 100644 --- a/example/MdiAreaWindowExample/mdimainwindow.h +++ b/example/MdiAreaWindowExample/mdimainwindow.h @@ -1,33 +1,33 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include "SARibbonMainWindow.h" -namespace Ui -{ -class MainWindow; -} -class QMdiSubWindow; -class SARibbonCategory; -class MainWindow : public SARibbonMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget* parent = nullptr); - ~MainWindow(); - -private slots: - void on_actionadd_window_triggered(); - void onMdiAreaSubWindowActivated(QMdiSubWindow* window); - void onSubWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState); - -private: - void buildMainPage(SARibbonCategory* mainPage); - -private: - Ui::MainWindow* ui; - QWidget* m_mdiCornerWidget { nullptr }; -}; - -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "SARibbonMainWindow.h" +namespace Ui +{ +class MainWindow; +} +class QMdiSubWindow; +class SARibbonCategory; +class MainWindow : public SARibbonMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private slots: + void on_actionadd_window_triggered(); + void onMdiAreaSubWindowActivated(QMdiSubWindow* window); + void onSubWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState); + +private: + void buildMainPage(SARibbonCategory* mainPage); + +private: + Ui::MainWindow* ui; + QWidget* m_mdiCornerWidget { nullptr }; +}; + +#endif // MAINWINDOW_H diff --git a/example/NormalMenuBarExample/MainWindow.cpp b/example/NormalMenuBarExample/MainWindow.cpp index b93350d3..0ccabba8 100644 --- a/example/NormalMenuBarExample/MainWindow.cpp +++ b/example/NormalMenuBarExample/MainWindow.cpp @@ -1,15 +1,15 @@ -#include "MainWindow.h" -#include "ui_MainWindow.h" - -MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent, false) -{ - if (!isUseRibbon()) { - ui = new Ui::MainWindow; - ui->setupUi(this); - } -} - -MainWindow::~MainWindow() -{ - delete ui; -} +#include "MainWindow.h" +#include "ui_MainWindow.h" + +MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent, false) +{ + if (!isUseRibbon()) { + ui = new Ui::MainWindow; + ui->setupUi(this); + } +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/example/NormalMenuBarExample/MainWindow.h b/example/NormalMenuBarExample/MainWindow.h index 108092ce..d6971e57 100644 --- a/example/NormalMenuBarExample/MainWindow.h +++ b/example/NormalMenuBarExample/MainWindow.h @@ -1,24 +1,24 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include "SARibbonMainWindow.h" - -namespace Ui -{ -class MainWindow; -} - -class MainWindow : public SARibbonMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget* parent = nullptr); - ~MainWindow(); - -private: - Ui::MainWindow* ui; -}; - -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "SARibbonMainWindow.h" + +namespace Ui +{ +class MainWindow; +} + +class MainWindow : public SARibbonMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; + +#endif // MAINWINDOW_H diff --git a/example/NormalMenuBarExample/main.cpp b/example/NormalMenuBarExample/main.cpp index 5a5bc412..926ce167 100644 --- a/example/NormalMenuBarExample/main.cpp +++ b/example/NormalMenuBarExample/main.cpp @@ -1,66 +1,66 @@ -#include "MainWindow.h" -#include -#include -#include - -//重定向qdebug的打印 -void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg); - -/** - * @brief 重定向qdebug的打印 - * @param type - * @param context - * @param msg - */ -void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ - QByteArray localMsg = msg.toLocal8Bit(); - - switch (type) { - case QtDebugMsg: - fprintf(stdout, "[Debug] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - - // case QtInfoMsg: - // fprintf(stdout, "[Info] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, - // context.function); break; - - case QtWarningMsg: - fprintf(stdout, "[Warning] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - - case QtCriticalMsg: - fprintf(stdout, "[Critical] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - - case QtFatalMsg: - fprintf(stdout, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - abort(); - break; - - default: - fprintf(stdout, "[Debug] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - } -#ifndef QT_NO_DEBUG_OUTPUT - fflush(stdout); -#endif -} - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif - qInstallMessageHandler(log_out_put); - QFont f = a.font(); - - f.setFamily("微软雅黑"); - a.setFont(f); - - MainWindow w; - w.show(); - - return (a.exec()); -} +#include "MainWindow.h" +#include +#include +#include + +//重定向qdebug的打印 +void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg); + +/** + * @brief 重定向qdebug的打印 + * @param type + * @param context + * @param msg + */ +void log_out_put(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + + switch (type) { + case QtDebugMsg: + fprintf(stdout, "[Debug] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + + // case QtInfoMsg: + // fprintf(stdout, "[Info] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, + // context.function); break; + + case QtWarningMsg: + fprintf(stdout, "[Warning] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + + case QtCriticalMsg: + fprintf(stdout, "[Critical] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + + case QtFatalMsg: + fprintf(stdout, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + abort(); + break; + + default: + fprintf(stdout, "[Debug] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + } +#ifndef QT_NO_DEBUG_OUTPUT + fflush(stdout); +#endif +} + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + qInstallMessageHandler(log_out_put); + QFont f = a.font(); + + f.setFamily("微软雅黑"); + a.setFont(f); + + MainWindow w; + w.show(); + + return (a.exec()); +} diff --git a/example/StaticExample/MainWindow.cpp b/example/StaticExample/MainWindow.cpp index 0ccf2cc6..4dbc29d0 100644 --- a/example/StaticExample/MainWindow.cpp +++ b/example/StaticExample/MainWindow.cpp @@ -1,43 +1,43 @@ -#include "MainWindow.h" -#include "SARibbon.h" -#include -#include -MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent) -{ - setWindowIcon(QIcon(":/app/icon/SA.svg")); - SARibbonBar* bar = ribbonBar(); - bar->applicationButton()->setText(tr(" File ")); - SARibbonCategory* mainCate = bar->addCategoryPage(tr("Main")); - SARibbonPannel* pannel = mainCate->addPannel(tr("actions")); - pannel->addAction(tr("action1"), QIcon(":/app/icon/action.svg"), QToolButton::InstantPopup); - pannel->addAction(tr("action2"), QIcon(":/app/icon/customize0.svg"), QToolButton::InstantPopup); - pannel->addAction(tr("action3"), QIcon(":/app/icon/save.svg"), QToolButton::InstantPopup); - pannel->addAction(tr("action4"), QIcon(":/app/icon/item.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannel->addAction(tr("action5"), QIcon(":/app/icon/folder-star.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - pannel->addAction(tr("action6"), QIcon(":/app/icon/test1.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); - SARibbonCategory* otherCate = bar->addCategoryPage(tr("Other")); - SARibbonPannel* pannel2 = otherCate->addPannel(tr("other")); - pannel2->addAction(tr("action1"), QIcon(":/app/icon/action.svg"), QToolButton::InstantPopup); - QComboBox* combo = new QComboBox(this); - pannel2->addMediumWidget(combo); - combo->addItem("RibbonThemeWindows7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); - combo->addItem("RibbonThemeOffice2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); - combo->addItem("RibbonThemeOffice2016Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); - combo->addItem("RibbonThemeOffice2021Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); - combo->addItem("RibbonThemeDark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); - combo->addItem("RibbonThemeDark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); - combo->setCurrentIndex(3); - connect(combo, QOverload< int >::of(&QComboBox::currentIndexChanged), [ this, combo ](int index) { - SARibbonTheme th = static_cast< SARibbonTheme >(combo->itemData(index).toInt()); - this->setRibbonTheme(th); - }); - resize(800, 600); - // 在构造函数设置主题会不完全生效 - // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeOffice2016Blue); }); - - // more example see MainWindowExample -} - -MainWindow::~MainWindow() -{ -} +#include "MainWindow.h" +#include "SARibbon.h" +#include +#include +MainWindow::MainWindow(QWidget* parent) : SARibbonMainWindow(parent) +{ + setWindowIcon(QIcon(":/app/icon/SA.svg")); + SARibbonBar* bar = ribbonBar(); + bar->applicationButton()->setText(tr(" File ")); + SARibbonCategory* mainCate = bar->addCategoryPage(tr("Main")); + SARibbonPannel* pannel = mainCate->addPannel(tr("actions")); + pannel->addAction(tr("action1"), QIcon(":/app/icon/action.svg"), QToolButton::InstantPopup); + pannel->addAction(tr("action2"), QIcon(":/app/icon/customize0.svg"), QToolButton::InstantPopup); + pannel->addAction(tr("action3"), QIcon(":/app/icon/save.svg"), QToolButton::InstantPopup); + pannel->addAction(tr("action4"), QIcon(":/app/icon/item.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannel->addAction(tr("action5"), QIcon(":/app/icon/folder-star.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + pannel->addAction(tr("action6"), QIcon(":/app/icon/test1.svg"), QToolButton::InstantPopup, SARibbonPannelItem::Small); + SARibbonCategory* otherCate = bar->addCategoryPage(tr("Other")); + SARibbonPannel* pannel2 = otherCate->addPannel(tr("other")); + pannel2->addAction(tr("action1"), QIcon(":/app/icon/action.svg"), QToolButton::InstantPopup); + QComboBox* combo = new QComboBox(this); + pannel2->addMediumWidget(combo); + combo->addItem("RibbonThemeWindows7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); + combo->addItem("RibbonThemeOffice2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); + combo->addItem("RibbonThemeOffice2016Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); + combo->addItem("RibbonThemeOffice2021Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); + combo->addItem("RibbonThemeDark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); + combo->addItem("RibbonThemeDark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); + combo->setCurrentIndex(3); + connect(combo, QOverload< int >::of(&QComboBox::currentIndexChanged), [ this, combo ](int index) { + SARibbonTheme th = static_cast< SARibbonTheme >(combo->itemData(index).toInt()); + this->setRibbonTheme(th); + }); + resize(800, 600); + // 在构造函数设置主题会不完全生效 + // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeOffice2016Blue); }); + + // more example see MainWindowExample +} + +MainWindow::~MainWindow() +{ +} diff --git a/example/StaticExample/MainWindow.h b/example/StaticExample/MainWindow.h index e4cd6d5e..8c63e139 100644 --- a/example/StaticExample/MainWindow.h +++ b/example/StaticExample/MainWindow.h @@ -1,15 +1,15 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include "SARibbon.h" - -class MainWindow : public SARibbonMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget* parent = nullptr); - ~MainWindow(); -}; -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "SARibbon.h" + +class MainWindow : public SARibbonMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); +}; +#endif // MAINWINDOW_H diff --git a/example/StaticExample/main.cpp b/example/StaticExample/main.cpp index f99c6b61..6b177dfd 100644 --- a/example/StaticExample/main.cpp +++ b/example/StaticExample/main.cpp @@ -1,14 +1,14 @@ -#include "MainWindow.h" - -#include - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); - QFont f = a.font(); - f.setFamily(u8"微软雅黑"); - a.setFont(f); - MainWindow w; - w.show(); - return a.exec(); -} +#include "MainWindow.h" + +#include + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + QFont f = a.font(); + f.setFamily(u8"微软雅黑"); + a.setFont(f); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/example/WidgetWithRibbon/InnerWidget.cpp b/example/WidgetWithRibbon/InnerWidget.cpp index 85714543..9950cb8e 100644 --- a/example/WidgetWithRibbon/InnerWidget.cpp +++ b/example/WidgetWithRibbon/InnerWidget.cpp @@ -1,17 +1,17 @@ -#include "InnerWidget.h" -#include "ui_InnerWidget.h" - -InnerWidget::InnerWidget(QWidget* parent) : QWidget(parent), ui(new Ui::InnerWidget) -{ - ui->setupUi(this); -} - -InnerWidget::~InnerWidget() -{ - delete ui; -} - -void InnerWidget::appendText(const QString& t) -{ - ui->plainTextEdit->appendPlainText(t); -} +#include "InnerWidget.h" +#include "ui_InnerWidget.h" + +InnerWidget::InnerWidget(QWidget* parent) : QWidget(parent), ui(new Ui::InnerWidget) +{ + ui->setupUi(this); +} + +InnerWidget::~InnerWidget() +{ + delete ui; +} + +void InnerWidget::appendText(const QString& t) +{ + ui->plainTextEdit->appendPlainText(t); +} diff --git a/example/WidgetWithRibbon/InnerWidget.h b/example/WidgetWithRibbon/InnerWidget.h index be36050b..34aef980 100644 --- a/example/WidgetWithRibbon/InnerWidget.h +++ b/example/WidgetWithRibbon/InnerWidget.h @@ -1,25 +1,25 @@ -#ifndef INNERWIDGET_H -#define INNERWIDGET_H - -#include - -namespace Ui -{ -class InnerWidget; -} - -class InnerWidget : public QWidget -{ - Q_OBJECT - -public: - explicit InnerWidget(QWidget* parent = nullptr); - ~InnerWidget(); -public Q_SLOTS: - void appendText(const QString& t); - -private: - Ui::InnerWidget* ui; -}; - -#endif // INNERWIDGET_H +#ifndef INNERWIDGET_H +#define INNERWIDGET_H + +#include + +namespace Ui +{ +class InnerWidget; +} + +class InnerWidget : public QWidget +{ + Q_OBJECT + +public: + explicit InnerWidget(QWidget* parent = nullptr); + ~InnerWidget(); +public Q_SLOTS: + void appendText(const QString& t); + +private: + Ui::InnerWidget* ui; +}; + +#endif // INNERWIDGET_H diff --git a/example/WidgetWithRibbon/MainWidget.cpp b/example/WidgetWithRibbon/MainWidget.cpp index 66e6edbf..bc403497 100644 --- a/example/WidgetWithRibbon/MainWidget.cpp +++ b/example/WidgetWithRibbon/MainWidget.cpp @@ -1,15 +1,15 @@ -#include "MainWidget.h" -#include "ui_MainWidget.h" -#include "RibbonWidget.h" -MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget) -{ - ui->setupUi(this); - ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 1"); - ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 2"); - ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 3"); -} - -MainWidget::~MainWidget() -{ - delete ui; -} +#include "MainWidget.h" +#include "ui_MainWidget.h" +#include "RibbonWidget.h" +MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget) +{ + ui->setupUi(this); + ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 1"); + ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 2"); + ui->tabWidget->addTab(new RibbonWidget(), "ribbon tab 3"); +} + +MainWidget::~MainWidget() +{ + delete ui; +} diff --git a/example/WidgetWithRibbon/MainWidget.h b/example/WidgetWithRibbon/MainWidget.h index ccc7be88..56891ac3 100644 --- a/example/WidgetWithRibbon/MainWidget.h +++ b/example/WidgetWithRibbon/MainWidget.h @@ -1,22 +1,22 @@ -#ifndef MAINWIDGET_H -#define MAINWIDGET_H - -#include - -namespace Ui { -class MainWidget; -} - -class MainWidget : public QWidget -{ - Q_OBJECT - -public: - explicit MainWidget(QWidget *parent = nullptr); - ~MainWidget(); - -private: - Ui::MainWidget *ui; -}; - -#endif // MAINWIDGET_H +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include + +namespace Ui { +class MainWidget; +} + +class MainWidget : public QWidget +{ + Q_OBJECT + +public: + explicit MainWidget(QWidget *parent = nullptr); + ~MainWidget(); + +private: + Ui::MainWidget *ui; +}; + +#endif // MAINWIDGET_H diff --git a/example/WidgetWithRibbon/RibbonWidget.cpp b/example/WidgetWithRibbon/RibbonWidget.cpp index 52ed6c78..8d09fae3 100644 --- a/example/WidgetWithRibbon/RibbonWidget.cpp +++ b/example/WidgetWithRibbon/RibbonWidget.cpp @@ -1,85 +1,85 @@ -#include "RibbonWidget.h" -#include "InnerWidget.h" -#include "SARibbonBar.h" -#include "SARibbonCategory.h" -#include "SARibbonPannel.h" -#include "SARibbonMainWindow.h" -#include "SARibbonQuickAccessBar.h" -RibbonWidget::RibbonWidget(QWidget* parent) : SARibbonWidget(parent) -{ - // 直接创建SARibbonBar - SARibbonBar* ribbonbar = ribbonBar(); - // QWidget模式下,没有必要再显示标题 - ribbonbar->setTitleVisible(false); - // QWidget模式下,直接使用紧凑模式效果更好 - ribbonbar->setRibbonStyle(SARibbonBar::RibbonStyleCompactThreeRow); - // 取消applicationbutton - ribbonbar->setApplicationButton(nullptr); - - buildRibbon(ribbonbar); - // - setWidget(new InnerWidget()); -} - -RibbonWidget::~RibbonWidget() -{ -} - -void RibbonWidget::buildRibbon(SARibbonBar* bar) -{ - SARibbonCategory* page1 = new SARibbonCategory(); - page1->setCategoryName("page1"); - SARibbonPannel* pannel1 = new SARibbonPannel("pannel1", page1); - page1->addPannel(pannel1); - QAction* act = createAction(" save ", ":/icon/icon/save.svg"); - act->setIconText(" save "); - pannel1->addLargeAction(act); - pannel1->addLargeAction(createAction("open", ":/icon/icon/folder-star.svg")); - pannel1->addSmallAction(createAction("action1", ":/icon/icon/action.svg")); - pannel1->addSmallAction(createAction("action2", ":/icon/icon/action2.svg")); - SARibbonPannel* pannel2 = new SARibbonPannel("pannel2", page1); - page1->addPannel(pannel2); - pannel2->addLargeAction(createAction("setting", ":/icon/icon/customize0.svg")); - pannel2->addLargeAction(createAction("windowsflag", ":/icon/icon/windowsflag-normal.svg")); - bar->addCategoryPage(page1); - // 加入主题 - mComboTheme = new QComboBox(); - mComboTheme->addItem("Theme Win7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); - mComboTheme->addItem("Theme Office2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); - mComboTheme->addItem("Theme Office2016 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); - mComboTheme->addItem("Theme Office2021 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); - mComboTheme->addItem("Theme Dark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); - mComboTheme->addItem("Theme Dark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); - mComboTheme->setCurrentIndex(mComboTheme->findData(static_cast< int >(SARibbonTheme::RibbonThemeOffice2013))); - connect(mComboTheme, - QOverload< int >::of(&QComboBox::currentIndexChanged), - this, - &RibbonWidget::onRibbonThemeComboBoxCurrentIndexChanged); - pannel2->addSeparator(); - pannel2->addSmallWidget(mComboTheme); - - SARibbonQuickAccessBar* qbar = bar->quickAccessBar(); - qbar->addAction(createAction("undo", ":/icon/icon/undo.svg")); - qbar->addAction(createAction("redo", ":/icon/icon/redo.svg")); -} - -QAction* RibbonWidget::createAction(const QString& text, const QString& iconurl) -{ - QAction* act = new QAction(this); - act->setText(text); - act->setIcon(QIcon(iconurl)); - act->setObjectName(text); - connect(act, &QAction::triggered, this, [ this, act ]() { - InnerWidget* w = qobject_cast< InnerWidget* >(widget()); - if (w) { - w->appendText(QString("action(%1) triggered").arg(act->text())); - } - }); - return act; -} - -void RibbonWidget::onRibbonThemeComboBoxCurrentIndexChanged(int index) -{ - SARibbonTheme t = static_cast< SARibbonTheme >(mComboTheme->itemData(index).toInt()); - setRibbonTheme(t); -} +#include "RibbonWidget.h" +#include "InnerWidget.h" +#include "SARibbonBar.h" +#include "SARibbonCategory.h" +#include "SARibbonPannel.h" +#include "SARibbonMainWindow.h" +#include "SARibbonQuickAccessBar.h" +RibbonWidget::RibbonWidget(QWidget* parent) : SARibbonWidget(parent) +{ + // 直接创建SARibbonBar + SARibbonBar* ribbonbar = ribbonBar(); + // QWidget模式下,没有必要再显示标题 + ribbonbar->setTitleVisible(false); + // QWidget模式下,直接使用紧凑模式效果更好 + ribbonbar->setRibbonStyle(SARibbonBar::RibbonStyleCompactThreeRow); + // 取消applicationbutton + ribbonbar->setApplicationButton(nullptr); + + buildRibbon(ribbonbar); + // + setWidget(new InnerWidget()); +} + +RibbonWidget::~RibbonWidget() +{ +} + +void RibbonWidget::buildRibbon(SARibbonBar* bar) +{ + SARibbonCategory* page1 = new SARibbonCategory(); + page1->setCategoryName("page1"); + SARibbonPannel* pannel1 = new SARibbonPannel("pannel1", page1); + page1->addPannel(pannel1); + QAction* act = createAction(" save ", ":/icon/icon/save.svg"); + act->setIconText(" save "); + pannel1->addLargeAction(act); + pannel1->addLargeAction(createAction("open", ":/icon/icon/folder-star.svg")); + pannel1->addSmallAction(createAction("action1", ":/icon/icon/action.svg")); + pannel1->addSmallAction(createAction("action2", ":/icon/icon/action2.svg")); + SARibbonPannel* pannel2 = new SARibbonPannel("pannel2", page1); + page1->addPannel(pannel2); + pannel2->addLargeAction(createAction("setting", ":/icon/icon/customize0.svg")); + pannel2->addLargeAction(createAction("windowsflag", ":/icon/icon/windowsflag-normal.svg")); + bar->addCategoryPage(page1); + // 加入主题 + mComboTheme = new QComboBox(); + mComboTheme->addItem("Theme Win7", static_cast< int >(SARibbonTheme::RibbonThemeWindows7)); + mComboTheme->addItem("Theme Office2013", static_cast< int >(SARibbonTheme::RibbonThemeOffice2013)); + mComboTheme->addItem("Theme Office2016 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2016Blue)); + mComboTheme->addItem("Theme Office2021 Blue", static_cast< int >(SARibbonTheme::RibbonThemeOffice2021Blue)); + mComboTheme->addItem("Theme Dark", static_cast< int >(SARibbonTheme::RibbonThemeDark)); + mComboTheme->addItem("Theme Dark2", static_cast< int >(SARibbonTheme::RibbonThemeDark2)); + mComboTheme->setCurrentIndex(mComboTheme->findData(static_cast< int >(SARibbonTheme::RibbonThemeOffice2013))); + connect(mComboTheme, + QOverload< int >::of(&QComboBox::currentIndexChanged), + this, + &RibbonWidget::onRibbonThemeComboBoxCurrentIndexChanged); + pannel2->addSeparator(); + pannel2->addSmallWidget(mComboTheme); + + SARibbonQuickAccessBar* qbar = bar->quickAccessBar(); + qbar->addAction(createAction("undo", ":/icon/icon/undo.svg")); + qbar->addAction(createAction("redo", ":/icon/icon/redo.svg")); +} + +QAction* RibbonWidget::createAction(const QString& text, const QString& iconurl) +{ + QAction* act = new QAction(this); + act->setText(text); + act->setIcon(QIcon(iconurl)); + act->setObjectName(text); + connect(act, &QAction::triggered, this, [ this, act ]() { + InnerWidget* w = qobject_cast< InnerWidget* >(widget()); + if (w) { + w->appendText(QString("action(%1) triggered").arg(act->text())); + } + }); + return act; +} + +void RibbonWidget::onRibbonThemeComboBoxCurrentIndexChanged(int index) +{ + SARibbonTheme t = static_cast< SARibbonTheme >(mComboTheme->itemData(index).toInt()); + setRibbonTheme(t); +} diff --git a/example/WidgetWithRibbon/RibbonWidget.h b/example/WidgetWithRibbon/RibbonWidget.h index 87d13870..38517461 100644 --- a/example/WidgetWithRibbon/RibbonWidget.h +++ b/example/WidgetWithRibbon/RibbonWidget.h @@ -1,25 +1,25 @@ -#ifndef RIBBONWIDGET_H -#define RIBBONWIDGET_H - -#include "SARibbonWidget.h" -#include - -class SARibbonBar; -class RibbonWidget : public SARibbonWidget -{ - Q_OBJECT - -public: - RibbonWidget(QWidget* parent = nullptr); - ~RibbonWidget(); - -private: - void buildRibbon(SARibbonBar* bar); - QAction* createAction(const QString& text, const QString& iconurl); -private Q_SLOTS: - void onRibbonThemeComboBoxCurrentIndexChanged(int index); - -private: - QComboBox* mComboTheme { nullptr }; -}; -#endif // WIDGET_H +#ifndef RIBBONWIDGET_H +#define RIBBONWIDGET_H + +#include "SARibbonWidget.h" +#include + +class SARibbonBar; +class RibbonWidget : public SARibbonWidget +{ + Q_OBJECT + +public: + RibbonWidget(QWidget* parent = nullptr); + ~RibbonWidget(); + +private: + void buildRibbon(SARibbonBar* bar); + QAction* createAction(const QString& text, const QString& iconurl); +private Q_SLOTS: + void onRibbonThemeComboBoxCurrentIndexChanged(int index); + +private: + QComboBox* mComboTheme { nullptr }; +}; +#endif // WIDGET_H diff --git a/example/WidgetWithRibbon/main.cpp b/example/WidgetWithRibbon/main.cpp index 4eccfc31..e571f968 100644 --- a/example/WidgetWithRibbon/main.cpp +++ b/example/WidgetWithRibbon/main.cpp @@ -1,11 +1,11 @@ -#include "MainWidget.h" - -#include - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); - MainWidget w; - w.show(); - return a.exec(); -} +#include "MainWidget.h" + +#include + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWidget w; + w.show(); + return a.exec(); +} diff --git a/src/SARibbon.cpp b/src/SARibbon.cpp index 2e89afc5..9b9bf7f8 100644 --- a/src/SARibbon.cpp +++ b/src/SARibbon.cpp @@ -1,16222 +1,16222 @@ -// 定义此宏,将SA_RIBBON_EXPORT定义为空 -#ifndef SA_RIBBON_BAR_NO_EXPORT -#define SA_RIBBON_BAR_NO_EXPORT -#endif - -// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 -#ifndef SA_COLOR_WIDGETS_NO_DLL -#define SA_COLOR_WIDGETS_NO_DLL -#endif - -/*** Start of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ -// This file provides an extra level of indirection for the @remap in the template -#include "SARibbon.h" - -/*** End of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ - -// disable warnings about unsafe standard library calls -#ifdef _MSC_VER -#pragma push_macro("_CRT_SECURE_NO_WARNINGS") -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif -#pragma warning(push) -#pragma warning(disable : 4996) // deprecated POSIX names -#endif - -/*** Start of inlined file: SAColorMenu.cpp ***/ -#include -#include -#include -#include -#include -#include - -class SAColorMenu::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu) -public: - PrivateData(SAColorMenu* p); - QColor getColorByDialog(); - void recordCustomColor(const QColor& c); - QWidgetAction* addWidget(QWidget* w); - // 创建一个无颜色的icon - QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32)); - -public: - QLabel* mTitleLabel { nullptr }; ///< 主题颜色标题 - QWidgetAction* mTitleLabelAction { nullptr }; - SAColorPaletteGridWidget* mThemeColorsWidget { nullptr }; ///< 主题颜色 - QWidgetAction* mThemeColorPaletteAction { nullptr }; ///< ThemeColorsPalette对应的action - QAction* mCustomColorAction { nullptr }; ///< 自定义颜色action - SAColorGridWidget* mCustomColorsWidget { nullptr }; ///< 自定义颜色记录 - QWidgetAction* mCustomColorsWidgetAction { nullptr }; ///< 自定义颜色窗口对应的action - QAction* mNoneColorAction { nullptr }; ///< 无颜色action - QList< QColor > mCustomColors; - int mMaxCustomColorSize { 10 }; ///< 记录最多的自定义颜色数量 - QScopedPointer< QColorDialog > mColorDlg { nullptr }; ///< 颜色对话框 -}; - -SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p) -{ -} - -QColor SAColorMenu::PrivateData::getColorByDialog() -{ - if (nullptr == mColorDlg) { - mColorDlg.reset(new QColorDialog()); - } - if (QDialog::Accepted == mColorDlg->exec()) { - return mColorDlg->currentColor(); - } - return QColor(); -} - -void SAColorMenu::PrivateData::recordCustomColor(const QColor& c) -{ - if (mCustomColors.size() < mMaxCustomColorSize) { - mCustomColors.push_back(c); - } else { - // 超过数量,就左移动 - for (int i = 1; i < mCustomColors.size(); ++i) { - mCustomColors[ i - 1 ] = mCustomColors[ i ]; - } - mCustomColors.back() = c; - } -} - -QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w) -{ - QWidgetAction* wa = new QWidgetAction(q_ptr); - wa->setDefaultWidget(w); - q_ptr->addAction(wa); - return wa; -} - -QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize) -{ - QPixmap pixmap(baseSize); - pixmap.fill(Qt::transparent); - QPainter p(&pixmap); - SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1)); - return QIcon(pixmap); -} -//=================================================== -// SAColorMenu -//=================================================== - -SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this)) -{ - init(SA::getStandardColorList()); -} - -SAColorMenu::SAColorMenu(const QString& title, QWidget* parent) - : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this)) -{ - init(SA::getStandardColorList()); -} - -SAColorMenu::~SAColorMenu() -{ -} - -/** - * @brief 快速绑定ColorToolButton - * @param btn - */ -void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn) -{ - if (!btn) { - return; - } - if (btn->menu() != this) { - btn->setMenu(this); - } - connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor); -} - -/** - * @brief ThemeColorsPalette对应的action - * @return - */ -QWidgetAction* SAColorMenu::themeColorsPaletteAction() const -{ - return d_ptr->mThemeColorPaletteAction; -} - -/** - * @brief CustomColorsWidget对应的action - * @return - */ -QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const -{ - return d_ptr->mCustomColorsWidgetAction; -} - -/** - * @brief 自定义颜色action - * @return - */ -QAction* SAColorMenu::customColorAction() const -{ - return d_ptr->mCustomColorAction; -} - -/** - * @brief 获取ThemeColorsPalette - * @return - */ -SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const -{ - return d_ptr->mThemeColorsWidget; -} - -/** - * @brief 获取自定义颜色grid - * @return - */ -SAColorGridWidget* SAColorMenu::customColorsWidget() const -{ - return d_ptr->mCustomColorsWidget; -} - -/** - * @brief 建立没有颜色的action,可以选择无颜色 - * - * 无颜色选中会发射selectedColor(QColor()) - * @param on - */ -void SAColorMenu::enableNoneColorAction(bool on) -{ - // 无颜色默认是在自定义颜色的上方 - if (on) { - if (d_ptr->mNoneColorAction) { - if (actions().contains(d_ptr->mNoneColorAction)) { - // 已经包含了NoneColorAction,退出 - return; - } else { - insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); - } - } else { - QIcon ic = d_ptr->createNoneColorIcon(); - d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this); - connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered); - insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); - } - } else { - removeAction(d_ptr->mNoneColorAction); - // remove后暂时不删除action - } -} - -/** - * @brief 获取None Color Action - * - * @note 注意,enableNoneColorAction(true),之后才不是nullptr - * @return 如果没有建立NoneColorAction,会返回nullptr - */ -QAction* SAColorMenu::noneColorAction() const -{ - return d_ptr->mNoneColorAction; -} - -/** - * @brief 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 - * @param c - */ -void SAColorMenu::emitSelectedColor(const QColor& c) -{ - Q_EMIT selectedColor(c); - hide(); -} - -void SAColorMenu::init(const QList< QColor >& themeCls) -{ - d_ptr->mTitleLabel = new QLabel(this); - d_ptr->mTitleLabel->setText(tr("Theme Colors")); - d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel); - - d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this); - d_ptr->mThemeColorsWidget->setColorCheckable(false); - d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget); - - d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this); // cn:自定义颜色 - addAction(d_ptr->mCustomColorAction); - - QSize clrSize = d_ptr->mThemeColorsWidget->colorIconSize(); - d_ptr->mCustomColorsWidget = new SAColorGridWidget(this); - d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height()); - d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight(); - d_ptr->mCustomColorsWidget->setColorIconSize(clrSize); - d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize); - d_ptr->mCustomColorsWidget->setColorCheckable(false); - d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget); - - connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered); - connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); - connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); -} - -/** - * @brief 自定义颜色 - * @param on - */ -void SAColorMenu::onCustomColorActionTriggered(bool on) -{ - Q_UNUSED(on); - QColor c = d_ptr->getColorByDialog(); - if (c.isValid()) { - d_ptr->recordCustomColor(c); - d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors); - updateGeometry(); - emitSelectedColor(c); - } -} - -/** - * @brief 无颜色 - * @param on - */ -void SAColorMenu::onNoneColorActionTriggered(bool on) -{ - Q_UNUSED(on); - emitSelectedColor(QColor()); -} - -/*** End of inlined file: SAColorMenu.cpp ***/ - -/*** Start of inlined file: SAColorGridWidget.cpp ***/ -#include -#include -#include - -class SAColorGridWidget::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget) -public: - PrivateData(SAColorGridWidget* p); - // 获取ColorToolButton - SAColorToolButton* getColorToolButtonAt(int index); - SAColorToolButton* getColorToolButtonAt(int r, int c); - SAColorToolButton* getCheckedButton() const; - void updateGridColor(bool isRemoveSpacer = false); - void updateGridColorSize(); - void updateGridColorCheckable(); - void iterationColorBtns(SAColorGridWidget::FunColorBtn fn); - void removeAt(int r, int c); - void setColorAt(const QColor& clr, int r, int c); - bool isSpacer(int r, int c) const; - -public: - QList< QColor > mColors; - QGridLayout* mGridLayout { nullptr }; - QButtonGroup* mButtonGroup { nullptr }; - QSize mIconSize { 16, 16 }; - int mColumnCount { 8 }; ///< 列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - bool mColorCheckable; ///< 设置颜色是否是checkable - bool mHorizontalSpacerToRight { false }; ///< 最右边是否有弹簧 -}; - -SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p) -{ - mGridLayout = new QGridLayout(p); - p->setLayout(mGridLayout); - mGridLayout->setSpacing(0); - mGridLayout->setContentsMargins(1, 1, 1, 1); - mButtonGroup = new QButtonGroup(p); - mButtonGroup->setExclusive(true); - p->setMinimumHeight(mIconSize.height()); - p->setMinimumWidth(mIconSize.width()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index) -{ - QLayoutItem* item = mGridLayout->itemAt(index); - if (nullptr == item) { - return nullptr; - } - return qobject_cast< SAColorToolButton* >(item->widget()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (nullptr == item) { - return nullptr; - } - return qobject_cast< SAColorToolButton* >(item->widget()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const -{ - return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton()); -} - -/** - * @brief 根据mColors更新布局 - */ -void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer) -{ - int row = 1; - int col = mColumnCount; - if (col <= 0) { - col = mColors.size(); - } else { - row = std::ceil(mColors.size() / (float)col); - } - int index = 0; - for (int r = 0; r < row; ++r) { - for (int c = 0; c < col; ++c) { - if (index < mColors.size()) { - setColorAt(mColors[ index ], r, c); - ++index; - } else { - removeAt(r, c); - } - } - } - // 清除多余单元格 - int nowGridRow = mGridLayout->rowCount(); - int nowGridCol = mGridLayout->columnCount(); - if (nowGridRow > row) { - // 多余的清除 - for (int r = row; r < nowGridRow; ++r) { - for (int c = 0; c < nowGridCol; ++c) { - removeAt(r, c); - } - } - } - if (nowGridCol > col) { - // 多余的列清除 - for (int r = 0; r < row; ++r) { - for (int c = col; c < nowGridCol; ++c) { - if (isRemoveSpacer) { - removeAt(r, c); - } else { - if (!isSpacer(r, c)) { - removeAt(r, c); - } - } - } - } - } -} - -/** - * @brief 更新colorsize - */ -void SAColorGridWidget::PrivateData::updateGridColorSize() -{ - QSize s = mIconSize; - iterationColorBtns([ s ](SAColorToolButton* btn) { - if (btn) { - btn->setIconSize(s); - } - }); -} - -void SAColorGridWidget::PrivateData::updateGridColorCheckable() -{ - bool v = mColorCheckable; - iterationColorBtns([ v ](SAColorToolButton* btn) { - if (btn) { - btn->setCheckable(v); - } - }); -} - -/** - * @brief 遍历所有的button - * @param fn - */ -void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn) -{ - int cnt = mGridLayout->count(); - for (int i = 0; i < cnt; ++i) { - SAColorToolButton* tl = getColorToolButtonAt(i); - fn(tl); - } -} - -/** - * @brief 删除网格窗口 - * @param r - * @param c - */ -void SAColorGridWidget::PrivateData::removeAt(int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - QWidget* w = item->widget(); - mGridLayout->removeItem(item); - delete item; - if (w) { - w->deleteLater(); - } - } -} - -void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget()); - if (tl) { - tl->setColor(clr); - } - } else { - SAColorToolButton* tl = new SAColorToolButton(SAColorToolButton::NoColorMenu, q_ptr); - tl->setToolButtonStyle(Qt::ToolButtonIconOnly); - tl->setIconSize(mIconSize); - tl->setMargins(QMargins(4, 4, 4, 4)); - tl->setColor(clr); - tl->setCheckable(mColorCheckable); - tl->setAutoRaise(true); - mButtonGroup->addButton(tl, r + c); - mGridLayout->addWidget(tl, r, c); - } -} - -bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) { - return true; - } - } - return false; -} - -//============================================================== -// SAColorGridWidget -//============================================================== - -SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this)) -{ - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked), - this, - &SAColorGridWidget::onButtonClicked); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed), - this, - &SAColorGridWidget::onButtonPressed); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased), - this, - &SAColorGridWidget::onButtonReleased); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled), - this, - &SAColorGridWidget::onButtonToggled); -} - -SAColorGridWidget::~SAColorGridWidget() -{ -} - -/** - * @brief 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - * @param c - */ -void SAColorGridWidget::setColumnCount(int c) -{ - d_ptr->mColumnCount = c; - d_ptr->updateGridColor(true); - if (d_ptr->mHorizontalSpacerToRight) { - setHorizontalSpacerToRight(); - } - updateGeometry(); -} - -/** - * @brief 设置颜色列表 - * @param c - */ -void SAColorGridWidget::setColorList(const QList< QColor >& cls) -{ - d_ptr->mColors = cls; - d_ptr->updateGridColor(); - updateGeometry(); -} - -/** - * @brief 获取颜色列表 - * @return - */ -QList< QColor > SAColorGridWidget::getColorList() const -{ - return d_ptr->mColors; -} - -/** - * @brief 获取间隔 - * @return - */ -int SAColorGridWidget::spacing() const -{ - return d_ptr->mGridLayout->spacing(); -} - -/** - * @brief 设置间隔 - * @param v - */ -void SAColorGridWidget::setSpacing(int v) -{ - d_ptr->mGridLayout->setSpacing(v); -} - -/** - * @brief 获取颜色的数量 - * @return - */ -int SAColorGridWidget::colorCount() const -{ - return d_ptr->mColors.size(); -} - -/** - * @brief 设置图标 size - * @return - */ -void SAColorGridWidget::setColorIconSize(const QSize& s) -{ - d_ptr->mIconSize = s; - setMinimumHeight(s.height()); - setMinimumWidth(s.width()); - d_ptr->updateGridColorSize(); -} - -/** - * @brief 获取图标 size - * @return - */ -QSize SAColorGridWidget::colorIconSize() const -{ - return d_ptr->mIconSize; -} - -/** - * @brief 设置颜色是否是checkable - * - * checkable的颜色按钮是可checked的 - * @param on - */ -void SAColorGridWidget::setColorCheckable(bool on) -{ - d_ptr->mColorCheckable = on; - d_ptr->updateGridColorCheckable(); -} - -/** - * @brief 颜色是否是checkable - * @return - */ -bool SAColorGridWidget::isColorCheckable() const -{ - return d_ptr->mColorCheckable; -} - -/** - * @brief 获取当前选中的颜色 - * @return - */ -QColor SAColorGridWidget::currentCheckedColor() const -{ - QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton(); - if (nullptr == btn) { - return QColor(); - } - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (nullptr == t) { - return QColor(); - } - return t->color(); -} - -/** - * @brief 获取index对应的colorbutton - * @param index - * @return 如果没有返回nullptr - */ -SAColorToolButton* SAColorGridWidget::colorButton(int index) const -{ - return d_ptr->getColorToolButtonAt(index); -} - -/** - * @brief 等同GridLayout的VerticalSpacing属性 - * @param v - */ -void SAColorGridWidget::setVerticalSpacing(int v) -{ - d_ptr->mGridLayout->setVerticalSpacing(v); -} -/** - * @brief 等同GridLayout的VerticalSpacing属性 - * @return - */ -int SAColorGridWidget::verticalSpacing() const -{ - return d_ptr->mGridLayout->verticalSpacing(); -} -/** - * @brief 等同GridLayout的HorizontalSpacing属性 - * @param v - */ -void SAColorGridWidget::setHorizontalSpacing(int v) -{ - d_ptr->mGridLayout->setHorizontalSpacing(v); -} -/** - * @brief 等同GridLayout的HorizontalSpacing属性 - * @return - */ -int SAColorGridWidget::horizontalSpacing() const -{ - return d_ptr->mGridLayout->horizontalSpacing(); -} - -/** - * @brief 清除选中状态,这时没有颜色是选中的 - */ -void SAColorGridWidget::clearCheckedState() -{ - if (d_ptr->mButtonGroup->exclusive()) { - SAColorToolButton* btn = d_ptr->getCheckedButton(); - if (btn) { - d_ptr->mButtonGroup->setExclusive(false); - btn->setChecked(false); - d_ptr->mButtonGroup->setExclusive(true); - } - } else { - d_ptr->iterationColorBtns([](SAColorToolButton* btn) { - if (btn->isChecked()) { - btn->setChecked(false); - } - }); - } -} - -void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn) -{ - d_ptr->iterationColorBtns(fn); -} - -void SAColorGridWidget::setRowMinimumHeight(int row, int minSize) -{ - d_ptr->mGridLayout->setRowMinimumHeight(row, minSize); -} - -void SAColorGridWidget::setHorizontalSpacerToRight(bool on) -{ - if (on) { - QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1); - } else { - d_ptr->removeAt(0, d_ptr->mColumnCount); - } - d_ptr->mHorizontalSpacerToRight = on; -} - -void SAColorGridWidget::onButtonClicked(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorClicked(t->color()); - } -} - -void SAColorGridWidget::onButtonPressed(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorPressed(t->color()); - } -} - -void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorToggled(t->color(), on); - } -} - -QSize SAColorGridWidget::sizeHint() const -{ - return d_ptr->mGridLayout->sizeHint(); - // int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing(); - // int h = d_ptr->mIconSize.height(); - // if (d_ptr->mColumnCount > 0) { - // h *= d_ptr->mColumnCount; - // if (!d_ptr->mColors.empty()) { - // int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount)); - // if (r > 0) { - // w *= r; - // } - // } - // } - // return QSize(w, h); -} - -void SAColorGridWidget::onButtonReleased(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorReleased(t->color()); - } -} - -namespace SA -{ - -QList< QColor > getStandardColorList() -{ - static QList< QColor > s_standardColorList({ QColor(192, 0, 0), - QColor(255, 0, 0), - QColor(255, 192, 0), - QColor(255, 255, 0), - QColor(146, 208, 80), - QColor(0, 176, 80), - QColor(0, 176, 240), - QColor(0, 112, 192), - QColor(0, 32, 96), - QColor(112, 48, 160) }); - return s_standardColorList; -} -} - -/*** End of inlined file: SAColorGridWidget.cpp ***/ - -/*** Start of inlined file: SAColorPaletteGridWidget.cpp ***/ -// Qt -#include -#include -#include -#include -// SA - -class SAColorPaletteGridWidget::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget) -public: - PrivateData(SAColorPaletteGridWidget* p); - // 生成color palette - QList< QColor > makeColorPalette(const QList< QColor >& clrList) const; - -public: - QList< int > mFactor { 180, 160, 140, 75, 50 }; ///< palette的比例因子,将调用QColor的lighter函数执行 - QVBoxLayout* mLayout { nullptr }; ///< 垂直布局 - SAColorGridWidget* mMainColorList { nullptr }; ///< 这个用于显示标准颜色 - SAColorGridWidget* mPaletteColorGrid { nullptr }; ///< 这个用于生成3行亮色,2行暗色的palette -}; - -SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p) -{ - mLayout = new QVBoxLayout(p); - p->setLayout(mLayout); - mMainColorList = new SAColorGridWidget(p); - mPaletteColorGrid = new SAColorGridWidget(p); - mLayout->addWidget(mMainColorList); - mLayout->addWidget(mPaletteColorGrid); - mLayout->setContentsMargins(1, 1, 1, 1); - mLayout->setSpacing(8); - QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - mMainColorList->setSizePolicy(sizePolicy); - mPaletteColorGrid->setSizePolicy(sizePolicy); - mMainColorList->setColumnCount(0); - mPaletteColorGrid->setVerticalSpacing(0); -} - -QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const -{ - QList< QColor > res; - for (int f : qAsConst(mFactor)) { - for (const QColor& c : qAsConst(clrList)) { - res.append(c.lighter(f)); - } - } - return res; -} -//============================================================== -// name -//============================================================== - -/** - * @brief 使用默认的标准颜色生成一个color palette - * @param par - */ -SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this)) -{ - init(); - setColorList(SA::getStandardColorList()); -} - -/** - * @brief 根据指定的颜色生成一个color palette - * @param cls - * @param par - */ -SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par) - : QWidget(par), d_ptr(new PrivateData(this)) -{ - init(); - setColorList(cls); -} - -SAColorPaletteGridWidget::~SAColorPaletteGridWidget() -{ -} -void SAColorPaletteGridWidget::init() -{ - connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked); - connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked); - QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - setSizePolicy(sizePolicy); - setColorIconSize(QSize(10, 10)); -} - -/** - * @brief 设置颜色系列,颜色系列的个数决定了主色卡的数量,palette色卡会根据factor进行生成 - * @param cls - */ -void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls) -{ - d_ptr->mMainColorList->setColumnCount(0); - d_ptr->mMainColorList->setColorList(cls); - d_ptr->mPaletteColorGrid->setColumnCount(cls.size()); - d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls)); -} - -/** - * @brief 获取颜色系列 - * @return - */ -QList< QColor > SAColorPaletteGridWidget::colorList() const -{ - return d_ptr->mMainColorList->getColorList(); -} - -/** - * @brief 设置颜色深浅比例factor,factor决定了palette的行数,factor有5个,就有5行 - * - * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行,每个系数会对标准颜色执行QColor::lighter操作 - * @param factor - */ -void SAColorPaletteGridWidget::setFactor(const QList< int >& factor) -{ - d_ptr->mFactor = factor; - d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList())); -} - -/** - * @brief 获取色卡 - * @return - */ -QList< int > SAColorPaletteGridWidget::factor() const -{ - return d_ptr->mFactor; -} - -/** - * @brief 设置颜色块的尺寸,默认为10*10 - * @param s - */ -void SAColorPaletteGridWidget::setColorIconSize(const QSize& s) -{ - d_ptr->mMainColorList->setColorIconSize(s); - d_ptr->mPaletteColorGrid->setColorIconSize(s); -} - -QSize SAColorPaletteGridWidget::colorIconSize() const -{ - return d_ptr->mMainColorList->colorIconSize(); -} - -/** - * @brief 设置颜色块是否能被checked - * @param on - */ -void SAColorPaletteGridWidget::setColorCheckable(bool on) -{ - d_ptr->mMainColorList->setColorCheckable(on); - d_ptr->mPaletteColorGrid->setColorCheckable(on); -} - -bool SAColorPaletteGridWidget::isColorCheckable() const -{ - return d_ptr->mMainColorList->isColorCheckable(); -} - -void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c) -{ - d_ptr->mPaletteColorGrid->clearCheckedState(); - Q_EMIT colorClicked(c); -} - -void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c) -{ - d_ptr->mMainColorList->clearCheckedState(); - Q_EMIT colorClicked(c); -} - -/*** End of inlined file: SAColorPaletteGridWidget.cpp ***/ - -/*** Start of inlined file: SAColorToolButton.cpp ***/ -#include -#include -#include -#include -#include -#include -#include - -#define SAColorToolButton_DEBUG_PRINT 0 -class SAColorToolButton::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton) -public: - PrivateData(SAColorToolButton* p); - void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect); - QRect getButtonRect(const QStyleOptionToolButton& opt); - QRect getIndicatorRect(const QStyleOptionToolButton& opt); - QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt); - QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt); - void removeMenu(); - -public: - QColor mColor { Qt::white }; - int mSpacing { 2 }; ///< 间隔 - QMargins mMargins { 3, 3, 3, 3 }; ///< box - ColorToolButtonStyle mColorButtonStyle { WithColorMenu }; ///< 样式 - static int s_indicatorArrorWidth; ///< 菜单宽度 -}; - -int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8; - -SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p) -{ -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - // 确定文本区域 - textRect = QRect(); - QRect buttonRect = getButtonRect(opt); - if (opt.icon.isNull()) { - colorRect = buttonRect; - iconRect = QRect(); - } else { - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - int colorHeight = tmpSize.height() / 4; - int totalHeight = colorHeight + tmpSize.height() + mSpacing; - if (totalHeight > buttonRect.height()) { - // 过高,这时要通过高度反推tmpSize和colorHeight - colorHeight = (buttonRect.height() - mSpacing) / 5; - tmpSize.setHeight(colorHeight * 4); - if (opt.iconSize.height() > 0) { - tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height()); // 等比例 - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - } - // 有icon,颜色位于图标下面 - int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 - int x = (buttonRect.width() - tmpSize.width()) / 2; - iconRect = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height()); - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - } -} - -/** - * @brief 纯文本情况,左边有5像素显示颜色 - * @param opt - * @param iconRect - * @param textRect - * @param colorRect 左边有5像素显示颜色 - */ -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - iconRect = QRect(); - QSize colorSize = opt.iconSize; - if (colorSize.isNull()) { - colorSize = QSize(16, 16); - } - colorSize = buttonRect.size().boundedTo(colorSize); - colorRect = QRect(buttonRect.left(), - buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, - colorSize.width(), - colorSize.height()); - textRect = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0); -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - if (opt.icon.isNull()) { - QSize colorSize = opt.iconSize; - if (colorSize.isNull()) { - colorSize = QSize(16, 16); - } - // 说明没有icon - // 这时所有都是color - // iconRect=Null - colorSize = buttonRect.size().boundedTo(colorSize); - iconRect = QRect(); - colorRect = QRect(buttonRect.left(), - buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, - colorSize.width(), - colorSize.height()); - textRect = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0); - } else { // 有图标 - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - int colorHeight = tmpSize.height() / 4; - int totalHeight = colorHeight + tmpSize.height() + mSpacing; - if (totalHeight > buttonRect.height()) { - // 过高,这时要通过高度反推tmpSize和colorHeight - colorHeight = (buttonRect.height() - mSpacing) / 5; - tmpSize.setHeight(colorHeight * 4); - tmpSize.setWidth(tmpSize.height()); // 对于异形也设置为正方行 - } - // 有icon,颜色位于图标下面 - int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 - iconRect = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height()); - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - textRect = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0); - } -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - // 获取字体高度 - int textHeight = opt.fontMetrics.height(); - tmpSize = buttonRect.size().boundedTo(tmpSize); - if (opt.icon.isNull()) { - int totalHeight = textHeight + opt.iconSize.height() + mSpacing; - if (totalHeight < buttonRect.height()) { - // 足够高 - colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + (buttonRect.height() - totalHeight) / 2, - tmpSize.width(), - opt.iconSize.height()); - } else { - // 空间不足 - colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + mSpacing, - tmpSize.width(), - opt.iconSize.height()); - } - iconRect = QRect(); - textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); - - } else { - // 有图标 - int colorHeight = tmpSize.height() / 4; - int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing; - if (totalHeight < buttonRect.height()) { - // 高度空间足够 - // 先布置icon - iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + (buttonRect.height() - totalHeight) / 2, - tmpSize.width(), - opt.iconSize.height()); - - } else { - // 空间不足 - iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + mSpacing, - tmpSize.width(), - opt.iconSize.height()); - } - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); - } -} - -QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect) -{ - if (opt.icon.isNull()) { - return (QPixmap()); - } - // 有图标 - QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state)); - return (opt.icon.pixmap(iconRect.size(), mode, state)); -} - -/** - * @brief 获取按钮的矩形区域 - * @param opt - * @return - */ -QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt) -{ - QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr); - return btnRect.marginsRemoved(mMargins); -} - -/** - * @brief 获取ToolButtonMenu的区域 - * @note 注意,如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数,返回的是QRect() - * @param opt - * @return - */ -QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt) -{ - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr); - } - return QRect(); -} - -/** - * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 - * @param opt - * @return - */ -QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt) -{ - QStyle::State bflags = opt.state & ~QStyle::State_Sunken; - if (bflags & QStyle::State_AutoRaise) { - if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { - bflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - if (opt.activeSubControls & QStyle::SC_ToolButton) { - bflags |= QStyle::State_Sunken; - } - } - return bflags; -} - -/** - * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 - * @param opt - * @return - */ -QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt) -{ - QStyle::State mflags = opt.state & ~QStyle::State_Sunken; - if (mflags & QStyle::State_AutoRaise) { - if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) { - mflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - mflags |= QStyle::State_Sunken; - } - return mflags; -} - -void SAColorToolButton::PrivateData::removeMenu() -{ - QMenu* m = q_ptr->menu(); - if (m) { - q_ptr->setMenu(nullptr); - m->deleteLater(); - } -} - -//============================================================== -// SAColorToolButton -//============================================================== - -SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this)) -{ - setColorToolButtonStyle(WithColorMenu); - connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); -} - -SAColorToolButton::SAColorToolButton(ColorToolButtonStyle style, QWidget* parent) - : QToolButton(parent), d_ptr(new PrivateData(this)) -{ - setColorToolButtonStyle(style); - connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); -} - -SAColorToolButton::~SAColorToolButton() -{ -} - -/** - * @brief 获取颜色 - * @return - */ -QColor SAColorToolButton::color() const -{ - return d_ptr->mColor; -} - -/** - * @brief 设置Margins - * @param mg - */ -void SAColorToolButton::setMargins(const QMargins& mg) -{ - d_ptr->mMargins = mg; - repaint(); -} - -QMargins SAColorToolButton::margins() const -{ - return d_ptr->mMargins; -} - -/** - * @brief 绘制无颜色表示 - * @param p - * @param colorRect 绘制的区域 - */ -void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect) -{ - p->save(); - QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); - p->setPen(pen); - p->setRenderHint(QPainter::SmoothPixmapTransform, true); - p->setRenderHint(QPainter::Antialiasing, true); - int ss = colorRect.width() / 3; - p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); - pen.setColor(Qt::black); - p->setPen(pen); - p->drawRect(colorRect); - p->restore(); -} - -/** - * @brief 设置颜色按钮的样式 - * @param s - */ -void SAColorToolButton::setColorToolButtonStyle(ColorToolButtonStyle s) -{ - if (NoColorMenu == s) { - d_ptr->removeMenu(); - } else { - createColorMenu(); - } - d_ptr->mColorButtonStyle = s; -} - -/** - * @brief 颜色按钮的样式 - * @return - */ -SAColorToolButton::ColorToolButtonStyle SAColorToolButton::colorToolButtonStyle() const -{ - return d_ptr->mColorButtonStyle; -} - -/** - * @brief 获取colorMenu - * - * @note 注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr - * @return SAColorMenu指针,如果没有,返回nullptr - */ -SAColorMenu* SAColorToolButton::colorMenu() const -{ - return qobject_cast< SAColorMenu* >(menu()); -} - -/** - * @brief 建立标准的颜色菜单 - * @return - */ -SAColorMenu* SAColorToolButton::createColorMenu() -{ - setPopupMode(QToolButton::InstantPopup); - SAColorMenu* m = new SAColorMenu(this); - m->enableNoneColorAction(true); - QAction* customColor = m->customColorAction(); - if (customColor) { - customColor->setIcon(QIcon(":/image/resource/define-color.svg")); - } - connect(m, &SAColorMenu::selectedColor, this, &SAColorToolButton::setColor); - setMenu(m); - return m; -} - -/** - * @brief 设置颜色 - * @note 会发射@sa colorChanged 信号 - * @param c - */ -void SAColorToolButton::setColor(const QColor& c) -{ - if (d_ptr->mColor != c) { - d_ptr->mColor = c; - repaint(); - Q_EMIT colorChanged(c); - } -} - -/** - * @brief 计算各个基本组件的位置 - * @param opt - * @param iconRect 图标区域 - * @param textRect 文本区域 - * @param colorRect 颜色区域 - */ -void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) -{ - switch (opt.toolButtonStyle) { - case Qt::ToolButtonTextOnly: - d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonTextBesideIcon: - d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonTextUnderIcon: - d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonFollowStyle: - case Qt::ToolButtonIconOnly: - d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); - break; - default: - d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); - break; - } -} - -void SAColorToolButton::paintEvent(QPaintEvent* e) -{ - Q_UNUSED(e); - QStylePainter p(this); - - QStyleOptionToolButton opt; - - initStyleOption(&opt); - QRect iconRect, colorRect, textRect; - calcRect(opt, iconRect, textRect, colorRect); - paintButton(&p, opt); - // 绘制图标 - paintIcon(&p, iconRect, opt); - // 绘制文字 - paintText(&p, textRect, opt); - // 绘制颜色 - paintColor(&p, colorRect, d_ptr->mColor, opt); -} - -void SAColorToolButton::resizeEvent(QResizeEvent* e) -{ - // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - QToolButton::resizeEvent(e); -} - -/** - * @brief sizeHint重载 - * @return - */ -QSize SAColorToolButton::sizeHint() const -{ - ensurePolished(); - QStyleOptionToolButton opt; - initStyleOption(&opt); - int w = 0, h = 0; - if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) { - // 和文本无关 - w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) { - QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); - textSize.setHeight(textSize.height() + 4); - QSize iconSize = opt.iconSize; - if (!opt.icon.isNull()) { - // 有图标,要有iconsize高度的1/4给颜色 - iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); - } - w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) { - QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); - textSize.setHeight(textSize.height() + 4); - QSize iconSize = opt.iconSize; - if (!opt.icon.isNull()) { - // 有图标,要有iconsize高度的1/4给颜色 - iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); - } - w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } - opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); - } - //! Qt6.4 取消了QApplication::globalStrut - return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2)); -} - -void SAColorToolButton::onButtonClicked(bool checked) -{ - Q_EMIT colorClicked(color(), checked); -} - -/** - * @brief 绘制按钮 - * @param p - * @param opt - */ -void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt) -{ - bool autoRaise = opt.state & QStyle::State_AutoRaise; - // 绘制按钮 - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this); - } - if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { - QStyleOption tool = opt; - tool.state = d_ptr->getButtonMenuStyleState(opt); - tool.rect = d_ptr->getIndicatorRect(opt); - // if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { - // style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this); - // } - style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this); - } - - // 绘制focus - if (opt.state & QStyle::State_HasFocus) { - QStyleOptionFocusRect fr; - fr.QStyleOption::operator=(opt); - fr.rect.adjust(3, 3, -3, -3); - if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { - fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0); - } - style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this); - } -} - -/** - * @brief 绘制icon - * @param p - * @param iconRect - * @param opt icon信息从QStyleOptionToolButton获取 - */ -void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt) -{ - if (!iconRect.isNull()) { - QPixmap pm = d_ptr->createIconPixmap(opt, iconRect); - style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm); - } -} - -/** - * @brief 绘制文本 - * @param p - * @param textRect - * @param opt - */ -void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt) -{ - if (opt.text.isEmpty()) { - return; - } - if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) { - return; - } - p->save(); - p->setFont(opt.font); - int alignment = Qt::TextShowMnemonic; - // 快捷键的下划线 - if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { - alignment |= Qt::TextHideMnemonic; - } - alignment |= Qt::AlignHCenter | Qt::AlignVCenter; - style()->drawItemText(p, - QStyle::visualRect(opt.direction, opt.rect, textRect), - alignment, - opt.palette, - opt.state & QStyle::State_Enabled, - opt.text, - QPalette::ButtonText); - p->restore(); -} - -/** - * @brief 绘制color - * @param p - * @param iconRect - * @param opt - */ -void SAColorToolButton::paintColor(QStylePainter* p, - const QRect& colorRect, - const QColor& color, - const QStyleOptionToolButton& opt) -{ - Q_UNUSED(opt); - if (colorRect.isNull()) { - return; - } - // 绘制颜色 - if (color.isValid()) { - p->fillRect(colorRect, color); - } else { - paintNoneColor(p, colorRect); - } -} - -/*** End of inlined file: SAColorToolButton.cpp ***/ - -// sa ribbon - -/*** Start of inlined file: SAFramelessHelper.cpp ***/ -#include -#include -#include -#include -#include -#include - -class SAPrivateFramelessWidgetData; - -/***** - * FramelessHelperPrivate - * 存储界面对应的数据集合,以及是否可移动、可缩放属性 - *****/ -class SAFramelessHelper::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SAFramelessHelper) - friend class SAPrivateFramelessWidgetData; - -public: - PrivateData(SAFramelessHelper* p); - QHash< QWidget*, SAPrivateFramelessWidgetData* > m_widgetDataHash; - bool m_bWidgetMovable { true }; - bool m_bWidgetResizable { true }; - bool m_bRubberBandOnResize { true }; - bool m_bRubberBandOnMove { true }; -}; - -SAFramelessHelper::PrivateData::PrivateData(SAFramelessHelper* p) : q_ptr(p) -{ -} - -/***** - * CursorPosCalculator - * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 - *****/ -class SAPrivateFramelessCursorPosCalculator -{ -public: - explicit SAPrivateFramelessCursorPosCalculator(); - void reset(); - void recalculate(const QPoint& globalMousePos, const QRect& frameRect); - -public: - bool mIsOnEdges { true }; - bool mIsOnLeftEdge { true }; - bool mIsOnRightEdge { true }; - bool mIsOnTopEdge { true }; - bool mIsOnBottomEdge { true }; - bool mIsOnTopLeftEdge { true }; - bool mIsOnBottomLeftEdge { true }; - bool mIsOnTopRightEdge { true }; - bool mIsOnBottomRightEdge { true }; - - static int s_borderWidth; - static int s_titleHeight; -}; - -int SAPrivateFramelessCursorPosCalculator::s_borderWidth = 5; -int SAPrivateFramelessCursorPosCalculator::s_titleHeight = 30; - -/***** CursorPosCalculator *****/ -SAPrivateFramelessCursorPosCalculator::SAPrivateFramelessCursorPosCalculator() -{ - reset(); -} - -void SAPrivateFramelessCursorPosCalculator::reset() -{ - mIsOnEdges = false; - mIsOnLeftEdge = false; - mIsOnRightEdge = false; - mIsOnTopEdge = false; - mIsOnBottomEdge = false; - mIsOnTopLeftEdge = false; - mIsOnBottomLeftEdge = false; - mIsOnTopRightEdge = false; - mIsOnBottomRightEdge = false; -} - -void SAPrivateFramelessCursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect) -{ - int globalMouseX = gMousePos.x(); - int globalMouseY = gMousePos.y(); - - int frameX = frameRect.x(); - int frameY = frameRect.y(); - - int frameWidth = frameRect.width(); - int frameHeight = frameRect.height(); - - mIsOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + s_borderWidth); - - mIsOnRightEdge = (globalMouseX >= frameX + frameWidth - s_borderWidth && globalMouseX <= frameX + frameWidth); - - mIsOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + s_borderWidth); - - mIsOnBottomEdge = (globalMouseY >= frameY + frameHeight - s_borderWidth && globalMouseY <= frameY + frameHeight); - - mIsOnTopLeftEdge = mIsOnTopEdge && mIsOnLeftEdge; - mIsOnBottomLeftEdge = mIsOnBottomEdge && mIsOnLeftEdge; - mIsOnTopRightEdge = mIsOnTopEdge && mIsOnRightEdge; - mIsOnBottomRightEdge = mIsOnBottomEdge && mIsOnRightEdge; - - mIsOnEdges = mIsOnLeftEdge || mIsOnRightEdge || mIsOnTopEdge || mIsOnBottomEdge; -} - -/***** - * WidgetData - * 更新鼠标样式、移动窗体、缩放窗体 - *****/ -class SAPrivateFramelessWidgetData -{ -public: - explicit SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget); - ~SAPrivateFramelessWidgetData(); - QWidget* widget(); - - // 处理鼠标事件-划过、按下、释放、移动 - bool handleWidgetEvent(QEvent* event); - - // 更新橡皮筋状态 - void updateRubberBandStatus(); - -private: - // 更新鼠标样式 - void updateCursorShape(const QPoint& gMousePos); - - // 重置窗体大小 - void resizeWidget(const QPoint& gMousePos); - - // 移动窗体 - void moveWidget(const QPoint& gMousePos); - - // 处理鼠标按下 - bool handleMousePressEvent(QMouseEvent* event); - - // 处理鼠标释放 - bool handleMouseReleaseEvent(QMouseEvent* event); - - // 处理鼠标移动 - bool handleMouseMoveEvent(QMouseEvent* event); - - // 处理鼠标离开 - bool handleLeaveEvent(QEvent* event); - - // 处理鼠标进入 - bool handleHoverMoveEvent(QHoverEvent* event); - - // 处理鼠标双击事件 - bool handleDoubleClickedMouseEvent(QMouseEvent* event); - -private: - SAFramelessHelper::PrivateData* d; - QRubberBand* m_pRubberBand; - QWidget* m_pWidget; - QPoint m_ptDragPos; - SAPrivateFramelessCursorPosCalculator m_pressedMousePos; - SAPrivateFramelessCursorPosCalculator m_moveMousePos; - bool m_bLeftButtonPressed; - bool m_bCursorShapeChanged; - bool m_bLeftButtonTitlePressed; - Qt::WindowFlags m_windowFlags; -}; - -/***** WidgetData *****/ -SAPrivateFramelessWidgetData::SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget) -{ - d = pd; - m_pWidget = pTopLevelWidget; - m_bLeftButtonPressed = false; - m_bCursorShapeChanged = false; - m_bLeftButtonTitlePressed = false; - m_pRubberBand = NULL; - - m_windowFlags = m_pWidget->windowFlags(); - m_pWidget->setMouseTracking(true); - m_pWidget->setAttribute(Qt::WA_Hover, true); - - updateRubberBandStatus(); -} - -SAPrivateFramelessWidgetData::~SAPrivateFramelessWidgetData() -{ - m_pWidget->setMouseTracking(false); - m_pWidget->setWindowFlags(m_windowFlags); - m_pWidget->setAttribute(Qt::WA_Hover, false); - - delete m_pRubberBand; - m_pRubberBand = NULL; -} - -QWidget* SAPrivateFramelessWidgetData::widget() -{ - return (m_pWidget); -} - -bool SAPrivateFramelessWidgetData::handleWidgetEvent(QEvent* event) -{ - switch (event->type()) { - case QEvent::MouseButtonPress: - return (handleMousePressEvent(static_cast< QMouseEvent* >(event))); - - case QEvent::MouseButtonRelease: - return (handleMouseReleaseEvent(static_cast< QMouseEvent* >(event))); - - case QEvent::MouseMove: - return (handleMouseMoveEvent(static_cast< QMouseEvent* >(event))); - - case QEvent::Leave: - return (handleLeaveEvent(static_cast< QMouseEvent* >(event))); - - case QEvent::HoverMove: - return (handleHoverMoveEvent(static_cast< QHoverEvent* >(event))); - - case QEvent::MouseButtonDblClick: - return (handleDoubleClickedMouseEvent(static_cast< QMouseEvent* >(event))); - - default: - break; - } - return (false); -} - -void SAPrivateFramelessWidgetData::updateRubberBandStatus() -{ - if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) { - if (NULL == m_pRubberBand) { - m_pRubberBand = new QRubberBand(QRubberBand::Rectangle); - } - } else { - delete m_pRubberBand; - m_pRubberBand = NULL; - } -} - -void SAPrivateFramelessWidgetData::updateCursorShape(const QPoint& gMousePos) -{ - if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) { - if (m_bCursorShapeChanged) { - m_pWidget->unsetCursor(); - } - return; - } - - m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry()); - - if (m_moveMousePos.mIsOnTopLeftEdge || m_moveMousePos.mIsOnBottomRightEdge) { - m_pWidget->setCursor(Qt::SizeFDiagCursor); - m_bCursorShapeChanged = true; - } else if (m_moveMousePos.mIsOnTopRightEdge || m_moveMousePos.mIsOnBottomLeftEdge) { - m_pWidget->setCursor(Qt::SizeBDiagCursor); - m_bCursorShapeChanged = true; - } else if (m_moveMousePos.mIsOnLeftEdge || m_moveMousePos.mIsOnRightEdge) { - m_pWidget->setCursor(Qt::SizeHorCursor); - m_bCursorShapeChanged = true; - } else if (m_moveMousePos.mIsOnTopEdge || m_moveMousePos.mIsOnBottomEdge) { - m_pWidget->setCursor(Qt::SizeVerCursor); - m_bCursorShapeChanged = true; - } else { - if (m_bCursorShapeChanged) { - m_pWidget->unsetCursor(); - m_bCursorShapeChanged = false; - } - } -} - -void SAPrivateFramelessWidgetData::resizeWidget(const QPoint& gMousePos) -{ - QRect origRect; - - if (d->m_bRubberBandOnResize) { - origRect = m_pRubberBand->frameGeometry(); - } else { - origRect = m_pWidget->frameGeometry(); - } - - int left = origRect.left(); - int top = origRect.top(); - int right = origRect.right(); - int bottom = origRect.bottom(); - - origRect.getCoords(&left, &top, &right, &bottom); - - int minWidth = m_pWidget->minimumWidth(); - int minHeight = m_pWidget->minimumHeight(); - - if (m_pressedMousePos.mIsOnTopLeftEdge) { - left = gMousePos.x(); - top = gMousePos.y(); - } else if (m_pressedMousePos.mIsOnBottomLeftEdge) { - left = gMousePos.x(); - bottom = gMousePos.y(); - } else if (m_pressedMousePos.mIsOnTopRightEdge) { - right = gMousePos.x(); - top = gMousePos.y(); - } else if (m_pressedMousePos.mIsOnBottomRightEdge) { - right = gMousePos.x(); - bottom = gMousePos.y(); - } else if (m_pressedMousePos.mIsOnLeftEdge) { - left = gMousePos.x(); - } else if (m_pressedMousePos.mIsOnRightEdge) { - right = gMousePos.x(); - } else if (m_pressedMousePos.mIsOnTopEdge) { - top = gMousePos.y(); - } else if (m_pressedMousePos.mIsOnBottomEdge) { - bottom = gMousePos.y(); - } - - QRect newRect(QPoint(left, top), QPoint(right, bottom)); - - if (newRect.isValid()) { - if (minWidth > newRect.width()) { - if (left != origRect.left()) { - newRect.setLeft(origRect.left()); - } else { - newRect.setRight(origRect.right()); - } - } - if (minHeight > newRect.height()) { - if (top != origRect.top()) { - newRect.setTop(origRect.top()); - } else { - newRect.setBottom(origRect.bottom()); - } - } - - if (d->m_bRubberBandOnResize) { - m_pRubberBand->setGeometry(newRect); - } else { - m_pWidget->setGeometry(newRect); - } - } -} - -void SAPrivateFramelessWidgetData::moveWidget(const QPoint& gMousePos) -{ - if (d->m_bRubberBandOnMove) { - m_pRubberBand->move(gMousePos - m_ptDragPos); - } else { - m_pWidget->move(gMousePos - m_ptDragPos); - } -} - -bool SAPrivateFramelessWidgetData::handleMousePressEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) { - m_bLeftButtonPressed = true; - m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.s_titleHeight; - - QRect frameRect = m_pWidget->frameGeometry(); - auto gp = SA_MOUSEEVENT_GLOBALPOS_POINT(event); - m_pressedMousePos.recalculate(gp, frameRect); - - m_ptDragPos = gp - frameRect.topLeft(); - - if (m_pressedMousePos.mIsOnEdges) { - if (m_pWidget->isMaximized()) { - // 窗口在最大化状态时,点击边界不做任何处理 - return (false); - } - if (d->m_bRubberBandOnResize) { - m_pRubberBand->setGeometry(frameRect); - m_pRubberBand->show(); - return (true); - } - } else if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) { - if (m_pWidget->isMaximized()) { - // 窗口在最大化状态时,点击标题栏不做任何处理 - return (false); - } - m_pRubberBand->setGeometry(frameRect); - m_pRubberBand->show(); - return (true); - } - } - return (false); -} - -bool SAPrivateFramelessWidgetData::handleMouseReleaseEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) { - m_bLeftButtonPressed = false; - m_bLeftButtonTitlePressed = false; - m_pressedMousePos.reset(); - if (m_pRubberBand && m_pRubberBand->isVisible()) { - m_pRubberBand->hide(); - m_pWidget->setGeometry(m_pRubberBand->geometry()); - return (true); - } - } - return (false); -} - -bool SAPrivateFramelessWidgetData::handleMouseMoveEvent(QMouseEvent* event) -{ - QPoint p = SA_MOUSEEVENT_GLOBALPOS_POINT(event); - if (m_bLeftButtonPressed) { - if (d->m_bWidgetResizable && m_pressedMousePos.mIsOnEdges) { - if (m_pWidget->isMaximized()) { - // 窗口在最大化状态时,点击边界不做任何处理 - return (false); - } - resizeWidget(p); - return (true); - } else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) { - if (m_pWidget->isMaximized()) { - // 先求出窗口到鼠标的相对位置 - QRect normalGeometry = m_pWidget->normalGeometry(); - m_pWidget->showNormal(); - p.ry() -= 10; - p.rx() -= (normalGeometry.width() / 2); - m_pWidget->move(p); - // 这时要重置m_ptDragPos - m_ptDragPos = QPoint(normalGeometry.width() / 2, 10); - return (true); - } - moveWidget(p); - return (true); - } - return (false); - } else if (d->m_bWidgetResizable) { - updateCursorShape(p); - } - return (false); -} - -bool SAPrivateFramelessWidgetData::handleLeaveEvent(QEvent* event) -{ - Q_UNUSED(event) - if (!m_bLeftButtonPressed) { - m_pWidget->unsetCursor(); - return (true); - } - return (false); -} - -bool SAPrivateFramelessWidgetData::handleHoverMoveEvent(QHoverEvent* event) -{ - if (d->m_bWidgetResizable) { - updateCursorShape(m_pWidget->mapToGlobal(SA_HOVEREVENT_POS_POINT(event))); - } - return (false); -} - -bool SAPrivateFramelessWidgetData::handleDoubleClickedMouseEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) { - if (m_pWidget) { - SARibbonMainWindow* mainwindow = qobject_cast< SARibbonMainWindow* >(m_pWidget); - if (mainwindow) { - if (mainwindow->windowFlags() & Qt::WindowMaximizeButtonHint) { - // 在最大化按钮显示时才进行shownormal处理 - bool titlePressed = event->pos().y() < m_moveMousePos.s_titleHeight; - if (titlePressed) { - if (m_pWidget->isMaximized()) { - m_pWidget->showNormal(); - } else { - m_pWidget->showMaximized(); - } - return (true); - } - } - } - } - } - return (false); -} - -//=================================================== -// SAFramelessHelper -//=================================================== -SAFramelessHelper::SAFramelessHelper(QObject* parent) : QObject(parent), d_ptr(new SAFramelessHelper::PrivateData(this)) -{ - d_ptr->m_bWidgetMovable = true; - d_ptr->m_bWidgetResizable = true; - d_ptr->m_bRubberBandOnResize = false; - d_ptr->m_bRubberBandOnMove = false; - if (parent) { - QWidget* w = qobject_cast< QWidget* >(parent); - if (w) { - w->setWindowFlags(w->windowFlags() | Qt::FramelessWindowHint); - setWidgetMovable(true); // 设置窗体可移动 - setWidgetResizable(true); // 设置窗体可缩放 - setRubberBandOnMove(false); // 设置橡皮筋效果-可移动 - setRubberBandOnResize(true); // 设置橡皮筋效果-可缩放 - activateOn(w); // 激活当前窗体 - } - } -} - -SAFramelessHelper::~SAFramelessHelper() -{ - QList< QWidget* > keys = d_ptr->m_widgetDataHash.keys(); - int size = keys.size(); - - for (int i = 0; i < size; ++i) { - delete d_ptr->m_widgetDataHash.take(keys[ i ]); - } -} - -bool SAFramelessHelper::eventFilter(QObject* obj, QEvent* event) -{ - switch (event->type()) { - case QEvent::MouseMove: - case QEvent::HoverMove: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::Leave: { - SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.value(static_cast< QWidget* >(obj)); - if (data) { - return (data->handleWidgetEvent(event)); - } - break; - } - - default: - break; - } - return (QObject::eventFilter(obj, event)); -} - -void SAFramelessHelper::activateOn(QWidget* topLevelWidget) -{ - if (!d_ptr->m_widgetDataHash.contains(topLevelWidget)) { - SAPrivateFramelessWidgetData* data = new SAPrivateFramelessWidgetData(d_ptr.get(), topLevelWidget); - d_ptr->m_widgetDataHash.insert(topLevelWidget, data); - - topLevelWidget->installEventFilter(this); - } -} - -void SAFramelessHelper::removeFrom(QWidget* topLevelWidget) -{ - SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.take(topLevelWidget); - - if (data) { - topLevelWidget->removeEventFilter(this); - delete data; - } -} - -void SAFramelessHelper::setRubberBandOnMove(bool movable) -{ - d_ptr->m_bRubberBandOnMove = movable; - QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); - - foreach (SAPrivateFramelessWidgetData* data, list) { - data->updateRubberBandStatus(); - } -} - -void SAFramelessHelper::setWidgetMovable(bool movable) -{ - d_ptr->m_bWidgetMovable = movable; -} - -void SAFramelessHelper::setWidgetResizable(bool resizable) -{ - d_ptr->m_bWidgetResizable = resizable; -} - -void SAFramelessHelper::setRubberBandOnResize(bool resizable) -{ - d_ptr->m_bRubberBandOnResize = resizable; - QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); - - foreach (SAPrivateFramelessWidgetData* data, list) { - data->updateRubberBandStatus(); - } -} - -void SAFramelessHelper::setBorderWidth(int width) -{ - if (width > 0) { - SAPrivateFramelessCursorPosCalculator::s_borderWidth = width; - } -} - -void SAFramelessHelper::setTitleHeight(int height) -{ - if (height > 0) { - SAPrivateFramelessCursorPosCalculator::s_titleHeight = height; - } -} - -bool SAFramelessHelper::widgetMovable() -{ - return (d_ptr->m_bWidgetMovable); -} - -bool SAFramelessHelper::widgetResizable() -{ - return (d_ptr->m_bWidgetResizable); -} - -bool SAFramelessHelper::rubberBandOnMove() -{ - return (d_ptr->m_bRubberBandOnMove); -} - -bool SAFramelessHelper::rubberBandOnResisze() -{ - return (d_ptr->m_bRubberBandOnResize); -} - -uint SAFramelessHelper::borderWidth() -{ - return (SAPrivateFramelessCursorPosCalculator::s_borderWidth); -} - -uint SAFramelessHelper::titleHeight() -{ - return (SAPrivateFramelessCursorPosCalculator::s_titleHeight); -} - -/*** End of inlined file: SAFramelessHelper.cpp ***/ - -/*** Start of inlined file: SARibbonApplicationButton.cpp ***/ -SARibbonApplicationButton::SARibbonApplicationButton(QWidget* parent) : QToolButton(parent) -{ - setFocusPolicy(Qt::NoFocus); - setAutoRaise(true); - setPopupMode(QToolButton::InstantPopup); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); -} - -SARibbonApplicationButton::SARibbonApplicationButton(const QString& text, QWidget* parent) : QToolButton(parent) -{ - setFocusPolicy(Qt::NoFocus); - setAutoRaise(true); - setPopupMode(QToolButton::InstantPopup); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setText(text); -} - -SARibbonApplicationButton::SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent) - : QToolButton(parent) -{ - setFocusPolicy(Qt::NoFocus); - setAutoRaise(true); - setPopupMode(QToolButton::InstantPopup); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setIcon(icon); - setText(text); -} - -SARibbonApplicationButton::~SARibbonApplicationButton() -{ -} - -/*** End of inlined file: SARibbonApplicationButton.cpp ***/ - -/*** Start of inlined file: SARibbonSystemButtonBar.cpp ***/ -#include -#include -#include -#include -#include -#include - -// 为了避免使用此框架的app设置了全局的qpushbutton 的 qss样式影响此按钮,定义了一个类 - -/** - * @brief The SARibbonSystemButtonBar class - */ -class SARibbonSystemButtonBar::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonSystemButtonBar) -public: - SARibbonSystemToolButton* buttonClose { nullptr }; - SARibbonSystemToolButton* buttonMinimize { nullptr }; - SARibbonSystemToolButton* buttonMaximize { nullptr }; - int mCloseStretch { 4 }; - int mMaxStretch { 3 }; - int mMinStretch { 3 }; - int mWindowButtonWidth { 35 }; - int mTitleBarHeight { 28 }; - Qt::WindowFlags mFlags { Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint }; - SARibbonButtonGroupWidget* mButtonGroup; - -public: - PrivateData(SARibbonSystemButtonBar* p) : q_ptr(p) - { - mButtonGroup = new SARibbonButtonGroupWidget(p); - mButtonGroup->setObjectName("SASystemButtonGroup"); - } - - void setupMinimizeButton(bool on) - { - SARibbonSystemButtonBar* par = q_ptr; - - if (on) { - if (buttonMinimize) { - buttonMinimize->deleteLater(); - buttonMinimize = nullptr; - } - buttonMinimize = new SARibbonSystemToolButton(par); - buttonMinimize->setObjectName(QStringLiteral("SAMinimizeWindowButton")); - buttonMinimize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 - buttonMinimize->show(); - par->connect(buttonMinimize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::minimizeWindow); - } else { - if (buttonMinimize) { - buttonMinimize->deleteLater(); - buttonMinimize = nullptr; - } - } - updateSize(); - } - - void setupMaximizeButton(bool on) - { - SARibbonSystemButtonBar* par = q_ptr; - - if (on) { - if (buttonMaximize) { - buttonMaximize->deleteLater(); - buttonMaximize = nullptr; - } - buttonMaximize = new SARibbonSystemToolButton(par); - buttonMaximize->setObjectName(QStringLiteral("SAMaximizeWindowButton")); - buttonMaximize->setCheckable(true); - buttonMaximize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 - // buttonMaximize->setIconSize(buttonMaximize->size() * mIconscale); - buttonMaximize->show(); - par->connect(buttonMaximize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::maximizeWindow); - } else { - if (buttonMaximize) { - buttonMaximize->deleteLater(); - ; - buttonMaximize = nullptr; - } - } - updateSize(); - } - - void setupCloseButton(bool on) - { - SARibbonSystemButtonBar* par = q_ptr; - - if (on) { - if (buttonClose) { - buttonClose->deleteLater(); - buttonClose = nullptr; - } - buttonClose = new SARibbonSystemToolButton(par); - buttonClose->setObjectName(QStringLiteral("SACloseWindowButton")); - buttonClose->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 - // buttonClose->setFlat(true); - par->connect(buttonClose, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::closeWindow); - // buttonClose->setIconSize(buttonClose->size() * mIconscale); - buttonClose->show(); - } else { - if (buttonClose) { - buttonClose->deleteLater(); - ; - buttonClose = nullptr; - } - } - updateSize(); - } - - void updateSize() - { - resizeElement(q_ptr->size()); - } - - void resizeElement(QSize size) - { - int x = size.width(); - if (buttonClose) { - int w = closeButtonWidthHint(); - x -= w; - buttonClose->setGeometry(x, 0, w, size.height()); - } - if (buttonMaximize) { - int w = maxButtonWidthHint(); - x -= w; - buttonMaximize->setGeometry(x, 0, w, size.height()); - } - if (buttonMinimize) { - int w = minButtonWidthHint(); - x -= w; - buttonMinimize->setGeometry(x, 0, w, size.height()); - } - if (mButtonGroup) { - mButtonGroup->setGeometry(0, 0, x, size.height()); - } - } - - int closeButtonWidthHint() const - { - qreal t = mCloseStretch + mMaxStretch + mMinStretch; - return (mCloseStretch * (3 * mWindowButtonWidth)) / t; - } - - int maxButtonWidthHint() const - { - qreal t = mCloseStretch + mMaxStretch + mMinStretch; - return (mMaxStretch * (3 * mWindowButtonWidth)) / t; - } - - int minButtonWidthHint() const - { - qreal t = mCloseStretch + mMaxStretch + mMinStretch; - return (mMinStretch * (3 * mWindowButtonWidth)) / t; - } - - QSize sizeHint() const - { - int height = mTitleBarHeight; - if (height < 20) { - height = 20; - } - QSize res(0, 0); - if (mButtonGroup) { - res = mButtonGroup->sizeHint(); - } - res.setHeight(height); - if (buttonClose) { - res.setWidth(res.width() + closeButtonWidthHint()); - } - if (buttonMaximize) { - res.setWidth(res.width() + maxButtonWidthHint()); - } - if (buttonMinimize) { - res.setWidth(res.width() + minButtonWidthHint()); - } - return res; - } -}; - -//=================================================== -// SARibbonSystemToolButton -//=================================================== -SARibbonSystemToolButton::SARibbonSystemToolButton(QWidget* p) : QToolButton(p) -{ - setAutoRaise(true); -} -//=================================================== -// SARibbonSystemButtonBar -//=================================================== -SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent) - : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) -{ - updateWindowFlag(); -} - -/** - * @brief 构造函数,强制使用flags,而不是用parent的flags进行构造 - * @param parent - * @param flags - */ -SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags) - : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) -{ - d_ptr->mFlags = flags; - updateWindowFlag(); -} - -SARibbonSystemButtonBar::~SARibbonSystemButtonBar() -{ -} - -void SARibbonSystemButtonBar::setupMinimizeButton(bool on) -{ - d_ptr->setupMinimizeButton(on); -} - -void SARibbonSystemButtonBar::setupMaximizeButton(bool on) -{ - d_ptr->setupMaximizeButton(on); -} - -void SARibbonSystemButtonBar::setupCloseButton(bool on) -{ - d_ptr->setupCloseButton(on); -} - -void SARibbonSystemButtonBar::updateWindowFlag() -{ - QWidget* topedWidget = this; - // 找到最顶层窗口 - while (topedWidget->parentWidget()) { - topedWidget = topedWidget->parentWidget(); - } - Qt::WindowFlags flags = topedWidget->windowFlags(); - updateWindowFlag(flags); -} - -/** - * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 - * @param flags - */ -void SARibbonSystemButtonBar::updateWindowFlag(Qt::WindowFlags flags) -{ - d_ptr->mFlags = flags; - setupMinimizeButton(flags & Qt::WindowMinimizeButtonHint); - setupMaximizeButton(flags & Qt::WindowMaximizeButtonHint); - setupCloseButton(flags & Qt::WindowCloseButtonHint); -} - -/** - * @brief 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 - * @param close 关闭按钮比例 - * @param max 最大化按钮比例 - * @param min 最小化按钮比例 - */ -void SARibbonSystemButtonBar::setButtonWidthStretch(int close, int max, int min) -{ - d_ptr->mMaxStretch = max; - d_ptr->mMinStretch = min; - d_ptr->mCloseStretch = close; -} - -/** - * @brief 标题栏高度 - * - * 标题栏高度会影响sizeHint - * @param h - */ -void SARibbonSystemButtonBar::setWindowTitleHeight(int h) -{ - d_ptr->mTitleBarHeight = h; -} - -/** - * @brief 标题栏高度 - * @return - */ -int SARibbonSystemButtonBar::windowTitleHeight() const -{ - return d_ptr->mTitleBarHeight; -} - -/** - * @brief 系统按钮的宽度 - * @param w - */ -void SARibbonSystemButtonBar::setWindowButtonWidth(int w) -{ - d_ptr->mWindowButtonWidth = w; -} - -/** - * @brief 系统按钮的宽度 - * @param w - */ -int SARibbonSystemButtonBar::windowButtonWidth() const -{ - return d_ptr->mWindowButtonWidth; -} - -/** - * @brief 设置窗口状态(最大最小化按钮状态) - * @param s - */ -void SARibbonSystemButtonBar::setWindowStates(Qt::WindowStates s) -{ - if (d_ptr->buttonMaximize) { - bool on = s.testFlag(Qt::WindowMaximized); - d_ptr->buttonMaximize->setChecked(on); - d_ptr->buttonMaximize->setToolTip(on ? tr("Restore") : tr("Maximize")); - } -} - -/** - * @brief 此函数返回的flags仅包括 Qt::WindowCloseButtonHint,Qt::WindowMaximizeButtonHint,Qt::WindowMinimizeButtonHint - * 三个 - * - * @return - */ -Qt::WindowFlags SARibbonSystemButtonBar::windowButtonFlags() const -{ - Qt::WindowFlags f = Qt::Widget; // widget是000 - - if (d_ptr->mFlags & Qt::WindowCloseButtonHint) { - f |= Qt::WindowCloseButtonHint; - } - if (d_ptr->mFlags & Qt::WindowMaximizeButtonHint) { - f |= Qt::WindowMaximizeButtonHint; - } - if (d_ptr->mFlags & Qt::WindowMinimizeButtonHint) { - f |= Qt::WindowMinimizeButtonHint; - } - - return (f); -} - -QSize SARibbonSystemButtonBar::sizeHint() const -{ - return (d_ptr->sizeHint()); -} - -bool SARibbonSystemButtonBar::eventFilter(QObject* obj, QEvent* event) -{ - if (obj == parent()) { - // SARibbonMainWindow的事件 - if (event->type() == QEvent::Resize) { - SARibbonMainWindow* mainWindow = qobject_cast< SARibbonMainWindow* >(obj); - if (!mainWindow) { - // 所有事件都不消费 - return QFrame::eventFilter(obj, event); - } - SARibbonBar* ribbonBar = mainWindow->ribbonBar(); - if (!ribbonBar) { - // 所有事件都不消费 - return QFrame::eventFilter(obj, event); - } - const int th = ribbonBar->titleBarHeight(); - if (th != height()) { - setWindowTitleHeight(th); - } - QRect fr = mainWindow->geometry(); - QSize wgSizeHint = sizeHint(); - setGeometry(fr.width() - wgSizeHint.width(), 0, wgSizeHint.width(), wgSizeHint.height()); - // 把设置好的尺寸给ribbonbar - ribbonBar->setWindowButtonGroupSize(size()); - } else if (event->type() == QEvent::WindowStateChange) { - SARibbonMainWindow* mainWindow = qobject_cast< SARibbonMainWindow* >(obj); - if (!mainWindow) { - // 所有事件都不消费 - return QFrame::eventFilter(obj, event); - } - setWindowStates(mainWindow->windowState()); - } - } - return QFrame::eventFilter(obj, event); -} - -QAbstractButton* SARibbonSystemButtonBar::minimizeButton() const -{ - return d_ptr->buttonMinimize; -} - -QAbstractButton* SARibbonSystemButtonBar::maximizeButton() const -{ - return d_ptr->buttonMaximize; -} - -QAbstractButton* SARibbonSystemButtonBar::closeButton() const -{ - return d_ptr->buttonClose; -} - -void SARibbonSystemButtonBar::setIconSize(const QSize& ic) -{ - d_ptr->mButtonGroup->setIconSize(ic); -} - -QSize SARibbonSystemButtonBar::iconSize() const -{ - return d_ptr->mButtonGroup->iconSize(); -} - -QAction* SARibbonSystemButtonBar::addAction(QAction* a, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) -{ - return d_ptr->mButtonGroup->addAction(a, buttonStyle, popMode); -} - -QAction* SARibbonSystemButtonBar::addAction(const QString& text, - const QIcon& icon, - Qt::ToolButtonStyle buttonStyle, - QToolButton::ToolButtonPopupMode popMode) -{ - return d_ptr->mButtonGroup->addAction(text, icon, buttonStyle, popMode); -} - -QAction* SARibbonSystemButtonBar::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) -{ - return d_ptr->mButtonGroup->addMenu(menu, buttonStyle, popMode); -} - -QAction* SARibbonSystemButtonBar::addSeparator() -{ - return d_ptr->mButtonGroup->addSeparator(); -} - -QAction* SARibbonSystemButtonBar::addWidget(QWidget* w) -{ - return d_ptr->mButtonGroup->addWidget(w); -} - -void SARibbonSystemButtonBar::resizeEvent(QResizeEvent* e) -{ - Q_UNUSED(e); - d_ptr->resizeElement(size()); -} - -void SARibbonSystemButtonBar::closeWindow() -{ - if (parentWidget()) { - parentWidget()->close(); - } -} - -void SARibbonSystemButtonBar::minimizeWindow() -{ - if (parentWidget()) { - parentWidget()->showMinimized(); - } -} - -void SARibbonSystemButtonBar::maximizeWindow() -{ - QWidget* par = parentWidget(); - - if (par) { - if (par->isMaximized()) { - par->showNormal(); - } else { - par->showMaximized(); - } - } -} - -/*** End of inlined file: SARibbonSystemButtonBar.cpp ***/ - -/*** Start of inlined file: SARibbonToolButton.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @def 定义文字换行时2行文本的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR 2.05 - -/** - * @def 定义文字不换行时单行文本的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR 1.2 - -/** - * @def 定义小按钮的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR 1.4 - -/** - * @def 文本宽度估算时的宽度比高度系数 - * - * 超过此系数的宽度时,开始尝试换行,例如按钮高度为h,如果单行文本的宽度大于h*系数,则触发换行估算 - */ -#define SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO 1.4 - -/** - * @def 开启此宏会打印一些常见信息 - */ -#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0 - -#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0 - -#if SARIBBONTOOLBUTTON_DEBUG_DRAW -#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - p.setPen(Qt::red); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) - -#else -#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) -#endif -namespace SA -{ - -QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt) -{ - debug << "==============" << "\nQStyleOption(" << (QStyleOption)opt << ")" - << "\n QStyleOptionComplex:" - "\n subControls(" - << opt.subControls - << " ) " - "\n activeSubControls(" - << opt.activeSubControls - << "\n QStyleOptionToolButton" - "\n features(" - << opt.features - << ")" - "\n toolButtonStyle(" - << opt.toolButtonStyle << ")"; - - return (debug); -} -} - -//=================================================== -// SARibbonToolButtonProxyStyle -//=================================================== - -class SARibbonToolButtonProxyStyle : public QProxyStyle -{ -public: - void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override - { - if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight - || pe == PE_IndicatorArrowLeft) { - if (opt->rect.width() <= 1 || opt->rect.height() <= 1) - return; - - QRect r = opt->rect; - int size = qMin(r.height(), r.width()); - QPixmap pixmap; - qreal pixelRatio = p->device()->devicePixelRatio(); - int border = qRound(pixelRatio * (size / 4)); - int sqsize = qRound(pixelRatio * (2 * (size / 2))); - QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - QPainter imagePainter(&image); - - QPolygon a; - switch (pe) { - case PE_IndicatorArrowUp: - a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2); - break; - case PE_IndicatorArrowDown: - a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2); - break; - case PE_IndicatorArrowRight: - a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); - break; - case PE_IndicatorArrowLeft: - a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); - break; - default: - break; - } - - int bsx = 0; - int bsy = 0; - - if (opt->state & State_Sunken) { - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget); - } - - QRect bounds = a.boundingRect(); - int sx = sqsize / 2 - bounds.center().x() - 1; - int sy = sqsize / 2 - bounds.center().y() - 1; - imagePainter.translate(sx + bsx, sy + bsy); - imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4)); - imagePainter.setBrush(Qt::NoBrush); - - if (!(opt->state & State_Enabled)) { - imagePainter.translate(1, 1); - imagePainter.setPen(QPen(opt->palette.light().color(), 1.4)); - imagePainter.drawPolyline(a); - imagePainter.translate(-1, -1); - imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4)); - } - - imagePainter.drawPolyline(a); - imagePainter.end(); - pixmap = QPixmap::fromImage(image); - pixmap.setDevicePixelRatio(pixelRatio); - - int xOffset = r.x() + (r.width() - size) / 2; - int yOffset = r.y() + (r.height() - size) / 2; - p->drawPixmap(xOffset, yOffset, pixmap); - } else { - QProxyStyle::drawPrimitive(pe, opt, p, widget); - } - } -}; - -//=================================================== -// SARibbonToolButton::PrivateData -//=================================================== - -class SARibbonToolButton::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton) -public: - PrivateData(SARibbonToolButton* p); - // 根据鼠标位置更新按钮的信息 - void updateStatusByMousePosition(const QPoint& pos); - // 更新绘图相关的尺寸 - void updateDrawRect(const QStyleOptionToolButton& opt); - // 更新SizeHint - void updateSizeHint(const QStyleOptionToolButton& opt); - // 计算涉及到的rect尺寸 - void calcDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 计算小按钮模式下的尺寸 - void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 计算大按钮模式下的尺寸 - void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况) - QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const; - // 判断是否有Indicator - bool hasIndicator(const QStyleOptionToolButton& opt) const; - // 计算sizehint - QSize calcSizeHint(const QStyleOptionToolButton& opt); - QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt); - QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt); - - // 计算文本绘制矩形的高度 - int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const; - // 估算一个最优的文本宽度 - int estimateLargeButtonTextWidth(int buttonHeight, - int textDrawRectHeight, - const QString& text, - const QFontMetrics& fm, - float widthHeightRatio = SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO, - int maxTrycount = 3); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; - // 获取文字的对其方式 - int getTextAlignment() const; - // 确认文字是否确切要换行显示 - bool isTextNeedWrap() const; - // 仅仅对\n进行剔除,和QString::simplified不一样 - static QString simplified(const QString& str); - -public: - bool mMouseOnSubControl { false }; ///< 这个用于标记MenuButtonPopup模式下,鼠标在文本区域 - bool mMenuButtonPressed { false }; ///< 由于Indicator改变,因此hitButton不能用QToolButton的hitButton - bool mWordWrap { false }; ///< 标记是否文字换行 @default false - SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton }; - int mSpacing { 1 }; ///< 按钮和边框的距离 - int mIndicatorLen { 8 }; ///< Indicator的长度 - QRect mDrawIconRect; ///< 记录icon的绘制位置 - QRect mDrawTextRect; ///< 记录text的绘制位置 - QRect mDrawIndicatorArrowRect; ///< 记录IndicatorArrow的绘制位置 - QSize mSizeHint; ///< 保存计算好的sizehint - bool mIsTextNeedWrap { false }; ///< 标记文字是否需要换行显示 -public: - static bool s_enableWordWrap; ///< 在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 -}; - -// 静态参数初始化 -bool SARibbonToolButton::PrivateData::s_enableWordWrap = false; - -SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p) -{ - auto proxy = new SARibbonToolButtonProxyStyle(); - proxy->setParent(p); // take ownership to avoid memleak - p->setStyle(proxy); -} - -/** - * @brief 根据鼠标的位置更新状态,主要用于判断鼠标是否位于subcontrol - * - * 此函数主要应用在action menu模式下 - * @param pos - */ -void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos) -{ - bool isMouseOnSubControl(false); - if (SARibbonToolButton::LargeButton == mButtonType) { - isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos); - } else { - // 小按钮模式就和普通toolbutton一样 - isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos); - } - - if (mMouseOnSubControl != isMouseOnSubControl) { - mMouseOnSubControl = isMouseOnSubControl; - // 从icon变到text过程中刷新一次 - q_ptr->update(); - } -} - -/** - * @brief 更新绘图的几个关键尺寸 - * - * 包括: - * - * - DrawIconRect 绘制图标的矩形区域 - * - * - DrawTextRect 绘制文本的矩形区域 - * - * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域 - * - * @param opt - */ -void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt) -{ - if (!mSizeHint.isValid()) { - updateSizeHint(opt); - } - // 先更新IndicatorLen - mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr); - if (mIndicatorLen < 3) { - if (SARibbonToolButton::LargeButton == mButtonType) { - mIndicatorLen = 8; - } else { - mIndicatorLen = 12; // 小按钮模式下设置为10 - } - } - calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen); -} - -/** - * @brief 更新sizehint - * @param opt - */ -void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt) -{ - mSizeHint = calcSizeHint(opt); -} - -/** - * @brief 计算绘图的几个关键区域 - * @param opt - * @param iconRect 绘制图标的矩形区域 - * @param textRect 绘制文本的矩形区域 - * @param indicatorArrowRect 绘制菜单下箭头的矩形区域 - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); - - } else { - calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); - } -} - -/** - * @brief 计算小按钮模式下的绘制尺寸 - * @param opt - * @param iconRect - * @param textRect - * @param indicatorArrowRect - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - switch (opt.toolButtonStyle) { - case Qt::ToolButtonIconOnly: { - if (hasIndicator(opt)) { - // 在仅有图标的小模式显示时,预留一个下拉箭头位置 - iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); - indicatorArrowRect = - QRect(opt.rect.right() - indicatorLen - spacing, iconRect.y(), indicatorLen, iconRect.height()); - } else { - iconRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); - indicatorArrowRect = QRect(); - } - // 文本区域为空 - textRect = QRect(); - } break; - case Qt::ToolButtonTextOnly: { - if (hasIndicator(opt)) { - // 在仅有图标的小模式显示时,预留一个下拉箭头位置 - textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); - indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height()); - } else { - textRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); - indicatorArrowRect = QRect(); - } - // 绘图区域为空 - iconRect = QRect(); - } break; - default: { - bool hasInd = hasIndicator(opt); - // icon Beside和under都是一样的 - QRect buttonRect = q_ptr->rect(); - buttonRect.adjust(spacing, spacing, -spacing, -spacing); - // 先设置IconRect - if (opt.icon.isNull()) { - // 没有图标 - iconRect = QRect(); - } else { - QSize iconSize = adjustIconSize(buttonRect, opt.iconSize); - iconRect = - QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height())); - } - // 后设置TextRect - if (opt.text.isEmpty()) { - textRect = QRect(); - } else { - // 分有菜单和没菜单两种情况 - int adjx = iconRect.isValid() ? (iconRect.width() + spacing) - : 0; // 在buttonRect上变换,因此如果没有图标是不用偏移spacing - if (hasInd) { - textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0); - } else { - textRect = buttonRect.adjusted(adjx, 0, 0, 0); // 在buttonRect上变换,因此如果没有图标是不用偏移spacing - } - } - // 最后设置Indicator - if (hasInd) { - if (textRect.isValid()) { - indicatorArrowRect = - QRect(buttonRect.right() - indicatorLen + 1, textRect.y(), indicatorLen, textRect.height()); - } else if (iconRect.isValid()) { - indicatorArrowRect = - QRect(buttonRect.right() - indicatorLen + 1, iconRect.y(), indicatorLen, iconRect.height()); - } else { - indicatorArrowRect = buttonRect; - } - } else { - indicatorArrowRect = QRect(); - } - } - } -} - -/** - * @brief 计算大按钮模式下的绘制尺寸(普通) - * @param opt - * @param iconRect - * @param textRect - * @param indicatorArrowRect - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - //! 3行模式的图标比较大,文字换行情况下,indicator会动态调整 - // 先获取文字矩形的高度 - int textHeight = calcTextDrawRectHeight(opt); - bool hIndicator = hasIndicator(opt); - if (!hIndicator) { - // 没有菜单,把len设置为0 - indicatorLen = 0; - indicatorArrowRect = QRect(); - } - // 这里要判断文字是否要换行显示,换行显示的文字的indicatorArrowRect所处的位置不一样 - // 先布置textRect - if (isEnableWordWrap()) { - // 在换行模式下 - if (isTextNeedWrap()) { - // 如果文字的确换行,indicator放在最右边 - textRect = QRect( - spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing - indicatorLen, textHeight); - if (hIndicator) { - indicatorArrowRect = - QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2); - } - } else { - // 如果文字不需要换行,indicator在下板行 - textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing, textHeight); - if (hIndicator) { - int dy = textRect.height() / 2; - dy += (dy - indicatorLen) / 2; - indicatorArrowRect = QRect(textRect.x(), textRect.y() + dy, textRect.width(), indicatorLen); - } - } - } else { - // 文字不换行,indicator放在最右边 - int y = opt.rect.bottom() - spacing - textHeight; - if (hIndicator) { - // 先布置indicator - indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight); - textRect = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight); - } else { - textRect = QRect(spacing, y, opt.rect.width() - 2 * spacing, textHeight); - } - } - // 剩下就是icon区域 - iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing); -} - -/** - * @brief 适应iconsize - * @param buttonRect - * @param originIconSize - * @return - */ -QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const -{ - QSize iconSize = originIconSize; - if (iconSize.height() > buttonRect.height()) { - // 说明图标的icon过大,要匹配到buttonRect - iconSize.setHeight(buttonRect.height()); - // 等比例设置宽度 - iconSize.setWidth(originIconSize.width() * iconSize.height() / originIconSize.height()); - } - return iconSize; -} - -/** - * @brief 判断是否有Indicator - * @param opt - * @return - */ -bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const -{ - return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu)); -} - -/** - * @brief 计算sizehint - * - * 此函数非常关键,因为所有尺寸计算都是基于原始的rect来的 - * @param opt - * @return - */ -QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt) -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - return calcLargeButtonSizeHint(opt); - } - return calcSmallButtonSizeHint(opt); -} - -QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt) -{ - int w = 0, h = 0; - - switch (opt.toolButtonStyle) { - case Qt::ToolButtonIconOnly: { - w = opt.iconSize.width() + 2 * mSpacing; - h = opt.iconSize.height() + 2 * mSpacing; - } break; - case Qt::ToolButtonTextOnly: { - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); - textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); - textSize.setHeight(calcTextDrawRectHeight(opt)); - w = textSize.width() + 2 * mSpacing; - h = textSize.height() + 2 * mSpacing; - } break; - default: { - // 先加入icon的尺寸 - w = opt.iconSize.width() + 2 * mSpacing; - h = opt.iconSize.height() + 2 * mSpacing; - // 再加入文本的长度 - if (!opt.text.isEmpty()) { - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); - textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); - textSize.setHeight(calcTextDrawRectHeight(opt)); - w += mSpacing; - w += textSize.width(); - h = qMax(h, (textSize.height() + (2 * mSpacing))); - } else { - // 没有文本的时候也要设置一下高度 - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " "); - h = qMax(h, (textSize.height() + (2 * mSpacing))); - } - } - } - if (hasIndicator(opt)) { - // 存在indicator的按钮,宽度尺寸要扩展 - w += mIndicatorLen; - } - if (w < 16) { - w = 16; - } - //! Qt6.4 取消了QApplication::globalStrut - return QSize(w, h).expandedTo(QSize(2, 2)); -} - -QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt) -{ - int w = 0; - int h = opt.fontMetrics.lineSpacing() * 4.8; // 3*1.6 - int minW = h * 0.75; // 最小宽度,在pannel里面的按钮,最小宽度要和icon适应 - - if (SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(q_ptr->parent())) { - // 对于建立在SARibbonPannel的基础上的大按钮,把高度设置为SARibbonPannel计算的大按钮高度 - h = pannel->largeButtonHeight(); - } - int textHeight = calcTextDrawRectHeight(opt); - // 估算字体的宽度作为宽度 - w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics); - w += (2 * mSpacing); - // 判断是否需要加上indicator - if (isEnableWordWrap() && isTextNeedWrap()) { - w += mIndicatorLen; - } - -#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text - << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5 // - << "\n| | | |-textHeight=" << textHeight // - << "\n| | | |-mDrawIconRect=" << mDrawIconRect // - << "\n| | | |-minW=" << minW // - << "\n| | | |-w=" << w // - ; -#endif - //! Qt6.4 取消了QApplication::globalStrut - return QSize(w, h).expandedTo(QSize(minW, textHeight)); -} - -/** - * @brief 计算文本高度 - * @param opt - * @return - */ -int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - if (isEnableWordWrap()) { - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR + opt.fontMetrics.leading(); - } else { - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR; - } - } - // 小按钮 - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR; -} - -/** - * @brief 估算一个最优的文字尺寸,在可以换行的情况下会进行换行,且只会换一行 - * @param buttonHeight 按钮的高度 - * @param textDrawRectHeight 文本绘制的高度 - * @param fm QFontMetrics - * @param widthHeightRatio 宽高比,宽度/高度的比值,如果大于这个比值,则会进行尝试换行以获取更低的宽度 - * @param maxTrycount 尝试次数 - * @return - */ -int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight, - int textDrawRectHeight, - const QString& text, - const QFontMetrics& fm, - float widthHeightRatio, - int maxTrycount) -{ - QSize textSize; - int space = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2; - int hintMaxWidth = buttonHeight * widthHeightRatio; ///< 建议的宽度 - if (isEnableWordWrap()) { - textSize = fm.size(Qt::TextShowMnemonic, text); - textSize.setWidth(textSize.width() + space); - - if (textSize.height() > fm.lineSpacing() * 1.1) { - //! 说明文字带有换行符,是用户手动换行,这种情况就直接返回字体尺寸,不进行估算 - mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 - return textSize.width(); - } - - // 这时候需要估算文本的长度 - if (textSize.width() <= hintMaxWidth) { - // 范围合理,直接返回 - mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 - return textSize.width(); - } - - //! 大于宽高比尝试进行文字换行 - //! 这里先对文本长度逐渐加长估算,一直到和原来长度一致为止 - int trycount = 0; - int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; - // 对于英文字体,直接宽度减半是无法满足完全显示两行的,需要进行预估 - QRect textRect(0, 0, textSize.width(), textDrawRectHeight); - do { - //! 先计算两行文本的紧凑矩形 - //! 从一半开始逐渐递增 - //! 第1次为 w/2 + w/2 * (0/3) - //! 第2次为 w/2 + w/2 * (1/3) - //! 第3次为 w/2 + w/2 * (2/3) - textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount)); - textRect = fm.boundingRect(textRect, alignment, text); - if (textRect.height() <= (fm.lineSpacing() * 2)) { - // 保证在两行 - mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 - return textRect.width(); - } - ++trycount; -#if SARIBBONTOOLBUTTON_DEBUG_DRAW - if (trycount > 1) { - qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount - << ",textRect=" << textRect; - } -#endif - } while (trycount < 3); - // 到这里说明前面的尝试失败,最终使用原始的长度 - return textSize.width(); - } - - //! 说明是不换行 - - mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 - // 文字不换行情况下,做simplified处理 - textSize = fm.size(Qt::TextShowMnemonic, simplified(text)); - textSize.setWidth(textSize.width() + space); - if (textSize.width() < hintMaxWidth) { - // 范围合理,直接返回 - return textSize.width(); - } - if (textSize.width() > q_ptr->maximumWidth()) { - // 超出了极限,就返回极限 - return q_ptr->maximumWidth(); - } - return hintMaxWidth; -} - -QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const -{ - if (opt.icon.isNull()) { // 没有有图标 - return (QPixmap()); - } - QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - // 添加高分屏支持 - QSize pxiampSize = iconsize - QSize(2, 2); - return opt.icon.pixmap(pxiampSize, mode, state); -} - -int SARibbonToolButton::PrivateData::getTextAlignment() const -{ - int alignment = Qt::TextShowMnemonic; - if (SARibbonToolButton::LargeButton == mButtonType) { - if (isEnableWordWrap()) { - alignment |= Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter; // 换行的情况下,顶部对齐 - } else { - alignment |= Qt::AlignCenter; - } - } else { - alignment |= Qt::AlignCenter; - } - return alignment; -} - -/** - * @brief 确认文字是否确切要换行显示 - * @return - */ -bool SARibbonToolButton::PrivateData::isTextNeedWrap() const -{ - return mIsTextNeedWrap; -} - -/** - * @brief 仅仅对\n进行剔除 - * @param str - * @return - */ -QString SARibbonToolButton::PrivateData::simplified(const QString& str) -{ - QString res = str; - res.remove('\n'); - return res; -} -//=================================================== -// SARibbonToolButton -//=================================================== - -SARibbonToolButton::SARibbonToolButton(QWidget* parent) - : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) -{ - setAutoRaise(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setButtonType(SmallButton); - setMouseTracking(true); -} - -SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent) - : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) -{ - setAutoRaise(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setDefaultAction(defaultAction); - setButtonType(SmallButton); - setMouseTracking(true); -} - -SARibbonToolButton::~SARibbonToolButton() -{ -} - -/** - * @brief 鼠标移动事件 - * - * 由于Ribbon的Indicator和正常的Toolbutton不一样,因此无法用QStyleOptionToolButton的activeSubControls的状态 - * - * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态 - * @param e - */ -void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e) -{ - d_ptr->updateStatusByMousePosition(e->pos()); - QToolButton::mouseMoveEvent(e); -} - -/** - * @brief SARibbonToolButton::mousePressEvent - * @param e - */ -void SARibbonToolButton::mousePressEvent(QMouseEvent* e) -{ - if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) { - d_ptr->updateStatusByMousePosition(e->pos()); - if (d_ptr->mMouseOnSubControl) { - d_ptr->mMenuButtonPressed = true; - showMenu(); - // showmenu结束后,在判断当前的鼠标位置是否是在subcontrol - d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos())); - return; - } - } - d_ptr->mMenuButtonPressed = false; - //! 注意这里要用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent - //! QToolButton的mousePressEvent主要是为了弹出菜单,这里弹出菜单的方式是不一样的,因此不能执行QToolButton的mousePressEvent - QAbstractButton::mousePressEvent(e); -} - -void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e) -{ - d_ptr->mMenuButtonPressed = false; - QToolButton::mouseReleaseEvent(e); -} - -void SARibbonToolButton::focusOutEvent(QFocusEvent* e) -{ - d_ptr->mMouseOnSubControl = false; - QToolButton::focusOutEvent(e); -} - -void SARibbonToolButton::leaveEvent(QEvent* e) -{ - d_ptr->mMouseOnSubControl = false; - QToolButton::leaveEvent(e); -} - -bool SARibbonToolButton::hitButton(const QPoint& pos) const -{ - if (QToolButton::hitButton(pos)) { - return (!d_ptr->mMenuButtonPressed); - } - return (false); -} - -/** - * @brief 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - * @param e - */ -void SARibbonToolButton::resizeEvent(QResizeEvent* e) -{ - // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - QToolButton::resizeEvent(e); - updateRect(); -} - -/** - * @brief toolbutton的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行 - * @note m_sizeHint的刷新需要注意 - * @return - */ -QSize SARibbonToolButton::sizeHint() const -{ - // if (!d_ptr->mSizeHint.isValid()) { // 22是给与sizehint的最小值,如果小于这个值,重新计算一下 - // QStyleOptionToolButton opt; - // initStyleOption(&opt); - // d_ptr->updateSizeHint(opt); - // } -#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| | |-SARibbonToolButton::sizeHint"; -#endif - QStyleOptionToolButton opt; - initStyleOption(&opt); - d_ptr->updateSizeHint(opt); - return d_ptr->mSizeHint; -} - -void SARibbonToolButton::paintEvent(QPaintEvent* e) -{ - Q_UNUSED(e); - QPainter p(this); - QStyleOptionToolButton opt; - initStyleOption(&opt); - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - // 在菜单弹出消失后,需要通过此方法取消掉鼠标停留 - if (!rect().contains(mapFromGlobal(QCursor::pos()))) { - opt.state &= ~QStyle::State_MouseOver; - } - } - paintButton(p, opt); - paintIcon(p, opt, d_ptr->mDrawIconRect); - paintText(p, opt, d_ptr->mDrawTextRect); - paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect); -} - -/** - * @brief 绘制按钮 - * @param p - * @param opt - */ -void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt) -{ - // QStyle::State_Sunken 代表按钮按下去了 - // QStyle::State_On 代表按钮按checked - // QStyle::State_MouseOver 代表当前鼠标位于按钮上面 - QStyleOption tool = opt; - bool autoRaise = opt.state & QStyle::State_AutoRaise; - // 绘制按钮 - if (autoRaise) { - // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态 - // 先绘制一个鼠标不在按钮上的状态 - if (opt.state & QStyle::State_Sunken) { - tool.state &= ~QStyle::State_MouseOver; - } - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - // 针对MenuButtonPopup的ribbon样式的特殊绘制 - if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) { - if (opt.state & QStyle::State_MouseOver) { // 鼠标在按钮上才进行绘制 - if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) { // 按钮的菜单弹出时不做处理 - if (LargeButton == d_ptr->mButtonType) { // 大按钮模式 - if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 - // 鼠标在文字区,把图标显示为正常(就是鼠标不放上去的状态) - tool.rect = d_ptr->mDrawIconRect; - tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 - tool.state &= ~QStyle::State_MouseOver; - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } else { - // 鼠标在图标区,把文字显示为正常 - if (!tool.state.testFlag(QStyle::State_Sunken)) { - // State_Sunken说明此按钮正在按下,这时候,文本区域不需要绘制,只有在非按下状态才需要绘制 - tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 - tool.state &= ~QStyle::State_MouseOver; - // 文字和Indicator都显示正常 - tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect); - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } - } - } else { // 小按钮模式 - if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 - // 鼠标在文字区,把图标和文字显示为正常 - tool.rect = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect); - tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } else { - // 鼠标在图标区,把文字显示为正常 - tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 - // 文字和Indicator都显示正常 - tool.rect = d_ptr->mDrawIndicatorArrowRect; - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } - } - } - } - } - // 绘制Focus - // if (opt.state & QStyle::State_HasFocus) { - // QStyleOptionFocusRect fr; - // fr.QStyleOption::operator=(opt); - // fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing); - // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this); - // } -} - -/** - * @brief 绘制图标 - * @param p - * @param opt - */ -void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) -{ - if (!iconDrawRect.isValid()) { - return; - } - - QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); - style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect); -} - -/** - * @brief 绘制文本 - * @param p - * @param opt - */ -void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect) -{ - int alignment = d_ptr->getTextAlignment(); - - if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { - alignment |= Qt::TextHideMnemonic; - } - QString text; - if (isSmallRibbonButton()) { - text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); - } else { - if (!isEnableWordWrap()) { - text = opt.fontMetrics.elidedText( - PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); - } else { - text = opt.text; - } - } - //! 以下内容参考QCommonStyle.cpp - //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const - //! case CC_ToolButton: - QStyle::State bflags = opt.state & ~QStyle::State_Sunken; - if (bflags & QStyle::State_AutoRaise) { - if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { - bflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - if (opt.activeSubControls & QStyle::SC_ToolButton) { - bflags |= QStyle::State_Sunken; - } - } - QStyleOptionToolButton label = opt; - label.state = bflags; - style()->drawItemText( - &p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect); -} - -/** - * @brief 绘制Indicator - * @param p - * @param opt - */ -void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect) -{ - if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) { - return; - } - - QStyleOption tool = opt; - tool.rect = indicatorDrawRect; - style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect); -} - -void SARibbonToolButton::drawArrow(const QStyle* style, - const QStyleOptionToolButton* toolbutton, - const QRect& rect, - QPainter* painter, - const QWidget* widget) -{ - QStyle::PrimitiveElement pe; - - switch (toolbutton->arrowType) { - case Qt::LeftArrow: - pe = QStyle::PE_IndicatorArrowLeft; - break; - - case Qt::RightArrow: - pe = QStyle::PE_IndicatorArrowRight; - break; - - case Qt::UpArrow: - pe = QStyle::PE_IndicatorArrowUp; - break; - - case Qt::DownArrow: - pe = QStyle::PE_IndicatorArrowDown; - break; - - default: - return; - } - QStyleOption arrowOpt = *toolbutton; - - arrowOpt.rect = rect; - style->drawPrimitive(pe, &arrowOpt, painter, widget); -} - -void SARibbonToolButton::actionEvent(QActionEvent* e) -{ - QToolButton::actionEvent(e); - updateRect(); -} - -/** - * @brief 按钮样式 - * @sa setButtonType - * @return - */ -SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const -{ - return (d_ptr->mButtonType); -} - -/** - * @brief 设置按钮样式 - * @note 设置按钮样式过程会调用setToolButtonStyle,如果要改变toolButtonStyle,如设置为Qt::ToolButtonIconOnly,需要在此函数之后设置 - * @param buttonType - */ -void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType) -{ - d_ptr->mButtonType = buttonType; - // 计算iconrect - // 根据字体计算文字的高度 - - if (LargeButton == buttonType) { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - } else { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - } - - updateRect(); -} - -/** - * @brief 是否是小按钮 - * @return - */ -bool SARibbonToolButton::isSmallRibbonButton() const -{ - return (d_ptr->mButtonType == SmallButton); -} - -/** - * @brief 是否是大按钮 - * @return - */ -bool SARibbonToolButton::isLargeRibbonButton() const -{ - return (d_ptr->mButtonType == LargeButton); -} - -QSize SARibbonToolButton::minimumSizeHint() const -{ - return (sizeHint()); -} - -/** - * @brief 间距是几个重要矩形的间隔 - * @return - */ -int SARibbonToolButton::spacing() const -{ - return d_ptr->mSpacing; -} - -void SARibbonToolButton::updateRect() -{ - QStyleOptionToolButton opt; - initStyleOption(&opt); - d_ptr->updateDrawRect(opt); -} - -/** - * @brief 设置在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 - * @param on - */ -void SARibbonToolButton::setEnableWordWrap(bool on) -{ - SARibbonToolButton::PrivateData::s_enableWordWrap = on; -} - -/** - * @brief 在lite模式下是否允许文字换行 - * @return - */ -bool SARibbonToolButton::isEnableWordWrap() -{ - return SARibbonToolButton::PrivateData::s_enableWordWrap; -} - -bool SARibbonToolButton::event(QEvent* e) -{ - switch (e->type()) { - case QEvent::WindowDeactivate: - d_ptr->mMouseOnSubControl = false; - break; - case QEvent::ActionChanged: - case QEvent::ActionRemoved: - case QEvent::ActionAdded: { - d_ptr->mMouseOnSubControl = false; - updateRect(); - } break; - default: - break; - } - - return (QToolButton::event(e)); -} - -void SARibbonToolButton::changeEvent(QEvent* e) -{ - if (e) { - if (e->type() == QEvent::FontChange) { - // 说明字体改变,需要重新计算和字体相关的信息 - updateRect(); - } - } - QToolButton::changeEvent(e); -} - -/*** End of inlined file: SARibbonToolButton.cpp ***/ - -/*** Start of inlined file: SARibbonColorToolButton.cpp ***/ -#include -#include -#include - -//=================================================== -// SARibbonColorToolButton::PrivateData -//=================================================== -const int c_ribbonbutton_color_height = 5; ///< 颜色块的高度 - -class SARibbonColorToolButton::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton) -public: - PrivateData(SARibbonColorToolButton* p); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; - QIcon createColorIcon(const QColor& c, const QSize& size) const; - -public: - QColor mColor; ///< 记录颜色 - SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon }; ///< 颜色显示样式 - QIcon mOldIcon; ///< 记录旧的icon -}; - -SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p) -{ -} - -QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const -{ - if (opt.icon.isNull()) { // 没有有图标 - return QPixmap(); - } - // 有icon,在icon下方加入颜色 - QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1); - QPixmap pixmap = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state); - QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4)); // 宽度上,颜色块多出2px - res.fill(Qt::transparent); - QPainter painter(&res); - int xpixmap = (res.width() - pixmap.width()) / 2; - int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height()) - / 2; // 这里要减去2而不是1,这样奇数偶数都不会影响 - int w = pixmap.width(); - int h = pixmap.height(); - QRect rpixmap = QRect(xpixmap, ypixmap, w, h); - painter.drawPixmap(rpixmap, pixmap); - QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1); - if (mColor.isValid()) { - painter.fillRect(colorRect, mColor); - } else { - QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - painter.setRenderHint(QPainter::Antialiasing, true); - int ss = colorRect.width() / 3; - painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); - pen.setColor(Qt::black); - painter.setPen(pen); - painter.drawRect(colorRect); - } - return res; -} - -QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const -{ - QPixmap res(size); - res.fill(Qt::transparent); - QPainter painter(&res); - if (c.isValid()) { - painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c); - } else { - QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap); - painter.setPen(pen); - painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2)); - pen.setColor(Qt::red); - painter.setPen(pen); - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - painter.setRenderHint(QPainter::Antialiasing, true); - painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1)); - } - return QIcon(res); -} - -//=================================================== -// SARibbonColorToolButton -//=================================================== - -SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent) - : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) -{ - connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); -} - -SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent) - : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) -{ - connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); -} - -SARibbonColorToolButton::~SARibbonColorToolButton() -{ -} - -/** - * @brief 获取按钮维护的颜色 - * @return - */ -QColor SARibbonColorToolButton::color() const -{ - return d_ptr->mColor; -} - -/** - * @brief 设置颜色显示的样式 - * @param s - */ -void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s) -{ - if (d_ptr->mColorStyle == s) { - return; - } - d_ptr->mColorStyle = s; - if (ColorUnderIcon == s) { - setIcon(d_ptr->mOldIcon); - } else { - d_ptr->mOldIcon = icon(); - setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32))); - } - repaint(); -} - -/** - * @brief 颜色显示的样式 - * @return - */ -SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const -{ - return d_ptr->mColorStyle; -} - -/** - * @brief 建立标准的颜色菜单 - * @return - */ -SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu() -{ - setPopupMode(QToolButton::MenuButtonPopup); - SAColorMenu* m = new SAColorMenu(this); - m->enableNoneColorAction(true); - QAction* customColor = m->customColorAction(); - if (customColor) { - customColor->setIcon(QIcon(":/image/resource/define-color.svg")); - } - connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor); - setMenu(m); - - updateRect(); - return m; -} - -/** - * @brief 设置按钮的颜色 - * - * 此时会生成一个新的icon - * @note 会发射@sa colorChanged 信号 - * @param c - */ -void SARibbonColorToolButton::setColor(const QColor& c) -{ - if (d_ptr->mColor != c) { - d_ptr->mColor = c; - if (ColorFillToIcon == colorStyle()) { - setIcon(d_ptr->createColorIcon(c, QSize(32, 32))); - } - repaint(); - Q_EMIT colorChanged(c); - } -} - -void SARibbonColorToolButton::onButtonClicked(bool checked) -{ - Q_EMIT colorClicked(d_ptr->mColor, checked); -} - -/** - * @brief 重写paintIcon函数,把颜色加到icon下面 - * @param p - * @param opt - * @param iconDrawRect - */ -void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) -{ - if (ColorUnderIcon == colorStyle()) { - // 有icon - QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); - style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); - } else { - SARibbonToolButton::paintIcon(p, opt, iconDrawRect); - } -} - -/*** End of inlined file: SARibbonColorToolButton.cpp ***/ - -/*** Start of inlined file: SARibbonLineWidgetContainer.cpp ***/ -#include - -SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget* par) : QWidget(par), m_innerWidget(nullptr) -{ - m_labelPrefix = new QLabel(this); - m_labelSuffix = new QLabel(this); - QHBoxLayout* lay = new QHBoxLayout(); - - lay->setContentsMargins(0, 0, 0, 0); - lay->setSpacing(0); - lay->addWidget(m_labelPrefix); - lay->addWidget(m_labelSuffix); - setLayout(lay); -} - -SARibbonLineWidgetContainer::~SARibbonLineWidgetContainer() -{ -} - -void SARibbonLineWidgetContainer::setWidget(QWidget* innerWidget) -{ - QHBoxLayout* lay = static_cast< QHBoxLayout* >(layout()); - - if (m_innerWidget) { - lay->replaceWidget(m_innerWidget, innerWidget); - } else { - lay->insertWidget(1, innerWidget); - } - m_innerWidget = innerWidget; -} - -void SARibbonLineWidgetContainer::setPrefix(const QString& str) -{ - m_labelPrefix->setText(str); -} - -void SARibbonLineWidgetContainer::setSuffix(const QString& str) -{ - m_labelSuffix->setText(str); -} - -QLabel* SARibbonLineWidgetContainer::labelPrefix() const -{ - return (m_labelPrefix); -} - -QLabel* SARibbonLineWidgetContainer::labelSuffix() const -{ - return (m_labelSuffix); -} - -/*** End of inlined file: SARibbonLineWidgetContainer.cpp ***/ - -/*** Start of inlined file: SARibbonActionsManager.cpp ***/ -#include -#include -#include -#include - -class SARibbonActionsManager::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager) -public: - PrivateData(SARibbonActionsManager* p); - void clear(); - - QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList - QMap< int, QString > mTagToName; ///< tag对应的名字 - QHash< QString, QAction* > mKeyToAction; ///< key对应action - QMap< QAction*, QString > mActionToKey; ///< action对应key - QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用 - int mSale; ///< 盐用于生成固定的id,在用户不主动设置key时,id基于msale生成,只要SARibbonActionsManager的调用registeAction顺序不变,生成的id都不变,因为它是基于自增实现的 -}; - -SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0) -{ -} - -void SARibbonActionsManager::PrivateData::clear() -{ - mTagToActions.clear(); - mTagToName.clear(); - mKeyToAction.clear(); - mActionToKey.clear(); - mTagToCategory.clear(); - mSale = 0; -} - -SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar) - : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this)) -{ - autoRegisteActions(bar); -} - -SARibbonActionsManager::~SARibbonActionsManager() -{ -} - -/** - * @brief 设置tag对应的名字,通过这个可以得到tag和文本的映射 - * @param tag - * @param name - * @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字 - */ -void SARibbonActionsManager::setTagName(int tag, const QString& name) -{ - d_ptr->mTagToName[ tag ] = name; -} - -/** - * @brief 获取tag对应的中文名字 - * @param tag - * @return - */ -QString SARibbonActionsManager::tagName(int tag) const -{ - return (d_ptr->mTagToName.value(tag, "")); -} - -/** - * @brief 移除tag - * @note 注意,这个函数非常耗时 - * @param tag - */ -void SARibbonActionsManager::removeTag(int tag) -{ - QList< QAction* > oldacts = actions(tag); - - // 开始移除 - d_ptr->mTagToActions.remove(tag); - d_ptr->mTagToName.remove(tag); - // 开始查找需要移出总表的action - QList< QAction* > needRemoveAct; - QList< QAction* > total; - - for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { - total += i.value(); - } - for (QAction* a : qAsConst(oldacts)) { - if (!total.contains(a)) { - needRemoveAct.append(a); - } - } - // 从总表移除action - for (QAction* a : qAsConst(needRemoveAct)) { - auto i = d_ptr->mActionToKey.find(a); - if (i != d_ptr->mActionToKey.end()) { - d_ptr->mKeyToAction.remove(i.value()); - d_ptr->mActionToKey.erase(i); - } - } -} - -/** - * @brief 把action注册到管理器中,实现action的管理 - * @param act - * @param tag tag是可以按照位进行叠加,见 @ref ActionTag 如果 - * 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值, - * registeAction的tag是直接记录进去的,如果要多个标签并存,在registe之前先或好tag - * @param key key是action对应的key,一个key只对应一个action,是查找action的关键 - * ,默认情况为一个QString(),这时key是QAction的objectName - * @param enableEmit 控制是否发射@ref actionTagChanged 信号 - * @note 同一个action多次注册不同的tag可以通过tag索引到action,但通过action只能索引到最后一个注册的tag - * @note tag的新增会触发actionTagChanged信号 - */ -bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit) -{ - if (nullptr == act) { - return (false); - } - QString k = key; - - if (k.isEmpty()) { - k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName()); - } - if (d_ptr->mKeyToAction.contains(k)) { - qWarning() << "key: " - << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction"; - return (false); - } - d_ptr->mKeyToAction[ k ] = act; - d_ptr->mActionToKey[ act ] = k; - // 记录tag 对 action - bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号 - d_ptr->mTagToActions[ tag ].append(act); - // 绑定槽 - connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); - if (isneedemit && enableEmit) { - // 说明新增tag - Q_EMIT actionTagChanged(tag, false); - } - return (true); -} - -/** - * @brief 取消action的注册 - * - * 如果tag对应的最后一个action被撤销,tag也将一块删除 - * @param act - * @note tag的删除会触发actionTagChanged信号 - * @note 如果action关联了多个tag,这些tag里的action都会被删除,对应的key也同理 - */ -void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit) -{ - if (nullptr == act) { - return; - } - // 绑定槽 - disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); - removeAction(act, enableEmit); -} - -/** - * @brief 移除action - * - * 仅移除内存内容 - * @param act - * @param enableEmit - */ -void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit) -{ - QList< int > deletedTags; // 记录删除的tag,用于触发actionTagChanged - QMap< int, QList< QAction* > > tagToActions; ///< tag : QList - - for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { - // 把不是act的内容转移到tagToActions和tagToActionKeys中,之后再和m_d里的替换 - auto tmpi = tagToActions.insert(i.key(), QList< QAction* >()); - int count = 0; - for (int j = 0; j < i.value().size(); ++j) { - if (i.value()[ j ] != act) { - tmpi.value().append(act); - ++count; - } - } - if (0 == count) { - // 说明这个tag没有内容 - tagToActions.erase(tmpi); - deletedTags.append(i.key()); - } - } - // 删除mKeyToAction - QString key = d_ptr->mActionToKey.value(act); - - d_ptr->mActionToKey.remove(act); - d_ptr->mKeyToAction.remove(key); - - // 置换 - d_ptr->mTagToActions.swap(tagToActions); - // 发射信号 - if (enableEmit) { - for (int tagdelete : qAsConst(deletedTags)) { - Q_EMIT actionTagChanged(tagdelete, true); - } - } -} - -/** - * @brief 等同actions - * @param tag - * @return - */ -QList< QAction* >& SARibbonActionsManager::filter(int tag) -{ - return (actions(tag)); -} - -/** - * @brief 根据tag得到actions - * @param tag - * @return - */ -QList< QAction* >& SARibbonActionsManager::actions(int tag) -{ - return (d_ptr->mTagToActions[ tag ]); -} - -const QList< QAction* > SARibbonActionsManager::actions(int tag) const -{ - return (d_ptr->mTagToActions[ tag ]); -} - -/** - * @brief 获取所有的标签 - * @return - */ -QList< int > SARibbonActionsManager::actionTags() const -{ - return (d_ptr->mTagToActions.keys()); -} - -/** - * @brief 通过key获取action - * @param key - * @return 如果没有key,返回nullptr - */ -QAction* SARibbonActionsManager::action(const QString& key) const -{ - return (d_ptr->mKeyToAction.value(key, nullptr)); -} - -/** - * @brief 通过action找到key - * @param act - * @return 如果找不到,返回QString() - */ -QString SARibbonActionsManager::key(QAction* act) const -{ - return (d_ptr->mActionToKey.value(act, QString())); -} - -/** - * @brief 返回所有管理的action数 - * @return - */ -int SARibbonActionsManager::count() const -{ - return (d_ptr->mKeyToAction.size()); -} - -/** - * @brief 返回所有管理的actions - * @return - */ -QList< QAction* > SARibbonActionsManager::allActions() const -{ - return (d_ptr->mKeyToAction.values()); -} - -/** - * @brief 自动加载SARibbonBar的action - * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object,找到action注册, - * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类 - * - * 此函数会把所有category下的action生成tag并注册,返回的QMap是记录了category对应的tag - * - * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action,但不在任何一个category下的作为NotInRibbonCategoryTag标签注册,默认名字会赋予not - * in ribbon, 可以通过@ref setTagName 改变 - * - * @param w - * @return - * @note 此函数的调用最好在category设置了标题后调用,因为会以category的标题作为标签的命名 - */ -QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar) -{ - QMap< int, SARibbonCategory* > res; - // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象,把所有action找到 - QWidget* parWidget = bar->parentWidget(); - QSet< QAction* > mainwindowActions; - if (parWidget) { - for (QObject* o : qAsConst(parWidget->children())) { - if (QAction* a = qobject_cast< QAction* >(o)) { - // 说明是action - if (!a->objectName().isEmpty()) { - mainwindowActions.insert(a); - } - } - } - } - // 开始遍历每个category,加入action - - if (nullptr == bar) { - // 非ribbon模式,直接退出 - return (res); - } - QSet< QAction* > categoryActions; - QList< SARibbonCategory* > categorys = bar->categoryPages(); - int tag = AutoCategoryDistinguishBeginTag; - - for (SARibbonCategory* c : qAsConst(categorys)) { - QList< SARibbonPannel* > pannels = c->pannelList(); - for (SARibbonPannel* p : qAsConst(pannels)) { - categoryActions += autoRegisteWidgetActions(p, tag, false); - } - setTagName(tag, c->categoryName()); - res[ tag ] = c; - ++tag; - } - // 找到不在功能区的actions - QSet< QAction* > notincategory = mainwindowActions - categoryActions; - - for (QAction* a : qAsConst(notincategory)) { - if (!a->objectName().isEmpty()) { - registeAction(a, NotInRibbonCategoryTag, a->objectName(), false); - } - } - if (notincategory.size() > 0) { - setTagName(NotInRibbonCategoryTag, tr("not in ribbon")); - } - for (auto i = res.begin(); i != res.end(); ++i) { - connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged); - } - d_ptr->mTagToCategory = res; - return (res); -} - -/** - * @brief 自动加载action - * @param w - * @param tag - * @param enableEmit - * @return 返回成功加入SARibbonActionsManager管理的action - */ -QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit) -{ - QSet< QAction* > res; - QList< QAction* > was = w->actions(); - - for (QAction* a : qAsConst(was)) { - if (res.contains(a) || a->objectName().isEmpty()) { - // 重复内容不重复加入 - // 没有object name不加入 - continue; - } - if (registeAction(a, tag, a->objectName(), enableEmit)) { - res.insert(a); - } - } - return (res); -} - -/** - * @brief 根据标题查找action - * @param text - * @return - */ -QList< QAction* > SARibbonActionsManager::search(const QString& text) -{ - QList< QAction* > res; - - if (text.isEmpty()) { - return (res); - } - QStringList kws = text.split(" "); - - if (kws.isEmpty()) { - kws.append(text); - } - QList< QAction* > acts = d_ptr->mActionToKey.keys(); - - for (const QString& k : qAsConst(kws)) { - for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) { - if (i.key()->text().contains(k, Qt::CaseInsensitive)) { - res.append(i.key()); - } - } - } - return (res); -} - -void SARibbonActionsManager::clear() -{ - d_ptr->clear(); -} - -/** - * @brief action 被delete时,将触发此槽把管理的action删除 - * @param o - * @note 这个函数不会触发actionTagChanged信号 - */ -void SARibbonActionsManager::onActionDestroyed(QObject* o) -{ - QAction* act = static_cast< QAction* >(o); - - removeAction(act, false); -} - -/** - * @brief autoRegisteActions函数会关联此槽,在标签内容改变时改变tag 对应 文本 - * @param title - */ -void SARibbonActionsManager::onCategoryTitleChanged(const QString& title) -{ - SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender()); - - if (nullptr == c) { - return; - } - int tag = d_ptr->mTagToCategory.key(c, -1); - - if (tag == -1) { - return; - } - setTagName(tag, title); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonActionsModel -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -class SARibbonActionsManagerModel::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel) -public: - PrivateData(SARibbonActionsManagerModel* p); - void updateRef(); - int count() const; - QAction* at(int index); - bool isNull() const; - -public: - SARibbonActionsManager* mMgr { nullptr }; - int mTag { SARibbonActionsManager::CommonlyUsedActionTag }; - QString mSeatchText; - QList< QAction* > mActions; -}; - -SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p) -{ -} - -void SARibbonActionsManagerModel::PrivateData::updateRef() -{ - if (isNull()) { - return; - } - if (!mSeatchText.isEmpty()) { - mActions = mMgr->search(mSeatchText); - } else { - mActions = mMgr->actions(mTag); - } -} - -int SARibbonActionsManagerModel::PrivateData::count() const -{ - if (isNull()) { - return (0); - } - return (mActions.size()); -} - -QAction* SARibbonActionsManagerModel::PrivateData::at(int index) -{ - if (isNull()) { - return (nullptr); - } - if (index >= count()) { - return (nullptr); - } - return (mActions.at(index)); -} - -bool SARibbonActionsManagerModel::PrivateData::isNull() const -{ - return (mMgr == nullptr); -} - -//=================================================== -// SARibbonActionsManagerModel -//=================================================== - -SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p) - : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) -{ -} - -SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p) - : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) -{ - setupActionsManager(m); -} - -SARibbonActionsManagerModel::~SARibbonActionsManagerModel() -{ -} - -int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const -{ - if (parent.isValid()) { // 非顶层 - return (0); - } - // 顶层 - return (d_ptr->count()); -} - -QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - Q_UNUSED(section); - if (role != Qt::DisplayRole) { - return (QVariant()); - } - if (Qt::Horizontal == orientation) { - return (tr("action name")); - } - return (QVariant()); -} - -Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const -{ - if (!index.isValid()) { - return (Qt::NoItemFlags); - } - return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); -} - -QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const -{ - QAction* act = indexToAction(index); - - if (nullptr == act) { - return (QVariant()); - } - - switch (role) { - case Qt::DisplayRole: { - QString str = act->text(); - if (!str.isEmpty()) { - return str; - } - // 如果文本为空,一般是没有设置文本,或者是窗口,如果是窗口,那么用windowtitle作为文本 - if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) { - if (QWidget* w = wa->defaultWidget()) { - str = w->windowTitle(); - } - } - return str; - } - case Qt::DecorationRole: - return (act->icon()); - - default: - break; - } - return (QVariant()); -} - -void SARibbonActionsManagerModel::setFilter(int tag) -{ - d_ptr->mTag = tag; - update(); -} - -void SARibbonActionsManagerModel::update() -{ - beginResetModel(); - d_ptr->updateRef(); - endResetModel(); -} - -void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m) -{ - d_ptr->mMgr = m; - d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; - d_ptr->mActions = m->filter(d_ptr->mTag); - connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); - update(); -} - -void SARibbonActionsManagerModel::uninstallActionsManager() -{ - if (!d_ptr->isNull()) { - disconnect( - d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); - d_ptr->mMgr = nullptr; - d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; - } - update(); -} - -QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const -{ - if (!index.isValid()) { - return (nullptr); - } - if (index.row() >= d_ptr->count()) { - return (nullptr); - } - return (d_ptr->at(index.row())); -} - -/** - * @brief 查找 - * @param text - */ -void SARibbonActionsManagerModel::search(const QString& text) -{ - d_ptr->mSeatchText = text; - update(); -} - -void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete) -{ - if (isdelete && (tag == d_ptr->mTag)) { - d_ptr->mTag = SARibbonActionsManager::UnknowActionTag; - update(); - } else { - if (tag == d_ptr->mTag) { - update(); - } - } -} - -/*** End of inlined file: SARibbonActionsManager.cpp ***/ - -/*** Start of inlined file: SARibbonLineEdit.cpp ***/ -#include - -SARibbonLineEdit::SARibbonLineEdit(QWidget* parent) : QLineEdit(parent) -{ -} - -SARibbonLineEdit::SARibbonLineEdit(const QString& text, QWidget* parent) : QLineEdit(text, parent) -{ -} - -SARibbonLineEdit::~SARibbonLineEdit() -{ -} - -/*** End of inlined file: SARibbonLineEdit.cpp ***/ - -/*** Start of inlined file: SARibbonCheckBox.cpp ***/ -#include - -SARibbonCheckBox::SARibbonCheckBox(QWidget* parent) : QCheckBox(parent) -{ -} - -SARibbonCheckBox::SARibbonCheckBox(const QString& text, QWidget* parent) : QCheckBox(text, parent) -{ -} - -SARibbonCheckBox::~SARibbonCheckBox() -{ -} - -/*** End of inlined file: SARibbonCheckBox.cpp ***/ - -/*** Start of inlined file: SARibbonComboBox.cpp ***/ -SARibbonComboBox::SARibbonComboBox(QWidget* parent) : QComboBox(parent) -{ -} - -SARibbonComboBox::~SARibbonComboBox() -{ -} - -/*** End of inlined file: SARibbonComboBox.cpp ***/ - -/*** Start of inlined file: SARibbonButtonGroupWidget.cpp ***/ -#include -#include -#include -#include -#include -#include -#include - -//=================================================== -// SARibbonButtonGroupWidget::PrivateData -//=================================================== -class SARibbonButtonGroupWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonButtonGroupWidget) -public: - PrivateData(SARibbonButtonGroupWidget* p); - void init(); - void removeAction(QAction* a); - -public: - QSize mIconSize { 20, 20 }; -}; - -SARibbonButtonGroupWidget::PrivateData::PrivateData(SARibbonButtonGroupWidget* p) : q_ptr(p) -{ -} - -void SARibbonButtonGroupWidget::PrivateData::init() -{ - QHBoxLayout* layout = new QHBoxLayout(q_ptr); - // 上下保留一点间隙 - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(1); - q_ptr->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); -} - -void SARibbonButtonGroupWidget::PrivateData::removeAction(QAction* a) -{ - QLayout* lay = q_ptr->layout(); - int c = lay->count(); - QList< QLayoutItem* > willRemoveItems; - for (int i = 0; i < c; ++i) { - QLayoutItem* item = lay->itemAt(i); - SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); - if (nullptr == btn) { - continue; - } - if (a == btn->defaultAction()) { - willRemoveItems.push_back(item); - } - } - // 从尾部删除 - for (auto i = willRemoveItems.rbegin(); i != willRemoveItems.rend(); ++i) { - lay->removeItem(*i); - } -} - -//=================================================== -// SARibbonButtonGroupWidget -//=================================================== - -SARibbonButtonGroupWidget::SARibbonButtonGroupWidget(QWidget* parent) - : QFrame(parent), d_ptr(new SARibbonButtonGroupWidget::PrivateData(this)) -{ - d_ptr->init(); -} - -SARibbonButtonGroupWidget::~SARibbonButtonGroupWidget() -{ -} - -/** - * @brief 设置图标尺寸 - * @param iconSize - */ -void SARibbonButtonGroupWidget::setIconSize(const QSize& ic) -{ - d_ptr->mIconSize = ic; - iterate([ ic ](SARibbonControlButton* btn) -> bool { - btn->setIconSize(ic); - return true; - }); -} - -/** - * @brief 图标尺寸 - * @return - */ -QSize SARibbonButtonGroupWidget::iconSize() const -{ - return d_ptr->mIconSize; -} - -QAction* SARibbonButtonGroupWidget::addAction(QAction* a, - Qt::ToolButtonStyle buttonStyle, - QToolButton::ToolButtonPopupMode popMode) -{ - SARibbonPannel::setActionToolButtonStyleProperty(a, buttonStyle); - SARibbonPannel::setActionToolButtonPopupModeProperty(a, popMode); - QWidget::addAction(a); - return (a); -} - -/** - * @brief 生成action - * @note action的所有权归SARibbonButtonGroupWidget - * @param text - * @param icon - * @param popMode - * @return - */ -QAction* SARibbonButtonGroupWidget::addAction(const QString& text, - const QIcon& icon, - Qt::ToolButtonStyle buttonStyle, - QToolButton::ToolButtonPopupMode popMode) -{ - QAction* a = new QAction(icon, text, this); - addAction(a, buttonStyle, popMode); - return (a); -} - -QAction* SARibbonButtonGroupWidget::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) -{ - QAction* a = menu->menuAction(); - addAction(a, buttonStyle, popMode); - return (a); -} - -QAction* SARibbonButtonGroupWidget::addSeparator() -{ - QAction* a = new QAction(this); - - a->setSeparator(true); - addAction(a); - return (a); -} - -QAction* SARibbonButtonGroupWidget::addWidget(QWidget* w) -{ - QWidgetAction* a = new QWidgetAction(this); - - a->setDefaultWidget(w); - w->setAttribute(Qt::WA_Hover); - addAction(a); - return (a); -} - -SARibbonControlButton* SARibbonButtonGroupWidget::actionToRibbonControlToolButton(QAction* action) -{ - SARibbonControlButton* res = nullptr; - iterate([ &res, action ](SARibbonControlButton* btn) -> bool { - if (btn->defaultAction() == action) { - res = btn; - return false; // 返回false退出迭代 - } - return true; - }); - return (res); -} - -QSize SARibbonButtonGroupWidget::sizeHint() const -{ - return (layout()->sizeHint()); -} - -QSize SARibbonButtonGroupWidget::minimumSizeHint() const -{ - return (layout()->minimumSize()); -} - -/** - * @brief 此函数会遍历SARibbonButtonGroupWidget下的所有SARibbonControlButton,执行函数指针(bool(SARibbonControlButton*)),函数指针返回false则停止迭代 - * @param fp - * @return 中途迭代退出返回false - */ -bool SARibbonButtonGroupWidget::iterate(SARibbonButtonGroupWidget::FpButtonIterate fp) -{ - QLayout* lay = layout(); - int c = lay->count(); - for (int i = 0; i < c; ++i) { - auto item = lay->itemAt(i); - if (!item) { - continue; - } - SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); - if (!btn) { - continue; - } - if (!fp(btn)) { - return false; - } - } - return true; -} - -/** - * @brief 处理action的事件 - * - * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 - * ActionAdded时会生成窗口 - * @param e - */ -void SARibbonButtonGroupWidget::actionEvent(QActionEvent* e) -{ - QAction* a = e->action(); - if (!a) { - return; - } - - switch (e->type()) { - case QEvent::ActionAdded: { - QWidget* w = nullptr; - if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(a)) { - widgetAction->setParent(this); - w = widgetAction->requestWidget(this); - if (w != nullptr) { - w->setAttribute(Qt::WA_LayoutUsesWidgetRect); - w->show(); - } - } else if (a->isSeparator()) { - SARibbonSeparatorWidget* sp = RibbonSubElementFactory->createRibbonSeparatorWidget(this); - w = sp; - } - // 不是widget,自动生成ButtonTyle - if (!w) { - SARibbonControlButton* button = RibbonSubElementFactory->createRibbonControlButton(this); - button->setAutoRaise(true); - button->setIconSize(d_ptr->mIconSize); - button->setFocusPolicy(Qt::NoFocus); - button->setDefaultAction(a); - // 属性设置 - QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(a); - button->setPopupMode(popMode); - Qt::ToolButtonStyle buttonStyle = SARibbonPannel::getActionToolButtonStyleProperty(a); - button->setToolButtonStyle(buttonStyle); - // 根据QAction的属性设置按钮的大小 - - connect(button, &SARibbonToolButton::triggered, this, &SARibbonButtonGroupWidget::actionTriggered); - w = button; - } - layout()->addWidget(w); - updateGeometry(); - } break; - - case QEvent::ActionChanged: { - // 让布局重新绘制 - layout()->invalidate(); - updateGeometry(); - } break; - - case QEvent::ActionRemoved: { - d_ptr->removeAction(e->action()); - updateGeometry(); - } break; - - default: - break; - } - QFrame::actionEvent(e); -} - -/*** End of inlined file: SARibbonButtonGroupWidget.cpp ***/ - -/*** Start of inlined file: SARibbonStackedWidget.cpp ***/ -#include -#include -#include -#include -#include - -/** - * @brief The SARibbonStackedWidgetPrivate class - */ -class SARibbonStackedWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonStackedWidget) -public: - QEventLoop* eventLoop { nullptr }; - -public: - PrivateData(SARibbonStackedWidget* p) : q_ptr(p) - { - } - - void init() - { - // Parent->setFocusPolicy(Qt::StrongFocus); - } -}; - -SARibbonStackedWidget::SARibbonStackedWidget(QWidget* parent) - : QStackedWidget(parent), d_ptr(new SARibbonStackedWidget::PrivateData(this)) -{ - d_ptr->init(); - setNormalMode(); -} - -SARibbonStackedWidget::~SARibbonStackedWidget() -{ - if (d_ptr->eventLoop) { - d_ptr->eventLoop->exit(); - } -} - -void SARibbonStackedWidget::setPopupMode() -{ - setMouseTracking(true); - setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); - setFrameShape(QFrame::Panel); -} - -bool SARibbonStackedWidget::isPopupMode() const -{ - return (windowFlags() & Qt::Popup); -} - -void SARibbonStackedWidget::setNormalMode() -{ - if (d_ptr->eventLoop) { - d_ptr->eventLoop->exit(); - d_ptr->eventLoop = nullptr; - } - setMouseTracking(false); - setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); - setFrameShape(QFrame::NoFrame); -} - -bool SARibbonStackedWidget::isNormalMode() const -{ - return (!isPopupMode()); -} - -void SARibbonStackedWidget::exec() -{ - show(); - if (!isPopupMode()) { - d_ptr->eventLoop = nullptr; - return; - } - QEventLoop event; - - d_ptr->eventLoop = &event; - event.exec(); - d_ptr->eventLoop = nullptr; -} - -void SARibbonStackedWidget::moveWidget(int from, int to) -{ - QWidget* w = widget(from); - - removeWidget(w); - insertWidget(to, w); -} - -void SARibbonStackedWidget::hideEvent(QHideEvent* e) -{ - if (isPopupMode()) { - if (d_ptr->eventLoop) { - d_ptr->eventLoop->exit(); - } - } - setFocus(); - Q_EMIT hidWindow(); - - QStackedWidget::hideEvent(e); -} - -void SARibbonStackedWidget::resizeEvent(QResizeEvent* e) -{ - QStackedWidget::resizeEvent(e); - for (int i = 0; i < count(); ++i) { - if (i == currentIndex()) { - continue; - } - QEvent* layE = new QEvent(QEvent::LayoutRequest); - QApplication::postEvent(widget(i), layE); - } -} - -/*** End of inlined file: SARibbonStackedWidget.cpp ***/ - -/*** Start of inlined file: SARibbonSeparatorWidget.cpp ***/ -#include -#include -#include -#include -#include - -SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent) -{ - setFrameShape(QFrame::VLine); - setFrameShadow(QFrame::Plain); - - if (QScreen* screen = QApplication::primaryScreen()) { - qreal dpr = screen->physicalDotsPerInch() / screen->logicalDotsPerInch(); - int scaledLineWidth = qRound(1.0 * dpr); // 假设基础 lineWidth 是 1 - setLineWidth(scaledLineWidth); - // qDebug() << "SARibbonSeparatorWidget:" << scaledLineWidth; - } else { - setLineWidth(1); - } -} - -SARibbonSeparatorWidget::~SARibbonSeparatorWidget() -{ -} - -/*** End of inlined file: SARibbonSeparatorWidget.cpp ***/ - -/*** Start of inlined file: SARibbonCtrlContainer.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -/** - * @brief The SARibbonCtrlContainerPrivate class - */ -class SARibbonCtrlContainer::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCtrlContainer) -public: - QWidget* mContainerWidget { nullptr }; - QHBoxLayout* mHorizontalLayout { nullptr }; - QLabel* mLabelPixmap { nullptr }; - QLabel* mLabelText { nullptr }; - QSize mIconSize { 24, 24 }; - QIcon mIcon; - PrivateData(SARibbonCtrlContainer* p) : q_ptr(p) - { - mHorizontalLayout = new QHBoxLayout(p); - mHorizontalLayout->setSpacing(0); - mHorizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); - mHorizontalLayout->setContentsMargins(0, 0, 0, 0); - - mLabelPixmap = new QLabel(p); - mLabelPixmap->setObjectName(QString::fromUtf8("labelPixmap")); - mLabelPixmap->setAlignment(Qt::AlignCenter); - mHorizontalLayout->addWidget(mLabelPixmap); - - mLabelText = new QLabel(p); - mLabelText->setObjectName(QString::fromUtf8("labelText")); - mHorizontalLayout->addWidget(mLabelText); - } - - void setContainerWidget(QWidget* w) - { - if (mContainerWidget) { - // 原来有widget - QWidget* oldwidget = mContainerWidget; - takeContainerWidget(oldwidget); - oldwidget->deleteLater(); - } - mContainerWidget = w; - if (!w) { - return; - } - if (w->parent() != q_ptr) { - w->setParent(q_ptr); - } - // ContainerWidget永远在最右边 - mHorizontalLayout->insertWidget(mHorizontalLayout->count(), w); - QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - w->setSizePolicy(sizePolicy); - } - - void takeContainerWidget(QWidget* w) - { - int i = mHorizontalLayout->indexOf(w); - if (i >= 0) { - QLayoutItem* item = mHorizontalLayout->takeAt(i); - delete item; - } - } -}; - -//=================================================== -// SARibbonCtrlContainer -//=================================================== -SARibbonCtrlContainer::SARibbonCtrlContainer(QWidget* parent) - : QWidget(parent), d_ptr(new SARibbonCtrlContainer::PrivateData(this)) -{ -} - -SARibbonCtrlContainer::~SARibbonCtrlContainer() -{ -} - -QWidget* SARibbonCtrlContainer::containerWidget() -{ - return (d_ptr->mContainerWidget); -} - -const QWidget* SARibbonCtrlContainer::containerWidget() const -{ - return (d_ptr->mContainerWidget); -} - -void SARibbonCtrlContainer::setEnableShowIcon(bool b) -{ - d_ptr->mLabelPixmap->setVisible(b); -} - -void SARibbonCtrlContainer::setEnableShowTitle(bool b) -{ - d_ptr->mLabelText->setVisible(b); -} - -/** - * @brief 判断是否存在容器窗口 - * @return - */ -bool SARibbonCtrlContainer::hasContainerWidget() const -{ - return (d_ptr->mContainerWidget != nullptr); -} - -/** - * @brief 设置图标 - * @param i - */ -void SARibbonCtrlContainer::setIcon(const QIcon& i) -{ - d_ptr->mIcon = i; - QPixmap pixmap = i.pixmap(d_ptr->mIconSize); - d_ptr->mLabelPixmap->setPixmap(pixmap); -} - -void SARibbonCtrlContainer::setIcon(const QPixmap& pixmap) -{ - d_ptr->mLabelPixmap->setPixmap(pixmap); -} - -/** - * @brief 获取图标 - * @return - */ -QIcon SARibbonCtrlContainer::icon() const -{ - return d_ptr->mIcon; -} - -/** - * @brief 设置文字 - * @param t - */ -void SARibbonCtrlContainer::setText(const QString& t) -{ - d_ptr->mLabelText->setText(t); -} - -/** - * @brief 获取文字 - * @return - */ -QString SARibbonCtrlContainer::text() const -{ - return d_ptr->mLabelText->text(); -} - -void SARibbonCtrlContainer::setContainerWidget(QWidget* w) -{ - d_ptr->setContainerWidget(w); -} - -/** - * @brief 获取显示icon的窗口 - * @return - */ -QWidget* SARibbonCtrlContainer::iconWidget() const -{ - return d_ptr->mLabelPixmap; -} - -/*** End of inlined file: SARibbonCtrlContainer.cpp ***/ - -/*** Start of inlined file: SARibbonQuickAccessBar.cpp ***/ -#include - -/** - * @brief The SARibbonQuickAccessBarPrivate class - */ -class SARibbonQuickAccessBar::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonQuickAccessBar) -public: - PrivateData(SARibbonQuickAccessBar* p); - -public: - SARibbonButtonGroupWidget* mGroupWidget { nullptr }; -}; - -SARibbonQuickAccessBar::PrivateData::PrivateData(SARibbonQuickAccessBar* p) : q_ptr(p) -{ -} - -//=================================================== -// SARibbonQuickAccessBar -//=================================================== -SARibbonQuickAccessBar::SARibbonQuickAccessBar(QWidget* parent) - : SARibbonCtrlContainer(parent), d_ptr(new SARibbonQuickAccessBar::PrivateData(this)) -{ - d_ptr->mGroupWidget = new SARibbonButtonGroupWidget(this); - d_ptr->mGroupWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); - setContainerWidget(d_ptr->mGroupWidget); -} - -SARibbonQuickAccessBar::~SARibbonQuickAccessBar() -{ -} - -void SARibbonQuickAccessBar::addSeparator() -{ - d_ptr->mGroupWidget->addSeparator(); -} - -void SARibbonQuickAccessBar::addAction(QAction* act, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) -{ - d_ptr->mGroupWidget->addAction(act, buttonStyle, popMode); -} - -void SARibbonQuickAccessBar::addWidget(QWidget* w) -{ - d_ptr->mGroupWidget->addWidget(w); -} - -void SARibbonQuickAccessBar::addMenu(QMenu* m, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) -{ - d_ptr->mGroupWidget->addMenu(m, buttonStyle, popMode); -} - -/** - * @brief 获取内部管理的ButtonGroupWidget - * @return - */ -SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() -{ - return d_ptr->mGroupWidget; -} - -/** - * @brief 获取内部管理的ButtonGroupWidget - * @return - */ -const SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() const -{ - return d_ptr->mGroupWidget; -} - -/** - * @brief 图标尺寸 - * @param s - */ -void SARibbonQuickAccessBar::setIconSize(const QSize& s) -{ - d_ptr->mGroupWidget->setIconSize(s); -} - -/** - * @brief SARibbonQuickAccessBar::iconSize - * @return - */ -QSize SARibbonQuickAccessBar::iconSize() const -{ - return d_ptr->mGroupWidget->iconSize(); -} - -/*** End of inlined file: SARibbonQuickAccessBar.cpp ***/ - -/*** Start of inlined file: SARibbonTabBar.cpp ***/ -#include -#include - -SARibbonTabBar::SARibbonTabBar(QWidget* parent) : QTabBar(parent), m_tabMargin(6, 0, 0, 0) -{ - setExpanding(false); -} - -SARibbonTabBar::~SARibbonTabBar() -{ -} - -const QMargins& SARibbonTabBar::tabMargin() const -{ - return (m_tabMargin); -} - -void SARibbonTabBar::setTabMargin(const QMargins& tabMargin) -{ - m_tabMargin = tabMargin; -} - -/** - * @brief tab的尺寸预估 - * - * 有别于系统默认的tabbar,SARibbonTabBar的tab高度和tabbar高度一致,且不考虑纵向分布情况 - * @param index - * @return - */ -QSize SARibbonTabBar::tabSizeHint(int index) const -{ - if (index < 0) { - return QSize(); - } - QStyleOptionTab opt; - initStyleOption(&opt, index); - int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this); - const QFontMetrics fm = fontMetrics(); - - int widgetWidth = 0; - int padding = 0; - if (!opt.leftButtonSize.isEmpty()) { - padding += 4; - widgetWidth += opt.leftButtonSize.width(); - } - if (!opt.rightButtonSize.isEmpty()) { - padding += 4; - widgetWidth += opt.rightButtonSize.width(); - } - if (!opt.icon.isNull()) { - padding += 4; - } - const int textWidth = fm.size(Qt::TextShowMnemonic, opt.text).width(); - QSize csz = QSize(textWidth + opt.iconSize.width() + hframe + widgetWidth + padding, height()); - return style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this); -} - -/*** End of inlined file: SARibbonTabBar.cpp ***/ - -/*** Start of inlined file: SARibbonControlButton.cpp ***/ -SARibbonControlButton::SARibbonControlButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonControlButton::~SARibbonControlButton() -{ -} -SARibbonControlToolButton::SARibbonControlToolButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonControlToolButton::~SARibbonControlToolButton() -{ -} - -/*** End of inlined file: SARibbonControlButton.cpp ***/ - -/*** Start of inlined file: SARibbonMenu.cpp ***/ -#include - -SARibbonMenu::SARibbonMenu(QWidget* parent) : QMenu(parent) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); -} - -SARibbonMenu::SARibbonMenu(const QString& title, QWidget* parent) : QMenu(title, parent) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); -} - -SARibbonMenu::~SARibbonMenu() -{ -} - -QAction* SARibbonMenu::addRibbonMenu(SARibbonMenu* menu) -{ - return (QMenu::addMenu(menu)); -} - -SARibbonMenu* SARibbonMenu::addRibbonMenu(const QString& title) -{ - SARibbonMenu* menu = new SARibbonMenu(title, this); - - return (menu); -} - -SARibbonMenu* SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title) -{ - SARibbonMenu* menu = new SARibbonMenu(title, this); - - menu->setIcon(icon); - return (menu); -} - -QAction* SARibbonMenu::addWidget(QWidget* w) -{ - QWidgetAction* action = new QWidgetAction(this); - - action->setDefaultWidget(w); - addAction(action); - return (action); -} - -/*** End of inlined file: SARibbonMenu.cpp ***/ - -/*** Start of inlined file: SARibbonPannelOptionButton.cpp ***/ -#include - -SARibbonPannelOptionButton::SARibbonPannelOptionButton(QWidget* parent) : QToolButton(parent) -{ - setAutoRaise(true); - setCheckable(false); - setToolButtonStyle(Qt::ToolButtonIconOnly); - setIconSize(QSize(10, 10)); - static QIcon s_default_icon = QIcon(":/image/resource/ribbonPannelOptionButton.png"); - setIcon(s_default_icon); -} - -SARibbonPannelOptionButton::~SARibbonPannelOptionButton() -{ -} - -/*** End of inlined file: SARibbonPannelOptionButton.cpp ***/ - -/*** Start of inlined file: SARibbonPannelItem.cpp ***/ -SARibbonPannelItem::SARibbonPannelItem(QWidget* widget) - : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large) -{ -} - -SARibbonPannelItem::~SARibbonPannelItem() -{ -} - -bool SARibbonPannelItem::isEmpty() const -{ - return (action == nullptr || !action->isVisible()); -} - -/*** End of inlined file: SARibbonPannelItem.cpp ***/ - -/*** Start of inlined file: SARibbonPannelLayout.cpp ***/ -#include -#include - -#define SARibbonPannelLayout_DEBUG_PRINT 1 -#define HELP_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - QPen _pen(Qt::DashLine); \ - _pen.setColor(Qt::blue); \ - p.setPen(_pen); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) - -SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true) -{ - setSpacing(1); - SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p); - - if (!tb) { - return; - } -} - -SARibbonPannelLayout::~SARibbonPannelLayout() -{ - // 参考QToolBarLayout - while (!mItems.isEmpty()) { - SARibbonPannelItem* item = mItems.takeFirst(); - if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) { - if (item->customWidget) { - widgetAction->releaseWidget(item->widget()); - } - } - delete item; - } -} - -/** - * @brief 通过action查找索引,用于actionEvent添加action用 - * @param action - * @return 没有查到返回-1 - */ -int SARibbonPannelLayout::indexByAction(QAction* action) const -{ - for (int i = 0; i < mItems.count(); ++i) { - if (mItems.at(i)->action == action) { - return (i); - } - } - return (-1); -} - -/** - * @brief 获取ribbonpannel - * @return - */ -SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const -{ - return qobject_cast< SARibbonPannel* >(parentWidget()); -} - -void SARibbonPannelLayout::addItem(QLayoutItem* item) -{ - Q_UNUSED(item); - qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead"); - return; -} - -/** - * @brief SARibbonPannel主要通过此函数来添加action - * @param act - * @param rp 布局策略 - */ -void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp) -{ - index = qMax(0, index); - index = qMin(mItems.count(), index); - SARibbonPannelItem* item = createItem(act, rp); - - if (item) { - mItems.insert(index, item); - // 标记需要重新计算尺寸 - invalidate(); - } -} - -/** - * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - * @param action - * @note 要去除OptionAction直接传入nullptr即可 - * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction - */ -void SARibbonPannelLayout::setOptionAction(QAction* action) -{ - SARibbonPannel* p = ribbonPannel(); - if (!p) { - return; - } - if (action) { - // 创建option action - if (nullptr == mOptionActionBtn) { - mOptionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p); - QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered); - // 确保m_optionActionBtn在label之上 - if (mTitleLabel) { - mTitleLabel->stackUnder(mOptionActionBtn); - } - } - mOptionActionBtn->setDefaultAction(action); - if (action->icon().isNull()) { - mOptionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); - } - // 标记需要重新计算尺寸 - invalidate(); - } else { - // 取消option action - if (mOptionActionBtn) { - mOptionActionBtn->hide(); - mOptionActionBtn->deleteLater(); - mOptionActionBtn = nullptr; - // 标记需要重新计算尺寸 - invalidate(); - } - } -} - -/** - * @brief 判断是否存在OptionAction - * @return 存在返回true - */ -bool SARibbonPannelLayout::isHaveOptionAction() const -{ - return (mOptionActionBtn != nullptr); -} - -QLayoutItem* SARibbonPannelLayout::itemAt(int index) const -{ - if ((index < 0) || (index >= mItems.count())) { - return (nullptr); - } - return (mItems.at(index)); -} - -QLayoutItem* SARibbonPannelLayout::takeAt(int index) -{ - if ((index < 0) || (index >= mItems.count())) { - return (nullptr); - } - SARibbonPannelItem* item = mItems.takeAt(index); - - QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action); - - if ((widgetAction != 0) && item->customWidget) { - widgetAction->releaseWidget(item->widget()); - } else { - // destroy the QToolButton/QToolBarSeparator - item->widget()->hide(); - item->widget()->deleteLater(); - } - - invalidate(); - return (item); -} - -int SARibbonPannelLayout::count() const -{ - return (mItems.count()); -} - -bool SARibbonPannelLayout::isEmpty() const -{ - - return (mItems.isEmpty()); -} - -void SARibbonPannelLayout::invalidate() -{ - mDirty = true; - QLayout::invalidate(); -} - -Qt::Orientations SARibbonPannelLayout::expandingDirections() const -{ - return (Qt::Horizontal); -} - -QSize SARibbonPannelLayout::minimumSize() const -{ - return (mSizeHint); -} - -QSize SARibbonPannelLayout::sizeHint() const -{ -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonPannel* pannel = ribbonPannel()) { - qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint; - } -#endif - return (mSizeHint); -} - -/** - * @brief 通过action获取SARibbonPannelItem - * @param action - * @return 如果没有返回nullptr - */ -SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const -{ - int index = indexByAction(action); - - if (index >= 0) { - return (mItems[ index ]); - } - return (nullptr); -} - -/** - * @brief 获取最后一个添加的item - * @return 如果没有返回nullptr - */ -SARibbonPannelItem* SARibbonPannelLayout::lastItem() const -{ - if (mItems.isEmpty()) { - return (nullptr); - } - return (mItems.last()); -} - -/** - * @brief 获取最后生成的窗口 - * @return 如果无窗口或者item为空,返回nullptr - */ -QWidget* SARibbonPannelLayout::lastWidget() const -{ - SARibbonPannelItem* item = lastItem(); - - if (item) { - return (item->widget()); - } - return (nullptr); -} - -/** - * @brief 移动两个item - * @param from - * @param to - * @note 移动完后所有都失效,需要重新布局 - */ -void SARibbonPannelLayout::move(int from, int to) -{ - if (from == to) { - return; - } - if (to < 0) { - to = 0; - } - if (to >= count()) { - to = count() - 1; - } - mItems.move(from, to); - invalidate(); -} - -/** - * @brief 判断是否需要重新布局 - * @return - */ -bool SARibbonPannelLayout::isDirty() const -{ - return (mDirty); -} - -void SARibbonPannelLayout::updateGeomArray() -{ - updateGeomArray(geometry()); -} - -/** - * @brief 布局所有action - */ -void SARibbonPannelLayout::doLayout() -{ -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonPannel* pannel = ribbonPannel()) { - qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName(); - } -#endif - if (isDirty()) { - updateGeomArray(); - } - QList< QWidget* > showWidgets, hideWidgets; - SARibbonPannel* pannel = ribbonPannel(); - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if (item->isEmpty()) { - hideWidgets << item->widget(); - } else { - // 在category发现item->setGeometry有点奇怪的现象,这里统一使用item->widget->setgeo - // item->setGeometry(item->itemWillSetGeometry); - if (item->widget()) { - item->widget()->setGeometry(item->itemWillSetGeometry); - } - showWidgets << item->widget(); - } - } - - // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 - for (QWidget* w : qAsConst(showWidgets)) { - if (!w->isVisible()) - w->show(); - } - for (QWidget* w : qAsConst(hideWidgets)) { - if (w->isVisible()) - w->hide(); - } - // 布局label - if (mTitleLabel) { - if (isEnableShowPannelTitle()) { - mTitleLabel->setGeometry(mTitleLabelGeometry); - if (!mTitleLabel->isVisibleTo(pannel)) { - mTitleLabel->show(); - } - } else { - if (mTitleLabel->isVisibleTo(pannel)) { - mTitleLabel->hide(); - } - } - } - // 布局m_optionActionBtn - if (mOptionActionBtn) { - mOptionActionBtn->setGeometry(mOptionActionBtnGeometry); - mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height())); - } -} - -/** - * @brief 把action转换为item - * - * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action) - * - * 对于普通QAction,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考SARibbonPannelItem::RowProportion, - * @param action - * @param rp 行高占比情况 - * @return 转换的SARibbonPannelItem - * @note 每个SARibbonPannelItem最终都会携带一个widget,传入的是QWidgetAction的话,会直接使用QWidgetAction带的widget, - * 否则会内部生成一个SARibbonToolButton - * - */ -SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - bool customWidget = false; - QWidget* widget = nullptr; - SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); - - if (!pannel) { - // 在没有pannel这个函数会返回nullptr - return (nullptr); - } - if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) { - widget = widgetAction->requestWidget(pannel); - if (widget != nullptr) { - widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); - customWidget = true; // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton - } - } else if (action->isSeparator()) { - SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel); - widget = sep; - } - // 不是widget,自动生成SARibbonToolbutton - if (!widget) { - SARibbonToolButton::RibbonButtonType buttonType = - ((rp == SARibbonPannelItem::Large) ? SARibbonToolButton::LargeButton : SARibbonToolButton::SmallButton); - - SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel); - button->setFocusPolicy(Qt::NoFocus); - button->setButtonType(buttonType); - button->setDefaultAction(action); - button->setIconSize(mDefaultToolButtonIconSize); - // 属性设置 - QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action); - button->setPopupMode(popMode); - // 根据QAction的属性设置按钮的大小 - - QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered); - widget = button; - } - // 这时总会有widget - widget->hide(); - SARibbonPannelItem* result = new SARibbonPannelItem(widget); - - result->rowProportion = rp; - result->customWidget = customWidget; - result->action = action; - return (result); -} - -/** - * @brief 更新尺寸 - */ -void SARibbonPannelLayout::updateGeomArray(const QRect& setrect) -{ - SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); - - if (!pannel) { - return; - } - - const int height = setrect.height(); - const QMargins& mag = contentsMargins(); - const int spacing = this->spacing(); - const int spacingRow = 1; // 高度间距,也就是行间距,不同行之间的距离 - int x = mag.left(); - const int yBegin = mag.top(); - int titleH = (mTitleHeight >= 0) ? mTitleHeight : 0; // 防止负数影响 - int titleSpace = (mTitleHeight >= 0) ? mTitleSpace : 0; // 对于没有标题的情况,spacing就不生效 - if (!isEnableShowPannelTitle()) { - titleH = 0; - titleSpace = 0; - } - // 获取pannel的布局模式 3行或者2行 - // rowcount 是ribbon的行,有2行和3行两种 - const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2; - // largeHeight是对应large占比的高度 - const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace; - const int yTitleBegin = height - mag.bottom() - titleH; - mLargeHeight = largeHeight; - // 计算smallHeight的高度 - const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount; - // Medium行的y位置 - const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3)); - const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow) - : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight); - // Small行的y位置 - const int ySmallRow0 = yBegin; - const int ySmallRow1 = yBegin + smallHeight + spacingRow; - const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow); - // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行, - // item->rowIndex主要用于SARibbonPannelItem::Medium - short row = 0; - int column = 0; - // 记录每列最大的宽度 - int columMaxWidth = 0; - // 记录总宽度 - int totalWidth = 0; - - int itemCount = mItems.count(); - -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - QString debug_print__log__; -#endif - // 本列第一、二行占比 - SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None; - SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item - for (int i = 0; i < itemCount; ++i) { - SARibbonPannelItem* item = mItems.at(i); - if (item->isEmpty()) { - // 如果是hide就直接跳过 - item->rowIndex = -1; - item->columnIndex = -1; - continue; - } - - QSize hint = item->sizeHint(); -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) { - auto ss__ = tb->sizeHint(); - debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n") - .arg(i) - .arg(ss__.width()) - .arg(ss__.height()) - .arg(tb->text()); - } -#endif - Qt::Orientations exp = item->expandingDirections(); - if (item->widget()) { - // 有窗口是水平扩展,则标记为扩展 - if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { - mExpandFlag = true; - } - } - SARibbonPannelItem::RowProportion rp = item->rowProportion; - if (SARibbonPannelItem::None == rp) { - // 为定义行占比但是垂直扩展,就定义为Large占比,否则就是small占比 - if (exp & Qt::Vertical) { - rp = SARibbonPannelItem::Large; - } else { - rp = SARibbonPannelItem::Small; - } - } - // 开始根据占比和layoutmode来布局 - switch (rp) { - case SARibbonPannelItem::Large: { - // !!在Large,如果不是处于新列的第一行,就需要进行换列处理 - // 把large一直设置在下一列的开始 - if (row != 0) { - x += (columMaxWidth + spacing); - ++column; - } - // - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight); - columMaxWidth = hint.width(); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } break; - - case SARibbonPannelItem::Medium: { - // 2行模式下Medium和small等价 - if (2 == rowCount) { - if (0 == row) { - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - // 下个row为1 - row = 1; - // x不变 - } else { - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } else { - // 3行模式 - if (0 == row) { - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - row = 1; - // x不变 - } else if (1 == row) { - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } else { - // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium - // 这时需要先换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - ++column; - // 换列后此时等价于0 == row - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - row = 1; - } - } - } break; - - case SARibbonPannelItem::Small: { - if (0 == row) { - // 第一行 - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Small; - columMaxWidth = hint.width(); - // 下个row为1 - row = 1; - // x不变 - } else if (1 == row) { - // 第二行 - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight); - if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { - // 三行模式,并且第一行是Medium - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - } - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 这里要看两行还是三行,确定是否要换列 - if (2 == rowCount) { - // 两行模式,换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } else { - // 三行模式,继续增加行数 - row = 2; - // x不变 - } - if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { - // 三行模式,并且第一行是Medium,换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } else { - // 第三行 - item->rowIndex = 2; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight); - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } break; - - default: - // 不可能出现 - break; - } - lastGeomItem = item; - } - // 最后一个元素,更新列数 - // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常 - if (lastGeomItem) { // 最后一个元素,更新totalWidth - if (lastGeomItem->columnIndex != column) { - // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引 - mColumnCount = column; - // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth要减去已经加入的spacing - totalWidth = x - spacing + mag.right(); - } else { - // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1 - mColumnCount = column + 1; - // 由于最后一个元素未触发换列,需要计算totalWidth - totalWidth = x + columMaxWidth + mag.right(); - } - } - - // 在设置完所有窗口后,再设置扩展属性的窗口 - if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) { - // 说明可以设置扩展属性的窗口 - recalcExpandGeomArray(setrect); - } - // 布局label - bool isTitleWidthThanPannel = false; - if (isEnableShowPannelTitle()) { - mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH); - // 这里要确认标题宽度是否大于totalWidth,如果大于,则要把标题的宽度作为totalwidth - QFontMetrics fm = mTitleLabel->fontMetrics(); - int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName()); - textWidth += 4; - if (totalWidth < textWidth) { - totalWidth = textWidth; - isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度 - } - } - // 布局optionActionButton - - if (isHaveOptionAction()) { - QSize optBtnSize = optionActionButtonSize(); - if (isEnableShowPannelTitle()) { - // 有标题 - mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(), - mTitleLabelGeometry.y(), - mTitleLabelGeometry.height(), - mTitleLabelGeometry.height()); - - // 特殊情况,如果pannel的标题长度大于totalWidth,那么说明totalWidth比较短 - // 这时候,optionActionBtn的宽度要加上到标题宽度上 - if (isTitleWidthThanPannel) { - // 由于文字是居中对齐,因此要扩展2个按钮的宽度 - totalWidth += (2 * titleH); - } - } else { - // 无标题 - mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(), - setrect.bottom() - optBtnSize.height() - mag.bottom(), - optBtnSize.width(), - optBtnSize.height()); - totalWidth += optBtnSize.width(); - } - } - // 刷新sizeHint - int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH); - this->mSizeHint = QSize(totalWidth, heightHint); -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName() - << "\n| | |-size hint =" << this->m_sizeHint // - << "\n| | |-totalWidth=" << totalWidth // - << "\n| | |-last x=" << x // - << "\n| | |-columMaxWidth=" << columMaxWidth // - << "\n| | |-spacing=" << spacing // - << "\n| | |-mag=" << mag // - << "\n| | |-largeHeight=" << largeHeight // - << "\n| | |-smallHeight=" << smallHeight // - ; - qDebug().noquote() << debug_print__log__; -#endif -} - -void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect) -{ - // 计算能扩展的尺寸 - int expandwidth = setrect.width() - this->mSizeHint.width(); - - if (expandwidth <= 0) { - // 没有必要设置 - return; - } - // 列扩展信息 - struct _columnExpandInfo - { - int oldColumnWidth = 0; ///< 原来的列宽 - int columnMaximumWidth = -1; ///< 列的最大宽度 - int columnExpandedWidth = 0; ///< 扩展后列的宽度 - QList< SARibbonPannelItem* > expandItems; - }; - // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展 - QMap< int, _columnExpandInfo > columnExpandInfo; - - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) { - // 只获取可见的 - QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex); - if (i == columnExpandInfo.end()) { - i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo()); - } - i.value().expandItems.append(item); - } - } - if (columnExpandInfo.size() <= 0) { - // 没有需要扩展的就退出 - return; - } - // 获取完可扩展的列和控件后,计算对应的列的尺寸 - // 计算能扩展的尺寸 - int oneColCanexpandWidth = expandwidth / columnExpandInfo.size(); - - for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) { - int& oldColumnWidth = i.value().oldColumnWidth; - int& columnMaximumWidth = i.value().columnMaximumWidth; - this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth); - if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) { - // 如果小于0说明没有这个列,这种属于异常,删除继续 - // oldColumnWidth > columnMaximumWidth也是异常 - i = columnExpandInfo.erase(i); - continue; - } - // 开始调整 - int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了 - if (colwidth >= columnMaximumWidth) { - // 过最大宽度要求 - i.value().columnExpandedWidth = columnMaximumWidth; - } else { - i.value().columnExpandedWidth = colwidth; - } - ++i; - } - // 从新调整尺寸 - // 由于会涉及其他列的变更,因此需要所有都遍历一下 - for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) { - int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth; - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if (item->isEmpty() || (item->columnIndex < i.key())) { - // 之前的列不用管 - continue; - } - if (item->columnIndex == i.key()) { - // 此列的扩展 - if (i.value().expandItems.contains(item)) { - // 此列需要扩展的item才扩展尺寸 - item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth); - } else { - // 此列不扩展的模块保持原来的尺寸 - continue; - } - } else { - // 后面的移动 - item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen); - } - } - } -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect - << ") pannelName=" << ribbonPannel()->pannelName() // - << ",expandwidth=" << expandwidth // - ; -#endif -} - -/** - * @brief 根据列数,计算窗口的宽度,以及最大宽度 - * @param colindex - * @param width 如果传入没有这个列,返回-1 - * @param maximum 如果传入没有这个列,返回-1 - */ -void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const -{ - width = -1; - maximum = -1; - for (SARibbonPannelItem* item : mItems) { - if (!item->isEmpty() && (item->columnIndex == colindex)) { - width = qMax(width, item->itemWillSetGeometry.width()); - maximum = qMax(maximum, item->widget()->maximumWidth()); - } - } -} - -SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const -{ - return mTitleLabel; -} - -void SARibbonPannelLayout::setToolButtonIconSize(const QSize& s) -{ - mDefaultToolButtonIconSize = s; -} - -QSize SARibbonPannelLayout::toolButtonIconSize() const -{ - return mDefaultToolButtonIconSize; -} - -/** - * @brief 获取optionAction 按钮尺寸 - * @return - */ -QSize SARibbonPannelLayout::optionActionButtonSize() const -{ - return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight)); -} - -void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel) -{ - mTitleLabel = newTitleLabel; - // 确保m_optionActionBtn在label之上 - if (mOptionActionBtn) { - if (mTitleLabel) { - mTitleLabel->stackUnder(mOptionActionBtn); - } - } -} - -/** - * @brief 标题区域和按钮的间隔 - * @return - */ -int SARibbonPannelLayout::pannelTitleSpace() const -{ - return mTitleSpace; -} - -/** - * @brief 设置标题区域和按钮的间隔 - * @param newTitleSpace - */ -void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace) -{ - if (mTitleSpace == newTitleSpace) { - return; - } - mTitleSpace = newTitleSpace; - invalidate(); -} - -/** - * @brief 标题高度 - * @return - */ -int SARibbonPannelLayout::pannelTitleHeight() const -{ - return mTitleHeight; -} - -/** - * @brief 设置标题高度 - * @param newTitleHeight - */ -void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight) -{ - if (mTitleHeight == newTitleHeight) { - return; - } - mTitleHeight = newTitleHeight; - invalidate(); -} - -/** - * @brief 判断是否存在标题 - * @return - */ -bool SARibbonPannelLayout::isEnableShowPannelTitle() const -{ - return mEnableShowTitle; -} - -/** - * @brief 设置显示标题 - * @param on - */ -void SARibbonPannelLayout::setEnableShowPannelTitle(bool on) -{ - if (mEnableShowTitle == on) { - return; - } - mEnableShowTitle = on; - invalidate(); -} - -/** - * @brief 大按钮的高度 - * @return - */ -int SARibbonPannelLayout::largeButtonHeight() const -{ - return mLargeHeight; -} - -void SARibbonPannelLayout::setGeometry(const QRect& rect) -{ - QRect old = geometry(); - if (old == rect) { - return; - } -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")======="; -#endif - QLayout::setGeometry(rect); - mDirty = false; - updateGeomArray(rect); - doLayout(); -} - -/*** End of inlined file: SARibbonPannelLayout.cpp ***/ - -/*** Start of inlined file: SARibbonPannel.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SARibbonPannel_DEBUG_PRINT -#define SARibbonPannel_DEBUG_PRINT 0 -#endif - -#define HELP_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - QPen _pen(Qt::DashLine); \ - _pen.setColor(Qt::blue); \ - p.setPen(_pen); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) -//=============================================================== -// SARibbonPannelLabel -//=============================================================== - -SARibbonPannelLabel::SARibbonPannelLabel(QWidget* parent) : QLabel(parent) -{ -} - -//=============================================================== -// SARibbonPannel::PrivateData -//=============================================================== - -class SARibbonPannel::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonPannel) -public: - PrivateData(SARibbonPannel* p); - // 根据m_pannelLayoutMode返回gridLayout应该增加的行数 - int rowadded(); - void createLayout(); - // 获取layout - SARibbonPannelLayout* pannelLayout() const; - // 返回最后一个添加的action对应的button,前提是最后一个是toolbutton,否则返回nullptr - SARibbonToolButton* lastAddActionButton(); - // 重置labe的字体,这个主要是为了让pannel的标题字体能适应标题高度 - void resetTitleLabelFont(); - // 标题 - QString pannelName() const; - void setPannelName(const QString& title); - -public: - bool m_isCanCustomize { true }; ///< 记录是否可自定义 - SARibbonPannel::PannelLayoutMode m_pannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< pannel的布局模式,默认为3行模式ThreeRowMode - SARibbonPannelOptionButton* m_optionActionButton { nullptr }; ///< 标题栏的y距离 - SARibbonPannelLabel* m_label { nullptr }; -}; - -SARibbonPannel::PrivateData::PrivateData(SARibbonPannel* p) : q_ptr(p) -{ - createLayout(); -} - -int SARibbonPannel::PrivateData::rowadded() -{ - switch (m_pannelLayoutMode) { - case SARibbonPannel::ThreeRowMode: - return (2); - - case SARibbonPannel::TwoRowMode: - return (3); - - default: - break; - } - return (2); -} - -void SARibbonPannel::PrivateData::createLayout() -{ - m_label = new SARibbonPannelLabel(q_ptr); - m_label->setAlignment(Qt::AlignCenter); - SARibbonPannelLayout* lay = new SARibbonPannelLayout(q_ptr); - lay->setPannelTitleLabel(m_label); - lay->setSpacing(2); - lay->setContentsMargins(2, 2, 2, 2); -} - -SARibbonPannelLayout* SARibbonPannel::PrivateData::pannelLayout() const -{ - return qobject_cast< SARibbonPannelLayout* >(q_ptr->layout()); -} - -SARibbonToolButton* SARibbonPannel::PrivateData::lastAddActionButton() -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (qobject_cast< SARibbonToolButton* >(lay->lastWidget())); - } - return (nullptr); -} - -/** - * @brief 重置label的字体 - * - * @note 此函数必须在布局设置后调用 - */ -void SARibbonPannel::PrivateData::resetTitleLabelFont() -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - int h = lay->pannelTitleHeight(); - QFont f = q_ptr->font(); - f.setPixelSize(h * 0.8); - if (m_label) { - m_label->setFont(f); - } - } -} - -QString SARibbonPannel::PrivateData::pannelName() const -{ - if (m_label) { - return m_label->text(); - } - return (QString()); -} - -void SARibbonPannel::PrivateData::setPannelName(const QString& title) -{ - if (m_label) { - m_label->setText(title); - } -} - -//================================================== -// SARibbonPannel -//================================================== - -SARibbonPannel::SARibbonPannel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) -{ - setPannelLayoutMode(ThreeRowMode); -} - -SARibbonPannel::SARibbonPannel(const QString& name, QWidget* parent) - : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) -{ - setPannelLayoutMode(ThreeRowMode); - setPannelName(name); -} - -SARibbonPannel::~SARibbonPannel() -{ -} - -/** - * @brief 把action的行属性设置进action中,action自身携带了行属性 - * @param action - * @param rp - */ -void SARibbonPannel::setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp)); -} - -/** - * @brief 获取action的行属性 - * @param action - * @return - */ -SARibbonPannelItem::RowProportion SARibbonPannel::getActionRowProportionProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok); - - if (isok) { - return (static_cast< SARibbonPannelItem::RowProportion >(r)); - } - return (SARibbonPannelItem::Large); -} - -/** - * @brief 设置action的ToolButtonPopupMode属性 - * @param action - * @param popMode - */ -void SARibbonPannel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode)); -} - -/** - * @brief 获取action的ToolButtonPopupMode属性 - * @param action - * @return - */ -QToolButton::ToolButtonPopupMode SARibbonPannel::getActionToolButtonPopupModeProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok); - - if (isok) { - return (static_cast< QToolButton::ToolButtonPopupMode >(r)); - } - return (QToolButton::InstantPopup); -} - -/** - * @brief 设置action的ToolButtonStyle属性 - * @param action - * @param buttonStyle - */ -void SARibbonPannel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle)); -} - -/** - * @brief 获取action的ToolButtonStyle属性 - * @param action - * @return - */ -Qt::ToolButtonStyle SARibbonPannel::getActionToolButtonStyleProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok); - - if (isok) { - return (static_cast< Qt::ToolButtonStyle >(r)); - } - return (Qt::ToolButtonIconOnly); -} - -/** - * @brief 添加action - * - * action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - * @param action - * @param rp 指定action的行占比 - * @param popMode 菜单弹出样式 - */ -void SARibbonPannel::addAction(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(action); - setActionRowProportionProperty(action, rp); - addAction(action); -} - -/** - * @brief 添加一个action - * @param act - * @param popMode 按钮的样式 - * @param rp action在pannel中的占位情况,默认是大图标 - */ -void SARibbonPannel::addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(act); - setActionRowProportionProperty(act, rp); - setActionToolButtonPopupModeProperty(act, popMode); - addAction(act); -} - -/** - @brief 添加大图标 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addLargeAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Large); -} - -/** - @brief 在三栏模式下,强制加为2栏action - @note 在两行模式下,Medium和Small等价 - 主要应用在ThreeRowMode下 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addMediumAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Medium); -} - -/** - @brief 添加小图标 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addSmallAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Small); -} - -void SARibbonPannel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Small); -} - -void SARibbonPannel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Large); -} - -void SARibbonPannel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Medium); -} - -/** - * @brief 生成并添加一个action - * - * 如果不对此action做操作,SARibbonPannel将管理此action - * - * @note action的父对象将设置为SARibbonPannel,SARibbonPannel在删除时将会删除子对象,会把这个action也删除, - * 如果不想此action也删除,需要对action重新设置父对象 - * - * @param text action的文字 - * @param icon action的图标 - * @param popMode 按钮的样式 - * @param rp action在pannel中的占位情况,默认是大图标 - * @return 返回添加的action - */ -QAction* SARibbonPannel::addAction(const QString& text, - const QIcon& icon, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp) -{ - QAction* action = new QAction(icon, text, this); - addAction(action, popMode, rp); - return (action); -} - -/** - * @brief 添加一个普通菜单 - * @param menu - * @param rp - * @param popMode,菜单弹出模式,默认InstantPopup模式 - * @return - */ -void SARibbonPannel::addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode) -{ - Q_CHECK_PTR(menu); - QAction* action = menu->menuAction(); - action->setIcon(menu->icon()); - action->setText(menu->title()); - action->setObjectName("action." + menu->objectName()); - addAction(action, popMode, rp); -} - -void SARibbonPannel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) -{ - addMenu(menu, SARibbonPannelItem::Large, popMode); -} - -void SARibbonPannel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) -{ - addMenu(menu, SARibbonPannelItem::Small, popMode); -} - -/** - * @brief 添加窗口 - * - * @param w - * @param rp - * @return 返回action(QWidgetAction) - * @note SARibbonPannel并不会管理此窗口内存,在delete SARibbonPannel时,此窗口如果父对象不是SARibbonPannel将不会被删除 - */ -QAction* SARibbonPannel::addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp) -{ - QWidgetAction* action = new QWidgetAction(this); - - action->setDefaultWidget(w); - action->setIcon(w->windowIcon()); - action->setText(w->windowTitle()); - action->setObjectName("action." + w->objectName()); - w->setAttribute(Qt::WA_Hover); - setActionRowProportionProperty(action, rp); - addAction(action); - return (action); -} - -/** - * @brief 添加窗口,占用ribbon的一行 - * @param w - * @return 返回action(QWidgetAction) - */ -QAction* SARibbonPannel::addSmallWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Small)); -} - -/** - * @brief 添加窗口,占用ribbon的一行 - * @param w - * @return - */ -QAction* SARibbonPannel::addMediumWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Medium)); -} - -/** - * @brief 添加窗口,占用所有行 - * @param w - * @return 返回action(QWidgetAction) - */ -QAction* SARibbonPannel::addLargeWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Large)); -} - -/** - * @brief SARibbonPannel::addGallery - * @return - * @note SARibbonPannel将拥有SARibbonGallery的管理权 - */ -SARibbonGallery* SARibbonPannel::addGallery(bool expanding) -{ - SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this); - - addWidget(gallery, SARibbonPannelItem::Large); - if (expanding) { - setExpanding(expanding); - } - return (gallery); -} - -/** - * @brief 添加分割线 - * @param top 上边距 @default 6 - * @param bottom 下边距 @default 6 - */ -QAction* SARibbonPannel::addSeparator() -{ - QAction* action = new QAction(this); - - action->setSeparator(true); - setActionRowProportionProperty(action, SARibbonPannelItem::Large); - addAction(action); - return (action); -} - -/** - * @brief 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr - * @param action - * @return 如果action没有对应的button,就返回nullptr - */ -SARibbonToolButton* SARibbonPannel::actionToRibbonToolButton(QAction* action) -{ -#if 0 - SARibbonPannelLayout* lay = qobject_cast< SARibbonPannelLayout* >(layout()); - - if (lay) { - int index = lay->indexOf(action); - if (index == -1) { - return (nullptr); - } - QLayoutItem* item = lay->takeAt(index); - SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(item ? item->widget() : nullptr); - return (btn); - } - return (nullptr); -#else - for (auto obj : qAsConst(children())) { - if (obj->isWidgetType()) { - if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) { - if (btn->defaultAction() == action) { - return btn; - } - } - } - } - return (nullptr); -#endif -} - -/** - * @brief 获取pannel下面的所有toolbutton - * @return - */ -QList< SARibbonToolButton* > SARibbonPannel::ribbonToolButtons() const -{ - const QObjectList& objs = children(); - QList< SARibbonToolButton* > res; - - for (QObject* o : objs) { - SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o); - if (b) { - res.append(b); - } - } - return (res); -} - -/** - * @brief 设置PannelLayoutMode - * @param mode - */ -void SARibbonPannel::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode mode) -{ - if (d_ptr->m_pannelLayoutMode == mode) { - return; - } - d_ptr->m_pannelLayoutMode = mode; - updateItemGeometry(); -} - -SARibbonPannel::PannelLayoutMode SARibbonPannel::pannelLayoutMode() const -{ - return (d_ptr->m_pannelLayoutMode); -} - -/** - * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - * @param action - * @note 要去除OptionAction直接传入nullptr即可 - * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction - */ -void SARibbonPannel::setOptionAction(QAction* action) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setOptionAction(action); - } -} - -/** - * @brief 判断是否存在OptionAction - * @return 存在返回true - */ -bool SARibbonPannel::isHaveOptionAction() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->isHaveOptionAction(); - } - return false; -} - -QSize SARibbonPannel::sizeHint() const -{ - int shWidth = 500; - int shHeight = 100; - if (QLayout* lay = layout()) { - QSize laySize = layout()->sizeHint(); - shWidth = laySize.width(); - shHeight = laySize.height(); - } - return QSize(shWidth, shHeight); -} - -QSize SARibbonPannel::minimumSizeHint() const -{ - return (layout()->minimumSize()); -} - -void SARibbonPannel::setToolButtonIconSize(const QSize& s) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setToolButtonIconSize(s); - } - // 对已经管理的QToolButton设置为iconsize - iterate([ s ](SARibbonToolButton* t) -> bool { - t->setIconSize(s); - return true; - }); -} - -QSize SARibbonPannel::toolButtonIconSize() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->toolButtonIconSize(); - } - return QSize(); -} - -/** - * @brief 遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 - * @param fp - * @return 返回false说明迭代中途退出,返回true则表示迭代完全 - */ -bool SARibbonPannel::iterate(SARibbonPannel::FpRibbonToolButtonIterate fp) -{ - const QObjectList& ols = children(); - for (QObject* obj : ols) { - if (SARibbonToolButton* t = qobject_cast< SARibbonToolButton* >(obj)) { - if (!fp(t)) { - return false; - } - } - } - return true; -} - -/** - * @brief 把pannel设置为扩展模式,此时会撑大水平区域 - * @param isExpanding - */ -void SARibbonPannel::setExpanding(bool isExpanding) -{ - setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed); -} - -/** - * @brief 判断此pannel是否为(水平)扩展模式 - * @return 是扩展模式返回true - */ -bool SARibbonPannel::isExpanding() const -{ - QSizePolicy sp = sizePolicy(); - - return (sp.horizontalPolicy() == QSizePolicy::Expanding); -} - -/** - @brief 设置标题栏的高度 - @param h - */ -void SARibbonPannel::setTitleHeight(int h) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setPannelTitleHeight(h); - } -} - -/** - * @brief 标题栏高度 - * @return - */ -int SARibbonPannel::titleHeight() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->pannelTitleHeight(); - } - return -1; -} - -/** - * @brief 判断是否显示标题,只有标题的高度被设置,才会显示标题 - * @return - */ -bool SARibbonPannel::isEnableShowTitle() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->isEnableShowPannelTitle(); - } - return false; -} - -/** - * @brief 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 - * @param on - */ -void SARibbonPannel::setEnableShowTitle(bool on) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->setEnableShowPannelTitle(on); - } -} - -/** - * @brief action对应的布局index,此操作一般用于移动,其他意义不大 - * @param act - * @return 没有查到返回-1 - */ -int SARibbonPannel::actionIndex(QAction* act) const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (lay->indexByAction(act)); - } - return (-1); -} - -/** - * @brief 移动action - * @param from 要移动action的位置,通过@ref actionIndex 获取 - * @param to 要移动的位置 - */ -void SARibbonPannel::moveAction(int from, int to) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (lay->move(from, to)); - } - updateGeometry(); // 通知layout进行重新布局 -} - -/** - * @brief 判断是否可以自定义 - * @return - */ -bool SARibbonPannel::isCanCustomize() const -{ - return (d_ptr->m_isCanCustomize); -} - -/** - * @brief 设置是否可以自定义 - * @param b - */ -void SARibbonPannel::setCanCustomize(bool b) -{ - d_ptr->m_isCanCustomize = b; -} - -/** - * @brief pannel名称 - * @return - */ -QString SARibbonPannel::pannelName() const -{ - return (d_ptr->pannelName()); -} - -/** - * @brief pannel的名称 - * @param title 标题 - * @note 此函数会触发@sa pannelNameChanged 信号 - */ -void SARibbonPannel::setPannelName(const QString& title) -{ - QString oldName = pannelName(); - if (oldName != title) { - d_ptr->setPannelName(title); - Q_EMIT pannelNameChanged(title); - } -} - -/** - * @brief 获取大图标的高度 - * @return - */ -int SARibbonPannel::largeButtonHeight() const -{ - const QMargins& mag = contentsMargins(); - return height() - mag.top() - mag.bottom() - titleHeight(); -} - -/** - * @brief 获取pannel layout - * @return - */ -SARibbonPannelLayout* SARibbonPannel::pannelLayout() const -{ - return d_ptr->pannelLayout(); -} - -/** - * @brief 更新布局 - */ -void SARibbonPannel::updateItemGeometry() -{ -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonPannel updateItemGeometry,pannelName=" << pannelName(); -#endif - // 此函数需要添加,否则SARibbonBar::setEnableWordWrap无法刷新按钮 - resetToolButtonSize(); - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->updateGeomArray(); - } -} - -/** - @brief 获取category指针,如果没有parent,或者不在category管理,返回nullptr - @return - */ -SARibbonCategory* SARibbonPannel::category() const -{ - return qobject_cast< SARibbonCategory* >(parent()); -} - -/** - @brief 获取ribbonBar指针,如果没有返回nullptr - @return - */ -SARibbonBar* SARibbonPannel::ribbonBar() const -{ - if (SARibbonCategory* c = category()) { - return c->ribbonBar(); - } - return nullptr; -} - -/** - * @brief 设置按钮之间的间隔 - * @param n - */ -void SARibbonPannel::setSpacing(int n) -{ - if (auto lay = layout()) { - lay->setSpacing(n); - } -} - -/** - * @brief 按钮之间的间隔 - * @return - */ -int SARibbonPannel::spacing() const -{ - if (auto lay = layout()) { - return lay->spacing(); - } - return 0; -} - -/** - * @brief pannel高度推荐 - * @param fm - * @param layMode - * @param pannelTitleHeight - * @return - */ -int SARibbonPannel::pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight) -{ - int textH = fm.lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 - switch (layMode) { - case SARibbonPannel::ThreeRowMode: { - // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 - return textH * 4.8 + pannelTitleHeight; - } break; - case SARibbonPannel::TwoRowMode: { - // 3=2*1.6 - return textH * 3.2 + pannelTitleHeight; - } break; - default: { - qWarning() << "unknow SARibbonPannel::PannelLayoutMode:" << layMode; - } - } - return (textH * 4.8 + pannelTitleHeight); -} - -/** - * @brief 重置按钮的尺寸,在布局改变后(尤其高度变更),按钮的尺寸需要手动变更 - */ -void SARibbonPannel::resetToolButtonSize() -{ - QList< SARibbonToolButton* > btns = ribbonToolButtons(); - - for (SARibbonToolButton* b : qAsConst(btns)) { - if ((nullptr == b)) { - continue; - } - b->updateRect(); - } -} - -bool SARibbonPannel::event(QEvent* e) -{ -#if SA_DEBUG_PRINT_EVENT - if (e->type() != QEvent::Paint) { - qDebug() << "SARibbonPannel event(" << e->type() << "),name=" << pannelName(); - } -#endif - // if (SARibbonPannelLayout* lay = pannelLayout()) { - // if (lay->isDirty() && e->type() == QEvent::LayoutRequest) { - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - // lay->m_dirty = false; - // } - // } - return QWidget::event(e); -} - -/** - * @brief 处理action的事件 - * - * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 - * - * ActionAdded时向布局请求,添加action,布局中同时触发了@ref SARibbonPannelLayout::createItem 函数 - * 此函数用于生成窗口,例如QRibbonToolButton - * - * ActionChanged时会让布局重新计算尺寸,并向category请求重新布局,有可能category的所有要重新调整尺寸 - * - * ActionRemoved会移除布局管理的QLayoutItem,并进行内存清理,这时窗口也会隐藏,同时销毁 - * - * @param e - * @note 所有action事件都会向category请求重新布局 - * - */ -void SARibbonPannel::actionEvent(QActionEvent* e) -{ - QAction* action = e->action(); - QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action); - - switch (e->type()) { - case QEvent::ActionAdded: { - SARibbonPannelLayout* lay = pannelLayout(); - if (nullptr != widgetAction) { - if (widgetAction->parent() != this) { - widgetAction->setParent(this); - } - } - int index = layout()->count(); - if (e->before()) { - // 说明是插入 - index = lay->indexByAction(e->before()); - if (-1 == index) { - index = layout()->count(); // 找不到的时候就插入到最后 - } - } - lay->insertAction(index, action, getActionRowProportionProperty(action)); - // 由于pannel的尺寸发生变化,需要让category也调整 - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - } break; - - case QEvent::ActionChanged: { - // 让布局重新绘制 - layout()->invalidate(); - - // updateGeometry(); - // 由于pannel的尺寸发生变化,需要让category也调整 - if (QWidget* parw = parentWidget()) { - if (QLayout* pl = parw->layout()) { - pl->invalidate(); - } - //! 强制发送一个resizeevent,让Category能重绘,如果没有这个函数,发现Category的layout虽然设置了invalidate(标记缓存失效) - //! 但并没有按顺序在pannel尺寸更新后更新Category的尺寸,导致有些pannel的尺寸识别出现异常 - //! 重打印信息上看,pannel的尺寸有进行更新,category的尺寸也进行了更新,但更新的次数和调用invalidate的次数不一样,需要手动触发ResizeEvent - //! 尝试过调用QEvent::LayoutRequest没有效果: - //! @code - //! QEvent* el = new QEvent(QEvent::LayoutRequest); - //! QApplication::postEvent(parw, el); - //! @endcode - //! - //! 调用parw->updateGeometry();也没有效果,目前看使用resizeevent是最有效果的 - //! - QResizeEvent* ersize = new QResizeEvent(parw->size(), QSize()); - QApplication::postEvent(parw, ersize); - } - } break; - - case QEvent::ActionRemoved: { - SARibbonPannelLayout* lay = pannelLayout(); - action->disconnect(this); - int index = lay->indexByAction(action); - if (index != -1) { - QLayoutItem* item = lay->takeAt(index); - delete item; - } - // 由于pannel的尺寸发生变化,需要让category也调整 - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - } break; - - default: - break; - } -} - -/** - * @brief 追踪字体改变事件,把新字体设置到childwidget中 - * @param e - */ -void SARibbonPannel::changeEvent(QEvent* e) -{ - if (nullptr == e) { - return; - } - if (e->type() == QEvent::FontChange) { - QFont f = font(); - QList< QWidget* > listWidgets = findChildren< QWidget* >(); - for (QWidget* w : listWidgets) { - w->setFont(f); - } - if (QLayout* lay = layout()) { - lay->invalidate(); - } - d_ptr->resetTitleLabelFont(); - } - QWidget::changeEvent(e); -} - -/** - * @brief 获取布局item - * @return - */ -const QList< SARibbonPannelItem* >& SARibbonPannel::ribbonPannelItem() const -{ - return (pannelLayout()->mItems); -} - -/** - * @brief 获取最近添加的按钮 - * @return 刚才添加的按钮的指针 - */ -SARibbonToolButton* SARibbonPannel::lastAddActionButton() -{ - return d_ptr->lastAddActionButton(); -} - -/*** End of inlined file: SARibbonPannel.cpp ***/ - -/*** Start of inlined file: SARibbonCategory.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// -/// \brief ribbon页的代理类 -/// 如果需要修改重绘SARibbonCategory,可以通过设置SARibbonCategory::setProxy -/// -class SARibbonCategory::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCategory) -public: - PrivateData(SARibbonCategory* p); - - SARibbonPannel* addPannel(const QString& title); - SARibbonPannel* insertPannel(const QString& title, int index); - void addPannel(SARibbonPannel* pannel); - void insertPannel(int index, SARibbonPannel* pannel); - - // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 - bool takePannel(SARibbonPannel* pannel); - - // 移除Pannel,Category会直接回收SARibbonPannel内存 - bool removePannel(SARibbonPannel* pannel); - SARibbonCategory* ribbonCategory(); - const SARibbonCategory* ribbonCategory() const; - - // 返回所有的Pannel - QList< SARibbonPannel* > pannelList(); - - // 更新item的布局,此函数会调用doItemLayout - void updateItemGeometry(); - - void doWheelEvent(QWheelEvent* event); - // 初始化 - void init(SARibbonCategory* c); - -public: - bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 - int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 - bool mIsContextCategory { false }; ///< 标记是否是上下文标签 - bool mIsCanCustomize { true }; ///< 标记是否可以自定义 - int mPannelSpacing { 0 }; ///< pannel的spacing - QSize mPannelToolButtonSize { 22, 22 }; ///< 记录pannel的默认图标大小 - SARibbonPannel::PannelLayoutMode mDefaultPannelLayoutMode { SARibbonPannel::ThreeRowMode }; -}; -SARibbonCategory::PrivateData::PrivateData(SARibbonCategory* p) : q_ptr(p) -{ -} - -SARibbonPannel* SARibbonCategory::PrivateData::addPannel(const QString& title) -{ - if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { - SARibbonPannel* p = insertPannel(title, lay->pannelCount()); - return p; - } - return nullptr; -} - -SARibbonPannel* SARibbonCategory::PrivateData::insertPannel(const QString& title, int index) -{ - SARibbonPannel* pannel = RibbonSubElementFactory->createRibbonPannel(ribbonCategory()); - - pannel->setPannelName(title); - pannel->setObjectName(title); - insertPannel(index, pannel); - return (pannel); -} - -void SARibbonCategory::PrivateData::addPannel(SARibbonPannel* pannel) -{ - if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { - insertPannel(lay->pannelCount(), pannel); - } -} - -/** - * @brief 插入pannel到layout - * - * 所有的添加操作最终会调用此函数 - * @param index - * @param pannel - */ -void SARibbonCategory::PrivateData::insertPannel(int index, SARibbonPannel* pannel) -{ - if (nullptr == pannel) { - return; - } - SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); - if (nullptr == lay) { - return; - } - if (pannel->parentWidget() != q_ptr) { - pannel->setParent(q_ptr); - } - // 同步状态 - pannel->setEnableShowTitle(mEnableShowPannelTitle); - pannel->setTitleHeight(mPannelTitleHeight); - pannel->setPannelLayoutMode(mDefaultPannelLayoutMode); - pannel->setSpacing(mPannelSpacing); - pannel->setToolButtonIconSize(mPannelToolButtonSize); - index = qMax(0, index); - index = qMin(lay->pannelCount(), index); - lay->insertPannel(index, pannel); - pannel->setVisible(true); - - QObject::connect(pannel, &SARibbonPannel::actionTriggered, ribbonCategory(), &SARibbonCategory::actionTriggered); -} - -bool SARibbonCategory::PrivateData::takePannel(SARibbonPannel* pannel) -{ - SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); - if (nullptr == lay) { - return false; - } - return lay->takePannel(pannel); -} - -bool SARibbonCategory::PrivateData::removePannel(SARibbonPannel* pannel) -{ - if (takePannel(pannel)) { - pannel->hide(); - pannel->deleteLater(); - return (true); - } - return (false); -} - -QList< SARibbonPannel* > SARibbonCategory::PrivateData::pannelList() -{ - if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { - return lay->pannelList(); - } - return QList< SARibbonPannel* >(); -} - -SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() -{ - return (q_ptr); -} - -const SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() const -{ - return (q_ptr); -} - -void SARibbonCategory::PrivateData::updateItemGeometry() -{ -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategory::PrivateData::updateItemGeometry,categoryName=" << q_ptr->categoryName(); -#endif - SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); - if (!lay) { - return; - } - const QList< SARibbonPannel* > pannels = lay->pannelList(); - for (auto pannel : pannels) { - pannel->updateItemGeometry(); - } - lay->updateGeometryArr(); - return; -} - -void SARibbonCategory::PrivateData::doWheelEvent(QWheelEvent* event) -{ - SARibbonCategoryLayout* lay = q_ptr->categoryLayout(); - if (nullptr == lay) { - return; - } - QSize contentSize = lay->categoryContentSize(); - // 求总宽 - int totalWidth = lay->categoryTotalWidth(); - - if (totalWidth > contentSize.width()) { - // 这个时候滚动有效 - int scrollpix = 40; - // Qt6 取消了QWheelEvent::delta函数 - // 是要下面方法可兼容qt5/6 - QPoint numPixels = event->pixelDelta(); - QPoint numDegrees = event->angleDelta() / 8; - if (!numPixels.isNull()) { - if (numDegrees.y() < 0) { - scrollpix = -scrollpix; - } - } else if (!numDegrees.isNull()) { - if (numDegrees.y() < 0) { - scrollpix = -scrollpix; - } - } - lay->scroll(scrollpix); - } else { - // 这时候无需处理事件,把滚动事件上发让父级也能接收 - event->ignore(); - // 如滚动过就还原 - if (lay->isScrolled()) { - lay->scroll(0); - } - } -} - -void SARibbonCategory::PrivateData::init(SARibbonCategory* c) -{ - c->setLayout(new SARibbonCategoryLayout(c)); - c->connect(c, &SARibbonCategory::windowTitleChanged, c, &SARibbonCategory::categoryNameChanged); -} - -//---------------------------------------------------- -// SARibbonCategory -//---------------------------------------------------- - -SARibbonCategory::SARibbonCategory(QWidget* p) : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) -{ - d_ptr->init(this); -} - -SARibbonCategory::SARibbonCategory(const QString& name, QWidget* p) - : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) -{ - setCategoryName(name); - d_ptr->init(this); -} - -SARibbonCategory::~SARibbonCategory() -{ -} - -/** - * @brief category的名字,等同windowTitle函数 - * @return - */ -QString SARibbonCategory::categoryName() const -{ - return (windowTitle()); -} - -/** - * @brief 设置category名字,等同setWindowTitle - * @param title - */ -void SARibbonCategory::setCategoryName(const QString& title) -{ - setWindowTitle(title); -} - -bool SARibbonCategory::event(QEvent* e) -{ -#if SA_DEBUG_PRINT_EVENT - if (e->type() != QEvent::Paint) { - qDebug() << "SARibbonCategory event(" << e->type() << "),name=" << categoryName(); - } -#endif - return QWidget::event(e); -} - -/** - * @brief pannel的模式 - * @return - */ -SARibbonPannel::PannelLayoutMode SARibbonCategory::pannelLayoutMode() const -{ - return (d_ptr->mDefaultPannelLayoutMode); -} - -/** - * @brief 设置pannel的模式 - * - * 在@ref SARibbonBar 调用@ref SARibbonBar::setRibbonStyle 函数时,会对所有的SARibbonCategory调用此函数 - * 把新的SARibbonPannel::PannelLayoutMode设置进去 - * @param m - */ -void SARibbonCategory::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) -{ - d_ptr->mDefaultPannelLayoutMode = m; - iterate([ m ](SARibbonPannel* p) -> bool { - p->setPannelLayoutMode(m); - return true; - }); - updateItemGeometry(); -} - -/** - * @brief 添加pannel - * - * @note pannel的所有权由SARibbonCategory来管理,请不要在外部对其进行销毁 - * @param title pannel的标题,在office/wps的三行模式下会显示在pannel的下方 - * @return 返回生成的@ref SARibbonPannel 指针 - * @see 对SARibbonPannel的其他操作,参考 @ref SARibbonCategory::takePannel - */ -SARibbonPannel* SARibbonCategory::addPannel(const QString& title) -{ - return (d_ptr->addPannel(title)); -} - -/** - * @brief 添加pannel - * @param pannel pannel的所有权SARibbonCategory来管理 - */ -void SARibbonCategory::addPannel(SARibbonPannel* pannel) -{ - d_ptr->addPannel(pannel); -} - -/** - * @brief qt designer专用 - * @param pannel - */ -void SARibbonCategory::addPannel(QWidget* pannel) -{ - SARibbonPannel* p = qobject_cast< SARibbonPannel* >(pannel); - if (p) { - addPannel(p); - } -} - -/** - * @brief 新建一个pannel,并插入到index位置 - * @param title pannel的title - * @param index 插入的位置,如果index超出category里pannel的个数,将插入到最后 - * @return 返回生成的@ref SARibbonPannel 指针 - * @note 如果 - */ -SARibbonPannel* SARibbonCategory::insertPannel(const QString& title, int index) -{ - return (d_ptr->insertPannel(title, index)); -} - -/** - * @brief 通过名字查找pannel - * @param title - * @return 如果有重名,只会返回第一个符合条件的 - */ -SARibbonPannel* SARibbonCategory::pannelByName(const QString& title) const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->pannelByName(title); - } - return nullptr; -} - -/** - * @brief 通过ObjectName查找pannel - * @param objname - * @return - */ -SARibbonPannel* SARibbonCategory::pannelByObjectName(const QString& objname) const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->pannelByObjectName(objname); - } - return nullptr; -} - -/** - * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr - * @param index - * @return 如果超过索引范围,会返回nullptr - */ -SARibbonPannel* SARibbonCategory::pannelByIndex(int index) const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->pannelByIndex(index); - } - return nullptr; -} - -/** - * @brief 查找pannel对应的索引 - * @param p - * @return 如果找不到,返回-1 - */ -int SARibbonCategory::pannelIndex(SARibbonPannel* p) const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->pannelIndex(p); - } - return -1; -} - -/** - * @brief 移动一个Pannel从from index到to index - * @param from 要移动pannel的index - * @param to 要移动到的位置 - */ -void SARibbonCategory::movePannel(int from, int to) -{ - if (from == to) { - return; - } - if (to < 0) { - to = 0; - } - if (to >= pannelCount()) { - to = pannelCount() - 1; - } - if (SARibbonCategoryLayout* lay = categoryLayout()) { - lay->movePannel(from, to); - } -} - -/** - * @brief 把pannel脱离SARibbonCategory的管理 - * @param pannel 需要提取的pannel - * @return 成功返回true,否则返回false - */ -bool SARibbonCategory::takePannel(SARibbonPannel* pannel) -{ - return (d_ptr->takePannel(pannel)); -} - -/** - * @brief 移除Pannel,Category会直接回收SARibbonPannel内存 - * @param pannel 需要移除的pannel - * @note 移除后pannel为野指针,一般操作完建议把pannel指针设置为nullptr - * - * 此操作等同于: - * - * @code - * SARibbonPannel* pannel; - * ... - * category->takePannel(pannel); - * pannel->hide(); - * pannel->deleteLater(); - * @endcode - */ -bool SARibbonCategory::removePannel(SARibbonPannel* pannel) -{ - return (d_ptr->removePannel(pannel)); -} - -/** - * @brief 移除pannel - * @param index pannel的索引,如果超出会返回false - * @return 成功返回true - */ -bool SARibbonCategory::removePannel(int index) -{ - SARibbonPannel* p = pannelByIndex(index); - - if (nullptr == p) { - return (false); - } - return (removePannel(p)); -} - -/** - * @brief 返回Category下的所有pannel - * @return - */ -QList< SARibbonPannel* > SARibbonCategory::pannelList() const -{ - return (d_ptr->pannelList()); -} - -QSize SARibbonCategory::sizeHint() const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->sizeHint(); - } - return QSize(1000, 100); -} - -/** - * @brief 如果是ContextCategory,此函数返回true - * @return - */ -bool SARibbonCategory::isContextCategory() const -{ - return (d_ptr->mIsContextCategory); -} - -/** - * @brief 返回pannel的个数 - * @return - */ -int SARibbonCategory::pannelCount() const -{ - if (SARibbonCategoryLayout* lay = categoryLayout()) { - return lay->pannelCount(); - } - return -1; -} - -/** - * @brief 判断是否可以自定义 - * @return - */ -bool SARibbonCategory::isCanCustomize() const -{ - return (d_ptr->mIsCanCustomize); -} - -/** - * @brief 设置是否可以自定义 - * @param b - */ -void SARibbonCategory::setCanCustomize(bool b) -{ - d_ptr->mIsCanCustomize = b; -} - -/** - * @brief pannel标题栏的高度 - * @return - */ -int SARibbonCategory::pannelTitleHeight() const -{ - return d_ptr->mPannelTitleHeight; -} -/** - * @brief 设置pannel的高度 - * @param h - */ -void SARibbonCategory::setPannelTitleHeight(int h) -{ - d_ptr->mPannelTitleHeight = h; - iterate([ h ](SARibbonPannel* p) -> bool { - p->setTitleHeight(h); - return true; - }); -} - -/** - * @brief 是否pannel显示标题栏 - * @return - */ -bool SARibbonCategory::isEnableShowPannelTitle() const -{ - return d_ptr->mEnableShowPannelTitle; -} - -/** - * @brief 设置显示pannel标题 - * @param on - */ -void SARibbonCategory::setEnableShowPannelTitle(bool on) -{ - d_ptr->mEnableShowPannelTitle = on; - iterate([ on ](SARibbonPannel* p) -> bool { - p->setEnableShowTitle(on); - return true; - }); -} - -/** - * @brief 获取对应的ribbonbar - * @return 如果没有加入ribbonbar的管理,此值为null - */ -SARibbonBar* SARibbonCategory::ribbonBar() const -{ - // 第一个par是stackwidget - if (QWidget* par = parentWidget()) { - // 理论此时是ribbonbar - par = par->parentWidget(); - while (par) { - if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(par)) { - return ribbon; - } - par = par->parentWidget(); - } - } - return nullptr; -} - -/** - * @brief 刷新category的布局,适用于改变ribbon的模式之后调用 - */ -void SARibbonCategory::updateItemGeometry() -{ -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategory name=" << categoryName() << " updateItemGeometry"; -#endif - d_ptr->updateItemGeometry(); -} - -/** - * @brief 此函数会遍历Category下的所有pannel,执行函数指针 - * @param fp 函数指针返回false则停止迭代 - * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 - */ -bool SARibbonCategory::iterate(FpPannelIterate fp) -{ - const QList< SARibbonPannel* > ps = pannelList(); - for (SARibbonPannel* p : ps) { - if (!fp(p)) { - return false; - } - } - return true; -} - -/** - @brief 设置Category的对齐方式 - @param al - */ -void SARibbonCategory::setCategoryAlignment(SARibbonAlignment al) -{ - SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); - if (lay) { - lay->setCategoryAlignment(al); - } -} - -/** - @brief Category的对齐方式 - @return - */ -SARibbonAlignment SARibbonCategory::categoryAlignment() const -{ - SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); - if (lay) { - return lay->categoryAlignment(); - } - return SARibbonAlignment::AlignLeft; -} - -/** - * @brief 设置pannel的spacing - * @param n - */ -void SARibbonCategory::setPannelSpacing(int n) -{ - d_ptr->mPannelSpacing = n; - iterate([ n ](SARibbonPannel* pannel) -> bool { - if (pannel) { - pannel->setSpacing(n); - } - return true; - }); -} - -/** - * @brief pannel的spacing - * @return - */ -int SARibbonCategory::pannelSpacing() const -{ - return d_ptr->mPannelSpacing; -} - -/** - * @brief 设置pannel按钮的icon尺寸,large action不受此尺寸影响 - * @param s - */ -void SARibbonCategory::setPannelToolButtonIconSize(const QSize& s) -{ - d_ptr->mPannelToolButtonSize = s; - iterate([ s ](SARibbonPannel* pannel) -> bool { - if (pannel) { - pannel->setToolButtonIconSize(s); - } - return true; - }); -} - -/** - * @brief pannel按钮的icon尺寸,large action不受此尺寸影响 - * @return - */ -QSize SARibbonCategory::pannelToolButtonIconSize() const -{ - return d_ptr->mPannelToolButtonSize; -} - -/** - * @brief 在超出边界情况下,滚轮可滚动pannel - * @param event - */ -void SARibbonCategory::wheelEvent(QWheelEvent* event) -{ - d_ptr->doWheelEvent(event); -} - -void SARibbonCategory::changeEvent(QEvent* event) -{ - switch (event->type()) { - case QEvent::StyleChange: { - if (layout()) { -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategory changeEvent(StyleChange),categoryName=" << categoryName(); -#endif - layout()->invalidate(); - } - } break; - case QEvent::FontChange: { -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategory changeEvent(FontChange),categoryName=" << categoryName(); -#endif - QFont f = font(); - iterate([ f ](SARibbonPannel* p) -> bool { - p->setFont(f); - return true; - }); - if (layout()) { - layout()->invalidate(); - } - } break; - default: - break; - }; - return QWidget::changeEvent(event); -} - -/** - * @brief 标记这个是上下文标签 - * @param isContextCategory - */ -void SARibbonCategory::markIsContextCategory(bool isContextCategory) -{ - d_ptr->mIsContextCategory = isContextCategory; -} - -/** - * @brief 获取SARibbonCategoryLayoutlayout - * @return - */ -SARibbonCategoryLayout* SARibbonCategory::categoryLayout() const -{ - return qobject_cast< SARibbonCategoryLayout* >(layout()); -} - -//=================================================== -// SARibbonCategoryScrollButton -//=================================================== -SARibbonCategoryScrollButton::SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p) : QToolButton(p) -{ - setArrowType(arr); -} - -SARibbonCategoryScrollButton::~SARibbonCategoryScrollButton() -{ -} - -/*** End of inlined file: SARibbonCategory.cpp ***/ - -/*** Start of inlined file: SARibbonCategoryLayout.cpp ***/ -#include - -#include -#include - -#ifndef SARibbonCategoryLayout_DEBUG_PRINT -#define SARibbonCategoryLayout_DEBUG_PRINT 1 -#endif -/** - * @brief The SARibbonCategoryLayoutPrivate class - */ -class SARibbonCategoryLayout::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout) -public: - PrivateData(SARibbonCategoryLayout* p); - // 计算所有元素的sizehint总宽度 - int totalSizeHintWidth() const; - -public: - bool mDirty { true }; - bool mIsRightScrollBtnShow { false }; ///< 标记右滚动按钮是否需要显示 - bool mIsLeftScrollBtnShow { false }; ///< 标记左滚动按钮是否需要显示 - SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 - SARibbonCategoryScrollButton* mRightScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 - int mTotalWidth { 0 }; - int mXBase { 0 }; - QSize mSizeHint; - QSize mMinSizeHint; - QList< SARibbonCategoryLayoutItem* > mItemList; - SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 -}; - -//============================================================= -// SARibbonCategoryLayoutPrivate -//============================================================= - -SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p) -{ -} - -/** - * @brief 计算所有元素的SizeHint宽度总和 - * @return - */ -int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const -{ - int total = 0; - QMargins mag = q_ptr->contentsMargins(); -#if SA_DEBUG_PRINT_SIZE_HINT - int debug_i__ = 0; - QString debug_totalSizeHintWidth__; -#endif - if (!mag.isNull()) { - total += (mag.left() + mag.right()); - } - // 先计算总长 - for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) { - if (item->isEmpty()) { -// 如果是hide就直接跳过 -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - ++debug_i__; - debug_totalSizeHintWidth__ += - QString(" [%1](%2)is empty skip\n").arg(debug_i__).arg(item->toPannelWidget()->pannelName()); -#endif - continue; - } - // 这里要使用widget()->sizeHint(),因为pannel的标题会影总体布局,此处需要修改 - // TODO - QSize pannelSize = item->widget()->sizeHint(); - QSize SeparatorSize(0, 0); - if (item->separatorWidget) { - SeparatorSize = item->separatorWidget->sizeHint(); - } - total += pannelSize.width(); - total += SeparatorSize.width(); -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - ++debug_i__; - debug_totalSizeHintWidth__ += QString("|-[%1]pannelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n") - .arg(debug_i__) - .arg(pannelSize.width()) - .arg(pannelSize.height()) - .arg(SeparatorSize.width()) - .arg(SeparatorSize.height()) - .arg(item->toPannelWidget()->pannelName()); -#endif - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total; - qDebug().noquote() << debug_totalSizeHintWidth__; -#endif - return (total); -} - -//============================================================= -// SARibbonCategoryLayout -//============================================================= - -SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent) - : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this)) -{ - setContentsMargins(1, 1, 1, 1); - d_ptr->mLeftScrollBtn = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent); - d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent); - d_ptr->mLeftScrollBtn->setVisible(false); - d_ptr->mRightScrollBtn->setVisible(false); - connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked); - connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked); -} - -SARibbonCategoryLayout::~SARibbonCategoryLayout() -{ - while (auto item = takePannelItem(0)) { - delete item; - } -} - -SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const -{ - return (qobject_cast< SARibbonCategory* >(parentWidget())); -} - -void SARibbonCategoryLayout::addItem(QLayoutItem* item) -{ - Q_UNUSED(item); - qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPannel instead"); - invalidate(); -} - -/** - * @brief 返回pannel的layout - * @param index 索引 - * @return - * @note 注意,pannel是和分割线一起的,但这个只返回一个pannel对应的layout - */ -QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const -{ - SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr); - - return (item); -} - -/** - * @brief 提取layout - * @param index - * @return - */ -QLayoutItem* SARibbonCategoryLayout::takeAt(int index) -{ - QLayoutItem* r = takePannelItem(index); - invalidate(); - return r; -} - -SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(int index) -{ - if ((index >= 0) && (index < d_ptr->mItemList.size())) { - SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index); - if (item->widget()) { - item->widget()->hide(); - } - if (item->separatorWidget) { - item->separatorWidget->hide(); - } - return (item); - } - return (nullptr); -} - -SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(SARibbonPannel* pannel) -{ - for (int i = 0; i < d_ptr->mItemList.size(); ++i) { - SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ]; - if (item->widget() == pannel) { - return (takePannelItem(i)); - } - } - return (nullptr); -} - -/** - * @brief 移除pannel,对应的分割线也会删除 - * @param pannel - * @return - */ -bool SARibbonCategoryLayout::takePannel(SARibbonPannel* pannel) -{ - SARibbonCategoryLayoutItem* i = takePannelItem(pannel); - if (i) { - SARibbonSeparatorWidget* sp = i->separatorWidget; - if (sp) { - sp->deleteLater(); - } - delete i; - invalidate(); - return true; - } - return false; -} - -int SARibbonCategoryLayout::count() const -{ - return (d_ptr->mItemList.size()); -} - -void SARibbonCategoryLayout::addPannel(SARibbonPannel* pannel) -{ - insertPannel(d_ptr->mItemList.count(), pannel); -} - -/** - * @brief 插入一个pannel - * @param index 索引 - * @param pannel - * @return 返回对应的分割线SARibbonSeparatorWidget - * @note 在SARibbonCategoryLayout的布局中,一个pannel会携带一个分割线 - */ -void SARibbonCategoryLayout::insertPannel(int index, SARibbonPannel* pannel) -{ - index = qMax(0, index); - index = qMin(d_ptr->mItemList.count(), index); - SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(pannel); - - // 分割线 - item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget()); - // 插入list中 - d_ptr->mItemList.insert(index, item); - // 标记需要重新计算尺寸 - invalidate(); -} - -QSize SARibbonCategoryLayout::sizeHint() const -{ - if (d_ptr->mSizeHint.isNull()) { - SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); - that->updateGeometryArr(); - } - return (d_ptr->mSizeHint); -} - -QSize SARibbonCategoryLayout::minimumSize() const -{ - if (d_ptr->mMinSizeHint.isNull()) { - SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); - that->updateGeometryArr(); - } - return (d_ptr->mMinSizeHint); -} - -/** - * @brief SARibbonCategory充满整个stacked widget - * @return - */ -Qt::Orientations SARibbonCategoryLayout::expandingDirections() const -{ - return (Qt::Horizontal | Qt::Vertical); -} - -void SARibbonCategoryLayout::invalidate() -{ - d_ptr->mDirty = true; - QLayout::invalidate(); -} -/** - * @brief category的内容尺寸(把margins减去) - * @return - */ -QSize SARibbonCategoryLayout::categoryContentSize() const -{ - SARibbonCategory* category = ribbonCategory(); - QSize s = category->size(); - QMargins mag = contentsMargins(); - if (!mag.isNull()) { - s.rheight() -= (mag.top() + mag.bottom()); - s.rwidth() -= (mag.right() + mag.left()); - } - return (s); -} - -/** - * @brief 更新尺寸 - */ -void SARibbonCategoryLayout::updateGeometryArr() -{ - SARibbonCategory* category = ribbonCategory(); - if (nullptr == category) { - return; - } - int categoryWidth = category->width(); - QMargins mag = contentsMargins(); - int height = category->height(); - int y = 0; - - if (!mag.isNull()) { - y = mag.top(); - height -= (mag.top() + mag.bottom()); - // categoryWidth不能把mag减去,减去后会导致categoryWidth不是实际的categoryWidth - // categoryWidth -= (mag.right() + mag.left()); - } - // total 是总宽,不是x坐标系,x才是坐标系 - int total = d_ptr->totalSizeHintWidth(); - - // 扩展的宽度 - int expandWidth = 0; - -// 如果total < categoryWidth,m_d->mXBase可以设置为0 -// 判断是否超过总长度 -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout::updateGeometryArr" << "\n|-category name=" << category->categoryName() // - << "\n|-category height=" << height // - << "\n|-totalSizeHintWidth=" << total // - << "\n|-y=" << y // - << "\n|-expandWidth:" << expandWidth // - << "\n|-mag=" << mag; -#endif - if (total > categoryWidth) { - // 超过总长度,需要显示滚动按钮 - if (0 == d_ptr->mXBase) { - // 已经移动到最左,需要可以向右移动 - d_ptr->mIsRightScrollBtnShow = true; - d_ptr->mIsLeftScrollBtnShow = false; - } else if (d_ptr->mXBase <= (categoryWidth - total)) { - // 已经移动到最右,需要可以向左移动 - d_ptr->mIsRightScrollBtnShow = false; - d_ptr->mIsLeftScrollBtnShow = true; - } else { - // 移动到中间两边都可以动 - d_ptr->mIsRightScrollBtnShow = true; - d_ptr->mIsLeftScrollBtnShow = true; - } - } else { - // 说明total 小于 categoryWidth - // 记录可以扩展的数量 - int canExpandingCount = 0; - d_ptr->mIsRightScrollBtnShow = false; - d_ptr->mIsLeftScrollBtnShow = false; - // 这个是避免一开始totalWidth > categorySize.width(),通过滚动按钮调整了m_d->mBaseX - // 随之调整了窗体尺寸,调整后totalWidth < categorySize.width()导致category在原来位置 - // 无法显示,必须这里把mBaseX设置为0 - - d_ptr->mXBase = 0; - // - - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* p = qobject_cast< SARibbonPannel* >(item->widget())) { - if (p->isExpanding()) { - // pannel可扩展 - ++canExpandingCount; - } - } - } - // 计算可扩展的宽度 - if (canExpandingCount > 0) { - expandWidth = (categoryWidth - total) / canExpandingCount; - } else { - expandWidth = 0; - } - } - int x = d_ptr->mXBase; - if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) { - // 如果是居中对齐,同时没有伸缩的pannel,同时总宽度没有超过category的宽度 - x = (categoryWidth - total) / 2; - } - total = 0; // total重新计算 - // 先按照sizeHint设置所有的尺寸 - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (item->isEmpty()) { - // 如果是hide就直接跳过 - if (item->separatorWidget) { - // pannel hide分割线也要hide - item->separatorWidget->hide(); - } - item->mWillSetGeometry = QRect(0, 0, 0, 0); - item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0); - continue; - } - SARibbonPannel* p = item->toPannelWidget(); - if (nullptr == p) { - qDebug() << "unknow widget in SARibbonCategoryLayout"; - continue; - } - // p->layout()->update(); - QSize pannelSize = p->sizeHint(); - QSize SeparatorSize(0, 0); - if (item->separatorWidget) { - SeparatorSize = item->separatorWidget->sizeHint(); - } - if (p->isExpanding()) { - // 可扩展,就把pannel扩展到最大 - pannelSize.setWidth(pannelSize.width() + expandWidth); - } - int w = pannelSize.width(); - - item->mWillSetGeometry = QRect(x, y, w, height); - x += w; - total += w; - w = SeparatorSize.width(); - item->mWillSetSeparatorGeometry = QRect(x, y, w, height); - x += w; - total += w; - } - d_ptr->mTotalWidth = total; - d_ptr->mSizeHint = QSize(d_ptr->mTotalWidth, height); - d_ptr->mMinSizeHint = QSize(categoryWidth, height); -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint - << ",Category name=" << category->categoryName(); -#endif -} - -/** - * @brief 执行layout调整 - */ -void SARibbonCategoryLayout::doLayout() -{ - if (d_ptr->mDirty) { - updateGeometryArr(); - } - SARibbonCategory* category = ribbonCategory(); - // 两个滚动按钮的位置永远不变 - d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height()); - d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height()); - QList< QWidget* > showWidgets, hideWidgets; -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - int debug_i__(0); - qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName(); -#endif - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (item->isEmpty()) { - hideWidgets << item->widget(); - if (item->separatorWidget) { - hideWidgets << item->separatorWidget; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() << ",will hide"; - ++debug_i__; -#endif - } else { - //! 这里不能用item->setGeometry(item->mWillSetGeometry);这样会得到一个很奇怪的显示效果 - //! 就是窗口的最左边不会移出去,而是把最右边压缩, - item->widget()->setGeometry(item->mWillSetGeometry); - // item->widget()->setFixedSize(item->mWillSetGeometry.size()); - // item->widget()->move(item->mWillSetGeometry.topLeft()); - // item->setGeometry(item->mWillSetGeometry); - showWidgets << item->widget(); - if (item->separatorWidget) { - item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry); - showWidgets << item->separatorWidget; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() - << "),willSetGeometry:" << item->mWillSetGeometry - << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry; - ++debug_i__; -#endif - } - } - - d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow); - d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow); - if (d_ptr->mIsRightScrollBtnShow) { - d_ptr->mRightScrollBtn->raise(); - } - if (d_ptr->mIsLeftScrollBtnShow) { - d_ptr->mLeftScrollBtn->raise(); - } - // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 - for (QWidget* w : qAsConst(showWidgets)) { - if (!w->isVisible()) { - w->show(); - } - } - for (QWidget* w : qAsConst(hideWidgets)) { - if (w->isVisible()) { - w->hide(); - } - } -} - -/** - * @brief 返回所有pannels - * @return - */ -QList< SARibbonPannel* > SARibbonCategoryLayout::pannels() const -{ - QList< SARibbonPannel* > res; - - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - SARibbonPannel* p = item->toPannelWidget(); - res.append(p); - } - return (res); -} - -/** - * @brief 通过ObjectName查找pannel - * @param objname - * @return - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByObjectName(const QString& objname) const -{ - for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) { - if (SARibbonPannel* pannel = item->toPannelWidget()) { - if (pannel->objectName() == objname) { - return pannel; - } - } - } - return nullptr; -} - -/** - * @brief 通过名字查找pannel - * @param title - * @return 如果有重名,只会返回第一个符合条件的 - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByName(const QString& pannelname) const -{ - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* pannel = item->toPannelWidget()) { - if (pannel->pannelName() == pannelname) { - return (pannel); - } - } - } - return (nullptr); -} - -/** - * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr - * @param i - * @return - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByIndex(int i) const -{ - if (i >= 0 && i < d_ptr->mItemList.size()) { - return d_ptr->mItemList[ i ]->toPannelWidget(); - } - return nullptr; -} - -/** - * @brief 移动pannel - * @param from - * @param to - */ -void SARibbonCategoryLayout::movePannel(int from, int to) -{ - d_ptr->mItemList.move(from, to); - doLayout(); -} - -/** - * @brief 返回pannel的个数 - * @return - */ -int SARibbonCategoryLayout::pannelCount() const -{ - return d_ptr->mItemList.size(); -} - -/** - * @brief 查找pannel对应的索引 - * @param p - * @return 如果找不到,返回-1 - */ -int SARibbonCategoryLayout::pannelIndex(SARibbonPannel* p) const -{ - int c = pannelCount(); - - for (int i = 0; i < c; ++i) { - if (d_ptr->mItemList[ i ]->toPannelWidget() == p) { - return (i); - } - } - return (-1); -} - -/** - * @brief 获取所有的pannel - * @return - */ -QList< SARibbonPannel* > SARibbonCategoryLayout::pannelList() const -{ - QList< SARibbonPannel* > res; - - for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* p = i->toPannelWidget()) { - res.append(p); - } - } - return (res); -} - -/** - * @brief 执行滚动 - * @param px - */ -void SARibbonCategoryLayout::scroll(int px) -{ - QSize contentSize = categoryContentSize(); - d_ptr->mXBase += px; - if (d_ptr->mXBase > 0) { - d_ptr->mXBase = 0; - } else if ((d_ptr->mXBase + d_ptr->mTotalWidth) < contentSize.width()) { - d_ptr->mXBase = contentSize.width() - d_ptr->mTotalWidth; - } - invalidate(); -} - -/** - * @brief 判断是否滚动过 - * @return - */ -bool SARibbonCategoryLayout::isScrolled() const -{ - return (d_ptr->mXBase != 0); -} - -/** - * @brief 这个宽度是实际内容的宽度,有可能大于size.width,也有可能小于 - * @return - */ -int SARibbonCategoryLayout::categoryTotalWidth() const -{ - return d_ptr->mTotalWidth; -} - -/** - @brief 设置Category的对齐方式 - - 居中对齐会让pannel以居中进行对齐 - @param al - */ -void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al) -{ - d_ptr->mCategoryAlignment = al; -} - -/** - @brief Category的对齐方式 - @return - */ -SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const -{ - return d_ptr->mCategoryAlignment; -} - -void SARibbonCategoryLayout::onLeftScrollButtonClicked() -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); - int width = category->width(); - // 求总宽 - int totalWidth = d_ptr->mTotalWidth; - - if (totalWidth > width) { - int tmp = d_ptr->mXBase + width; - if (tmp > 0) { - tmp = 0; - } - d_ptr->mXBase = tmp; - } else { - d_ptr->mXBase = 0; - } - invalidate(); -} - -void SARibbonCategoryLayout::onRightScrollButtonClicked() -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); - int width = category->width(); - // 求总宽 - int totalWidth = d_ptr->mTotalWidth; - - if (totalWidth > width) { - int tmp = d_ptr->mXBase - width; - if (tmp < (width - totalWidth)) { - tmp = width - totalWidth; - } - d_ptr->mXBase = tmp; - } else { - d_ptr->mXBase = 0; - } - invalidate(); -} - -void SARibbonCategoryLayout::setGeometry(const QRect& rect) -{ - QRect old = geometry(); - if (old == rect) { - return; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName() - << ")======="; -#endif - QLayout::setGeometry(rect); - d_ptr->mDirty = false; - updateGeometryArr(); - doLayout(); -} -//============================================================= -// SARibbonCategoryLayoutItem -//============================================================= - -SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPannel* w) : QWidgetItem(w) -{ - separatorWidget = nullptr; -} - -SARibbonCategoryLayoutItem::~SARibbonCategoryLayoutItem() -{ -} - -SARibbonPannel* SARibbonCategoryLayoutItem::toPannelWidget() -{ - return qobject_cast< SARibbonPannel* >(widget()); -} - -/*** End of inlined file: SARibbonCategoryLayout.cpp ***/ - -/*** Start of inlined file: SARibbonContextCategory.cpp ***/ -#include -#include - -#include - -/** - * @brief The SARibbonCategoryData class - */ -class SAPrivateRibbonCategoryData -{ -public: - SARibbonCategory* categoryPage; -}; - -/** - * @brief The SARibbonContextCategoryPrivate class - */ -class SARibbonContextCategory::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory) -public: - PrivateData(SARibbonContextCategory* p); - -public: - QList< SAPrivateRibbonCategoryData > mCategoryDataList; - QVariant mContextID; - QColor mContextColor; - QString mContextTitle; -}; -SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p) -{ -} - -//=================================================== -// SARibbonContextCategory -//=================================================== -SARibbonContextCategory::SARibbonContextCategory(QWidget* parent) - : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this)) -{ -} - -SARibbonContextCategory::~SARibbonContextCategory() -{ -} - -/** - * @brief 添加标签 - * @param title 标签名字 - */ -SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title) -{ - SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget()); - category->setCategoryName(title); - addCategoryPage(category); - return (category); -} - -/** - * @brief 添加标签 - * @param page - */ -void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category) -{ - if (isHaveCategory(category)) { - // cn:SARibbonContextCategory已经持有标签:%1,将跳过 - qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName()); - return; - } - category->markIsContextCategory(true); - connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged); - SAPrivateRibbonCategoryData catData; - catData.categoryPage = category; - d_ptr->mCategoryDataList.append(catData); - category->installEventFilter(this); - Q_EMIT categoryPageAdded(category); -} - -int SARibbonContextCategory::categoryCount() const -{ - return (d_ptr->mCategoryDataList.count()); -} - -void SARibbonContextCategory::setId(const QVariant& id) -{ - d_ptr->mContextID = id; -} - -QVariant SARibbonContextCategory::id() const -{ - return (d_ptr->mContextID); -} - -void SARibbonContextCategory::setContextColor(const QColor color) -{ - d_ptr->mContextColor = color; -} - -QColor SARibbonContextCategory::contextColor() const -{ - return (d_ptr->mContextColor); -} - -QWidget* SARibbonContextCategory::parentWidget() const -{ - return (qobject_cast< QWidget* >(parent())); -} - -bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e) -{ - if (nullptr == watched) { - return (false); - } - switch (e->type()) { - case QEvent::Close: { - SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched); - if (c) { -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - qDebug() << " -----------> close event"; -#endif - takeCategory(c); - } - } break; - - default: - break; - } - return (false); -} - -/** - * @brief 获取上下文标签的标题 - * @return - */ -QString SARibbonContextCategory::contextTitle() const -{ - return (d_ptr->mContextTitle); -} - -/** - * @brief 设置上下文标签的标题,标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle - * @param contextTitle - * @note 此函数会触发信号@sa contextTitleChanged - */ -void SARibbonContextCategory::setContextTitle(const QString& contextTitle) -{ - d_ptr->mContextTitle = contextTitle; - Q_EMIT contextTitleChanged(contextTitle); -} - -/** - * @brief 获取对应的tab页 - * @param index - * @return - */ -SARibbonCategory* SARibbonContextCategory::categoryPage(int index) -{ - return (d_ptr->mCategoryDataList[ index ].categoryPage); -} - -/** - * @brief 获取所有的SARibbonCategory* - * @return - */ -QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const -{ - QList< SARibbonCategory* > res; - - for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->mCategoryDataList)) { - res.append(c.categoryPage); - } - return (res); -} - -/** - * @brief 移除这个category,这时SARibbonContextCategory不再管理这个category - * @param category - * @return 成功移除返回true - */ -bool SARibbonContextCategory::takeCategory(SARibbonCategory* category) -{ - for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { - if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { - d_ptr->mCategoryDataList.takeAt(i); - return (true); - } - } - return (false); -} - -/** - * @brief 判断上下文是否维护了此SARibbonCategory - * @param category - * @return - */ -bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const -{ - for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { - if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { - return (true); - } - } - return (false); -} - -void SARibbonContextCategory::onCategoryTitleChanged(const QString& title) -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender()); - if (category) { - Q_EMIT categoryTitleChanged(category, title); - } -} - -/*** End of inlined file: SARibbonContextCategory.cpp ***/ - -/*** Start of inlined file: SARibbonGalleryItem.cpp ***/ -SARibbonGalleryItem::SARibbonGalleryItem() : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr) -{ -} - -SARibbonGalleryItem::SARibbonGalleryItem(const QString& text, const QIcon& icon) - : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr) -{ - setText(text); - setIcon(icon); - setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); -} - -SARibbonGalleryItem::SARibbonGalleryItem(QAction* act) : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable) -{ - setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); - setAction(act); -} - -SARibbonGalleryItem::~SARibbonGalleryItem() -{ -} - -void SARibbonGalleryItem::setData(int role, const QVariant& data) -{ - mDatas[ role ] = data; -} - -QVariant SARibbonGalleryItem::data(int role) const -{ - if (mAction) { - switch (role) { - case Qt::DisplayRole: - return (mAction->text()); - - case Qt::ToolTipRole: - return (mAction->toolTip()); - - case Qt::DecorationRole: - return (mAction->icon()); - default: - break; - } - } - return (mDatas.value(role)); -} - -void SARibbonGalleryItem::setText(const QString& text) -{ - setData(Qt::DisplayRole, text); -} - -QString SARibbonGalleryItem::text() const -{ - if (mAction) { - return (mAction->text()); - } - return (data(Qt::DisplayRole).toString()); -} - -void SARibbonGalleryItem::setToolTip(const QString& text) -{ - setData(Qt::ToolTipRole, text); -} - -QString SARibbonGalleryItem::toolTip() const -{ - if (mAction) { - return (mAction->toolTip()); - } - return (data(Qt::ToolTipRole).toString()); -} - -void SARibbonGalleryItem::setIcon(const QIcon& ico) -{ - setData(Qt::DecorationRole, ico); -} - -QIcon SARibbonGalleryItem::icon() const -{ - if (mAction) { - return (mAction->icon()); - } - return (qvariant_cast< QIcon >(data(Qt::DecorationRole))); -} - -bool SARibbonGalleryItem::isSelectable() const -{ - return (mFlags & Qt::ItemIsSelectable); -} - -void SARibbonGalleryItem::setSelectable(bool isSelectable) -{ - if (isSelectable) { - mFlags |= Qt::ItemIsSelectable; - } else { - mFlags = (mFlags & (~Qt::ItemIsSelectable)); - } -} - -bool SARibbonGalleryItem::isEnable() const -{ - if (mAction) { - return (mAction->isEnabled()); - } - return (mFlags & Qt::ItemIsEnabled); -} - -void SARibbonGalleryItem::setEnable(bool isEnable) -{ - if (mAction) { - mAction->setEnabled(isEnable); - } - - if (isEnable) { - mFlags |= Qt::ItemIsEnabled; - } else { - mFlags = (mFlags & (~Qt::ItemIsEnabled)); - } -} - -void SARibbonGalleryItem::setFlags(Qt::ItemFlags flag) -{ - mFlags = flag; - if (mAction) { - mAction->setEnabled(flag & Qt::ItemIsEnabled); - } -} - -Qt::ItemFlags SARibbonGalleryItem::flags() const -{ - return (mFlags); -} - -void SARibbonGalleryItem::setAction(QAction* act) -{ - mAction = act; - if (nullptr == mAction) { - return; - } - if (act->isEnabled()) { - mFlags |= Qt::ItemIsEnabled; - } else { - mFlags = (mFlags & (~Qt::ItemIsEnabled)); - } -} - -QAction* SARibbonGalleryItem::action() -{ - return (mAction); -} - -void SARibbonGalleryItem::setTextAlignment(Qt::Alignment a) -{ - setData(Qt::TextAlignmentRole, (int)a); -} - -Qt::Alignment SARibbonGalleryItem::textAlignment() const -{ - return qvariant_cast< Qt::Alignment >(data(Qt::TextAlignmentRole)); -} - -/*** End of inlined file: SARibbonGalleryItem.cpp ***/ - -/*** Start of inlined file: SARibbonGalleryGroup.cpp ***/ -#include -#include -#include -#include - -/** - * @brief The SARibbonGalleryGroupPrivate class - */ -class SARibbonGalleryGroup::PrivateData -{ -public: - SARibbonGalleryGroup* q_ptr; - QString mGroupTitle; - SARibbonGalleryGroup::GalleryGroupStyle mPreStyle { SARibbonGalleryGroup::IconWithText }; - SARibbonGalleryGroup::DisplayRow mDisplayRow { SARibbonGalleryGroup::DisplayOneRow }; - bool mBlockRecalc { false }; - int mGridMinimumWidth { 0 }; ///< grid最小宽度 - int mGridMaximumWidth { 0 }; ///< grid最大宽度 - QActionGroup* mActionGroup { nullptr }; ///< 所有GalleryGroup管理的actions都由这个actiongroup管理 -public: - PrivateData(SARibbonGalleryGroup* p) : q_ptr(p) - { - mActionGroup = new QActionGroup(p); - p->connect(mActionGroup, &QActionGroup::triggered, p, &SARibbonGalleryGroup::triggered); - p->connect(mActionGroup, &QActionGroup::hovered, p, &SARibbonGalleryGroup::hovered); - } -}; - -//=================================================== -// SARibbonGalleryGroupItemDelegate -//=================================================== - -SARibbonGalleryGroupItemDelegate::SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent) - : QStyledItemDelegate(parent), mGroup(group) -{ -} - -SARibbonGalleryGroupItemDelegate::~SARibbonGalleryGroupItemDelegate() -{ -} - -void SARibbonGalleryGroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - if (nullptr == mGroup) { - return; - } - switch (mGroup->galleryGroupStyle()) { - case SARibbonGalleryGroup::IconWithText: - paintIconWithText(painter, option, index); - break; - case SARibbonGalleryGroup::IconWithWordWrapText: - paintIconWithTextWordWrap(painter, option, index); - break; - case SARibbonGalleryGroup::IconOnly: - paintIconOnly(painter, option, index); - break; - default: - paintIconWithText(painter, option, index); - break; - } -} - -void SARibbonGalleryGroupItemDelegate::paintIconOnly(QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - QStyle* style = mGroup->style(); - int sp = mGroup->spacing(); - sp += 3; - painter->save(); - painter->setClipRect(option.rect); - style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, mGroup); - // draw the icon - QRect iconRect = option.rect; - - iconRect.adjust(sp, sp, -sp, -sp); - QIcon ico = qvariant_cast< QIcon >(index.data(Qt::DecorationRole)); - - ico.paint(painter, iconRect, Qt::AlignCenter, QIcon::Normal, QIcon::On); - painter->restore(); -} - -void SARibbonGalleryGroupItemDelegate::paintIconWithText(QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - QStyledItemDelegate::paint(painter, option, index); -} - -void SARibbonGalleryGroupItemDelegate::paintIconWithTextWordWrap(QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - QStyledItemDelegate::paint(painter, option, index); -} - -QSize SARibbonGalleryGroupItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - Q_UNUSED(index); - Q_UNUSED(option); - return mGroup->gridSize(); -} - -//=================================================== -// SARibbonGalleryGroupModel -//=================================================== - -SARibbonGalleryGroupModel::SARibbonGalleryGroupModel(QObject* parent) : QAbstractListModel(parent) -{ -} - -SARibbonGalleryGroupModel::~SARibbonGalleryGroupModel() -{ - clear(); -} - -int SARibbonGalleryGroupModel::rowCount(const QModelIndex& parent) const -{ - return (parent.isValid() ? 0 : mItems.size()); -} - -Qt::ItemFlags SARibbonGalleryGroupModel::flags(const QModelIndex& index) const -{ - if (!index.isValid() || (index.row() >= mItems.size())) { - return (Qt::NoItemFlags); - } - return (mItems.at(index.row())->flags()); -} - -QVariant SARibbonGalleryGroupModel::data(const QModelIndex& index, int role) const -{ - if (!index.isValid() || (index.row() >= mItems.count())) { - return (QVariant()); - } - return (mItems.at(index.row())->data(role)); -} - -QModelIndex SARibbonGalleryGroupModel::index(int row, int column, const QModelIndex& parent) const -{ - if (hasIndex(row, column, parent)) { - return (createIndex(row, column, mItems.at(row))); - } - return (QModelIndex()); -} - -bool SARibbonGalleryGroupModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!index.isValid() || (index.row() >= mItems.count())) { - return (false); - } - - mItems.at(index.row())->setData(role, value); - return (true); -} - -void SARibbonGalleryGroupModel::clear() -{ - beginResetModel(); - for (int i = 0; i < mItems.count(); ++i) { - if (mItems.at(i)) { - delete mItems.at(i); - } - } - mItems.clear(); - endResetModel(); -} - -SARibbonGalleryItem* SARibbonGalleryGroupModel::at(int row) const -{ - return (mItems.value(row)); -} - -void SARibbonGalleryGroupModel::insert(int row, SARibbonGalleryItem* item) -{ - beginInsertRows(QModelIndex(), row, row); - mItems.insert(row, item); - endInsertRows(); -} - -SARibbonGalleryItem* SARibbonGalleryGroupModel::take(int row) -{ - if ((row < 0) || (row >= mItems.count())) { - return (0); - } - - beginRemoveRows(QModelIndex(), row, row); - SARibbonGalleryItem* item = mItems.takeAt(row); - - endRemoveRows(); - return (item); -} - -void SARibbonGalleryGroupModel::append(SARibbonGalleryItem* item) -{ - beginInsertRows(QModelIndex(), mItems.count(), mItems.count() + 1); - mItems.append(item); - endInsertRows(); -} - -//=================================================== -// SARibbonGalleryGroup -//=================================================== -SARibbonGalleryGroup::SARibbonGalleryGroup(QWidget* w) - : QListView(w), d_ptr(new SARibbonGalleryGroup::PrivateData(this)) -{ - setViewMode(QListView::IconMode); - setResizeMode(QListView::Adjust); - setSelectionRectVisible(true); - setUniformItemSizes(true); - setSpacing(1); - setItemDelegate(new SARibbonGalleryGroupItemDelegate(this, this)); - connect(this, &QAbstractItemView::clicked, this, &SARibbonGalleryGroup::onItemClicked); - SARibbonGalleryGroupModel* m = new SARibbonGalleryGroupModel(this); - setModel(m); -} - -SARibbonGalleryGroup::~SARibbonGalleryGroup() -{ -} - -/** - * @brief 是否禁止计算 - * @param on - */ -void SARibbonGalleryGroup::setRecalcGridSizeBlock(bool on) -{ - d_ptr->mBlockRecalc = on; -} - -bool SARibbonGalleryGroup::isRecalcGridSizeBlock() const -{ - return d_ptr->mBlockRecalc; -} - -/** - * @brief 重新计算grid和icon的尺寸 - */ -void SARibbonGalleryGroup::recalcGridSize() -{ - recalcGridSize(height()); -} - -void SARibbonGalleryGroup::recalcGridSize(int galleryHeight) -{ - if (isRecalcGridSizeBlock()) { - return; - } - // 首先通过DisplayRow计算GridSize - int dr = static_cast< int >(displayRow()); - if (dr < 1) { - dr = 1; - } else if (dr > 3) { - dr = 3; - } - int h = galleryHeight / dr; - if (h <= 1) { - h = galleryHeight; - } - int w = h; - if (gridMinimumWidth() > 0) { - if (w < gridMinimumWidth()) { - w = gridMinimumWidth(); - } - } - if (gridMaximumWidth() > 0) { - if (w > gridMaximumWidth()) { - w = gridMaximumWidth(); - } - } - setGridSize(QSize(w, h)); - // 在通过GalleryGroupStyle确定icon的尺寸 - const int shiftpix = - 4; // 这个是移动像素,qt在鼠标移动到图标上时会移动一下,给用户明确的动态,导致如果布局很满会超出显示范围,因此要在此基础上缩放一点 - switch (galleryGroupStyle()) { - case IconWithText: { - int textHeight = fontMetrics().lineSpacing(); - int iconHeight = h - textHeight - 2 * spacing() - shiftpix; - if (iconHeight > 0) { - setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight)); - } else { - setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); - } - break; - } - case IconWithWordWrapText: { - int textHeight = fontMetrics().lineSpacing() * 2; - int iconHeight = h - textHeight; - if (iconHeight > 0) { - setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight - 2 * spacing() - shiftpix)); - } else { - setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); - } - break; - } - case IconOnly: { - setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); - break; - } - default: { - setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); - break; - } - } -#if 0 - qDebug() << "SARibbonGalleryGroup::recalcGridSize(" << galleryHeight << "): gridSize=" << gridSize() - << " iconSize=" << iconSize(); -#endif -} - -/// -/// \brief 设置默认的预设样式 -/// \param style -/// -void SARibbonGalleryGroup::setGalleryGroupStyle(SARibbonGalleryGroup::GalleryGroupStyle style) -{ - d_ptr->mPreStyle = style; - if (style == IconWithWordWrapText) { - setWordWrap(true); - } - recalcGridSize(); -} - -SARibbonGalleryGroup::GalleryGroupStyle SARibbonGalleryGroup::galleryGroupStyle() const -{ - return d_ptr->mPreStyle; -} - -void SARibbonGalleryGroup::addItem(const QString& text, const QIcon& icon) -{ - if (nullptr == groupModel()) { - return; - } - addItem(new SARibbonGalleryItem(text, icon)); -} - -/** - * @brief 添加一个条目 - * - * @param item 条目的内存所有权归属SARibbonGalleryGroup管理 - */ -void SARibbonGalleryGroup::addItem(SARibbonGalleryItem* item) -{ - if (nullptr == groupModel()) { - return; - } - groupModel()->append(item); -} - -void SARibbonGalleryGroup::addActionItem(QAction* act) -{ - if (nullptr == groupModel()) { - return; - } - d_ptr->mActionGroup->addAction(act); - groupModel()->append(new SARibbonGalleryItem(act)); -} - -void SARibbonGalleryGroup::addActionItemList(const QList< QAction* >& acts) -{ - SARibbonGalleryGroupModel* model = groupModel(); - - if (nullptr == model) { - return; - } - for (QAction* a : acts) { - d_ptr->mActionGroup->addAction(a); - } - for (int i = 0; i < acts.size(); ++i) { - model->append(new SARibbonGalleryItem(acts[ i ])); - } -} - -/// -/// \brief 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 -/// -void SARibbonGalleryGroup::setupGroupModel() -{ - setModel(new SARibbonGalleryGroupModel(this)); -} - -SARibbonGalleryGroupModel* SARibbonGalleryGroup::groupModel() -{ - return (qobject_cast< SARibbonGalleryGroupModel* >(model())); -} - -void SARibbonGalleryGroup::setGroupTitle(const QString& title) -{ - d_ptr->mGroupTitle = title; - Q_EMIT groupTitleChanged(d_ptr->mGroupTitle); -} - -QString SARibbonGalleryGroup::groupTitle() const -{ - return (d_ptr->mGroupTitle); -} - -void SARibbonGalleryGroup::selectByIndex(int i) -{ - SARibbonGalleryGroupModel* model = groupModel(); - - if (nullptr == model) { - return; - } - QModelIndex ind = model->index(i, 0, QModelIndex()); - QItemSelectionModel* selmodel = selectionModel(); - - if (selmodel) { - selmodel->select(ind, QItemSelectionModel::SelectCurrent); - } -} - -/** - * @brief 设置显示的行数 - * @param r - */ -void SARibbonGalleryGroup::setDisplayRow(DisplayRow r) -{ - d_ptr->mDisplayRow = r; - recalcGridSize(); -} - -/** - * @brief Gallery显示的行数 - * @return - */ -SARibbonGalleryGroup::DisplayRow SARibbonGalleryGroup::displayRow() const -{ - return d_ptr->mDisplayRow; -} - -/** - * @brief 设置grid最小的宽度,默认为0(不限制) - * @param w - */ -void SARibbonGalleryGroup::setGridMinimumWidth(int w) -{ - d_ptr->mGridMinimumWidth = w; -} - -/** - * @brief grid最小的宽度,默认为0(不限制) - * @return - */ -int SARibbonGalleryGroup::gridMinimumWidth() const -{ - return d_ptr->mGridMinimumWidth; -} - -/** - * @brief 设置grid最大的宽度,默认为0(不限制) - * @param w - */ -void SARibbonGalleryGroup::setGridMaximumWidth(int w) -{ - d_ptr->mGridMaximumWidth = w; -} - -/** - * @brief grid最大的的宽度,默认为0(不限制) - * @param w - */ -int SARibbonGalleryGroup::gridMaximumWidth() const -{ - return d_ptr->mGridMaximumWidth; -} - -/** - * @brief 获取SARibbonGalleryGroup管理的actiongroup - * @return - */ -QActionGroup* SARibbonGalleryGroup::actionGroup() const -{ - return d_ptr->mActionGroup; -} - -void SARibbonGalleryGroup::onItemClicked(const QModelIndex& index) -{ - if (index.isValid()) { - SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); - if (item) { - QAction* act = item->action(); - if (act) { - act->activate(QAction::Trigger); - } - } - } -} - -void SARibbonGalleryGroup::onItemEntered(const QModelIndex& index) -{ - if (index.isValid()) { - SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); - if (item) { - QAction* act = item->action(); - if (act) { - act->activate(QAction::Hover); - } - } - } -} - -/*** End of inlined file: SARibbonGalleryGroup.cpp ***/ - -/*** Start of inlined file: SARibbonGallery.cpp ***/ -#include -#include -#define ICON_ARROW_UP QIcon(":/image/resource/ArrowUp.png") -#define ICON_ARROW_DOWN QIcon(":/image/resource/ArrowDown.png") -#define ICON_ARROW_MORE QIcon(":/image/resource/ArrowMore.png") -#include -#include -#include -#include -#include - -#include - -/** - * @brief The SARibbonGalleryPrivate class - */ -class SARibbonGallery::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery) -public: - static int sGalleryButtonMaximumWidth; - SARibbonGalleryButton* mButtonUp { nullptr }; - SARibbonGalleryButton* mButtonDown { nullptr }; - SARibbonGalleryButton* mButtonMore { nullptr }; -#if 0 - SARibbonMenu *popupWidget; -#else - SARibbonGalleryViewport* mPopupWidget { nullptr }; -#endif - SARibbonGalleryGroup* mViewportGroup { nullptr }; - QBoxLayout* mButtonLayout { nullptr }; - QBoxLayout* mLayout { nullptr }; - PrivateData(SARibbonGallery* p) : q_ptr(p) - { - } - - void init() - { - mButtonUp = new SARibbonGalleryButton(q_ptr); - mButtonDown = new SARibbonGalleryButton(q_ptr); - mButtonMore = new SARibbonGalleryButton(q_ptr); - mButtonUp->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonDown->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonMore->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp")); - mButtonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown")); - mButtonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore")); - mButtonUp->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonDown->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonMore->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonUp->setIcon(ICON_ARROW_UP); - mButtonDown->setIcon(ICON_ARROW_DOWN); - mButtonMore->setIcon(ICON_ARROW_MORE); - q_ptr->connect(mButtonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp); - q_ptr->connect(mButtonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown); - q_ptr->connect(mButtonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail); - // 信号转发 - q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered); - mPopupWidget = nullptr; - mViewportGroup = nullptr; - mButtonLayout = new QBoxLayout(QBoxLayout::TopToBottom); - mButtonLayout->setSpacing(0); - mButtonLayout->setContentsMargins(0, 0, 0, 0); - mButtonLayout->addWidget(mButtonUp); - mButtonLayout->addWidget(mButtonDown); - mButtonLayout->addWidget(mButtonMore); - mLayout = new QBoxLayout(QBoxLayout::RightToLeft); - mLayout->setSpacing(0); - mLayout->setContentsMargins(0, 0, 0, 0); - mLayout->addLayout(mButtonLayout); - mLayout->addStretch(); - q_ptr->setLayout(mLayout); - } - - bool isValid() const - { - return (q_ptr != nullptr); - } - - void createPopupWidget() - { - if (nullptr == mPopupWidget) { -#if 0 - popupWidget = new SARibbonMenu(Parent); -#else - mPopupWidget = new SARibbonGalleryViewport(q_ptr); -#endif - } - } - - void setViewPort(SARibbonGalleryGroup* v) - { - if (nullptr == mViewportGroup) { - mViewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr); - mLayout->addWidget(mViewportGroup, 1); - } - mViewportGroup->setRecalcGridSizeBlock(true); - mViewportGroup->setGalleryGroupStyle(v->galleryGroupStyle()); - mViewportGroup->setDisplayRow(v->displayRow()); - mViewportGroup->setSpacing(v->spacing()); - mViewportGroup->setGridMaximumWidth(v->gridMaximumWidth()); - mViewportGroup->setGridMinimumWidth(v->gridMinimumWidth()); - mViewportGroup->setRecalcGridSizeBlock(false); - mViewportGroup->recalcGridSize(mViewportGroup->height()); - mViewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - mViewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - mViewportGroup->setModel(v->model()); - mViewportGroup->show(); - } -}; - -// 静态变量初始化 - -/** - * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth - */ -int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15; - -//=================================================== -// SARibbonGalleryButton -//=================================================== - -SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonGalleryButton::~SARibbonGalleryButton() -{ -} -//=================================================== -// SARibbonGalleryViewport -//=================================================== - -SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent) -{ - setWindowFlags(Qt::Popup); - QPalette pl = palette(); - pl.setBrush(QPalette::Window, pl.brush(QPalette::Base)); - setPalette(pl); - m_layout = new QVBoxLayout(this); - m_layout->setSpacing(0); - m_layout->setContentsMargins(0, 0, 0, 0); -} - -/** - * @brief 添加窗口不带标题 - * @param w - */ -void SARibbonGalleryViewport::addWidget(QWidget* w) -{ - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(w); -} - -/** - * @brief 添加窗口,带标题 - * @param w - * @param title - */ -void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title) -{ - QLabel* label = new QLabel(this); - label->setText(title); - label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(label); - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(w); - _widgetToTitleLable[ w ] = label; -} - -/** - * @brief 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 - * @param g - * @return 如果没有管理group,将返回nullptr - */ -QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w) -{ - return _widgetToTitleLable.value(w, nullptr); -} - -/** - * @brief 移除窗口 - * @param w - */ -void SARibbonGalleryViewport::removeWidget(QWidget* w) -{ - QLabel* label = titleLabel(w); - if (label) { - m_layout->removeWidget(label); - } - m_layout->removeWidget(w); -} - -/** - * @brief widget的标题改变 - * @param g - * @param title - */ -void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title) -{ - QLabel* l = titleLabel(w); - if (l) { - l->setText(title); - } -} - -//=================================================== -// SARibbonGallery -//=================================================== - -SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this)) -{ - d_ptr->init(); - setFrameShape(QFrame::Box); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setMinimumWidth(200); -} - -SARibbonGallery::~SARibbonGallery() -{ -} - -QSize SARibbonGallery::sizeHint() const -{ - return (QSize(100, 62)); -} - -/** - * @brief 获取一个空白SARibbonGalleryGroup - * @return - */ -SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup() -{ - SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); - addGalleryGroup(group); - return (group); -} - -/** - * @brief 添加一组SARibbonGalleryGroup - * @param group - */ -void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group) -{ - group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - SARibbonGalleryViewport* viewport = ensureGetPopupViewPort(); - viewport->addWidget(group, group->groupTitle()); - connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked); - connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) { - viewport->onTitleChanged(group, t); - }); - connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered); - connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered); - setCurrentViewGroup(group); -} - -/** - * @brief 添加一组actions - * @param title actions组的名字 - * @param actions - * @return 返回SARibbonGalleryGroup,用户可以通过修改SARibbonGalleryGroup属性控制其显示方法 - */ -SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions) -{ - SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); - if (!title.isEmpty()) { - group->setGroupTitle(title); - } - group->addActionItemList(actions); - addGalleryGroup(group); - return (group); -} - -void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group) -{ - d_ptr->setViewPort(group); - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const -{ - return (d_ptr->mViewportGroup); -} - -/** - * @brief 获取弹出窗口 - * @return - */ -SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const -{ - return d_ptr->mPopupWidget; -} - -/** - * @brief 设置最右边三个控制按钮的最大宽度(默认15) - * @param w - */ -void SARibbonGallery::setGalleryButtonMaximumWidth(int w) -{ - SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w; -} - -/** - * @brief 上翻页 - */ -void SARibbonGallery::pageDown() -{ - if (d_ptr->mViewportGroup) { - QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); - int v = vscrollBar->value(); - v += vscrollBar->singleStep(); - vscrollBar->setValue(v); - } -} - -/** - * @brief 下翻页 - */ -void SARibbonGallery::pageUp() -{ - if (d_ptr->mViewportGroup) { - QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); - int v = vscrollBar->value(); - v -= vscrollBar->singleStep(); - vscrollBar->setValue(v); - } -} - -/** - * @brief 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 - */ -void SARibbonGallery::showMoreDetail() -{ - if (nullptr == d_ptr->mPopupWidget) { - return; - } - QSize popupMenuSize = d_ptr->mPopupWidget->sizeHint(); - QPoint start = mapToGlobal(QPoint(0, 0)); - - int width = d_ptr->mViewportGroup->width(); // viewport - - width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); // scrollbar - d_ptr->mPopupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height()); - d_ptr->mPopupWidget->show(); -} - -void SARibbonGallery::onItemClicked(const QModelIndex& index) -{ - QObject* obj = sender(); - SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj); - if (group) { - setCurrentViewGroup(group); - SARibbonGalleryGroup* curGroup = currentViewGroup(); - curGroup->scrollTo(index); - curGroup->setCurrentIndex(index); - } -} - -void SARibbonGallery::onTriggered(QAction* action) -{ - Q_UNUSED(action); - // 点击后关闭弹出窗口 - if (d_ptr->mPopupWidget) { - if (d_ptr->mPopupWidget->isVisible()) { - d_ptr->mPopupWidget->hide(); - } - } -} - -SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort() -{ - if (nullptr == d_ptr->mPopupWidget) { - d_ptr->createPopupWidget(); - } - return (d_ptr->mPopupWidget); -} - -void SARibbonGallery::resizeEvent(QResizeEvent* event) -{ - QFrame::resizeEvent(event); - // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸 - int h = layout()->contentsRect().height(); - if (d_ptr->mViewportGroup) { - h = d_ptr->mViewportGroup->height(); - d_ptr->mViewportGroup->recalcGridSize(); - } - if (d_ptr->mPopupWidget) { - QLayout* lay = d_ptr->mPopupWidget->layout(); - if (!lay) { - return; - } - int c = lay->count(); - for (int i = 0; i < c; ++i) { - QLayoutItem* item = lay->itemAt(i); - if (!item) { - continue; - } - QWidget* w = item->widget(); - if (!w) { - continue; - } - SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w); - if (!g) { - continue; - } - g->recalcGridSize(h); - } - } -} - -void SARibbonGallery::paintEvent(QPaintEvent* event) -{ - QFrame::paintEvent(event); -} - -/*** End of inlined file: SARibbonGallery.cpp ***/ - -/*** Start of inlined file: SARibbonBar.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HELP_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - QPen _pen(Qt::DashDotDotLine); \ - _pen.setColor(QColor(219, 26, 59)); \ - p.setPen(_pen); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) - -class _SAContextCategoryManagerData -{ -public: - SARibbonContextCategory* contextCategory; - QList< int > tabPageIndex; - bool operator==(const SARibbonContextCategory* contextPage) - { - return (this->contextCategory == contextPage); - } -}; - -/** - * @todo 此处要修改,此方式容易异常 - */ -class _SARibbonTabData -{ -public: - SARibbonCategory* category; - int index; - _SARibbonTabData() : category(nullptr), index(-1) - { - } -}; -Q_DECLARE_METATYPE(_SARibbonTabData) - -class SARibbonBar::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonBar) -public: - QPointer< QAbstractButton > mApplicationButton; - QPointer< SARibbonTabBar > mRibbonTabBar; - QPointer< SARibbonStackedWidget > mStackedContainerWidget; - QPointer< SARibbonButtonGroupWidget > mRightButtonGroup; ///< 在tab bar右边的按钮群 - QPointer< SARibbonQuickAccessBar > mQuickAccessBar; ///< 快速响应栏 - QAction* mMinimumCategoryButtonAction { nullptr }; ///< 隐藏面板按钮action - QList< _SAContextCategoryManagerData > mCurrentShowingContextCategory; - QList< SARibbonContextCategory* > mContextCategoryList; ///< 存放所有的上下文标签 - QList< _SARibbonTabData > mHidedCategory; - int mIconRightBorderPosition { 1 }; ///< 标题栏x值得最小值,在有图标和快捷启动按钮,此值都需要变化 - SARibbonBar::RibbonStyles mRibbonStyle { SARibbonBar::RibbonStyleLooseThreeRow }; ///< ribbon的风格 - SARibbonBar::RibbonMode mCurrentRibbonMode { SARibbonBar::NormalRibbonMode }; ///< 记录当前模式 - QSize mWindowButtonSize; ///< 由SARibbonMainWindow告诉的windowbutton的尺寸 - QList< QColor > mContextCategoryColorList; ///< contextCategory的色系 - int mContextCategoryColorListIndex { -1 }; ///< 记录contextCategory色系索引 - QColor mTitleTextColor; ///< 标题文字颜色,默认无效,无效的情况下和SARibbonBar的qss:color属性一致 - QColor mTabBarBaseLineColor { QColor(186, 201, 219) }; ///< tabbar 底部会绘制一条线条,定义线条颜色 - QColor mContextCategoryTitleTextColor { Qt::black }; ///< 记录Context category的标题字体颜色 - Qt::Alignment mTitleAligment { Qt::AlignCenter }; ///< 标题对齐方式 - bool mIsTitleVisible { true }; ///< 标题是否显示 - SARibbonAlignment mRibbonAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 - SARibbonPannel::PannelLayoutMode mDefaulePannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< 默认的PannelLayoutMode - bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 - bool mIsTabOnTitle { false }; ///< 是否tab在标题栏上 - int mTitleBarHeight { 30 }; ///< 标题栏高度 - int mTabBarHeight { 28 }; ///< tabbar高度 - int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 - int mCategoryHeight { 60 }; ///< Category的高度 - int mPannelSpacing { 0 }; ///< pannel的spacing - QSize mPannelToolButtonSize { 22, 22 }; ///< 记录pannel的默认图标大小 - std::unique_ptr< int > mUserDefTitleBarHeight; ///< 用户定义的标题栏高度,正常不使用用户设定的高度,而是使用自动计算的高度 - std::unique_ptr< int > mUserDefTabBarHeight; ///< 用户定义的tabbar高度,正常不使用用户设定的高度,而是使用自动计算的高度 - std::unique_ptr< int > mUserDefCategoryHeight; ///< 用户定义的Category的高度,正常不使用用户设定的高度,而是使用自动计算的高度 -public: - PrivateData(SARibbonBar* par) : q_ptr(par) - { - mContextCategoryColorList = SARibbonBar::defaultContextCategoryColorList(); - } - void init(); - int systemTabBarHeight() const; - // 计算tabbar高度 - int calcTabBarHeight(); - // 根据字体信息计算标题栏高度 - int calcTitleBarHeight(); - // 根据字体信息计算category的高度 - int calcCategoryHeight(); - // 计算tabbar高度 - static int - calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode); - // 获取当前最小模式下的高度 - int getCurrentMinimumModeMainBarHeight() const; - // 获取当前正常模式下的高度 - int getCurrentNormalModeMainBarHeight() const; - // 重置尺寸 - void resetSize(); - // 获取标题栏高度 - int titleBarHeight() const; - // tabbar 高度 - int tabBarHeigth() const; - // category高度 - int categoryHeight() const; - // 更新推荐的尺寸值 - void updateHintSize(); - - void setApplicationButton(QAbstractButton* btn); - - bool isContainContextCategoryInList(SARibbonContextCategory* contextCategory); - - void setMinimumMode(); - - void setNormalMode(); - - QColor getContextCategoryColor(); - - void updateTabData(); - - /** - * @brief 通过输入高度计算iconSize - * @param h - * @return - */ - static QSize calcIconSizeByHeight(int h); -}; - -void SARibbonBar::PrivateData::init() -{ - mApplicationButton = RibbonSubElementFactory->createRibbonApplicationButton(q_ptr); - q_ptr->connect(mApplicationButton.data(), &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); - mRibbonTabBar = RibbonSubElementFactory->createRibbonTabBar(q_ptr); - mRibbonTabBar->setObjectName(QStringLiteral("objSARibbonTabBar")); - mRibbonTabBar->setDrawBase(false); - q_ptr->connect(mRibbonTabBar.data(), &QTabBar::currentChanged, q_ptr, &SARibbonBar::onCurrentRibbonTabChanged); - q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabClicked); - q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarDoubleClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabDoubleClicked); - q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabMoved, q_ptr, &SARibbonBar::onTabMoved); - // - mStackedContainerWidget = RibbonSubElementFactory->createRibbonStackedWidget(q_ptr); - mStackedContainerWidget->setObjectName(QStringLiteral("objSARibbonStackedContainerWidget")); - q_ptr->connect(mStackedContainerWidget.data(), &SARibbonStackedWidget::hidWindow, q_ptr, &SARibbonBar::onStackWidgetHided); - // 捕获事件,在popmode时必须用到 - mStackedContainerWidget->installEventFilter(q_ptr); - // - mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(q_ptr); - mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); - mQuickAccessBar->setIcon(q_ptr->windowIcon()); - // - mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(q_ptr); - // - setNormalMode(); -} - -int SARibbonBar::PrivateData::systemTabBarHeight() const -{ - return q_ptr->style()->pixelMetric(QStyle::PM_TabBarBaseHeight) - + q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabHSpace) - + q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabOverlap); -} - -/** - * @brief 估算tabbar的高度 - * @param fm - * @return - */ -int SARibbonBar::PrivateData::calcTabBarHeight() -{ - int defaultHeight = systemTabBarHeight(); - int fontHeight = q_ptr->fontMetrics().lineSpacing(); // 不要用height,像宋体这种字体,height=12,lineSpacing=14,有些就无法显示 - int defaultHeight2 = fontHeight * 1.6; - if (defaultHeight2 < fontHeight + 10) { - defaultHeight2 = fontHeight + 10; // 主要为了满足office2021主题,tab下有个4px的横杠 - } - int r = qMax(defaultHeight, defaultHeight2); - if (r < 20) { - r = 20; - } - return r; -} - -/** - * @brief 估算标题栏的高度 - * @param fm - * @return - */ -int SARibbonBar::PrivateData::calcTitleBarHeight() -{ - int defaultHeight = q_ptr->style()->pixelMetric(QStyle::PM_TitleBarHeight); - int defaultHeight2 = q_ptr->fontMetrics().height() * 1.8; - int r = qMax(defaultHeight, defaultHeight2); - if (r < 25) { - r = 25; - } - return r; -} - -/** - * @brief 估算category的高度 - * @note 经过对照,1.6行高和office的高度比较接近 - * @param fm - * @param s - * @return - */ -int SARibbonBar::PrivateData::calcCategoryHeight() -{ - int textH = q_ptr->fontMetrics().lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 - if (SARibbonPannel::ThreeRowMode == mDefaulePannelLayoutMode) { - // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 - return textH * 4.8 + mPannelTitleHeight; - } else { - // 3=2*1.6 - return textH * 3.2 + mPannelTitleHeight; - } - return (textH * 4.8 + mPannelTitleHeight); -} - -/** - * @brief 计算总高 - * - * @note 此总高是普通模式下的总高 - * - * @param tabHegith - * @param titleHeight - * @param categoryHeight - * @return - */ -int SARibbonBar::PrivateData::calcMainBarHeight(int tabHegith, - int titleHeight, - int categoryHeight, - bool tabOnTitle, - SARibbonBar::RibbonMode rMode) -{ - if (rMode == MinimumRibbonMode) { - // 最小模式,没有categoryHeight - if (tabOnTitle) { - return titleHeight; - } else { - return titleHeight + tabHegith; - } - } else { - if (tabOnTitle) { - return titleHeight + categoryHeight; - } else { - return tabHegith + titleHeight + categoryHeight; - } - } - return tabHegith + titleHeight + categoryHeight; -} - -int SARibbonBar::PrivateData::getCurrentMinimumModeMainBarHeight() const -{ - return calcMainBarHeight( - tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::MinimumRibbonMode); -} - -int SARibbonBar::PrivateData::getCurrentNormalModeMainBarHeight() const -{ - return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::NormalRibbonMode); -} - -/** - * @brief 重新计算尺寸 - */ -void SARibbonBar::PrivateData::resetSize() -{ - updateHintSize(); - const int th = tabBarHeigth(); - int mainBarHeight = calcMainBarHeight(th, titleBarHeight(), categoryHeight(), mIsTabOnTitle, mCurrentRibbonMode); - // 处于最小模式下时,bar的高度为tabbar的bottom,这个调整必须在resize event之后 - q_ptr->setFixedHeight(mainBarHeight); -} - -/** - * @brief 标题栏高度 - * @return - */ -int SARibbonBar::PrivateData::titleBarHeight() const -{ - if (mUserDefTitleBarHeight) { - return *mUserDefTitleBarHeight; - } - return mTitleBarHeight; -} - -/** - * @brief tab高度 - * @return - */ -int SARibbonBar::PrivateData::tabBarHeigth() const -{ - if (mUserDefTabBarHeight) { - return *mUserDefTabBarHeight; - } - return mTabBarHeight; -} - -/** - * @brief category高度 - * @return - */ -int SARibbonBar::PrivateData::categoryHeight() const -{ - if (mUserDefCategoryHeight) { - return *mUserDefCategoryHeight; - } else { - return mCategoryHeight; - } -} - -/** - * @brief 更新推荐的尺寸值 - */ -void SARibbonBar::PrivateData::updateHintSize() -{ - mTitleBarHeight = calcTitleBarHeight(); - // mTabBarHeight有大于0的值说明用户设置了,就使用用户设置的值 - mTabBarHeight = calcTabBarHeight(); - mCategoryHeight = calcCategoryHeight(); -} - -void SARibbonBar::PrivateData::setApplicationButton(QAbstractButton* btn) -{ - if (mApplicationButton) { - mApplicationButton->hide(); - mApplicationButton->deleteLater(); - } - if (btn) { - if (btn->parent() != q_ptr) { - btn->setParent(q_ptr); - } - btn->move(0, q_ptr->titleBarHeight()); - btn->show(); - q_ptr->connect(btn, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); - } - mApplicationButton = btn; -} - -bool SARibbonBar::PrivateData::isContainContextCategoryInList(SARibbonContextCategory* contextCategory) -{ - for (int i = 0; i < mCurrentShowingContextCategory.size(); ++i) { - if (mCurrentShowingContextCategory[ i ] == contextCategory) { - return (true); - } - } - return (false); -} - -void SARibbonBar::PrivateData::setMinimumMode() -{ - mCurrentRibbonMode = SARibbonBar::MinimumRibbonMode; - mStackedContainerWidget->setPopupMode(); - mStackedContainerWidget->setFocusPolicy(Qt::NoFocus); - mStackedContainerWidget->clearFocus(); - mRibbonTabBar->setFocus(); - mStackedContainerWidget->hide(); - resetSize(); -} - -void SARibbonBar::PrivateData::setNormalMode() -{ - mCurrentRibbonMode = SARibbonBar::NormalRibbonMode; - mStackedContainerWidget->setNormalMode(); - mStackedContainerWidget->setFocus(); - mStackedContainerWidget->show(); - resetSize(); -} - -QColor SARibbonBar::PrivateData::getContextCategoryColor() -{ - if (mContextCategoryColorList.isEmpty()) { - mContextCategoryColorListIndex = -1; - return (QColor()); - } - ++mContextCategoryColorListIndex; - if ((mContextCategoryColorListIndex >= mContextCategoryColorList.size()) || (mContextCategoryColorListIndex < 0)) { - mContextCategoryColorListIndex = 0; - } - return (mContextCategoryColorList.at(mContextCategoryColorListIndex)); -} - -void SARibbonBar::PrivateData::updateTabData() -{ - int tabcount = mRibbonTabBar->count(); - - for (int i = 0; i < tabcount; ++i) { - QVariant var = mRibbonTabBar->tabData(i); - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - p.index = i; - mRibbonTabBar->setTabData(i, QVariant::fromValue(p)); - } - } - // 刷新完tabdata信息也要接着刷新ContextCategory信息 - for (_SAContextCategoryManagerData& cd : mCurrentShowingContextCategory) { - cd.tabPageIndex.clear(); - for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { - SARibbonCategory* category = cd.contextCategory->categoryPage(i); - for (int t = 0; t < tabcount; ++t) { - QVariant v = mRibbonTabBar->tabData(t); - if (v.isValid()) { - _SARibbonTabData d = v.value< _SARibbonTabData >(); - if (d.category == category) { - cd.tabPageIndex.append(t); - } - } else { - cd.tabPageIndex.append(-1); - } - } - } - } -} - -QSize SARibbonBar::PrivateData::calcIconSizeByHeight(int h) -{ - if (h - 8 >= 20) { - return QSize(h - 8, h - 8); - } - return QSize(h - 4, h - 4); -} - -//=================================================== -// SARibbonBar -//=================================================== - -/** - * @brief SARibbonBar构造函数 - * @param parent - */ -SARibbonBar::SARibbonBar(QWidget* parent) : QMenuBar(parent), d_ptr(new SARibbonBar::PrivateData(this)) -{ - d_ptr->init(); - ensurePolished(); - setNativeMenuBar(false); - // #ifdef Q_OS_MACOS - // setNativeMenuBar(false); - // #endif - // #ifdef Q_OS_LINUX - // setNativeMenuBar(false); - // #endif - if (parent) { - connect(parent, &QWidget::windowTitleChanged, this, &SARibbonBar::onWindowTitleChanged); - connect(parent, &QWidget::windowIconChanged, this, &SARibbonBar::onWindowIconChanged); - } - setRibbonStyle(RibbonStyleLooseThreeRow); -} - -SARibbonBar::~SARibbonBar() -{ -} - -/** - * @brief 判断样式是否为2行 - * @param s - * @return 2行返回true,返回false代表当前是3行 - */ -bool SARibbonBar::isTwoRowStyle(SARibbonBar::RibbonStyles s) -{ - return s.testFlag(SARibbonBar::RibbonStyleTwoRow); -} - -/** - * @brief 判断样式是否为3行 - * @param s - * @return 3行返回true,返回false代表当前是2行 - */ -bool SARibbonBar::isThreeRowStyle(RibbonStyles s) -{ - return s.testFlag(SARibbonBar::RibbonStyleThreeRow); -} - -/** - * @brief 判断是否是宽松样式 - * @param s - * @return 宽松样式(带标题栏)返回true,否则就是紧凑样式 - */ -bool SARibbonBar::isLooseStyle(SARibbonBar::RibbonStyles s) -{ - return s.testFlag(SARibbonBar::RibbonStyleLoose); -} - -/** - * @brief 判断是否是紧凑样式 - * @param s - * @return 紧凑样式(不带标题栏)返回true,否则就是宽松样式 - */ -bool SARibbonBar::isCompactStyle(RibbonStyles s) -{ - return s.testFlag(SARibbonBar::RibbonStyleCompact); -} - -/** - * @brief 获取版本信息 - * @return {SA_RIBBON_BAR_VERSION_MAJ}.{SA_RIBBON_BAR_VERSION_MIN}.{SA_RIBBON_BAR_VERSION_PAT} - */ -QString SARibbonBar::versionString() -{ - return QString("%1.%2.%3").arg(SA_RIBBON_BAR_VERSION_MAJ).arg(SA_RIBBON_BAR_VERSION_MIN).arg(SA_RIBBON_BAR_VERSION_PAT); -} - -/** - @brief 获取默认的上下文标签颜色列表 - @return - */ -QList< QColor > SARibbonBar::defaultContextCategoryColorList() -{ - QList< QColor > res; - res // - << QColor(206, 232, 252) // 蓝 - << QColor(253, 238, 179) // 黄 - << QColor(212, 255, 174) // 绿 - << QColor(255, 196, 214) // 红 - << QColor(255, 216, 153) // 橙 - << QColor(255, 224, 243) // 玫红 - ; - return res; -} - -/** - * @brief 提供高分屏的支持静态函数 - * - * @note 此函数需要在main函数,QApplication生成之前调用 - * @code - * int main(int argc, char* argv[]){ - * SARibbonBar::initHighDpi(); - * QApplication a(argc, argv); - * ... - * } - * @endcode - */ -void SARibbonBar::initHighDpi() -{ -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#endif -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - // 在有些时候,无法显示分割线,是由于PassThrough导致的 - QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); -#endif -} - -/** - * @brief 返回applicationButton - * @return 默认的applicationButton是@ref SARibbonApplicationButton 生成,通过@ref setApplicationButton 可设置为其他button - */ -QAbstractButton* SARibbonBar::applicationButton() -{ - return (d_ptr->mApplicationButton); -} - -/** - * @brief 设置applicationButton,如果想隐藏,可以传入nullptr - * - * 默认会有一个SARibbonApplicationButton,如果想取消,可传入nullptr,或者自定义的button也可以传入 - * - * @note applicationButton的所有权归SARibbonBar所有,不要在外部对applicationButton进行delete操作 - * @param btn applicationButton指针,可以传入@ref SARibbonApplicationButton, - * SA已经对SARibbonApplicationButton进行了样式设置 - */ -void SARibbonBar::setApplicationButton(QAbstractButton* btn) -{ - d_ptr->setApplicationButton(btn); - if (btn) { - if (btn->objectName().isEmpty()) { - btn->setObjectName(QStringLiteral("SARibbonApplicationButton")); - } - btn->setVisible(true); - // btn->setGeometry(applicationButtonGeometry()); - } - // 无论设置为什么都触发resize - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -/** - * @brief 返回tabbar - * @return SARibbonTabBar指针 - * @sa SARibbonTabBar - */ -SARibbonTabBar* SARibbonBar::ribbonTabBar() -{ - return (d_ptr->mRibbonTabBar); -} - -/** - * @brief 添加一个标签 - * 如果需要删除,直接delete即可,SARibbonBar会对其进行处理 - * @param title 标签名字,默认情况下SARibbonCategory的object name也被设置为title - * @return 返回一个窗口容器,在Category里可以添加其他控件 - * @sa SARibbonCategory - */ -SARibbonCategory* SARibbonBar::addCategoryPage(const QString& title) -{ - SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); - - category->setObjectName(title); - category->setCategoryName(title); - addCategoryPage(category); - return (category); -} - -/** - * @brief 添加一个标签 - * @param category - */ -void SARibbonBar::addCategoryPage(SARibbonCategory* category) -{ - if (nullptr == category) { - return; - } - int index = d_ptr->mRibbonTabBar->count(); - insertCategoryPage(category, index); -} - -/** - * @brief qtdesigner专用 - * @param category - */ -void SARibbonBar::addCategoryPage(QWidget* category) -{ - SARibbonCategory* c = qobject_cast< SARibbonCategory* >(category); - - if (c) { - addCategoryPage(c); - } -} - -/** - * @brief 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 - * @param title category的标题 - * @param index category的位置 - * @return - */ -SARibbonCategory* SARibbonBar::insertCategoryPage(const QString& title, int index) -{ - SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); - - category->setObjectName(title); - category->setCategoryName(title); - insertCategoryPage(category, index); - return (category); -} - -/** - * @brief 插入一个category - * @param category SARibbonCategory指针 - * @param index 插入的位置,如果超出范围,将默认插入到最后 - */ -void SARibbonBar::insertCategoryPage(SARibbonCategory* category, int index) -{ - if (nullptr == category) { - return; - } - category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); - category->setPannelSpacing(d_ptr->mPannelSpacing); - category->setPannelToolButtonIconSize(d_ptr->mPannelToolButtonSize); - int i = d_ptr->mRibbonTabBar->insertTab(index, category->categoryName()); - - _SARibbonTabData tabdata; - - tabdata.category = category; - tabdata.index = i; - d_ptr->mRibbonTabBar->setTabData(i, QVariant::fromValue(tabdata)); - d_ptr->mStackedContainerWidget->insertWidget(index, category); - // 更新index信息 - d_ptr->updateTabData(); - QApplication::postEvent(this, new QResizeEvent(size(), size())); - - connect(category, &QWidget::windowTitleChanged, this, &SARibbonBar::onCategoryWindowTitleChanged); - connect(category, &SARibbonCategory::actionTriggered, this, &SARibbonBar::actionTriggered); -} - -/** - * @brief 通过名字查找Category - * @param title Category的名字,既标签的标题 - * @return 如果没有找到,将返回nullptr,如果有重名,将返回第一个查询到的名字,因此,尽量避免重名标签 - * @note 由于翻译等原因,可能title会变化,因此如果想通过固定内容查找category,应该使用 @ref categoryByObjectName - * @see categoryByObjectName - */ -SARibbonCategory* SARibbonBar::categoryByName(const QString& title) const -{ - int c = d_ptr->mStackedContainerWidget->count(); - - for (int i = 0; i < c; ++i) { - SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); - if (w) { - if (w->categoryName() == title) { - return (w); - } - } - } - return (nullptr); -} - -/** - * @brief 通过ObjectName查找Category - * @param objname - * @return 如果没有找到,将返回nullptr,如果有同样的ObjectName,将返回第一个查询到的名字,因此,尽量避免ObjectName重名 - * @see categoryByName - */ -SARibbonCategory* SARibbonBar::categoryByObjectName(const QString& objname) const -{ - int c = d_ptr->mStackedContainerWidget->count(); - - for (int i = 0; i < c; ++i) { - SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); - if (w) { - if (w->objectName() == objname) { - return (w); - } - } - } - return (nullptr); -} - -/** - * @brief 通过索引找到category,如果超过索引范围,会返回nullptr - * @param index 索引 - * @return 如果超过索引范围,会返回nullptr - * @note 如果此时有上下文标签,上下文的标签也会返回 - * @note 通过索引查找的category必须是visible状态的category,如果通过@ref hideCategory 隐藏的标签,通过索引是找不到的 - * @note 通过@ref categoryByObjectName 可以找到所有加入过的标签, - * 如果想得到ribbonbar管理的所有标签,可以通过函数@ref categoryPages 得到 - * @see categoryIndex categoryByObjectName categoryByName - */ -SARibbonCategory* SARibbonBar::categoryByIndex(int index) const -{ - QVariant var = d_ptr->mRibbonTabBar->tabData(index); - - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - return (p.category); - } - return (nullptr); -} - -/** - * @brief 隐藏category,并不会删除或者取走,只是隐藏 - * @param category - */ -void SARibbonBar::hideCategory(SARibbonCategory* category) -{ - int tabcount = d_ptr->mRibbonTabBar->count(); - - for (int i = 0; i < tabcount; ++i) { - QVariant var = d_ptr->mRibbonTabBar->tabData(i); - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - if (p.category == category) { - d_ptr->mHidedCategory.append(p); - d_ptr->mRibbonTabBar->removeTab(i); // 仅仅把tab移除 - // 注意Category隐藏后,contex的位置就会发生变化,需要更新 - d_ptr->updateTabData(); - return; - } - } - } -} - -/** - * @brief 显示被隐藏的category - * @param category - */ -void SARibbonBar::showCategory(SARibbonCategory* category) -{ - for (auto i = d_ptr->mHidedCategory.begin(); i != d_ptr->mHidedCategory.end(); ++i) { - if (i->category == category) { - // 说明要显示 - int index = d_ptr->mRibbonTabBar->insertTab(i->index, i->category->categoryName()); - i->index = index; - d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(*i)); - d_ptr->mHidedCategory.erase(i); // 移除 - // 更新index信息 - d_ptr->updateTabData(); - raiseCategory(category); - return; - } - } - raiseCategory(category); -} - -/** - * @brief 判断这个category是否在显示状态,也就是tabbar有这个category - * @param category - * @return - */ -bool SARibbonBar::isCategoryVisible(const SARibbonCategory* c) const -{ - int tabindex = categoryIndex(c); - - return (tabindex >= 0); -} - -/** - * @brief 获取category的索引 - * @param c - * @return 如果找不到,返回-1 - */ -int SARibbonBar::categoryIndex(const SARibbonCategory* c) const -{ - // category的顺序不能以stackedwidget为准,因为存在contextcategory,contextcategory正常是不显示的 - int tabcount = d_ptr->mRibbonTabBar->count(); - - for (int i = 0; i < tabcount; ++i) { - QVariant var = d_ptr->mRibbonTabBar->tabData(i); - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - if (p.category == c) { - return (i); - } - } - } - return (-1); -} - -/** - * @brief 移动一个Category从from index到to index - * @param from - * @param to - */ -void SARibbonBar::moveCategory(int from, int to) -{ - d_ptr->mRibbonTabBar->moveTab(from, to); - // 这时要刷新所有tabdata的index信息 - d_ptr->updateTabData(); - // 这里会触发tabMoved信号,在tabMoved信号中调整stacked里窗口的位置 -} - -/** - * @brief 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 - * - * @return - */ -QList< SARibbonCategory* > SARibbonBar::categoryPages(bool getAll) const -{ - int c = d_ptr->mStackedContainerWidget->count(); - QList< SARibbonCategory* > res; - - for (int i = 0; i < c; ++i) { - SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); - if (w) { - if (!getAll && w->isContextCategory()) { - // 不是getall且是上下文时跳过 - continue; - } - res.append(w); - } - } - return (res); -} - -/** - * @brief 移除SARibbonCategory - * - * SARibbonBar不会delete SARibbonCategory*,但这个SARibbonCategory会脱离SARibbonBar的管理 - * 表现在tabbar会移除,面板会移除,使用此函数后可以对SARibbonCategory进行delete - * @param category - */ -void SARibbonBar::removeCategory(SARibbonCategory* category) -{ - int index = tabIndex(category); - bool isupdate = false; - if (index >= 0) { - d_ptr->mRibbonTabBar->removeTab(index); - isupdate = true; - } - d_ptr->mStackedContainerWidget->removeWidget(category); - // 同时验证这个category是否是contexcategory里的 - - for (SARibbonContextCategory* c : qAsConst(d_ptr->mContextCategoryList)) { - c->takeCategory(category); - } - // 这时要刷新所有tabdata的index信息 - if (isupdate) { - d_ptr->updateTabData(); - } - // 移除完后需要重绘 - repaint(); - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -/** - * @brief 添加上下文标签 - * - * 上下文标签是特殊时候触发的标签,需要用户手动触发 - * - * 调用@ref SARibbonContextCategory::addCategoryPage 可在上下文标签中添加SARibbonCategory, - * 在上下文标签添加的SARibbonCategory,只有在上下文标签显示的时候才会显示 - * @param title 上下文标签的标题,在Office模式下会显示,在wps模式下不显示。默认情况下SARibbonContextCategory的object name也被设置为title - * @param color 上下文标签的颜色,如果指定为空QColor(),将会使用SARibbonBar的默认色系 - * @param id 上下文标签的id,以便进行查找 - * @return 返回上下文标签指针 - * @note SARibbonBar拥有SARibbonContextCategory的管理权,用户避免在外部直接delete,如果要删除,调用@ref destroyContextCategory 函数 - */ -SARibbonContextCategory* SARibbonBar::addContextCategory(const QString& title, const QColor& color, const QVariant& id) -{ - SARibbonContextCategory* context = RibbonSubElementFactory->createRibbonContextCategory(this); - - context->setObjectName(title); - context->setContextTitle(title); - context->setId(id); - context->setContextColor(color.isValid() ? color : d_ptr->getContextCategoryColor()); - addContextCategory(context); - return (context); -} - -/** - * @brief 添加上下文标签 - * @param context - */ -void SARibbonBar::addContextCategory(SARibbonContextCategory* context) -{ - if (nullptr == context) { - return; - } - connect(context, &SARibbonContextCategory::categoryPageAdded, this, &SARibbonBar::onContextsCategoryPageAdded); - connect(context, &SARibbonContextCategory::categoryTitleChanged, this, &SARibbonBar::onContextsCategoryCategoryNameChanged); - // remove并没有绑定,主要是remove后在stacked里也不会显示,remove且delete的话,stacked里也会删除 - d_ptr->mContextCategoryList.append(context); -} - -/** - * @brief 显示上下文标签 - * @param context 上下文标签指针 - */ -void SARibbonBar::showContextCategory(SARibbonContextCategory* context) -{ - if (isContextCategoryVisible(context)) { - return; - } - _SAContextCategoryManagerData contextCategoryData; - - contextCategoryData.contextCategory = context; - for (int i = 0; i < context->categoryCount(); ++i) { - SARibbonCategory* category = context->categoryPage(i); - // 此句如果模式重复设置不会进行多余操作 - category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); - // 切换模式后会改变高度,上下文标签显示时要保证显示出来 - int index = d_ptr->mRibbonTabBar->addTab(category->categoryName()); - contextCategoryData.tabPageIndex.append(index); - - _SARibbonTabData tabdata; - tabdata.category = category; - tabdata.index = index; - d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(tabdata)); - } - d_ptr->mCurrentShowingContextCategory.append(contextCategoryData); - // 由于上下文都是在最后追加,不需要调用updateTabData(); - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -/** - * @brief 隐藏上下文标签 - * @param context 上下文标签指针 - */ -void SARibbonBar::hideContextCategory(SARibbonContextCategory* context) -{ - bool needResize = false; - - for (int i = 0; i < d_ptr->mCurrentShowingContextCategory.size(); ++i) { - if (d_ptr->mCurrentShowingContextCategory[ i ].contextCategory == context) { - const QList< int >& indexs = d_ptr->mCurrentShowingContextCategory[ i ].tabPageIndex; - for (int j = indexs.size() - 1; j >= 0; --j) { - d_ptr->mRibbonTabBar->removeTab(indexs[ j ]); - } - // 注意,再删除ContextCategory后,tab的序号就会改变,这时,这个tab后面的都要调整它的序号 - needResize = true; - d_ptr->mCurrentShowingContextCategory.removeAt(i); - // 移除了ContextCategory后需要break - break; - } - } - if (needResize) { - d_ptr->updateTabData(); - QApplication::postEvent(this, new QResizeEvent(size(), size())); - } -} - -/** - * @brief 判断上下文是否在显示状态 - * @param context - * @return 在显示状态返回true - * @sa setContextCategoryVisible - */ -bool SARibbonBar::isContextCategoryVisible(SARibbonContextCategory* context) -{ - return (d_ptr->isContainContextCategoryInList(context)); -} - -/** - * @brief 设置上下文标签的显示状态 - * - * 上下文标签的当前显示状态可通过 @ref isContextCategoryVisible 进行判断 - * @param context 上下文标签 - * @param visible 显示状态,true为显示 - */ -void SARibbonBar::setContextCategoryVisible(SARibbonContextCategory* context, bool visible) -{ - if (nullptr == context) { - return; - } - if (visible) { - showContextCategory(context); - } else { - hideContextCategory(context); - } -} - -/** - * @brief 获取所有的上下文标签 - * @return 返回上下文标签列表 - */ -QList< SARibbonContextCategory* > SARibbonBar::contextCategoryList() const -{ - return (d_ptr->mContextCategoryList); -} - -/** - * @brief 销毁上下文标签,上下文标签的SARibbonCategory也会随之销毁 - * @param context 需要销毁的上下文标签指针 - */ -void SARibbonBar::destroyContextCategory(SARibbonContextCategory* context) -{ - if (nullptr == context) { - return; - } - //! 1、如果上下文标签显示中,先隐藏 - if (isContextCategoryVisible(context)) { - hideContextCategory(context); - } - //! 2、删除上下文标签的相关内容 - d_ptr->mContextCategoryList.removeAll(context); - //! - QList< SARibbonCategory* > res = context->categoryList(); - - for (SARibbonCategory* c : qAsConst(res)) { - c->hide(); - c->deleteLater(); - } - context->deleteLater(); - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -/** - * @brief 设置为最小/正常模式 - * - * 隐藏模式下,只会显示tabbar,不会显示内容,默认状态是显示模式 - * - * 默认下双击tabbar会切换隐藏显示模式,如果想禁用此功能,可以重载 @ref onCurrentRibbonTabDoubleClicked - * 函数,不对函数进行任何处理即可 - * - * @param isMinimum 参数为true时,切换为Minimum模式 - * @see 此函数会改变@ref RibbonState 状态,通过@ref currentRibbonState 函数可以查看当前状态 - */ -void SARibbonBar::setMinimumMode(bool isMinimum) -{ -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - qDebug() << "SARibbonBar::setHideMode " << isMinimum; -#endif - if (isMinimum) { - d_ptr->setMinimumMode(); - } else { - d_ptr->setNormalMode(); - } - QResizeEvent resizeEvent(size(), size()); - QApplication::sendEvent(this, &resizeEvent); - // 发射信号 - Q_EMIT ribbonModeChanged(isMinimum ? MinimumRibbonMode : NormalRibbonMode); -} - -/// -/// \brief 当前ribbon是否是隐藏模式 -/// \return -/// -bool SARibbonBar::isMinimumMode() const -{ - return (d_ptr->mStackedContainerWidget->isPopupMode()); -} - -/// -/// \brief 设置显示隐藏ribbon按钮 -/// -void SARibbonBar::showMinimumModeButton(bool isShow) -{ - if (isShow) { - activeRightButtonGroup(); - - d_ptr->mMinimumCategoryButtonAction = new QAction(this); - d_ptr->mMinimumCategoryButtonAction->setIcon(style()->standardIcon( - isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); - connect(d_ptr->mMinimumCategoryButtonAction, &QAction::triggered, this, [ this ]() { - this->setMinimumMode(!isMinimumMode()); - this->d_ptr->mMinimumCategoryButtonAction->setIcon(style()->standardIcon( - isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); - }); - d_ptr->mRightButtonGroup->addAction(d_ptr->mMinimumCategoryButtonAction); - - } else { - if (nullptr != d_ptr->mMinimumCategoryButtonAction) { - d_ptr->mMinimumCategoryButtonAction->deleteLater(); - d_ptr->mMinimumCategoryButtonAction = nullptr; - } - } - QResizeEvent resizeEvent(size(), size()); - - QApplication::sendEvent(this, &resizeEvent); -} - -/** - * @brief 是否显示隐藏ribbon按钮 - * @return - */ -bool SARibbonBar::haveShowMinimumModeButton() const -{ - return (nullptr != d_ptr->mMinimumCategoryButtonAction); -} - -/** - @brief 隐藏ribbon对应的action - @return - */ -QAction* SARibbonBar::minimumModeAction() const -{ - return d_ptr->mMinimumCategoryButtonAction; -} - -/** - * @brief 当前ribbon的状态(正常|最小化) - * @return - */ -SARibbonBar::RibbonMode SARibbonBar::currentRibbonState() const -{ - return (d_ptr->mCurrentRibbonMode); -} - -/** - @brief tabBar的高度 - @return - */ -int SARibbonBar::tabBarHeight() const -{ - return d_ptr->tabBarHeigth(); - // return d_ptr->mRibbonTabBar->height(); -} - -/** - * @brief 设置tabbar的高度 - * - * 用户调用setTabBarHeight后,将使用用户设定的高度,而不使用自动计算的高度,这时tabbar高度不会跟随字体等信息重新计算 - * - * @note 注意,在RibbonStyleCompact**模式下,tabbar高度要保证小于等于titlebar高度,否则会显示异常 - * @note 此函数不会自动刷新,如果需要刷新调用此函数后需要调用@ref updateRibbonGeometry - * @param h - */ -void SARibbonBar::setTabBarHeight(int h, bool resizeByNow) -{ - if (nullptr == d_ptr->mUserDefTabBarHeight) { - d_ptr->mUserDefTabBarHeight = std::make_unique< int >(h); - } else { - *(d_ptr->mUserDefTabBarHeight) = h; - } - if (resizeByNow) { - updateRibbonGeometry(); - } -} - -/** - @brief 返回标题栏高度 - @sa setTitleBarHeight - @return - */ -int SARibbonBar::titleBarHeight() const -{ - return d_ptr->titleBarHeight(); -} - -/** - @brief 设置标题栏的高度 - @sa titleBarHeight - @note 此操作会发射@ref titleBarHeightChanged 信号 - @param h - */ -void SARibbonBar::setTitleBarHeight(int h, bool resizeByNow) -{ - int oldHeight = d_ptr->mTitleBarHeight; - if (nullptr == d_ptr->mUserDefTitleBarHeight) { - d_ptr->mUserDefTitleBarHeight = std::make_unique< int >(h); - } else { - *(d_ptr->mUserDefTitleBarHeight) = h; - // - oldHeight = *(d_ptr->mUserDefTitleBarHeight); - } - - if (resizeByNow) { - updateRibbonGeometry(); - } - Q_EMIT titleBarHeightChanged(oldHeight, h); -} - -/** - * @brief category的高度 - * @return - */ -int SARibbonBar::categoryHeight() const -{ - return d_ptr->mStackedContainerWidget->height(); -} - -/** - * @brief 设置category的高度 - * @param h - * @param resizeByNow - */ -void SARibbonBar::setCategoryHeight(int h, bool resizeByNow) -{ - if (nullptr == d_ptr->mUserDefCategoryHeight) { - d_ptr->mUserDefCategoryHeight = std::make_unique< int >(h); - } else { - *(d_ptr->mUserDefCategoryHeight) = h; - } - resizeStackedContainerWidget(); - if (resizeByNow) { - updateRibbonGeometry(); - } -} - -void SARibbonBar::onWindowTitleChanged(const QString& title) -{ - Q_UNUSED(title); - update(); -} - -void SARibbonBar::onWindowIconChanged(const QIcon& i) -{ - if (quickAccessBar()) { - quickAccessBar()->setIcon(i); - } -} - -/** - * @brief category的名字发生改变触发 - * @param title - */ -void SARibbonBar::onCategoryWindowTitleChanged(const QString& title) -{ - // 全部更新一遍 - Q_UNUSED(title); - updateCategoryTitleToTabName(); -} - -/// -/// \brief ribbon的显示界面隐藏 -/// -void SARibbonBar::onStackWidgetHided() -{ -} - -/** - * @brief 标签切换触发槽函数 - * @param index - */ -void SARibbonBar::onCurrentRibbonTabChanged(int index) -{ - QVariant var = d_ptr->mRibbonTabBar->tabData(index); - SARibbonCategory* category = nullptr; - - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - category = p.category; - } - if (!category) { - return; - } - if (d_ptr->mStackedContainerWidget->currentWidget() != category) { - d_ptr->mStackedContainerWidget->setCurrentWidget(category); - } - Q_EMIT currentRibbonTabChanged(index); - if (isMinimumMode()) { - d_ptr->mRibbonTabBar->clearFocus(); - if (!d_ptr->mStackedContainerWidget->isVisible()) { - if (d_ptr->mStackedContainerWidget->isPopupMode()) { - // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 - QHoverEvent ehl(QEvent::HoverLeave, - d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), - d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); - QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); - resizeStackedContainerWidget(); - d_ptr->mStackedContainerWidget->setFocus(); - // exec之前先发射信息号,否则会被exec阻塞 - d_ptr->mStackedContainerWidget->show(); - } - } - } -} - -/** - * @brief ribbon tab bar单击 - * - * 此实现必须在eventfilter中传递stackedwidget的QEvent::MouseButtonDblClick事件到tabbar中,否则会导致双击变两次单击 - * - * 单击事件仅用于响应点击同一个tab时 - * @param index - */ -void SARibbonBar::onCurrentRibbonTabClicked(int index) -{ - if (index != d_ptr->mRibbonTabBar->currentIndex()) { - // 点击的标签不一致通过changed槽去处理 - return; - } - if (this->isMinimumMode()) { - if (!this->d_ptr->mStackedContainerWidget->isVisible()) { - if (this->d_ptr->mStackedContainerWidget->isPopupMode()) { - // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 - QHoverEvent ehl(QEvent::HoverLeave, - d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), - d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); - QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); - // 弹出前都调整一下位置,避免移动后位置异常 - resizeStackedContainerWidget(); - this->d_ptr->mStackedContainerWidget->setFocus(); - this->d_ptr->mStackedContainerWidget->exec(); - } - } - } -} - -/** - * @brief ribbon tab bar双击 - * - * 默认情况下双击会切换最小和正常模式 - * @param index - */ -void SARibbonBar::onCurrentRibbonTabDoubleClicked(int index) -{ - Q_UNUSED(index); - setMinimumMode(!isMinimumMode()); -} - -void SARibbonBar::onContextsCategoryPageAdded(SARibbonCategory* category) -{ - Q_ASSERT_X(category != nullptr, "onContextsCategoryPageAdded", "add nullptr page"); - d_ptr->mStackedContainerWidget->addWidget(category); // 这里stackedWidget用append,其他地方都应该使用insert -} - -/** - * @brief 上下文标签管理的标签的名字发生变换 - * @param category - * @param title - */ -void SARibbonBar::onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title) -{ - Q_UNUSED(category); - Q_UNUSED(title); - updateCategoryTitleToTabName(); -} - -/** - * @brief 标签移动的信号 - * @param from - * @param to - */ -void SARibbonBar::onTabMoved(int from, int to) -{ - const QSignalBlocker blocker(d_ptr->mStackedContainerWidget); - // 调整stacked widget的顺序,调整顺序是为了调用categoryPages函数返回的QList顺序和tabbar一致 - d_ptr->mStackedContainerWidget->moveWidget(from, to); -} - -/** - * @brief 根据SARibbonCategory*指针查找tabbar的index - * - * @param c SARibbonCategory对应的QObject指针 - * @return 如果没有找到,返回-1 - * @note 此函数不会调用SARibbonCategory*的任何方法,因此可以在SARibbonCategory的destroyed槽中调用 - */ -int SARibbonBar::tabIndex(SARibbonCategory* obj) -{ - const int size = d_ptr->mRibbonTabBar->count(); - - for (int i = 0; i < size; ++i) { - QVariant v = d_ptr->mRibbonTabBar->tabData(i); - if (v.isValid()) { - _SARibbonTabData innervalue = v.value< _SARibbonTabData >(); - if (innervalue.category == obj) { - return (i); - } - } - } - // 如果找不到就从stackedwidget中找 - - return (-1); -} - -void SARibbonBar::resizeAll() -{ - if (isLooseStyle()) { - resizeInLooseStyle(); - } else { - resizeInCompactStyle(); - } - update(); -} - -/** - * @brief 把ribbonbar的内容,同步进各个category中 - * @param autoUpdate - */ -void SARibbonBar::synchronousCategoryData(bool autoUpdate) -{ - iterate([ this ](SARibbonCategory* c) -> bool { - c->setEnableShowPannelTitle(this->isEnableShowPannelTitle()); - c->setPannelTitleHeight(this->pannelTitleHeight()); - c->setCategoryAlignment(this->ribbonAlignment()); - c->setPannelLayoutMode(this->pannelLayoutMode()); - return true; - }); - - //! 直接给一个resizeevent,让所有刷新 - if (autoUpdate) { - QResizeEvent* e = new QResizeEvent(size(), QSize()); - QApplication::postEvent(this, e); - } -} - -/** - @brief 激活tabbar右边的按钮群 - @return 返回右边的按钮群指针 - */ -SARibbonButtonGroupWidget* SARibbonBar::activeRightButtonGroup() -{ - if (nullptr == d_ptr->mRightButtonGroup) { - d_ptr->mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(this); - } - d_ptr->mRightButtonGroup->show(); - return d_ptr->mRightButtonGroup; -} - -/** - @brief 返回右边的按钮群指针 - @return 如果没有创建,返回nullptr - */ -SARibbonButtonGroupWidget* SARibbonBar::rightButtonGroup() -{ - return d_ptr->mRightButtonGroup; -} - -/** - @brief 激活QuickAccessBar - @return - */ -SARibbonQuickAccessBar* SARibbonBar::activeQuickAccessBar() -{ - if (nullptr == d_ptr->mQuickAccessBar) { - d_ptr->mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(this); - d_ptr->mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); - d_ptr->mQuickAccessBar->setIcon(windowIcon()); - } - d_ptr->mQuickAccessBar->show(); - return d_ptr->mQuickAccessBar; -} - -SARibbonQuickAccessBar* SARibbonBar::quickAccessBar() -{ - return (d_ptr->mQuickAccessBar); -} - -/** - * @brief 设置ribbonbar的风格,此函数会重新设置所有元素,包括button的布局方式, - * 尤其是从三行变到两行的过程,重设的内容较多 - * @note 此函数会自动触发ResizeEvent,不需要手动调用 - * @note 默认情况下2行模式不换行,如果想在两行模式换行,在调用SARibbonBar::setRibbonStyle后,再SARibbonToolButton::setEnableWordWrap(true) - * ,同时再调用updateRibbonElementGeometry()刷新布局 - * @param v 样式,见@ref SARibbonBar::RibbonStyle - */ -void SARibbonBar::setRibbonStyle(SARibbonBar::RibbonStyles v) -{ - // 先幅值给变量 - d_ptr->mRibbonStyle = v; -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "setRibbonStyle(" << v << ")" // - << "\n isThreeRowStyle=" << isThreeRowStyle() // - << "\n isTwoRowStyle=" << isTwoRowStyle() // - << "\n isLooseStyle=" << isLooseStyle() // - << "\n isCompactStyle=" << isCompactStyle() // - ; -#endif - // 执行判断 - setEnableWordWrap(isThreeRowStyle(v)); - setTabOnTitle(isCompactStyle()); - d_ptr->mQuickAccessBar->setEnableShowIcon(isLooseStyle()); - setEnableShowPannelTitle(isThreeRowStyle(v)); - setPannelLayoutMode(isThreeRowStyle(v) ? SARibbonPannel::ThreeRowMode : SARibbonPannel::TwoRowMode); - - // 此函数会调用setFixedHeight - synchronousCategoryData(false); // 这里不急着刷新,下面会继续刷新 - d_ptr->resetSize(); - - Q_EMIT ribbonStyleChanged(d_ptr->mRibbonStyle); -} - -/** - @brief 返回当前ribbon的风格 - @return 返回当前ribbon的风格 - */ -SARibbonBar::RibbonStyles SARibbonBar::currentRibbonStyle() const -{ - return (d_ptr->mRibbonStyle); -} - -/** - @brief 切换到对应标签 - @param index 标签索引 - */ -void SARibbonBar::setCurrentIndex(int index) -{ - d_ptr->mRibbonTabBar->setCurrentIndex(index); - // onCurrentRibbonTabChanged(index); -} - -/** - * @brief 返回当前的tab索引 - * @return 当前的索引 - */ -int SARibbonBar::currentIndex() -{ - return (d_ptr->mRibbonTabBar->currentIndex()); -} - -/** - * @brief 确保标签显示出来,tab并切换到对应页 - * @param category 标签指针 - */ -void SARibbonBar::raiseCategory(SARibbonCategory* category) -{ - int index = d_ptr->mStackedContainerWidget->indexOf(category); - - if (index >= 0) { - setCurrentIndex(index); - } -} - -/** - * @brief 判断当前的样式是否为2行 - * @return - */ -bool SARibbonBar::isTwoRowStyle() const -{ - return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::TwoRowMode); -} - -/** - * @brief 判断当前的样式是否为3行 - * @return - */ -bool SARibbonBar::isThreeRowStyle() const -{ - return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::ThreeRowMode); -} - -/** - * @brief 判断当前的样式是否为宽松样式 - * @return - */ -bool SARibbonBar::isLooseStyle() const -{ - return (SARibbonBar::isLooseStyle(currentRibbonStyle())); -} - -/** - * @brief 判断当前的样式是否为紧凑样式 - * @return - */ -bool SARibbonBar::isCompactStyle() const -{ - return (SARibbonBar::isCompactStyle(currentRibbonStyle())); -} - -/** - * @brief 设置标题的文字颜色 - * - * 标题时mainwindow的windowTitle,如果要设置标题,直接调用SARibbonMainWindow::setWindowTitle 进行设置 - * - * 如果不设置标题颜色,默认是SARibbonBar的qss的color属性 - * @param clr - * @note 此函数不会刷新,刷新请自行调用repaint - */ -void SARibbonBar::setWindowTitleTextColor(const QColor& clr) -{ - d_ptr->mTitleTextColor = clr; -} - -/** - * @brief 获取标题的文字颜色 - * @return 如果返回的是无效颜色,!QColor::isValid(),说明没有手动设置颜色,颜色将跟随SARibbonBar的qss的文字颜色 - */ -QColor SARibbonBar::windowTitleTextColor() const -{ - return d_ptr->mTitleTextColor; -} - -/** - * @brief tabbar 底部会绘制一条线条,此接口定义线条颜色 - * @param clr - */ -void SARibbonBar::setTabBarBaseLineColor(const QColor& clr) -{ - d_ptr->mTabBarBaseLineColor = clr; -} - -/** - * @brief 获取tabbar底部基线颜色 - * @return - */ -QColor SARibbonBar::tabBarBaseLineColor() const -{ - return d_ptr->mTabBarBaseLineColor; -} - -/** - * @brief 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局 - * - * @note 此函数调用较慢,避免在高速要求场合调用 - */ -void SARibbonBar::updateRibbonGeometry() -{ - d_ptr->resetSize(); - iterate([](SARibbonCategory* c) -> bool { - c->updateItemGeometry(); - return true; - }); - // !主题变更后,tabbar的长度需要进行刷新 - - //! 直接给一个resizeevent,让所有刷新 - QResizeEvent* e = new QResizeEvent(size(), QSize()); - QApplication::postEvent(this, e); -} - -/** - * @brief SARibbonPannel的布局模式 - * @return - */ -SARibbonPannel::PannelLayoutMode SARibbonBar::pannelLayoutMode() const -{ - return d_ptr->mDefaulePannelLayoutMode; -} - -/** - * @brief SARibbonPannel的布局模式设置 - * @param m - */ -void SARibbonBar::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) -{ - d_ptr->mDefaulePannelLayoutMode = m; - iterate([ m ](SARibbonCategory* c) -> bool { - c->setPannelLayoutMode(m); - return true; - }); -} - -/** - * @brief 设置tab在title上面,这样可以省略title区域 - * @param on - */ -void SARibbonBar::setTabOnTitle(bool on) -{ - if (d_ptr->mIsTabOnTitle != on) { - d_ptr->mIsTabOnTitle = on; - d_ptr->resetSize(); - } -} - -/** - * @brief tab是否在title上面 - * @return - */ -bool SARibbonBar::isTabOnTitle() const -{ - return d_ptr->mIsTabOnTitle; -} - -/** - * @brief 设置标题的对齐方式 - * @param al - */ -void SARibbonBar::setWindowTitleAligment(Qt::Alignment al) -{ - d_ptr->mTitleAligment = al; -} - -/** - * @brief 获取标题的对齐方式 - * @return - */ -Qt::Alignment SARibbonBar::windowTitleAligment() const -{ - return d_ptr->mTitleAligment; -} - -/** - * @brief 设置ribbonbar的按钮文字允许换行 - * @param on - */ -void SARibbonBar::setEnableWordWrap(bool on) -{ - SARibbonToolButton::setEnableWordWrap(on); - updateRibbonGeometry(); -} - -/** - * @brief 判断是否允许换行 - * @return - */ -bool SARibbonBar::isEnableWordWrap() const -{ - return SARibbonToolButton::isEnableWordWrap(); -} - -/** - * @brief pannel标题栏的高度 - * @return - */ -int SARibbonBar::pannelTitleHeight() const -{ - return d_ptr->mPannelTitleHeight; -} - -/** - * @brief 设置pannel的高度 - * @param h - */ -void SARibbonBar::setPannelTitleHeight(int h) -{ - d_ptr->mPannelTitleHeight = h; - iterate([ h ](SARibbonCategory* c) -> bool { - c->setPannelTitleHeight(h); - return true; - }); -} - -/** - * @brief 是否pannel显示标题栏 - * @return - */ -bool SARibbonBar::isEnableShowPannelTitle() const -{ - return d_ptr->mEnableShowPannelTitle; -} - -/** - * @brief 设置显示pannel标题 - * @param on - */ -void SARibbonBar::setEnableShowPannelTitle(bool on) -{ - d_ptr->mEnableShowPannelTitle = on; - iterate([ on ](SARibbonCategory* c) -> bool { - c->setEnableShowPannelTitle(on); - return true; - }); -} - -/** - * @brief 设置pannel的spacing - * @param n - */ -void SARibbonBar::setPannelSpacing(int n) -{ - d_ptr->mPannelSpacing = n; - // 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing - iterate([ n ](SARibbonCategory* category) -> bool { - if (category) { - category->setPannelSpacing(n); - } - return true; - }); -} - -/** - * @brief pannel的spacing - * @return - */ -int SARibbonBar::pannelSpacing() const -{ - return d_ptr->mPannelSpacing; -} - -/** - * @brief 设置pannel按钮的icon尺寸,large action不受此尺寸影响 - * @param s - */ -void SARibbonBar::setPannelToolButtonIconSize(const QSize& s) -{ - d_ptr->mPannelToolButtonSize = s; - // 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing - iterate([ s ](SARibbonCategory* category) -> bool { - if (category) { - category->setPannelToolButtonIconSize(s); - } - return true; - }); -} - -/** - * @brief pannel按钮的icon尺寸,large action不受此尺寸影响 - * @return - */ -QSize SARibbonBar::pannelToolButtonIconSize() const -{ - return d_ptr->mPannelToolButtonSize; -} - -/** - * @brief ribbonbar内部的StackedWidget - * 所有的category都放置在StackedWidget中 - * @return - */ -SARibbonStackedWidget* SARibbonBar::ribbonStackedWidget() -{ - return d_ptr->mStackedContainerWidget; -} - -/** - * @brief 设置是否显示标题 - * @param on - */ -void SARibbonBar::setTitleVisible(bool on) -{ - d_ptr->mIsTitleVisible = on; -} - -/** - * @brief 判断标题是否显示 - * @return - */ -bool SARibbonBar::isTitleVisible() const -{ - return d_ptr->mIsTitleVisible; -} - -/** - @brief 设置上下文标签的颜色列表 - - 上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 - @param cls - */ -void SARibbonBar::setContextCategoryColorList(const QList< QColor >& cls) -{ - d_ptr->mContextCategoryColorList = cls; - if (d_ptr->mContextCategoryColorList.isEmpty()) { - d_ptr->mContextCategoryColorList = defaultContextCategoryColorList(); - } - d_ptr->mContextCategoryColorListIndex = 0; - // 这时需要对已经显示的contextCategoryData的颜色进行重新设置 - for (SARibbonContextCategory* c : d_ptr->mContextCategoryList) { - c->setContextColor(d_ptr->getContextCategoryColor()); - } -} - -/** - @brief 获取上下文标签的颜色列表 - @return - */ -QList< QColor > SARibbonBar::contextCategoryColorList() const -{ - return d_ptr->mContextCategoryColorList; -} - -/** - * @brief 设置contextCategory 标题的颜色 - * @param clr - */ -void SARibbonBar::setContextCategoryTitleTextColor(const QColor& clr) -{ - d_ptr->mContextCategoryTitleTextColor = clr; -} - -/** - * @brief contextCategory 标题的颜色 - * @return - */ -QColor SARibbonBar::contextCategoryTitleTextColor() const -{ - return d_ptr->mContextCategoryTitleTextColor; -} - -/** - @brief 设置ribbon的对齐方式 - @param al - */ -void SARibbonBar::setRibbonAlignment(SARibbonAlignment al) -{ - d_ptr->mRibbonAlignment = al; - synchronousCategoryData(); -} - -/** - @brief ribbon的对齐方式 - @return - */ -SARibbonAlignment SARibbonBar::ribbonAlignment() const -{ - return d_ptr->mRibbonAlignment; -} - -/** - * @brief 此函数会遍历SARibbonBar下的所有Category,执行函数指针bool(SARibbonCategory*) - * @param fp 函数指针返回false则停止迭代 - * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 - */ -bool SARibbonBar::iterate(FpCategoryIterate fp) -{ - const QList< SARibbonCategory* > cs = categoryPages(true); - for (SARibbonCategory* c : cs) { - if (!fp(c)) { - return false; - } - } - return true; -} - -/** - * @brief 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针bool(SARibbonPannel*) - * @param fp 函数指针返回false则停止迭代 - * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 - */ -bool SARibbonBar::iterate(FpPannelIterate fp) -{ - const QList< SARibbonCategory* > cs = categoryPages(true); - for (SARibbonCategory* c : cs) { - if (!c->iterate(fp)) { - return false; - } - } - return true; -} - -/** - * @brief 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget - * @param on - * @param c - */ -void SARibbonBar::setCornerWidgetVisible(bool on, Qt::Corner c) -{ - if (QWidget* w = cornerWidget(c)) { - w->setVisible(on); - } -} - -/** - * @brief SARibbonBar::eventFilter - * @param obj - * @param e - * @return - */ -bool SARibbonBar::eventFilter(QObject* obj, QEvent* e) -{ - if (obj) { - // 调整多文档时在窗口模式下的按钮更新 - if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) { - if ((QEvent::UpdateLater == e->type()) || (QEvent::MouseButtonRelease == e->type()) - || (QEvent::WindowActivate == e->type())) { - QApplication::postEvent(this, new QResizeEvent(size(), size())); - } - } else if (obj == d_ptr->mStackedContainerWidget) { - // 在stack 是popup模式时,点击的是stackedContainerWidget区域外的时候,如果是在ribbonTabBar上点击 - // 那么忽略掉这次点击,把点击下发到ribbonTabBar,这样可以避免stackedContainerWidget在点击ribbonTabBar后 - // 隐藏又显示,出现闪烁 - if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) { - if (d_ptr->mStackedContainerWidget->isPopupMode()) { - QMouseEvent* mouseEvent = static_cast< QMouseEvent* >(e); - if (!d_ptr->mStackedContainerWidget->rect().contains(mouseEvent->pos())) { - QWidget* clickedWidget = QApplication::widgetAt(mouseEvent->globalPos()); - if (clickedWidget == d_ptr->mRibbonTabBar) { - const QPoint targetPoint = clickedWidget->mapFromGlobal(mouseEvent->globalPos()); - QMouseEvent* evPress = new QMouseEvent(mouseEvent->type(), - targetPoint, - mouseEvent->globalPos(), - mouseEvent->button(), - mouseEvent->buttons(), - mouseEvent->modifiers()); - QApplication::postEvent(clickedWidget, evPress); - return (true); - } - } - } - } - } - } - return (QMenuBar::eventFilter(obj, e)); -} - -/** - * @brief 根据情况重置tabbar的宽度,主要针对wps模式 - */ -int SARibbonBar::calcMinTabBarWidth() const -{ - // 20200831 - // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 - // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 - // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow - // 计算tab所占用的宽度 - const QMargins& mg = d_ptr->mRibbonTabBar->tabMargin(); - return d_ptr->mRibbonTabBar->sizeHint().width() + (mg.left() + mg.right()); -} - -/** - * @brief 正常模式下的高度 - * - * 有可能SARibbonBar::height和mainBarHeight不相等,这种情况发生在RibbonState::MinimumRibbonMode状态下 - * @return 高度 - */ -int SARibbonBar::normalModeMainBarHeight() const -{ - return d_ptr->getCurrentNormalModeMainBarHeight(); -} - -/** - @brief 最小模式下的高度 - @return - */ -int SARibbonBar::minimumModeMainBarHeight() const -{ - return d_ptr->getCurrentMinimumModeMainBarHeight(); -} - -/** - * @brief 更新所有的category title对应的tab名 - * - * 此函数会对所有的category的名字和tab进行匹配,如果匹配不上会重新设置tab名 - */ -void SARibbonBar::updateCategoryTitleToTabName() -{ - SARibbonTabBar* tab = d_ptr->mRibbonTabBar; - for (int i = 0; i < tab->count(); ++i) { - // 鉴于tab不会很多,不考虑效率问题 - QVariant var = tab->tabData(i); - if (var.isValid()) { - _SARibbonTabData p = var.value< _SARibbonTabData >(); - if (p.category && p.category->categoryName() != tab->tabText(i)) { - tab->setTabText(i, p.category->categoryName()); - } - } - } - repaint(); -} - -/** - * @brief 告知WindowButtonGroup的尺寸 - * @param s - */ -void SARibbonBar::setWindowButtonGroupSize(const QSize& s) -{ - d_ptr->mWindowButtonSize = s; -} - -void SARibbonBar::paintEvent(QPaintEvent* e) -{ - Q_UNUSED(e); - if (isLooseStyle()) { - paintInLooseStyle(); - } else { - paintInCompactStyle(); - } -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - QPainter p(this); - HELP_DRAW_RECT(p, m_d->quickAccessBar->geometry()); - HELP_DRAW_RECT(p, m_d->ribbonTabBar->geometry()); - HELP_DRAW_RECT(p, m_d->stackedContainerWidget->geometry()); -#endif -} - -void SARibbonBar::paintInLooseStyle() -{ - QPainter p(this); - - //!绘制tabbar下的基线,这个函数仅仅对office2013主题有用,大部分主题都不绘制基线 - paintTabbarBaseLine(p); - - //! 显示上下文标签 - p.save(); - QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; - // bool isCurrentSelectContextCategoryPage = false; - - QPoint contextCategoryRegion(width(), -1); - QMargins border = contentsMargins(); - - for (int i = 0; i < contextCategoryDataList.size(); ++i) { - QRect contextTitleRect; - QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; - QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); - if (!indexs.isEmpty()) { - contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); - QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); - contextTitleRect.setRight(endRect.right()); - contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); - contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); // 减1像素,避免tabbar基线覆盖 - contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); - // 把区域顶部扩展到窗口顶部 - contextTitleRect.setTop(border.top()); - // 绘制 - paintContextCategoryTab(p, contextCategoryDataList[ i ].contextCategory->contextTitle(), contextTitleRect, clr); - // 更新上下文标签的范围,用于控制标题栏的显示 - if (contextTitleRect.left() < contextCategoryRegion.x()) { - contextCategoryRegion.setX(contextTitleRect.left()); - } - if (contextTitleRect.right() > contextCategoryRegion.y()) { - contextCategoryRegion.setY(contextTitleRect.right()); - } - } - // isCurrentSelectContextCategoryPage = indexs.contains(d_ptr->mRibbonTabBar->currentIndex()); - // if (isCurrentSelectContextCategoryPage) { - // QPen pen; - // pen.setColor(clr); - // pen.setWidth(1); - // p.setPen(pen); - // p.setBrush(Qt::NoBrush); - // p.drawRect(d_ptr->mStackedContainerWidget->geometry()); - // isCurrentSelectContextCategoryPage = false; - // } - } - p.restore(); - //! 显示标题等 - QWidget* parWindow = parentWidget(); - - if (parWindow) { - QRect titleRegion; - if (contextCategoryRegion.y() < 0) { - titleRegion.setRect(d_ptr->mQuickAccessBar->geometry().right() + 1, - border.top(), - width() - d_ptr->mIconRightBorderPosition - border.right() - - d_ptr->mWindowButtonSize.width() - d_ptr->mQuickAccessBar->geometry().right() - 1, - titleBarHeight()); - } else { - int leftwidth = contextCategoryRegion.x() - d_ptr->mQuickAccessBar->geometry().right() - - d_ptr->mIconRightBorderPosition; - int rightwidth = width() - contextCategoryRegion.y() - d_ptr->mWindowButtonSize.width(); - // if (width() - contextCategoryRegion.y() > contextCategoryRegion.x()-x) { - if (rightwidth > leftwidth) { - // 说明右边的区域大一点,标题显示在右,显示在右边需要减去windowbutton宽度 - titleRegion.setRect(contextCategoryRegion.y(), border.top(), rightwidth, titleBarHeight()); - } else { - // 说明左边的大一点 - titleRegion.setRect(d_ptr->mIconRightBorderPosition + d_ptr->mQuickAccessBar->geometry().right(), - border.top(), - leftwidth, - titleBarHeight()); - } - } -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - p.save(); - p.setBrush(QColor(255, 0, 0, 120)); - p.drawRect(titleRegion); - p.restore(); -#endif - paintWindowTitle(p, parWindow->windowTitle(), titleRegion); - } -} - -void SARibbonBar::paintInCompactStyle() -{ - QPainter p(this); - //! - paintTabbarBaseLine(p); - //! 显示上下文标签 - p.save(); - QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; - QMargins border = contentsMargins(); - for (int i = 0; i < contextCategoryDataList.size(); ++i) { - QRect contextTitleRect; - QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; - QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); - if (!indexs.isEmpty()) { - contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); - QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); - contextTitleRect.setRight(endRect.right()); - contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); - contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); - contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); - // 把区域顶部扩展到窗口顶部 - contextTitleRect.setTop(border.top()); - // 绘制 - paintContextCategoryTab(p, QString(), contextTitleRect, clr); - } - } - p.restore(); - //! 显示标题等 - - QWidget* parWindow = parentWidget(); - - if (parWindow) { - int start = d_ptr->mRibbonTabBar->x() + d_ptr->mRibbonTabBar->width(); - int width = d_ptr->mQuickAccessBar->x() - start; - if (width > 20) { - QRect titleRegion(start, border.top(), width, titleBarHeight()); -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - p.save(); - p.setBrush(QColor(255, 0, 0, 120)); - p.drawRect(titleRegion); - p.restore(); -#endif - paintWindowTitle(p, parWindow->windowTitle(), titleRegion); - } - } -} - -void SARibbonBar::resizeStackedContainerWidget() -{ - QMargins border = contentsMargins(); - const QRect& ribbonTabBarGeometry = d_ptr->mRibbonTabBar->geometry(); - - int x = border.left(); - int y = ribbonTabBarGeometry.bottom() + 1; - int w = width() - border.left() - border.right(); - int h = d_ptr->categoryHeight(); - if (d_ptr->mStackedContainerWidget->isPopupMode()) { - // 弹出模式时,位置为全局位置 - QPoint absPosition = mapToGlobal(QPoint(x, y)); - x = absPosition.x(); - y = absPosition.y(); - } - d_ptr->mStackedContainerWidget->setFixedSize(QSize(w, h)); - d_ptr->mStackedContainerWidget->setGeometry(x, y, w, h); -} - -/** - * @brief 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 - */ -void SARibbonBar::updateContextCategoryManagerData() -{ - const int c = d_ptr->mRibbonTabBar->count(); - - for (_SAContextCategoryManagerData& cd : d_ptr->mCurrentShowingContextCategory) { - cd.tabPageIndex.clear(); - for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { - SARibbonCategory* category = cd.contextCategory->categoryPage(i); - for (int t = 0; t < c; ++t) { - QVariant v = d_ptr->mRibbonTabBar->tabData(t); - if (v.isValid()) { - _SARibbonTabData d = v.value< _SARibbonTabData >(); - if (d.category == category) { - cd.tabPageIndex.append(t); - } - } else { - cd.tabPageIndex.append(-1); - } - } - } - } -} - -/** - * @brief 绘制上下文标签的背景 - * @param painter 绘图QPainter - * @param title 上下文标签的title - * @param contextRect 上下文标签的绘制区域 - * @param color 上下文标签赋予的颜色 - */ -void SARibbonBar::paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color) -{ - // 绘制上下文标签 - // 首先有5像素的实体粗线位于顶部 - QMargins border = contentsMargins(); - painter.save(); - painter.setPen(Qt::NoPen); - painter.setBrush(color); - painter.drawRect(contextRect); - const int contextLineWidth = 5; - // 绘制很线 - QColor gColor = color.darker(); - painter.fillRect(QRect(contextRect.x(), contextRect.y(), contextRect.width(), contextLineWidth), gColor); - - // 只有在office模式下才需要绘制标题 - if (isLooseStyle()) { - if (!title.isEmpty()) { - QRect textRect = QRect(contextRect.x(), - contextRect.y() + contextLineWidth, - contextRect.width(), - contextRect.height() - contextLineWidth - d_ptr->mRibbonTabBar->height()); - painter.setPen(contextCategoryTitleTextColor()); - painter.drawText(textRect, Qt::AlignCenter, title); - } - } - painter.restore(); -} - -void SARibbonBar::resizeEvent(QResizeEvent* e) -{ - Q_UNUSED(e); - if (isLooseStyle()) { - resizeInLooseStyle(); - } else { - resizeInCompactStyle(); - } - update(); -} - -/** - * @brief 重写moveevent是为了在移动时调整isPopupMode状态下的stackedContainerWidget位置 - * @param event - */ -void SARibbonBar::moveEvent(QMoveEvent* e) -{ - if (d_ptr->mStackedContainerWidget) { - if (d_ptr->mStackedContainerWidget->isPopupMode()) { - // 弹出模式时,窗口发生了移动,同步调整StackedContainerWidget的位置 - resizeStackedContainerWidget(); - } - } - QMenuBar::moveEvent(e); -} - -/** - * @brief 跟踪字体改变 - * @param event - */ -void SARibbonBar::changeEvent(QEvent* e) -{ - if (nullptr == e) { - return; - } - switch (e->type()) { - case QEvent::FontChange: { - QFont f = font(); - QList< QWidget* > listWidgets = findChildren< QWidget* >(); - for (QWidget* w : listWidgets) { - w->setFont(f); - } - updateRibbonGeometry(); - } break; - case QEvent::StyleChange: { - updateRibbonGeometry(); - } break; - default: - break; - } - QMenuBar::changeEvent(e); -} - -bool SARibbonBar::event(QEvent* e) -{ - switch (e->type()) { - case QEvent::Show: - // 第一次显示刷新 - updateRibbonGeometry(); - break; - default: - break; - } - return QMenuBar::event(e); -} - -void SARibbonBar::resizeInLooseStyle() -{ - synchronousCategoryData(false); - - QMargins border = contentsMargins(); - int x = border.left(); - int y = border.top(); - - // cornerWidget - TopLeftCorner - const int validTitleBarHeight = d_ptr->titleBarHeight(); - const int tabH = d_ptr->tabBarHeigth(); - // tabbar的标签不会因为tabbar的高度而铺满,tabbar的标签是固定高度的,tabbar拉高只会把底部露出来,因此,systab是理论合理的高度 - // 布局corner widget - x += d_ptr->mIconRightBorderPosition + 5; - if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { - if (connerL->isVisibleTo(this)) { - QSize connerSize = connerL->sizeHint(); - if (connerSize.height() < validTitleBarHeight) { - int detal = (validTitleBarHeight - connerSize.height()) / 2; - connerL->setGeometry(x, y + detal, connerSize.width(), connerSize.height()); - } else { - connerL->setGeometry(x, y, connerSize.width(), validTitleBarHeight); - } - x = connerL->geometry().right() + 5; - } - } - // quick access bar定位 - if (d_ptr->mQuickAccessBar) { - if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { - if (d_ptr->mQuickAccessBar->height() != validTitleBarHeight) { - d_ptr->mQuickAccessBar->setFixedHeight(validTitleBarHeight); - } - QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); - // 上下留1px的边线 - d_ptr->mQuickAccessBar->setGeometry(x, y + 1, quickAccessBarSize.width(), validTitleBarHeight); - } - } - // 第二行,开始布局applicationButton,tabbar,tabBarRightSizeButtonGroupWidget,TopRightCorner - x = border.left(); - y += validTitleBarHeight; // 此时,y值在titlebar下面 - // applicationButton 定位 - if (d_ptr->mApplicationButton) { - if (d_ptr->mApplicationButton->isVisibleTo(this)) { - // 保证 - d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); - x = d_ptr->mApplicationButton->geometry().right(); - } - } - // top right是一定要配置的,对于多文档窗口,子窗口的缩放等按钮就是通过这个窗口实现, - // 由于这个窗口一定要在最右,因此先对这个窗口进行布局 - // cornerWidget - TopRightCorner - // 获取最右边的位置 - int endX = width() - border.right(); - - if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { - if (connerW->isVisibleTo(this)) { - QSize connerSize = connerW->sizeHint(); - endX -= connerSize.width(); - if (connerSize.height() < tabH) { - int detal = (tabH - connerSize.height()) / 2; - connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); - } else { - connerW->setGeometry(endX, y, connerSize.width(), tabH); - } - } - } - // applicationButton和TopRightCorner完成定位,才可以定位tab bar - // tab bar 定位 - - // tabBar 右边的附加按钮组,这里一般会附加一些类似登录等按钮组 - // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() - if (d_ptr->mRightButtonGroup) { - QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); - endX -= wSize.width(); - // 上下留1px的边线 - d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), tabH - 2); - } - // 最后确定tabbar宽度 - int tabBarAllowedWidth = endX - x; - if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { - d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); - } else { - // 居中对齐的情况下,Tab要居中显示 - // 得到tab的推荐尺寸 - int mintabBarWidth = calcMinTabBarWidth(); - if (mintabBarWidth >= tabBarAllowedWidth) { - // 这时tabbar没有居中对齐的必要性,空间位置不够了 - d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); - } else { - // 说明tabbar的宽度有居中的可能性 - int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; - d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); - } - } - resizeStackedContainerWidget(); -} - -void SARibbonBar::resizeInCompactStyle() -{ - synchronousCategoryData(false); - QMargins border = contentsMargins(); - int x = border.left(); - int y = border.top(); - - const int validTitleBarHeight = titleBarHeight(); - - // 先布局右边内容 - // cornerWidget - TopRightCorner - int endX = width() - border.right() - d_ptr->mWindowButtonSize.width(); - - if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { - if (connerW->isVisibleTo(this)) { - QSize connerSize = connerW->sizeHint(); - endX -= connerSize.width(); - if (connerSize.height() < validTitleBarHeight) { - int detal = (validTitleBarHeight - connerSize.height()) / 2; - connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); - } else { - connerW->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); - } - } - } - - // tabBar 右边的附加按钮组 - // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() - if (d_ptr->mRightButtonGroup) { - QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); - endX -= wSize.width(); - // 上下留1px的边线 - d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), validTitleBarHeight - 2); - } - // quick access bar定位 - if (d_ptr->mQuickAccessBar) { - if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { - QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); - endX -= quickAccessBarSize.width(); - // 上下留1px的边线 - d_ptr->mQuickAccessBar->setGeometry(endX, y + 1, quickAccessBarSize.width(), validTitleBarHeight - 2); - } - } - // cornerWidget - TopLeftCorner - if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { - if (connerL->isVisibleTo(this)) { - QSize connerSize = connerL->sizeHint(); - endX -= connerSize.width(); - if (connerSize.height() < validTitleBarHeight) { - int detal = (validTitleBarHeight - connerSize.height()) / 2; - connerL->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); - } else { - connerL->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); - } - } - } - - // tab 的y值需要重新计算 - // 紧凑模式下,tabbarHeight不生效 - int tabH = tabBarHeight(); - if (tabH > validTitleBarHeight) { - // 这种直接把tabH设置为validTitleBarHeight - tabH = validTitleBarHeight; - } - - y = y + validTitleBarHeight - tabH; // 如果tabH较小,则下以,让tab底部和title的底部对齐 - - // applicationButton 定位,与TabBar同高 - if (d_ptr->mApplicationButton) { - if (d_ptr->mApplicationButton->isVisibleTo(this)) { - d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); - x = d_ptr->mApplicationButton->geometry().right() + 2; - } - } - // tab bar 定位 wps模式下applicationButton的右边就是tab bar - int tabBarAllowedWidth = endX - x; - // 20200831 - // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 - // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 - // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow - // 计算tab所占用的宽度 - int mintabBarWidth = calcMinTabBarWidth(); - - if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { - if (mintabBarWidth < tabBarAllowedWidth) { - tabBarAllowedWidth = mintabBarWidth; - } - d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); - } else { - // 居中对齐 - if (mintabBarWidth >= tabBarAllowedWidth) { - // 这时tabbar没有居中对齐的必要性,空间位置不够了 - d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); - } else { - // 说明tabbar的宽度有居中的可能性 - int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; - d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); - } - } - // 调整整个stackedContainer - resizeStackedContainerWidget(); -} - -/** - * @brief 绘制tabbar下的基准线,这个方法仅仅在office2013模式下需要 - * @param painter - */ -void SARibbonBar::paintTabbarBaseLine(QPainter& painter) -{ - if (!d_ptr->mTabBarBaseLineColor.isValid()) { - return; - } - painter.save(); - // 在tabbar下绘制一条线 - const int lineY = d_ptr->mRibbonTabBar->geometry().bottom(); - QPen pen(d_ptr->mTabBarBaseLineColor); - QMargins border = contentsMargins(); - - pen.setWidth(1); - pen.setStyle(Qt::SolidLine); - painter.setPen(pen); - painter.drawLine(QPoint(border.left(), lineY), QPoint(width() - border.right() - 1, lineY)); - painter.restore(); -} - -/// -/// \brief 绘制标题栏 -/// \param painter -/// \param title 标题 -/// \param contextCategoryRegion 当前显示的上下文标签的范围,上下文标签是可以遮挡标题栏的,因此需要知道上下文标签的范围 -/// x表示左边界,y表示右边界 -/// -void SARibbonBar::paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion) -{ - // 如果标题不显示直接跳出 - if (!isTitleVisible()) { - return; - } - painter.save(); - if (d_ptr->mTitleTextColor.isValid()) { - painter.setPen(d_ptr->mTitleTextColor); - } - painter.drawText(titleRegion, d_ptr->mTitleAligment, title); - painter.restore(); -} - -#if SA_DEBUG_PRINT_SARIBBONBAR -QDebug operator<<(QDebug debug, const SARibbonBar& ribbon) -{ - QDebugStateSaver saver(debug); - QFontMetrics fm = ribbon.fontMetrics(); - debug.nospace() << "SARibbonBar(" << ribbon.versionString() << ")" // - << "\nribbon font metrics info:" // - << "\n - lineSpacing:" << fm.lineSpacing() // - << "\n - height:" << fm.height() // - << "\n - em:" << fm.boundingRect("M").width() // - << "\n - ex:" << fm.boundingRect("X").height() // - << "\nribbon info:" // - << "\n -mTitleBarHeight=" << ribbon.d_ptr->mTitleBarHeight // - << "\n -mTabBarHeight=" << ribbon.d_ptr->mTabBarHeight // - << "\n -mPannelTitleHeight=" << ribbon.d_ptr->mPannelTitleHeight // - << "\n -mCategoryHeight=" << ribbon.d_ptr->mCategoryHeight // - << "\n -mIsTabOnTitle=" << ribbon.d_ptr->mIsTabOnTitle // - << "\n -mEnableShowPannelTitle=" << ribbon.d_ptr->mEnableShowPannelTitle // - << "\n -mWindowButtonSize=" << ribbon.d_ptr->mWindowButtonSize // - << "\n -mIconRightBorderPosition=" << ribbon.d_ptr->mIconRightBorderPosition // - ; - - return debug; -} -#endif - -/*** End of inlined file: SARibbonBar.cpp ***/ - -/*** Start of inlined file: SARibbonElementFactory.cpp ***/ -#include -#include - -SARibbonElementFactory::SARibbonElementFactory() -{ -} - -SARibbonElementFactory::~SARibbonElementFactory() -{ -} - -SARibbonBar* SARibbonElementFactory::createRibbonBar(QWidget* parent) -{ - return (new SARibbonBar(parent)); -} - -SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent) -{ - return (new SARibbonTabBar(parent)); -} - -SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent) -{ - return (new SARibbonApplicationButton(parent)); -} - -SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent) -{ - return (new SARibbonCategory(parent)); -} - -SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent) -{ - return (new SARibbonContextCategory(parent)); -} - -SARibbonPannel* SARibbonElementFactory::createRibbonPannel(QWidget* parent) -{ - return (new SARibbonPannel(parent)); -} - -SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent) -{ - return (new SARibbonSeparatorWidget(parent)); -} - -SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent) -{ - return (new SARibbonGallery(parent)); -} - -SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent) -{ - return (new SARibbonGalleryGroup(parent)); -} - -SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent) -{ - return (new SARibbonToolButton(parent)); -} - -SARibbonControlButton* SARibbonElementFactory::createRibbonControlButton(QWidget* parent) -{ - return (new SARibbonControlButton(parent)); -} - -SARibbonControlToolButton* SARibbonElementFactory::createRibbonControlToolButton(QWidget* parent) -{ - return (new SARibbonControlToolButton(parent)); -} - -SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent) -{ - return (new SARibbonStackedWidget(parent)); -} - -SARibbonButtonGroupWidget* SARibbonElementFactory::craeteButtonGroupWidget(QWidget* parent) -{ - return (new SARibbonButtonGroupWidget(parent)); -} - -SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent) -{ - return (new SARibbonQuickAccessBar(parent)); -} - -SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent) -{ - return (new SARibbonSystemButtonBar(parent)); -} - -/** - * @brief 创建SARibbonPannelOptionButton - * @param pannel 附属的pannel - * @return - * @sa SARibbonPannelOptionButton - */ -SARibbonPannelOptionButton* SARibbonElementFactory::createRibbonPannelOptionButton(SARibbonPannel* pannel) -{ - return (new SARibbonPannelOptionButton(pannel)); -} - -/*** End of inlined file: SARibbonElementFactory.cpp ***/ - -/*** Start of inlined file: SARibbonElementManager.cpp ***/ -SARibbonElementManager::SARibbonElementManager() -{ - mFactory.reset(new SARibbonElementFactory()); -} - -SARibbonElementManager::~SARibbonElementManager() -{ -} - -SARibbonElementManager* SARibbonElementManager::instance() -{ - static SARibbonElementManager s_instance; - - return (&(s_instance)); -} - -SARibbonElementFactory* SARibbonElementManager::factory() -{ - return (mFactory.data()); -} - -void SARibbonElementManager::setupFactory(SARibbonElementFactory* fac) -{ - mFactory.reset(fac); -} - -/*** End of inlined file: SARibbonElementManager.cpp ***/ - -/*** Start of inlined file: SARibbonCustomizeData.cpp ***/ -#include -#include -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonCustomizeData -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -SARibbonCustomizeData::SARibbonCustomizeData() - : indexValue(-1) - , actionRowProportionValue(SARibbonPannelItem::Large) - , mType(UnknowActionType) - , mActionsManagerPointer(nullptr) -{ -} - -SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr) - : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), mType(type), mActionsManagerPointer(mgr) -{ -} - -/** - * @brief 获取CustomizeData的action type - * @return - */ -SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const -{ - return (mType); -} - -/** - * @brief 设置CustomizeData的action type - * @param a - */ -void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a) -{ - mType = a; -} - -/** - * @brief 判断是否是一个正常的CustomizeData - * - * 实际逻辑actionType() != UnknowActionType - * @return 有用的CustomizeData返回true - */ -bool SARibbonCustomizeData::isValid() const -{ - return (actionType() != UnknowActionType); -} - -/** - * @brief 应用SARibbonCustomizeData到SARibbonBar - * @param m - * @return 如果应用失败,返回false,如果actionType==UnknowActionType直接返回false - */ -bool SARibbonCustomizeData::apply(SARibbonBar* bar) const -{ - if (nullptr == bar) { - return (false); - } - switch (actionType()) { - case UnknowActionType: - return (false); - - case AddCategoryActionType: { - // 添加标签 - SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue); - if (nullptr == c) { - return (false); - } - c->setObjectName(categoryObjNameValue); - SARibbonCustomizeData::setCanCustomize(c); - return (true); - } - - case AddPannelActionType: { - // 添加pannel - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* p = c->insertPannel(keyValue, indexValue); - p->setObjectName(pannelObjNameValue); - SARibbonCustomizeData::setCanCustomize(p); - return (true); - } - - case AddActionActionType: { - if (nullptr == mActionsManagerPointer) { - return (false); - } - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - SARibbonCustomizeData::setCanCustomize(act); - pannel->addAction(act, actionRowProportionValue); - return (true); - } - - case RemoveCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - bar->removeCategory(c); - return (true); - } - - case RemovePannelActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - c->removePannel(pannel); - return (true); - } - - case RemoveActionActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - pannel->removeAction(act); - return (true); - } - - case ChangeCategoryOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - int currentindex = bar->categoryIndex(c); - if (-1 == currentindex) { - return (false); - } - int toindex = currentindex + indexValue; - bar->moveCategory(currentindex, toindex); - return (true); - } - - case ChangePannelOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - int pannelIndex = c->pannelIndex(pannel); - if (-1 == pannelIndex) { - return (false); - } - c->movePannel(pannelIndex, pannelIndex + indexValue); - return (true); - } - - case ChangeActionOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - int actindex = pannel->actionIndex(act); - if (actindex <= -1) { - return (false); - } - pannel->moveAction(actindex, actindex + indexValue); - return (true); - } - - case RenameCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - c->setCategoryName(keyValue); - return (true); - } - - case RenamePannelActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - pannel->setPannelName(keyValue); - return (true); - } - - case VisibleCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - if (1 == indexValue) { - bar->showCategory(c); - } else { - bar->hideCategory(c); - } - return (true); - } - - default: - break; - } - return (false); -} - -/** - * @brief 获取actionmanager指针 - * @return - */ -SARibbonActionsManager* SARibbonCustomizeData::actionManager() -{ - return (mActionsManagerPointer); -} - -/** - * @brief 设置ActionsManager - * @param mgr - */ -void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr) -{ - mActionsManagerPointer = mgr; -} - -/** - * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData - * @param title category 的标题 - * @param index category要插入的位置 - * @param objName category的object name - * @return 返回AddCategoryActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName) -{ - SARibbonCustomizeData d(AddCategoryActionType); - - d.indexValue = index; - d.keyValue = title; - d.categoryObjNameValue = objName; - return (d); -} - -/** - * @brief 创建一个AddPannelActionType的SARibbonCustomizeData - * @param title pannel的标题 - * @param index pannel的index - * @param categoryobjName pannel的category的objectname - * @param objName pannel的objname - * @return 返回AddPannelActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddPannelCustomizeData(const QString& title, - int index, - const QString& categoryobjName, - const QString& objName) -{ - SARibbonCustomizeData d(AddPannelActionType); - - d.indexValue = index; - d.keyValue = title; - d.pannelObjNameValue = objName; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 添加action - * @param key action的索引 - * @param mgr action管理器 - * @param rp 定义action的占位情况 - * @param categoryObjName action添加到的category的objname - * @param pannelObjName action添加到的category下的pannel的objname - * @param index action添加到的pannel的索引 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key, - SARibbonActionsManager* mgr, - SARibbonPannelItem::RowProportion rp, - const QString& categoryObjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(AddActionActionType, mgr); - - d.keyValue = key; - d.categoryObjNameValue = categoryObjName; - d.pannelObjNameValue = pannelObjName; - d.actionRowProportionValue = rp; - - return (d); -} - -/** - * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData - * @param newname 新名字 - * @param index category的索引 - * @return 返回RenameCategoryActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname, - const QString& categoryobjName) -{ - SARibbonCustomizeData d(RenameCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize rename category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.keyValue = newname; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 创建一个RenamePannelActionType的SARibbonCustomizeData - * @param newname pannel的名字 - * @param indexValue pannel的索引 - * @param categoryobjName pannel对应的category的object name - * @return 返回RenamePannelActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRenamePannelCustomizeData(const QString& newname, - const QString& categoryobjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(RenamePannelActionType); - - if (pannelObjName.isEmpty() || categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize rename pannel," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.keyValue = newname; - d.pannelObjNameValue = pannelObjName; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 对应ChangeCategoryOrderActionType - * @param categoryobjName 需要移动的categoryobjName - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, - int moveindex) -{ - SARibbonCustomizeData d(ChangeCategoryOrderActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change category order," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应ChangePannelOrderActionType - * @param categoryobjName 需要移动的pannel对应的categoryobjName - * @param pannelObjName 需要移动的pannelObjName - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangePannelOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - int moveindex) -{ - SARibbonCustomizeData d(ChangePannelOrderActionType); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change pannel order," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应ChangeActionOrderActionType - * @param categoryobjName 需要移动的pannel对应的categoryobjName - * @param pannelObjName 需要移动的pannelObjName - * @param key SARibbonActionsManager管理的key名 - * @param mgr SARibbonActionsManager指针 - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr, - int moveindex) -{ - SARibbonCustomizeData d(ChangeActionOrderActionType, mgr); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change action order," - "but get an empty category/pannel/action object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.keyValue = key; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应RemoveCategoryActionType - * @param categoryobjName 需要移除的objname - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName) -{ - SARibbonCustomizeData d(RemoveCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 对应RemovePannelActionType - * @param categoryobjName pannel对应的category的obj name - * @param pannelObjName pannel对应的 obj name - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemovePannelCustomizeData(const QString& categoryobjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(RemovePannelActionType); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove pannel," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - return (d); -} - -/** - * @brief 对应RemoveActionActionType - * @param categoryobjName pannel对应的category的obj name - * @param pannelObjName pannel对应的 obj name - * @param key SARibbonActionsManager管理的key名 - * @param mgr SARibbonActionsManager指针 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr) -{ - SARibbonCustomizeData d(RemoveActionActionType, mgr); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove action," - "but get an empty category/pannel/action object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.keyValue = key; - return (d); -} - -/** - * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData - * @param categoryobjName - * @param isShow - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow) -{ - SARibbonCustomizeData d(VisibleCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize visible category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.indexValue = isShow ? 1 : 0; - return (d); -} - -/** - * @brief 判断外置属性,是否允许自定义 - * @param obj - * @return - */ -bool SARibbonCustomizeData::isCanCustomize(QObject* obj) -{ - QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE); - - if (v.isValid()) { - return (v.toBool()); - } - return (false); -} - -/** - * @brief 设置外置属性允许自定义 - * @param obj - * @param canbe - */ -void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe) -{ - obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe); -} - -QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex); - -QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex) -{ - QList< SARibbonCustomizeData > res; - - for (int i = 0; i < csd.size(); ++i) { - if (!willremoveIndex.contains(i)) { - res << csd[ i ]; - } - } - return (res); -} - -/** - * @brief 对QList进行简化操作 - * - * 此函数会执行如下操作: - * 1、针对同一个category/pannel连续出现的添加和删除操作进行移除(前一步添加,后一步删除) - * - * 2、针对VisibleCategoryActionType,对于连续出现的操作只保留最后一步 - * - * 3、针对RenameCategoryActionType和RenamePannelActionType操作,只保留最后一个 - * - * 4、针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并为一个动作, - * 如果合并后原地不动,则删除 - * - * @param csd - * @return 返回简化的QList - */ -QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd) -{ - int size = csd.size(); - - if (size <= 1) { - return (csd); - } - QList< SARibbonCustomizeData > res; - QList< int > willremoveIndex; // 记录要删除的index - - //! 首先针对连续出现的添加和删除操作进行优化 - for (int i = 1; i < size; ++i) { - if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) { - if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) { - willremoveIndex << i - 1 << i; - } - } else if ((csd[ i - 1 ].actionType() == AddPannelActionType) && (csd[ i ].actionType() == RemovePannelActionType)) { - if ((csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) - && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { - willremoveIndex << i - 1 << i; - } - } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) { - if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) - && (csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) - && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { - willremoveIndex << i - 1 << i; - } - } - } - res = remove_indexs(csd, willremoveIndex); - willremoveIndex.clear(); - - //! 筛选VisibleCategoryActionType,对于连续出现的操作只保留最后一步 - size = res.size(); - for (int i = 1; i < size; ++i) { - if ((res[ i - 1 ].actionType() == VisibleCategoryActionType) - && (res[ i ].actionType() == VisibleCategoryActionType)) { - if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) { - // 要保证操作的是同一个内容 - willremoveIndex << i - 1; // 删除前一个只保留最后一个 - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 针对RenameCategoryActionType和RenamePannelActionType操作,只需保留最后一个 - size = res.size(); - for (int i = 0; i < size; ++i) { - if (res[ i ].actionType() == RenameCategoryActionType) { - // 向后查询,如果查询到有同一个Category改名,把这个索引加入删除队列 - for (int j = i + 1; j < size; ++j) { - if ((res[ j ].actionType() == RenameCategoryActionType) - && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { - willremoveIndex << i; - } - } - } else if (res[ i ].actionType() == RenamePannelActionType) { - // 向后查询,如果查询到有同一个pannel改名,把这个索引加入删除队列 - for (int j = i + 1; j < size; ++j) { - if ((res[ j ].actionType() == RenamePannelActionType) - && (res[ i ].pannelObjNameValue == res[ j ].pannelObjNameValue) - && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { - willremoveIndex << i; - } - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并 - size = res.size(); - for (int i = 1; i < size; ++i) { - if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType) - && (res[ i ].actionType() == ChangeCategoryOrderActionType) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } else if ((res[ i - 1 ].actionType() == ChangePannelOrderActionType) - && (res[ i ].actionType() == ChangePannelOrderActionType) - && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType) - && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue) - && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 上一步操作可能会产生indexvalue为0的情况,此操作把indexvalue为0的删除 - size = res.size(); - for (int i = 0; i < size; ++i) { - if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePannelOrderActionType) - || (res[ i ].actionType() == ChangeActionOrderActionType)) { - if (0 == res[ i ].indexValue) { - willremoveIndex << i; - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - return (res); -} - -/*** End of inlined file: SARibbonCustomizeData.cpp ***/ - -/*** Start of inlined file: SARibbonCustomizeWidget.cpp ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonCustomizeWidget -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds) -{ - if (cds.size() <= 0) { - return (false); - } - - xml->writeStartElement("sa-ribbon-customize"); - for (const SARibbonCustomizeData& d : cds) { - xml->writeStartElement("customize-data"); - xml->writeAttribute("type", QString::number(d.actionType())); - xml->writeAttribute("index", QString::number(d.indexValue)); - xml->writeAttribute("key", d.keyValue); - xml->writeAttribute("category", d.categoryObjNameValue); - xml->writeAttribute("pannel", d.pannelObjNameValue); - xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue)); - - xml->writeEndElement(); - } - xml->writeEndElement(); - if (xml->hasError()) { - qWarning() << "write has error"; - } - return (true); -} - -QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr) -{ - // 先找到"sa-ribbon-customize" - while (!xml->atEnd()) { - - if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) { - break; - } - xml->readNext(); - } - QList< SARibbonCustomizeData > res; - - // 开始遍历"customize-data" - while (!xml->atEnd()) { - if (xml->isStartElement() && (xml->name().toString() == "customize-data")) { - // 首先读取属性type - SARibbonCustomizeData d; - QXmlStreamAttributes attrs = xml->attributes(); - if (!attrs.hasAttribute("type")) { - // 说明异常,跳过这个 - xml->readNextStartElement(); - continue; - } - bool isOk = false; - int v = xml->attributes().value("type").toInt(&isOk); - if (!isOk) { - // 说明异常,跳过这个 - xml->readNextStartElement(); - continue; - } - d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v)); - // 开始读取子对象 - if (attrs.hasAttribute("index")) { - v = xml->attributes().value("index").toInt(&isOk); - if (isOk) { - d.indexValue = v; - } - } - if (attrs.hasAttribute("key")) { - d.keyValue = attrs.value("key").toString(); - } - if (attrs.hasAttribute("category")) { - d.categoryObjNameValue = attrs.value("category").toString(); - } - if (attrs.hasAttribute("pannel")) { - d.pannelObjNameValue = attrs.value("pannel").toString(); - } - if (attrs.hasAttribute("row-prop")) { - v = xml->attributes().value("row-prop").toInt(&isOk); - if (isOk) { - d.actionRowProportionValue = static_cast< SARibbonPannelItem::RowProportion >(v); - } - } - d.setActionsManager(mgr); - res.append(d); - } - xml->readNext(); - } - if (xml->hasError()) { - qWarning() << xml->errorString(); - } - return (res); -} - -int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) -{ - int c = 0; - - for (const SARibbonCustomizeData& d : cds) { - if (d.apply(bar)) { - ++c; - } - } - return (c); -} - -int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) -{ - int c = 0; - // todo 支持反向操作 - return (c); -} - -bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr) -{ - QFile f(filePath); - - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - return (false); - } - f.seek(0); - QXmlStreamReader xml(&f); - - return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr)); -} - -/** - * @brief 构建SARibbonCustomizeWidget的Ui - */ -class SARibbonCustomizeWidgetUi -{ -public: - QHBoxLayout* horizontalLayoutMain; - QVBoxLayout* verticalLayoutSelect; - QLabel* labelSelectAction; - QHBoxLayout* horizontalLayoutSearch; - QComboBox* comboBoxActionIndex; - QLineEdit* lineEditSearchAction; - QListView* listViewSelect; - QVBoxLayout* verticalLayoutMidButtons; - QSpacerItem* verticalSpacerUp; - QPushButton* pushButtonAdd; - QPushButton* pushButtonDelete; - QPushButton* pushButtonReset; - QSpacerItem* verticalSpacerDown; - QLabel* labelProportion; - QComboBox* comboBoxActionProportion; - QVBoxLayout* verticalLayoutResult; - QLabel* labelCustomize; - QHBoxLayout* horizontalLayoutCategorySelect; - QRadioButton* radioButtonMainCategory; - QRadioButton* radioButtonAllCategory; - QButtonGroup* radioButtonGroup; - QTreeView* treeViewResult; - QHBoxLayout* horizontalLayoutActionOptBtns; - QPushButton* pushButtonNewCategory; - QPushButton* pushButtonNewPannel; - QPushButton* pushButtonRename; - QVBoxLayout* verticalLayoutRightButtons; - QSpacerItem* verticalSpacerUp2; - QToolButton* toolButtonUp; - QToolButton* toolButtonDown; - QSpacerItem* verticalSpacerDown2; - - void setupUi(QWidget* customizeWidget) - { - if (customizeWidget->objectName().isEmpty()) { - customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget")); - } - customizeWidget->resize(800, 600); - horizontalLayoutMain = new QHBoxLayout(customizeWidget); - horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain")); - verticalLayoutSelect = new QVBoxLayout(); - verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect")); - labelSelectAction = new QLabel(customizeWidget); - labelSelectAction->setObjectName(QStringLiteral("labelSelectAction")); - - verticalLayoutSelect->addWidget(labelSelectAction); - - horizontalLayoutSearch = new QHBoxLayout(); - horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch")); - comboBoxActionIndex = new QComboBox(customizeWidget); - comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex")); - comboBoxActionIndex->setEditable(false); - - horizontalLayoutSearch->addWidget(comboBoxActionIndex); - - lineEditSearchAction = new QLineEdit(customizeWidget); - lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction")); - - horizontalLayoutSearch->addWidget(lineEditSearchAction); - - verticalLayoutSelect->addLayout(horizontalLayoutSearch); - - listViewSelect = new QListView(customizeWidget); - listViewSelect->setObjectName(QStringLiteral("listViewSelect")); - - verticalLayoutSelect->addWidget(listViewSelect); - - horizontalLayoutMain->addLayout(verticalLayoutSelect); - - verticalLayoutMidButtons = new QVBoxLayout(); - verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons")); - verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutMidButtons->addItem(verticalSpacerUp); - - pushButtonAdd = new QPushButton(customizeWidget); - pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd")); - pushButtonAdd->setEnabled(false); - - verticalLayoutMidButtons->addWidget(pushButtonAdd); - - pushButtonDelete = new QPushButton(customizeWidget); - pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete")); - pushButtonDelete->setEnabled(false); - - verticalLayoutMidButtons->addWidget(pushButtonDelete); - - verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutMidButtons->addItem(verticalSpacerDown); - - labelProportion = new QLabel(customizeWidget); - labelProportion->setObjectName(QStringLiteral("labelProportion")); - verticalLayoutMidButtons->addWidget(labelProportion); - - comboBoxActionProportion = new QComboBox(customizeWidget); - comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion")); - comboBoxActionProportion->setEditable(false); - verticalLayoutMidButtons->addWidget(comboBoxActionProportion); - horizontalLayoutMain->addLayout(verticalLayoutMidButtons); - - verticalLayoutResult = new QVBoxLayout(); - verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult")); - labelCustomize = new QLabel(customizeWidget); - labelCustomize->setObjectName(QStringLiteral("labelCustomize")); - - verticalLayoutResult->addWidget(labelCustomize); - - horizontalLayoutCategorySelect = new QHBoxLayout(); - horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect")); - radioButtonMainCategory = new QRadioButton(customizeWidget); - radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory")); - radioButtonMainCategory->setChecked(false); - - horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory); - - radioButtonAllCategory = new QRadioButton(customizeWidget); - radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory")); - radioButtonAllCategory->setChecked(true); - - horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory); - - radioButtonGroup = new QButtonGroup(customizeWidget); - radioButtonGroup->addButton(radioButtonMainCategory); - radioButtonGroup->addButton(radioButtonAllCategory); - - verticalLayoutResult->addLayout(horizontalLayoutCategorySelect); - - treeViewResult = new QTreeView(customizeWidget); - treeViewResult->setObjectName(QStringLiteral("treeViewResult")); - treeViewResult->setHeaderHidden(true); - treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection); - treeViewResult->setAnimated(true); // 支持动画 - treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不允许直接在item上重命名 - - verticalLayoutResult->addWidget(treeViewResult); - - horizontalLayoutActionOptBtns = new QHBoxLayout(); - horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns")); - pushButtonNewCategory = new QPushButton(customizeWidget); - pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory); - - pushButtonNewPannel = new QPushButton(customizeWidget); - pushButtonNewPannel->setObjectName(QStringLiteral("pushButtonNewPannel")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonNewPannel); - - pushButtonRename = new QPushButton(customizeWidget); - pushButtonRename->setObjectName(QStringLiteral("pushButtonRename")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonRename); - - pushButtonReset = new QPushButton(customizeWidget); - pushButtonReset->setObjectName(QStringLiteral("pushButtonReset")); - horizontalLayoutActionOptBtns->addWidget(pushButtonReset); - - verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns); - - horizontalLayoutMain->addLayout(verticalLayoutResult); - - verticalLayoutRightButtons = new QVBoxLayout(); - verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons")); - verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutRightButtons->addItem(verticalSpacerUp2); - - toolButtonUp = new QToolButton(customizeWidget); - toolButtonUp->setObjectName(QStringLiteral("pushButtonUp")); - toolButtonUp->setArrowType(Qt::UpArrow); - toolButtonUp->setAutoRaise(true); - - verticalLayoutRightButtons->addWidget(toolButtonUp); - - toolButtonDown = new QToolButton(customizeWidget); - toolButtonDown->setObjectName(QStringLiteral("pushButtonDown")); - toolButtonDown->setArrowType(Qt::DownArrow); - toolButtonDown->setAutoRaise(true); - - verticalLayoutRightButtons->addWidget(toolButtonDown); - - verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutRightButtons->addItem(verticalSpacerDown2); - - horizontalLayoutMain->addLayout(verticalLayoutRightButtons); - - retranslateUi(customizeWidget); - } // setupUi - - void retranslateUi(QWidget* customizeWidget) - { - customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR)); - labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR)); // cn:请选择 - lineEditSearchAction->setInputMask(QString()); - lineEditSearchAction->setText(QString()); - lineEditSearchAction->setPlaceholderText( - QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR)); // cn:查找命令 - pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR)); // cn:添加 >> - pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR)); // cn:<< 移除 - labelCustomize->setText( - QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR)); // cn:自定义功能区 - radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR)); // cn:主选项卡 - radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR)); // cn:所有选项卡 - pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR)); // cn:新建选项卡 - pushButtonNewPannel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR)); // cn:新建组 - pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR)); // cn:重命名 - pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR)); // cn:重置 - labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR)); // cn:比例 - } // retranslateUi -}; - -/** - * @brief 管理SARibbonCustomizeWidget的业务逻辑 - */ -class SARibbonCustomizeWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget) -public: - SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory }; ///< 显示类型 - SARibbonBar* mRibbonBar { nullptr }; ///< 保存SARibbonMainWindow的指针 - SARibbonActionsManager* mActionMgr { nullptr }; ///< action管理器 - SARibbonActionsManagerModel* mAcionModel { nullptr }; ///< action管理器对应的model - QStandardItemModel* mRibbonModel { nullptr }; ///< 用于很成ribbon的树 - int mCustomizeCategoryCount { 0 }; ///< 记录自定义Category的个数 - int mCustomizePannelCount { 0 }; ///< 记录自定义Pannel的个数 -public: - PrivateData(SARibbonCustomizeWidget* p); - void updateModel(); - - QList< SARibbonCustomizeData > mCustomizeDatasCache; ///< 缓存记录所有的自定义动作 - QList< SARibbonCustomizeData > mCustomizeDatasApplied; ///< 应用后的所有的自定义动作 - QList< SARibbonCustomizeData > mOldCustomizeDatas; ///< 记录旧的自定义动作,本地文件缓存 - // 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch_suf - static QString makeRandomObjName(const QString& pre); - - int itemLevel(QStandardItem* item) const; - - // - bool isCustomizeItem(QStandardItem* item) const; - - // 把item转换为category - SARibbonCategory* itemToCategory(QStandardItem* item) const; - - // 把item转换为SARibbonPannel - SARibbonPannel* itemToPannel(QStandardItem* item) const; - - // 获取item对应的object name - QString itemObjectName(QStandardItem* item) const; - - // 判断是否可以自定义 - bool isItemCanCustomize(QStandardItem* item) const; - - // 从item转为action - QAction* itemToAction(QStandardItem* item) const; -}; - -SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p) - : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p)) -{ -} - -void SARibbonCustomizeWidget::PrivateData::updateModel() -{ - if (mRibbonBar == nullptr) { - return; - } - mRibbonModel->clear(); - SARibbonBar* ribbonbar = mRibbonBar; - QList< SARibbonCategory* > categorys = ribbonbar->categoryPages(); - - for (const SARibbonCategory* c : qAsConst(categorys)) { - if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) { - // 如果是只显示主内容,如果是上下文标签就忽略 - continue; - } - QStandardItem* ci = new QStandardItem(); - if (c->isContextCategory()) { - ci->setText(QString("[%1]").arg(c->categoryName())); - } else { - ci->setText(c->categoryName()); - } - if (c->isCanCustomize() && !c->isContextCategory()) { - // 上下文标签不做显示隐藏处理 - ci->setCheckable(true); - ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked); - ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ci->setData(0, SARibbonCustomizeWidget::LevelRole); - ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole); - QList< SARibbonPannel* > pannels = c->pannelList(); - for (const SARibbonPannel* p : qAsConst(pannels)) { - QStandardItem* pi = new QStandardItem(p->pannelName()); - pi->setData(1, SARibbonCustomizeWidget::LevelRole); - pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole); - if (p->isCanCustomize()) { - pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ci->appendRow(pi); - const QList< SARibbonPannelItem* >& items = p->ribbonPannelItem(); - for (SARibbonPannelItem* i : qAsConst(items)) { - if (i->action->isSeparator()) { - continue; - } - QStandardItem* ii = new QStandardItem(); - // if (i->customWidget) { - // //如果是自定义窗口 - // if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) { - // delete ii; - // continue; //如果窗口啥也没有,就跳过 - // } - // ii->setText(i->widget()->windowTitle()); - // ii->setIcon(i->widget()->windowIcon()); - // if (SARibbonCustomizeData::isCanCustomize(i->widget())) { - // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 - // } - // } else { - // //不是自定义,说明是action - // ii->setText(i->action->text()); - // ii->setIcon(i->action->icon()); - // if (SARibbonCustomizeData::isCanCustomize(i->action)) { - // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 - // } - // } - ii->setText(i->action->text()); - ii->setIcon(i->action->icon()); - if (SARibbonCustomizeData::isCanCustomize(i->action)) { - ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ii->setData(2, SARibbonCustomizeWidget::LevelRole); - ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole); - pi->appendRow(ii); - } - } - mRibbonModel->appendRow(ci); - } -} - -/** - * @brief 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch - * @param pre 前缀 - * @return - */ -QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre) -{ - return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch())); -} - -/** - * @brief 获取item的level - * @param item - * @return - */ -int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const -{ - return (item->data(SARibbonCustomizeWidget::LevelRole).toInt()); -} - -/** - * @brief 判断itemn为自定义的item,自定义的item都带有CustomizeRole角色 - * @param item - * @return - */ -bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const -{ - if (nullptr == item) { - return (false); - } - return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid()); -} - -/** - * @brief 把item转换为category - * @param item - * @return无法转换返回nullptr - */ -SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const -{ - int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); - - if (level != 0) { - return (nullptr); - } - qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); - - return (reinterpret_cast< SARibbonCategory* >(p)); -} - -/** - * @brief 把item转换为pannel - * @param item - * @return 无法转换返回nullptr - */ -SARibbonPannel* SARibbonCustomizeWidget::PrivateData::itemToPannel(QStandardItem* item) const -{ - int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); - - if (level != 1) { - return (nullptr); - } - qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); - - return (reinterpret_cast< SARibbonPannel* >(p)); -} - -/** - * @brief 获取item对应的objectname - * @param item - * @return 如果无法获取,返回一个空的QString - */ -QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const -{ - QString objName; - - if (isCustomizeItem(item)) { - // 说明是自定义的 - objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString(); - } else { - // 说明这个是非自定义的 - int level = itemLevel(item); - if (0 == level) { - SARibbonCategory* category = itemToCategory(item); - if (category) { - objName = category->objectName(); - } - } else if (1 == level) { - SARibbonPannel* pannel = itemToPannel(item); - if (pannel) { - objName = pannel->objectName(); - } - } - } - return (objName); -} - -/** - * @brief 判断item是否可自定义 - * @param item - * @return - */ -bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const -{ - if (nullptr == item) { - return (false); - } - QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole); - - if (v.isValid()) { - return (v.toBool()); - } - return (false); -} - -/** - * @brief 从item 转为action - * @param item - * @return - */ -QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const -{ - if (2 != itemLevel(item)) { - return (nullptr); - } - // 这里要非常注意,SARibbonCustomizeWidget::CustomizeRole为true时,说明这个是自定义的内容, - // 这时PointerRole里存放的是action指针,不是SARibbonPannelItem - QAction* act = nullptr; - - if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) { - act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); - } else { - SARibbonPannelItem* pi = - reinterpret_cast< SARibbonPannelItem* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); - act = (pi->action); - } - return (act); -} - -//=================================================== -// SARibbonCustomizeWidget -//=================================================== -/** - * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget - * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针 - * @param parent 用于界面生成的parent,可以和SARibbonMainWindow一样 - * @param f 同QWidget::QWidget的第二个参数 - */ -SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f) - : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) -{ - init(ribbonWindow->ribbonBar()); -} - -SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f) - : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) -{ - init(ribbonbar); -} - -void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar) -{ - d_ptr->mRibbonBar = ribbonbar; - ui->setupUi(this); - ui->listViewSelect->setModel(d_ptr->mAcionModel); - ui->treeViewResult->setModel(d_ptr->mRibbonModel); - initConnection(); - updateModel(); -} - -SARibbonCustomizeWidget::~SARibbonCustomizeWidget() -{ - delete ui; -} - -void SARibbonCustomizeWidget::initConnection() -{ - // 这个需要qt5.8以上支持 - // connect(ui->comboBoxActionIndex, QOverload::of(&QComboBox::currentIndexChanged) - // , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); - connect(ui->comboBoxActionIndex, - static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged), - this, - &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); - // 这个需要qt5.8以上支持 - // connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked) - // , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); - connect(ui->radioButtonGroup, - static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked), - this, - &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); - connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked); - connect(ui->pushButtonNewPannel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPannelClicked); - connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked); - connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked); - connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked); - connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked); - connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked); - connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked); - connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked); - connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged); - connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited); - connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked); -} - -/** - * @brief 设置action管理器 - * @param mgr - */ -void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr) -{ - d_ptr->mActionMgr = mgr; - if (d_ptr->mActionMgr) { - d_ptr->mAcionModel->uninstallActionsManager(); - } - d_ptr->mAcionModel->setupActionsManager(mgr); - // 更新左边复选框 - QList< int > tags = mgr->actionTags(); - - ui->comboBoxActionIndex->clear(); - for (int tag : qAsConst(tags)) { - if (mgr->tagName(tag).isEmpty()) - continue; - ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag); - } -} - -/** - * @brief //判断用户是否有要存储的内容,对应save动作 - * @return - */ -bool SARibbonCustomizeWidget::isApplied() const -{ - return (d_ptr->mCustomizeDatasApplied.size() > 0); -} - -/** - * @brief 判断用户是否有改动内容,对应apply动作 - * @return - */ -bool SARibbonCustomizeWidget::isCached() const -{ - return (d_ptr->mCustomizeDatasCache.size() > 0); -} - -/** - * @brief 获取model - * @return - */ -const QStandardItemModel* SARibbonCustomizeWidget::model() const -{ - return (d_ptr->mRibbonModel); -} - -/** - * @brief 根据当前的radiobutton选项来更新model - */ -void SARibbonCustomizeWidget::updateModel() -{ - updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory); - if (d_ptr->mRibbonBar) { - SARibbonBar* bar = d_ptr->mRibbonBar; - if (bar) { - ui->comboBoxActionProportion->clear(); - if (bar->isTwoRowStyle()) { - ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); - ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); - } else { - ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); - ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPannelItem::Medium); - ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); - } - } - } -} - -/** - * @brief 更新model - */ -void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type) -{ - d_ptr->mShowType = type; - d_ptr->updateModel(); -} - -/** - * @brief 应用所有设定 - * @return 应用成功返回true - * @note 所有设定有一个应用成功都会返回true - */ -bool SARibbonCustomizeWidget::applys() -{ - simplify(); - if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) { - // 将临时操作存入已应用操作,并清空临时操作 - makeActionsApplied(); - clearCache(); - return true; - } else { - return false; - } -} - -/** - * @brief 转换为xml - * - * 此函数仅会写element,不会写document相关内容,因此如果需要写document, - * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() - * - * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); - * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, - * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: - * @code - * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口 - * dlg.setupActionsManager(m_actMgr); - * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { - * dlg.applys(); - * QByteArray str; - * QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造,遇到中文会异常 - * xml.setAutoFormatting(true); - * xml.setAutoFormattingIndent(2); - * xml.setCodec("utf-8");//在writeStartDocument之前指定编码 - * xml.writeStartDocument(); - * bool isok = dlg.toXml(&xml); - * xml.writeEndDocument(); - * if (isok) { - * QFile f("customize.xml"); - * if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) { - * QTextStream s(&f); - * s.setCodec("utf-8");//指定编码输出 - * s << str; - * s.flush(); - * } - * m_edit->append("write xml:");//m_edit的定义为:QTextEdit *m_edit; - * m_edit->append(str); - * } - * } - * @endcode - * @return 如果出现异常,返回false,如果没有自定义数据也会返回false - * @see sa_customize_datas_to_xml - */ -bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const -{ - QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas; - - if (isApplied()) - res << d_ptr->mCustomizeDatasApplied; - if (isCached()) - res << d_ptr->mCustomizeDatasCache; - - res = SARibbonCustomizeData::simplify(res); - return (sa_customize_datas_to_xml(xml, res)); -} - -/** - * @brief 把配置写入文件中 - * @param xmlpath - * @return - */ -bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const -{ - QFile f(xmlpath); - - if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - return (false); - } - QXmlStreamWriter xml(&f); - - xml.setAutoFormatting(true); - xml.setAutoFormattingIndent(2); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. - xml.setCodec("utf-8"); // 在writeStartDocument之前指定编码 -#endif - xml.writeStartDocument(); - bool isOK = toXml(&xml); - - xml.writeEndDocument(); - f.close(); - return (isOK); -} - -/** - * @brief 从xml中加载QList - * - * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - * @param xml - * @return - * @note 此函数要在@ref setupActionsManager 函数之后调用 - */ -void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml) -{ - QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr); - - d_ptr->mOldCustomizeDatas = cds; -} - -/** - * @brief 从xml中加载QList - * - * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - * @param xmlpath - * @note 此函数要在@ref setupActionsManager 函数之后调用 - * @note 如果程序启动后加载了自定义配置,再调用此窗口时需要调用此函数,把原来的配置加载进来, - * 在生成新动作时会把旧动作保存,但在调用applys时不会调用此加载的动作 - */ -void SARibbonCustomizeWidget::fromXml(const QString& xmlpath) -{ - QFile f(xmlpath); - - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - return; - } - f.seek(0); - QXmlStreamReader xml(&f); - - fromXml(&xml); -} - -/** - * @brief 应用xml配置 - * - * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: - * @code - * //只能调用一次 - * static bool has_call = false; - * if (!has_call) { - * QFile f("customize.xml"); - * if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) { - * return; - * } - * f.seek(0); - * QXmlStreamReader xml(&f); - * has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr); - * } - * @endcode - * @param xml - * @param bar SARibbonBar - * @return 所有设定有一个应用成功都会返回true - * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file - */ -bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr) -{ - // 先找到sa-ribbon-customize标签 - QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr); - - return (sa_customize_datas_apply(cds, bar) > 0); -} - -/** - * @brief 清除已应用的动作 - */ -void SARibbonCustomizeWidget::makeActionsApplied() -{ - d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache; -} - -/** - * @brief 清除applied的动作,cancel操作后需要清空已应用的动作 - */ -void SARibbonCustomizeWidget::clearApplied() -{ - d_ptr->mCustomizeDatasApplied.clear(); -} - -/** - * @brief 清除缓存动作 - * - * 在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 - */ -void SARibbonCustomizeWidget::clearCache() -{ - d_ptr->mCustomizeDatasCache.clear(); -} - -/** - * @brief 清除所有动作,不包含本地读取的数据 - */ -void SARibbonCustomizeWidget::clear() -{ - clearApplied(); - clearCache(); -} - -/** - * @brief 精简 - */ -void SARibbonCustomizeWidget::simplify() -{ - d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache); -} - -/** - * @brief 获取当前界面选中的行属性 - * @return - */ -SARibbonPannelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const -{ - return (static_cast< SARibbonPannelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt())); -} - -/** - * @brief 获取listview中选中的action - * @return 如果没有选中action,返回nullptr - * @note 如果要获取treeview选中的action,使用@ref itemToAction 函数 - */ -QAction* SARibbonCustomizeWidget::selectedAction() const -{ - QItemSelectionModel* m = ui->listViewSelect->selectionModel(); - - if ((nullptr == m) || !m->hasSelection()) { - return (nullptr); - } - QModelIndex i = m->currentIndex(); - - return (d_ptr->mAcionModel->indexToAction(i)); -} - -/** - * @brief 把item转换为action - * @param item - * @return 如果没有action可转换,返回nullptr - */ -QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const -{ - return (d_ptr->itemToAction(item)); -} - -/** - * @brief 获取ribbon tree选中的item - * @return - */ -QStandardItem* SARibbonCustomizeWidget::selectedItem() const -{ - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if ((nullptr == m) || !m->hasSelection()) { - return (nullptr); - } - QModelIndex i = m->currentIndex(); - - return (d_ptr->mRibbonModel->itemFromIndex(i)); -} - -/** - * @brief 获取选中的ribbon tree 的level - * @return -1为选中异常,0代表选中了category 1代表选中了pannel 2代表选中了action - */ -int SARibbonCustomizeWidget::selectedRibbonLevel() const -{ - QStandardItem* item = selectedItem(); - - if (item) { - return (itemLevel(item)); - } - return (-1); -} - -/** - * @brief 获取StandardItem 的level - * @param item - * @return - */ -int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const -{ - return (d_ptr->itemLevel(item)); -} - -/** - * @brief 设置某个item被选中 - * @param item - */ -void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible) -{ - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if (nullptr == m) { - return; - } - if (m) { - m->clearSelection(); - m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent); - } - if (ensureVisible) { - ui->treeViewResult->scrollTo(item->index()); - } -} - -/** - * @brief 判断itemn能否改动,可以改动返回true - * @param item - * @return - */ -bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const -{ - return (d_ptr->isItemCanCustomize(item)); -} - -bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const -{ - return (isItemCanCustomize(selectedItem())); -} - -/** - * @brief 判断itemn能否改动,可以改动返回true - * @param item - * @return - */ -bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const -{ - return (d_ptr->isCustomizeItem(item)); -} - -bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const -{ - return (isCustomizeItem(selectedItem())); -} - -void SARibbonCustomizeWidget::removeItem(QStandardItem* item) -{ - if (item->parent()) { - item->parent()->removeRow(item->row()); - } else { - d_ptr->mRibbonModel->removeRow(item->row()); - } -} - -void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index) -{ - int tag = ui->comboBoxActionIndex->itemData(index).toInt(); - - d_ptr->mAcionModel->setFilter(tag); -} - -void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b) -{ - updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory); -} - -void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked() -{ - int row = d_ptr->mRibbonModel->rowCount(); - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if (m && m->hasSelection()) { - QModelIndex i = m->currentIndex(); - while (i.parent().isValid()) { - i = i.parent(); - } - // 获取选中的最顶层item - row = i.row() + 1; - } - QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount))); - - ni->setData(0, SARibbonCustomizeWidget::LevelRole); - d_ptr->mRibbonModel->insertRow(row, ni); - // 设置新增的为选中 - setSelectItem(ni); - // 把动作插入动作列表中 - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData( - ni->text(), ni->row(), SARibbonCustomizeWidget::PrivateData::makeRandomObjName("category")); - - d_ptr->mCustomizeDatasCache.append(d); - ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); - ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); -} - -void SARibbonCustomizeWidget::onPushButtonNewPannelClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - int level = selectedRibbonLevel(); - - QStandardItem* ni = new QStandardItem(tr("new pannel[customize]%1").arg(++(d_ptr->mCustomizePannelCount))); - - ni->setData(1, SARibbonCustomizeWidget::LevelRole); - - if (0 == level) { - // 说明是category,插入到最后 - item->appendRow(ni); - } else if (1 == level) { - // 说明选择的是pannel,插入到此pannel之后 - QStandardItem* categoryItem = item->parent(); - if (nullptr == categoryItem) { - return; - } - categoryItem->insertRow(item->row() + 1, ni); - } else { - // 不符合就删除退出 - delete ni; - ni = nullptr; - return; - } - // 查找category的object name - QStandardItem* categoryItem = ni->parent(); - QString categoryObjName = ""; - - categoryObjName = d_ptr->itemObjectName(categoryItem); - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPannelCustomizeData( - ni->text(), ni->row(), categoryObjName, SARibbonCustomizeWidget::PrivateData::makeRandomObjName("pannel")); - - d_ptr->mCustomizeDatasCache.append(d); - ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); - ni->setData(d.pannelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); - setSelectItem(ni); -} - -void SARibbonCustomizeWidget::onPushButtonRenameClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - bool ok; - QString text = ""; - - text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok); - - if (!ok || text.isEmpty()) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 改Category名 - QString cateObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else if (1 == level) { - QString cateObjName = d_ptr->itemObjectName(item->parent()); - QString pannelObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePannelCustomizeData(text, cateObjName, pannelObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else { - // action 不允许改名 - return; - } - item->setText(text); -} - -void SARibbonCustomizeWidget::onPushButtonAddClicked() -{ - QAction* act = selectedAction(); - QStandardItem* item = selectedItem(); - - if ((nullptr == act) || (nullptr == item)) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 选中category不进行操作 - return; - } else if (2 == level) { - // 选中action,添加到这个action之后,把item设置为pannel - item = item->parent(); - } - QString pannelObjName = d_ptr->itemObjectName(item); - QString categoryObjName = d_ptr->itemObjectName(item->parent()); - QString key = d_ptr->mActionMgr->key(act); - - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData( - key, d_ptr->mActionMgr, selectedRowProportion(), categoryObjName, pannelObjName); - - d_ptr->mCustomizeDatasCache.append(d); - - QStandardItem* actItem = new QStandardItem(act->icon(), act->text()); - - actItem->setData(2, SARibbonCustomizeWidget::LevelRole); - actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole); - actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole); - actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole); // 把action指针传入 - item->appendRow(actItem); -} - -void SARibbonCustomizeWidget::onPushButtonDeleteClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - if (!isItemCanCustomize(item)) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 删除category - SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item)); - d_ptr->mCustomizeDatasCache.append(d); - } else if (1 == level) { - // 删除pannel - QString catObjName = d_ptr->itemObjectName(item->parent()); - QString pannelObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePannelCustomizeData(catObjName, pannelObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else if (2 == level) { - // 删除Action - QString catObjName = d_ptr->itemObjectName(item->parent()->parent()); - QString pannelObjName = d_ptr->itemObjectName(item->parent()); - QAction* act = itemToAction(item); - QString key = d_ptr->mActionMgr->key(act); - if (key.isEmpty() || catObjName.isEmpty() || pannelObjName.isEmpty()) { - return; - } - - SARibbonCustomizeData d = - SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, pannelObjName, key, d_ptr->mActionMgr); - d_ptr->mCustomizeDatasCache.append(d); - } - // 执行删除操作 - removeItem(item); - // 删除后重新识别 - ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0); - ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize()); -} - -void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index) -{ - // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 - // 点击了listview,判断treeview的状态 - Q_UNUSED(index); - ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0); - ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize()); -} - -void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index) -{ - Q_UNUSED(index); - // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - int level = itemLevel(item); - - ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item)); - ui->pushButtonDelete->setEnabled(isItemCanCustomize(item)); // 有CustomizeRole,必有CanCustomizeRole - ui->pushButtonRename->setEnabled( - level != 2 || isItemCanCustomize(item)); // QAction 不能改名 , 有CustomizeRole,必有CanCustomizeRole -} - -void SARibbonCustomizeWidget::onToolButtonUpClicked() -{ - QStandardItem* item = selectedItem(); - - if ((nullptr == item) || (0 == item->row())) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 移动category - SARibbonCustomizeData d = - SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = d_ptr->mRibbonModel->takeItem(r); - d_ptr->mRibbonModel->removeRow(r); - d_ptr->mRibbonModel->insertRow(r - 1, item); - } else if (1 == level) { - QStandardItem* paritem = item->parent(); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData( - d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = paritem->takeChild(r); - paritem->removeRow(r); - paritem->insertRow(r - 1, item); - } else if (2 == level) { - QStandardItem* pannelItem = item->parent(); - QStandardItem* categoryItem = pannelItem->parent(); - QAction* act = itemToAction(item); - if (!act) { - return; - } - QString key = d_ptr->mActionMgr->key(act); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData( - d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(pannelItem), key, d_ptr->mActionMgr, -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = pannelItem->takeChild(r); - pannelItem->removeRow(r); - pannelItem->insertRow(r - 1, item); - } - - // 保持焦点,方便连续操作 - setSelectItem(item); - onTreeViewResultClicked(item->index()); -} - -void SARibbonCustomizeWidget::onToolButtonDownClicked() -{ - QStandardItem* item = selectedItem(); - - if (item == nullptr) { - return; - } - int count = 0; - - if (item->parent()) { - count = item->parent()->rowCount(); - } else { - count = d_ptr->mRibbonModel->rowCount(); - } - if ((nullptr == item) || ((count - 1) == item->row())) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 移动category - SARibbonCustomizeData d = - SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), 1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = d_ptr->mRibbonModel->takeItem(item->row()); - d_ptr->mRibbonModel->removeRow(r); - d_ptr->mRibbonModel->insertRow(r + 1, item); - } else if (1 == level) { - QStandardItem* paritem = item->parent(); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData( - d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), 1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = paritem->takeChild(r); - paritem->removeRow(r); - paritem->insertRow(r + 1, item); - } else if (2 == level) { - QStandardItem* pannelItem = item->parent(); - QStandardItem* categoryItem = pannelItem->parent(); - QAction* act = itemToAction(item); - if (!act) { - return; - } - QString key = d_ptr->mActionMgr->key(act); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData( - d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(pannelItem), key, d_ptr->mActionMgr, -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = pannelItem->takeChild(r); - pannelItem->removeRow(r); - pannelItem->insertRow(r + 1, item); - } - - // 保持焦点,方便连续操作 - setSelectItem(item); - onTreeViewResultClicked(item->index()); -} - -void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item) -{ - if (item == nullptr) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - if (item->isCheckable()) { - QString objname = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = - SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, item->checkState() == Qt::Checked); - d_ptr->mCustomizeDatasCache.append(d); - } - } -} - -void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text) -{ - d_ptr->mAcionModel->search(text); -} - -void SARibbonCustomizeWidget::onPushButtonResetClicked() -{ - int btn = QMessageBox::warning(this, - tr("Warning"), - tr("Are you sure reset all customize setting?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - - if (btn == QMessageBox::Yes) { - clear(); - } -} - -/*** End of inlined file: SARibbonCustomizeWidget.cpp ***/ - -/*** Start of inlined file: SARibbonCustomizeDialog.cpp ***/ -#include -#include -#include -#include - -/** - * @brief The SARibbonCustomizeDialogUi class - */ -class SARibbonCustomizeDialogUi -{ -public: - SARibbonCustomizeWidget* customWidget; - QVBoxLayout* verticalLayoutMain; - QHBoxLayout* horizontalLayoutButtonGroup; - QPushButton* pushButtonCancel; - QPushButton* pushButtonOk; - QSpacerItem* spacerItemleft; - void setupUi(SARibbonMainWindow* ribbonWindow, QWidget* customizeDialog) - { - if (customizeDialog->objectName().isEmpty()) { - customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog")); - } - customizeDialog->resize(800, 600); - verticalLayoutMain = new QVBoxLayout(customizeDialog); - verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain")); - - customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog); - customWidget->setObjectName(QStringLiteral("customWidget")); - verticalLayoutMain->addWidget(customWidget); - - horizontalLayoutButtonGroup = new QHBoxLayout(); - horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup")); - - spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - horizontalLayoutButtonGroup->addItem(spacerItemleft); - - pushButtonCancel = new QPushButton(customizeDialog); - pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel")); - horizontalLayoutButtonGroup->addWidget(pushButtonCancel); - - pushButtonOk = new QPushButton(customizeDialog); - pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel")); - horizontalLayoutButtonGroup->addWidget(pushButtonOk); - verticalLayoutMain->addItem(horizontalLayoutButtonGroup); - retranslateUi(customizeDialog); - } - - void retranslateUi(QWidget* customizeDialog) - { - customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR)); - pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR)); - pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR)); - } -}; - -//////////////////////////////////////////////////////////////////// - -SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, QWidget* p, Qt::WindowFlags f) - : QDialog(p, f), ui(new SARibbonCustomizeDialogUi) -{ - ui->setupUi(ribbonWindow, this); - initConnection(); -} - -/** - * @brief 设置action管理器 - * - * 等同@ref SARibbonCustomizeWidget::setupActionsManager - * @param mgr - */ -SARibbonCustomizeDialog::~SARibbonCustomizeDialog() -{ -} -void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager* mgr) -{ - ui->customWidget->setupActionsManager(mgr); -} - -/** - * @brief //判断用户是否有要存储的内容,对应save动作 - * @return - */ -bool SARibbonCustomizeDialog::isApplied() const -{ - return ui->customWidget->isApplied(); -} - -/** - * @brief 判断用户是否有改动内容,对应apply动作 - * @return - */ -bool SARibbonCustomizeDialog::isCached() const -{ - return ui->customWidget->isCached(); -} - -void SARibbonCustomizeDialog::initConnection() -{ - connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept); - connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject); -} - -/** - * @brief 等同SARibbonCustomizeWidget::applys - * - * @ref SARibbonCustomizeWidget::applys - * @return - */ -bool SARibbonCustomizeDialog::applys() -{ - return ui->customWidget->applys(); -} - -/** - * @brief 清除所有动作 - * - * @ref SARibbonCustomizeWidget::clear - */ -void SARibbonCustomizeDialog::clear() -{ - ui->customWidget->clear(); -} - -/** - * @brief 转换为xml - * - * @ref SARibbonCustomizeWidget::toXml - * @param xml - * @return - */ -bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter* xml) const -{ - return (ui->customWidget->toXml(xml)); -} - -/** - * @brief 等同SARibbonCustomizeWidget::toXml - * @ref SARibbonCustomizeWidget::toXml - * @param xmlpath - * @return - */ -bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const -{ - return (ui->customWidget->toXml(xmlpath)); -} - -/** - * @brief 等同SARibbonCustomizeWidget::fromXml - * @param xml - */ -void SARibbonCustomizeDialog::fromXml(QXmlStreamReader* xml) -{ - ui->customWidget->fromXml(xml); -} - -/** - * @brief 等同SARibbonCustomizeWidget::fromXml - * @param xmlpath - */ -void SARibbonCustomizeDialog::fromXml(const QString& xmlpath) -{ - ui->customWidget->fromXml(xmlpath); -} - -/** - * @brief 返回SARibbonCustomizeWidget窗口指针 - * - * 通过SARibbonCustomizeWidget窗口可以操作更多的内容 - * - * @return SARibbonCustomizeWidget指针,参考@ref SARibbonCustomizeWidget - */ -SARibbonCustomizeWidget* SARibbonCustomizeDialog::customizeWidget() const -{ - return (ui->customWidget); -} - -/*** End of inlined file: SARibbonCustomizeDialog.cpp ***/ - -/*** Start of inlined file: SARibbonMainWindow.cpp ***/ -#include -#include -#include -#include -#include -#include - -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER -#include - -#else - -#endif - -/** - * @brief The SARibbonMainWindowPrivate class - */ -class SARibbonMainWindow::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonMainWindow) -public: - PrivateData(SARibbonMainWindow* p); - void installFrameless(SARibbonMainWindow* p); - -public: - SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; - SARibbonSystemButtonBar* mWindowButtonGroup { nullptr }; -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - QWK::WidgetWindowAgent* mFramelessHelper { nullptr }; -#else - SAFramelessHelper* mFramelessHelper { nullptr }; -#endif - SARibbonMainWindowEventFilter* mEventFilter { nullptr }; -}; - -SARibbonMainWindow::PrivateData::PrivateData(SARibbonMainWindow* p) : q_ptr(p) -{ -} - -void SARibbonMainWindow::PrivateData::installFrameless(SARibbonMainWindow* p) -{ -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - mFramelessHelper = new QWK::WidgetWindowAgent(p); - mFramelessHelper->setup(p); -#else - mFramelessHelper = new SAFramelessHelper(p); -#endif -} - -//=================================================== -// SARibbonMainWindow -//=================================================== -SARibbonMainWindow::SARibbonMainWindow(QWidget* parent, bool useRibbon, const Qt::WindowFlags flags) - : QMainWindow(parent, flags), d_ptr(new SARibbonMainWindow::PrivateData(this)) -{ - connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonMainWindow::onPrimaryScreenChanged); - if (useRibbon) { - d_ptr->installFrameless(this); - setRibbonBar(createRibbonBar()); - setRibbonTheme(ribbonTheme()); - } -} - -SARibbonMainWindow::~SARibbonMainWindow() -{ -} - -/** - * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr - * @return - */ -SARibbonBar* SARibbonMainWindow::ribbonBar() const -{ - return qobject_cast< SARibbonBar* >(menuWidget()); -} - -/** - * @brief 设置ribbonbar - * @param bar - */ -void SARibbonMainWindow::setRibbonBar(SARibbonBar* ribbon) -{ - QWidget* old = QMainWindow::menuWidget(); - if (old) { - // 如果之前已经设置了menubar,要把之前的删除 - old->deleteLater(); - } - QMainWindow::setMenuWidget(ribbon); - const int th = ribbon->titleBarHeight(); - // 设置window按钮 - if (nullptr == d_ptr->mWindowButtonGroup) { - d_ptr->mWindowButtonGroup = RibbonSubElementFactory->createWindowButtonGroup(this); - // SARibbonSystemButtonBar的eventfilter捕获mainwindow的事件 - // 通过eventerfilter来处理mainwindow的事件,避免用户错误的继承resizeEvent导致systembar的位置异常 - installEventFilter(d_ptr->mWindowButtonGroup); - } - SARibbonSystemButtonBar* sysBar = d_ptr->mWindowButtonGroup; - sysBar->setWindowStates(windowState()); - sysBar->setWindowTitleHeight(th); - sysBar->raise(); // 确保sysbar在最顶层,避免第二次设置ribbonbar的时候,被ribbonbar覆盖了sysbar - sysBar->show(); -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - auto helper = d_ptr->mFramelessHelper; - helper->setTitleBar(ribbon); - // 以下这些窗口,需要允许点击 - helper->setHitTestVisible(sysBar); // IMPORTANT! - helper->setHitTestVisible(ribbon->ribbonTabBar()); // IMPORTANT! - helper->setHitTestVisible(ribbon->rightButtonGroup()); // IMPORTANT! - helper->setHitTestVisible(ribbon->applicationButton()); // IMPORTANT! - helper->setHitTestVisible(ribbon->quickAccessBar()); // IMPORTANT! - helper->setHitTestVisible(ribbon->ribbonStackedWidget()); // IMPORTANT! -#if SARIBBON_ENABLE_SNAP_LAYOUT - if (sysBar->closeButton()) { - helper->setSystemButton(QWK::WindowAgentBase::Close, sysBar->closeButton()); - } - if (sysBar->minimizeButton()) { - helper->setSystemButton(QWK::WindowAgentBase::Minimize, sysBar->minimizeButton()); - } - if (sysBar->maximizeButton()) { - helper->setSystemButton(QWK::WindowAgentBase::Maximize, sysBar->maximizeButton()); - } -#endif -#else - // 捕获ribbonbar的事件 - ribbon->installEventFilter(this); - // 设置窗体的标题栏高度 - d_ptr->mFramelessHelper->setTitleHeight(th); - d_ptr->mFramelessHelper->setRubberBandOnResize(false); -#endif - if (!d_ptr->mEventFilter) { - d_ptr->mEventFilter = new SARibbonMainWindowEventFilter(this); - installEventFilter(d_ptr->mEventFilter); - } - // 最后要提升,否则新加入的会被覆盖 - if (d_ptr->mWindowButtonGroup) { - d_ptr->mWindowButtonGroup->raise(); - } -} - -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - -/** - * @brief 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 - * @param w - * @param visible - */ -void SARibbonMainWindow::setFramelessHitTestVisible(QWidget* w, bool visible) -{ - auto helper = d_ptr->mFramelessHelper; - helper->setHitTestVisible(const_cast< QWidget* >(w), visible); -} -#else - -SAFramelessHelper* SARibbonMainWindow::framelessHelper() -{ - return (d_ptr->mFramelessHelper); -} - -#endif - -bool SARibbonMainWindow::eventFilter(QObject* obj, QEvent* e) -{ -#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER -#else - // 这个过滤是为了把ribbonBar上的动作传递到mainwindow,再传递到frameless, - // 由于ribbonbar会遮挡掉frameless的区域,导致frameless无法捕获这些消息 - // 因此必须ribbonBar()->installEventFilter(this); - // 20240101发现installEventFilter后,SARibbonMainWindow没有执行这个回调 - if (obj == ribbonBar()) { - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - case QEvent::Leave: - case QEvent::HoverMove: - case QEvent::MouseButtonDblClick: - QApplication::sendEvent(this, e); - - default: - break; - } - } -#endif - return (QMainWindow::eventFilter(obj, e)); -} - -/** - * @brief 获取系统按钮组,可以在此基础上添加其他按钮 - * @return - */ -SARibbonSystemButtonBar* SARibbonMainWindow::windowButtonBar() const -{ - return d_ptr->mWindowButtonGroup; -} - -/** - * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 - */ -void SARibbonMainWindow::updateWindowFlag(Qt::WindowFlags flags) -{ - if (SARibbonSystemButtonBar* g = d_ptr->mWindowButtonGroup) { - g->updateWindowFlag(flags); - } -} - -/** - * @brief SARibbonMainWindow::setRibbonTheme - * - * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - * @code - * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); - * @endcode - * @param theme - */ -void SARibbonMainWindow::setRibbonTheme(SARibbonTheme theme) -{ - sa_set_ribbon_theme(this, theme); - d_ptr->mCurrentRibbonTheme = theme; - if (SARibbonBar* bar = ribbonBar()) { - auto theme = ribbonTheme(); - // 尺寸修正 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeOffice2016Blue: - case SARibbonTheme::RibbonThemeDark: - case SARibbonTheme::RibbonThemeDark2: { - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2013.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 0px; - //! margin-left: 5px; - //! margin-bottom: 0px; - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - tab->setTabMargin(QMargins(5, 0, 0, 0)); - } break; - case SARibbonTheme::RibbonThemeOffice2021Blue: { - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2021.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 5px; - //! margin-left: 5px; - //! margin-bottom: 0px; - tab->setTabMargin(QMargins(5, 0, 5, 0)); - } - default: - break; - } - // 上下文标签颜色设置,以及基线颜色设置 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeDark: - bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2016Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2021Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 - break; - default: - break; - } - // 基线颜色设置 - if (SARibbonTheme::RibbonThemeOffice2013 == theme) { - bar->setTabBarBaseLineColor(QColor(186, 201, 219)); - } else { - bar->setTabBarBaseLineColor(QColor()); - } - } -} - -SARibbonTheme SARibbonMainWindow::ribbonTheme() const -{ - return (d_ptr->mCurrentRibbonTheme); -} - -bool SARibbonMainWindow::isUseRibbon() const -{ - return (nullptr != ribbonBar()); -} - -/** - * @brief 创建ribbonbar的工厂函数 - * - * 用户如果重写了SARibbonBar,可以通过重新此虚函数返回自己的Ribbon实例 - * @return - */ -SARibbonBar* SARibbonMainWindow::createRibbonBar() -{ - SARibbonBar* bar = RibbonSubElementFactory->createRibbonBar(this); - bar->setContentsMargins(3, 0, 3, 0); - return bar; -} - -/** - * @brief 主屏幕切换触发的信号 - * @param screen - */ -void SARibbonMainWindow::onPrimaryScreenChanged(QScreen* screen) -{ - Q_UNUSED(screen); - // 主屏幕切换后,从新计算所有尺寸 - if (SARibbonBar* bar = ribbonBar()) { - qDebug() << "Primary Screen Changed"; - bar->updateRibbonGeometry(); - } -} - -void sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme) -{ - QFile file; - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - file.setFileName(":/theme/resource/theme-win7.qss"); - break; - case SARibbonTheme::RibbonThemeOffice2013: - file.setFileName(":/theme/resource/theme-office2013.qss"); - break; - case SARibbonTheme::RibbonThemeOffice2016Blue: - file.setFileName(":/theme/resource/theme-office2016-blue.qss"); - break; - case SARibbonTheme::RibbonThemeOffice2021Blue: - file.setFileName(":/theme/resource/theme-office2021-blue.qss"); - break; - case SARibbonTheme::RibbonThemeDark: - file.setFileName(":/theme/resource/theme-dark.qss"); - break; - case SARibbonTheme::RibbonThemeDark2: - file.setFileName(":/theme/resource/theme-dark2.qss"); - break; - default: - file.setFileName(":/theme/resource/theme-office2013.qss"); - break; - } - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - return; - } - // 有反馈用qstring接住文件内容,再设置进去才能生效(qt5.7版本) - QString qss = QString::fromUtf8(file.readAll()); - w->setStyleSheet(qss); -} - -//---------------------------------------------------- -// SARibbonMainWindowEventFilter -//---------------------------------------------------- -SARibbonMainWindowEventFilter::SARibbonMainWindowEventFilter(QObject* par) : QObject(par) -{ -} - -SARibbonMainWindowEventFilter::~SARibbonMainWindowEventFilter() -{ -} - -bool SARibbonMainWindowEventFilter::eventFilter(QObject* obj, QEvent* e) -{ - if (e) { - if (e->type() == QEvent::Resize) { - if (SARibbonMainWindow* m = qobject_cast< SARibbonMainWindow* >(obj)) { - if (SARibbonBar* ribbon = m->ribbonBar()) { - ribbon->setFixedWidth(m->size().width()); - } - } - } - } - return QObject::eventFilter(obj, e); -} - -/*** End of inlined file: SARibbonMainWindow.cpp ***/ - -/*** Start of inlined file: SARibbonWidget.cpp ***/ -#include -#include -#include -#include - -/** - * @brief The SARibbonWidget::PrivateData class - */ -class SARibbonWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonWidget) -public: - PrivateData(SARibbonWidget* p); - void installFrameless(SARibbonWidget* p); - -public: - SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; -}; - -SARibbonWidget::PrivateData::PrivateData(SARibbonWidget* p) : q_ptr(p) -{ -} - -//=================================================== -// SARibbonWidget -//=================================================== -SARibbonWidget::SARibbonWidget(QWidget* parent) : QWidget(parent), d_ptr(new SARibbonWidget::PrivateData(this)) -{ - // 直接创建SARibbonBar - QVBoxLayout* verticalLayout = new QVBoxLayout(this); - verticalLayout->setSpacing(1); - verticalLayout->setObjectName("verticalLayout"); - verticalLayout->setContentsMargins(0, 0, 0, 0); - SARibbonBar* ribbon = new SARibbonBar(this); - setRibbonBar(ribbon); - connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonWidget::onPrimaryScreenChanged); -} - -SARibbonWidget::~SARibbonWidget() -{ -} - -/** - * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr - * @return - */ -SARibbonBar* SARibbonWidget::ribbonBar() const -{ - QLayout* lay = layout(); - if (lay) { - return qobject_cast< SARibbonBar* >(lay->menuBar()); - } - return nullptr; -} - -/** - * @brief 设置ribbonbar - * @param bar - */ -void SARibbonWidget::setRibbonBar(SARibbonBar* bar) -{ - QLayout* lay = layout(); - if (lay) { - lay->setMenuBar(bar); - } -} - -/** - * @brief SARibbonMainWindow::setRibbonTheme - * - * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - * @code - * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); - * @endcode - * @param theme - */ -void SARibbonWidget::setRibbonTheme(SARibbonTheme theme) -{ - sa_set_ribbon_theme(this, theme); - d_ptr->mCurrentRibbonTheme = theme; - if (SARibbonBar* bar = ribbonBar()) { - auto theme = ribbonTheme(); - // 尺寸修正 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeOffice2016Blue: - case SARibbonTheme::RibbonThemeDark: - case SARibbonTheme::RibbonThemeDark2: { - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2013.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 0px; - //! margin-left: 5px; - //! margin-bottom: 0px; - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - tab->setTabMargin(QMargins(5, 0, 0, 0)); - } break; - case SARibbonTheme::RibbonThemeOffice2021Blue: { - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2021.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 5px; - //! margin-left: 5px; - //! margin-bottom: 0px; - tab->setTabMargin(QMargins(5, 0, 5, 0)); - } - default: - break; - } - // 上下文标签颜色设置,以及基线颜色设置 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeDark: - bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2016Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2021Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 - break; - default: - break; - } - // 基线颜色设置 - if (SARibbonTheme::RibbonThemeOffice2013 == theme) { - bar->setTabBarBaseLineColor(QColor(186, 201, 219)); - } else { - bar->setTabBarBaseLineColor(QColor()); - } - } -} - -SARibbonTheme SARibbonWidget::ribbonTheme() const -{ - return (d_ptr->mCurrentRibbonTheme); -} - -bool SARibbonWidget::isUseRibbon() const -{ - return (nullptr != ribbonBar()); -} - -/** - @brief 设置窗口 - - @param 窗口指针 - @note 窗口的所有权归SARibbonWidget管理 - @sa widget - @note 原来设置的窗口会被delete - */ -void SARibbonWidget::setWidget(QWidget* w) -{ - QWidget* oldwidget = takeWidget(); - if (oldwidget) { - oldwidget->hide(); - oldwidget->deleteLater(); - } - if (QLayout* lay = layout()) { - lay->addWidget(w); - } -} - -/** - @brief 获取设置的窗口 - - @return 如果没有,返回nullptr - */ -QWidget* SARibbonWidget::widget() const -{ - QLayout* lay = layout(); - if (lay) { - if (lay->count() == 0) { - return nullptr; - } - return lay->itemAt(0)->widget(); - } - return nullptr; -} - -QWidget* SARibbonWidget::takeWidget() -{ - QLayout* lay = layout(); - if (nullptr == lay) { - return nullptr; - } - if (lay->count() == 0) { - return nullptr; - } - auto item = lay->itemAt(0); - if (nullptr == item) { - return nullptr; - } - lay->removeItem(item); - auto w = item->widget(); - delete item; - if (!w) { - return nullptr; - } - return w; -} - -/** - * @brief 主屏幕切换触发的信号 - * @param screen - */ -void SARibbonWidget::onPrimaryScreenChanged(QScreen* screen) -{ - Q_UNUSED(screen); - // 主屏幕切换后,从新计算所有尺寸 - if (SARibbonBar* bar = ribbonBar()) { - qDebug() << "Primary Screen Changed"; - bar->updateRibbonGeometry(); - } -} - -/*** End of inlined file: SARibbonWidget.cpp ***/ - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pop_macro("_CRT_SECURE_NO_WARNINGS") -#endif +// 定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif + +// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +/*** Start of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ +// This file provides an extra level of indirection for the @remap in the template +#include "SARibbon.h" + +/*** End of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ + +// disable warnings about unsafe standard library calls +#ifdef _MSC_VER +#pragma push_macro("_CRT_SECURE_NO_WARNINGS") +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#pragma warning(push) +#pragma warning(disable : 4996) // deprecated POSIX names +#endif + +/*** Start of inlined file: SAColorMenu.cpp ***/ +#include +#include +#include +#include +#include +#include + +class SAColorMenu::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu) +public: + PrivateData(SAColorMenu* p); + QColor getColorByDialog(); + void recordCustomColor(const QColor& c); + QWidgetAction* addWidget(QWidget* w); + // 创建一个无颜色的icon + QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32)); + +public: + QLabel* mTitleLabel { nullptr }; ///< 主题颜色标题 + QWidgetAction* mTitleLabelAction { nullptr }; + SAColorPaletteGridWidget* mThemeColorsWidget { nullptr }; ///< 主题颜色 + QWidgetAction* mThemeColorPaletteAction { nullptr }; ///< ThemeColorsPalette对应的action + QAction* mCustomColorAction { nullptr }; ///< 自定义颜色action + SAColorGridWidget* mCustomColorsWidget { nullptr }; ///< 自定义颜色记录 + QWidgetAction* mCustomColorsWidgetAction { nullptr }; ///< 自定义颜色窗口对应的action + QAction* mNoneColorAction { nullptr }; ///< 无颜色action + QList< QColor > mCustomColors; + int mMaxCustomColorSize { 10 }; ///< 记录最多的自定义颜色数量 + QScopedPointer< QColorDialog > mColorDlg { nullptr }; ///< 颜色对话框 +}; + +SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p) +{ +} + +QColor SAColorMenu::PrivateData::getColorByDialog() +{ + if (nullptr == mColorDlg) { + mColorDlg.reset(new QColorDialog()); + } + if (QDialog::Accepted == mColorDlg->exec()) { + return mColorDlg->currentColor(); + } + return QColor(); +} + +void SAColorMenu::PrivateData::recordCustomColor(const QColor& c) +{ + if (mCustomColors.size() < mMaxCustomColorSize) { + mCustomColors.push_back(c); + } else { + // 超过数量,就左移动 + for (int i = 1; i < mCustomColors.size(); ++i) { + mCustomColors[ i - 1 ] = mCustomColors[ i ]; + } + mCustomColors.back() = c; + } +} + +QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w) +{ + QWidgetAction* wa = new QWidgetAction(q_ptr); + wa->setDefaultWidget(w); + q_ptr->addAction(wa); + return wa; +} + +QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize) +{ + QPixmap pixmap(baseSize); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1)); + return QIcon(pixmap); +} +//=================================================== +// SAColorMenu +//=================================================== + +SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::SAColorMenu(const QString& title, QWidget* parent) + : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::~SAColorMenu() +{ +} + +/** + * @brief 快速绑定ColorToolButton + * @param btn + */ +void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn) +{ + if (!btn) { + return; + } + if (btn->menu() != this) { + btn->setMenu(this); + } + connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor); +} + +/** + * @brief ThemeColorsPalette对应的action + * @return + */ +QWidgetAction* SAColorMenu::themeColorsPaletteAction() const +{ + return d_ptr->mThemeColorPaletteAction; +} + +/** + * @brief CustomColorsWidget对应的action + * @return + */ +QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const +{ + return d_ptr->mCustomColorsWidgetAction; +} + +/** + * @brief 自定义颜色action + * @return + */ +QAction* SAColorMenu::customColorAction() const +{ + return d_ptr->mCustomColorAction; +} + +/** + * @brief 获取ThemeColorsPalette + * @return + */ +SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const +{ + return d_ptr->mThemeColorsWidget; +} + +/** + * @brief 获取自定义颜色grid + * @return + */ +SAColorGridWidget* SAColorMenu::customColorsWidget() const +{ + return d_ptr->mCustomColorsWidget; +} + +/** + * @brief 建立没有颜色的action,可以选择无颜色 + * + * 无颜色选中会发射selectedColor(QColor()) + * @param on + */ +void SAColorMenu::enableNoneColorAction(bool on) +{ + // 无颜色默认是在自定义颜色的上方 + if (on) { + if (d_ptr->mNoneColorAction) { + if (actions().contains(d_ptr->mNoneColorAction)) { + // 已经包含了NoneColorAction,退出 + return; + } else { + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + QIcon ic = d_ptr->createNoneColorIcon(); + d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this); + connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered); + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + removeAction(d_ptr->mNoneColorAction); + // remove后暂时不删除action + } +} + +/** + * @brief 获取None Color Action + * + * @note 注意,enableNoneColorAction(true),之后才不是nullptr + * @return 如果没有建立NoneColorAction,会返回nullptr + */ +QAction* SAColorMenu::noneColorAction() const +{ + return d_ptr->mNoneColorAction; +} + +/** + * @brief 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + * @param c + */ +void SAColorMenu::emitSelectedColor(const QColor& c) +{ + Q_EMIT selectedColor(c); + hide(); +} + +void SAColorMenu::init(const QList< QColor >& themeCls) +{ + d_ptr->mTitleLabel = new QLabel(this); + d_ptr->mTitleLabel->setText(tr("Theme Colors")); + d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel); + + d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this); + d_ptr->mThemeColorsWidget->setColorCheckable(false); + d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget); + + d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this); // cn:自定义颜色 + addAction(d_ptr->mCustomColorAction); + + QSize clrSize = d_ptr->mThemeColorsWidget->colorIconSize(); + d_ptr->mCustomColorsWidget = new SAColorGridWidget(this); + d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height()); + d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight(); + d_ptr->mCustomColorsWidget->setColorIconSize(clrSize); + d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize); + d_ptr->mCustomColorsWidget->setColorCheckable(false); + d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget); + + connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered); + connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); + connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); +} + +/** + * @brief 自定义颜色 + * @param on + */ +void SAColorMenu::onCustomColorActionTriggered(bool on) +{ + Q_UNUSED(on); + QColor c = d_ptr->getColorByDialog(); + if (c.isValid()) { + d_ptr->recordCustomColor(c); + d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors); + updateGeometry(); + emitSelectedColor(c); + } +} + +/** + * @brief 无颜色 + * @param on + */ +void SAColorMenu::onNoneColorActionTriggered(bool on) +{ + Q_UNUSED(on); + emitSelectedColor(QColor()); +} + +/*** End of inlined file: SAColorMenu.cpp ***/ + +/*** Start of inlined file: SAColorGridWidget.cpp ***/ +#include +#include +#include + +class SAColorGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget) +public: + PrivateData(SAColorGridWidget* p); + // 获取ColorToolButton + SAColorToolButton* getColorToolButtonAt(int index); + SAColorToolButton* getColorToolButtonAt(int r, int c); + SAColorToolButton* getCheckedButton() const; + void updateGridColor(bool isRemoveSpacer = false); + void updateGridColorSize(); + void updateGridColorCheckable(); + void iterationColorBtns(SAColorGridWidget::FunColorBtn fn); + void removeAt(int r, int c); + void setColorAt(const QColor& clr, int r, int c); + bool isSpacer(int r, int c) const; + +public: + QList< QColor > mColors; + QGridLayout* mGridLayout { nullptr }; + QButtonGroup* mButtonGroup { nullptr }; + QSize mIconSize { 16, 16 }; + int mColumnCount { 8 }; ///< 列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + bool mColorCheckable; ///< 设置颜色是否是checkable + bool mHorizontalSpacerToRight { false }; ///< 最右边是否有弹簧 +}; + +SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p) +{ + mGridLayout = new QGridLayout(p); + p->setLayout(mGridLayout); + mGridLayout->setSpacing(0); + mGridLayout->setContentsMargins(1, 1, 1, 1); + mButtonGroup = new QButtonGroup(p); + mButtonGroup->setExclusive(true); + p->setMinimumHeight(mIconSize.height()); + p->setMinimumWidth(mIconSize.width()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index) +{ + QLayoutItem* item = mGridLayout->itemAt(index); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const +{ + return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton()); +} + +/** + * @brief 根据mColors更新布局 + */ +void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer) +{ + int row = 1; + int col = mColumnCount; + if (col <= 0) { + col = mColors.size(); + } else { + row = std::ceil(mColors.size() / (float)col); + } + int index = 0; + for (int r = 0; r < row; ++r) { + for (int c = 0; c < col; ++c) { + if (index < mColors.size()) { + setColorAt(mColors[ index ], r, c); + ++index; + } else { + removeAt(r, c); + } + } + } + // 清除多余单元格 + int nowGridRow = mGridLayout->rowCount(); + int nowGridCol = mGridLayout->columnCount(); + if (nowGridRow > row) { + // 多余的清除 + for (int r = row; r < nowGridRow; ++r) { + for (int c = 0; c < nowGridCol; ++c) { + removeAt(r, c); + } + } + } + if (nowGridCol > col) { + // 多余的列清除 + for (int r = 0; r < row; ++r) { + for (int c = col; c < nowGridCol; ++c) { + if (isRemoveSpacer) { + removeAt(r, c); + } else { + if (!isSpacer(r, c)) { + removeAt(r, c); + } + } + } + } + } +} + +/** + * @brief 更新colorsize + */ +void SAColorGridWidget::PrivateData::updateGridColorSize() +{ + QSize s = mIconSize; + iterationColorBtns([ s ](SAColorToolButton* btn) { + if (btn) { + btn->setIconSize(s); + } + }); +} + +void SAColorGridWidget::PrivateData::updateGridColorCheckable() +{ + bool v = mColorCheckable; + iterationColorBtns([ v ](SAColorToolButton* btn) { + if (btn) { + btn->setCheckable(v); + } + }); +} + +/** + * @brief 遍历所有的button + * @param fn + */ +void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn) +{ + int cnt = mGridLayout->count(); + for (int i = 0; i < cnt; ++i) { + SAColorToolButton* tl = getColorToolButtonAt(i); + fn(tl); + } +} + +/** + * @brief 删除网格窗口 + * @param r + * @param c + */ +void SAColorGridWidget::PrivateData::removeAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + QWidget* w = item->widget(); + mGridLayout->removeItem(item); + delete item; + if (w) { + w->deleteLater(); + } + } +} + +void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget()); + if (tl) { + tl->setColor(clr); + } + } else { + SAColorToolButton* tl = new SAColorToolButton(SAColorToolButton::NoColorMenu, q_ptr); + tl->setToolButtonStyle(Qt::ToolButtonIconOnly); + tl->setIconSize(mIconSize); + tl->setMargins(QMargins(4, 4, 4, 4)); + tl->setColor(clr); + tl->setCheckable(mColorCheckable); + tl->setAutoRaise(true); + mButtonGroup->addButton(tl, r + c); + mGridLayout->addWidget(tl, r, c); + } +} + +bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) { + return true; + } + } + return false; +} + +//============================================================== +// SAColorGridWidget +//============================================================== + +SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this)) +{ + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked), + this, + &SAColorGridWidget::onButtonClicked); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed), + this, + &SAColorGridWidget::onButtonPressed); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased), + this, + &SAColorGridWidget::onButtonReleased); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled), + this, + &SAColorGridWidget::onButtonToggled); +} + +SAColorGridWidget::~SAColorGridWidget() +{ +} + +/** + * @brief 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + * @param c + */ +void SAColorGridWidget::setColumnCount(int c) +{ + d_ptr->mColumnCount = c; + d_ptr->updateGridColor(true); + if (d_ptr->mHorizontalSpacerToRight) { + setHorizontalSpacerToRight(); + } + updateGeometry(); +} + +/** + * @brief 设置颜色列表 + * @param c + */ +void SAColorGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mColors = cls; + d_ptr->updateGridColor(); + updateGeometry(); +} + +/** + * @brief 获取颜色列表 + * @return + */ +QList< QColor > SAColorGridWidget::getColorList() const +{ + return d_ptr->mColors; +} + +/** + * @brief 获取间隔 + * @return + */ +int SAColorGridWidget::spacing() const +{ + return d_ptr->mGridLayout->spacing(); +} + +/** + * @brief 设置间隔 + * @param v + */ +void SAColorGridWidget::setSpacing(int v) +{ + d_ptr->mGridLayout->setSpacing(v); +} + +/** + * @brief 获取颜色的数量 + * @return + */ +int SAColorGridWidget::colorCount() const +{ + return d_ptr->mColors.size(); +} + +/** + * @brief 设置图标 size + * @return + */ +void SAColorGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mIconSize = s; + setMinimumHeight(s.height()); + setMinimumWidth(s.width()); + d_ptr->updateGridColorSize(); +} + +/** + * @brief 获取图标 size + * @return + */ +QSize SAColorGridWidget::colorIconSize() const +{ + return d_ptr->mIconSize; +} + +/** + * @brief 设置颜色是否是checkable + * + * checkable的颜色按钮是可checked的 + * @param on + */ +void SAColorGridWidget::setColorCheckable(bool on) +{ + d_ptr->mColorCheckable = on; + d_ptr->updateGridColorCheckable(); +} + +/** + * @brief 颜色是否是checkable + * @return + */ +bool SAColorGridWidget::isColorCheckable() const +{ + return d_ptr->mColorCheckable; +} + +/** + * @brief 获取当前选中的颜色 + * @return + */ +QColor SAColorGridWidget::currentCheckedColor() const +{ + QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton(); + if (nullptr == btn) { + return QColor(); + } + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (nullptr == t) { + return QColor(); + } + return t->color(); +} + +/** + * @brief 获取index对应的colorbutton + * @param index + * @return 如果没有返回nullptr + */ +SAColorToolButton* SAColorGridWidget::colorButton(int index) const +{ + return d_ptr->getColorToolButtonAt(index); +} + +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @param v + */ +void SAColorGridWidget::setVerticalSpacing(int v) +{ + d_ptr->mGridLayout->setVerticalSpacing(v); +} +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @return + */ +int SAColorGridWidget::verticalSpacing() const +{ + return d_ptr->mGridLayout->verticalSpacing(); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @param v + */ +void SAColorGridWidget::setHorizontalSpacing(int v) +{ + d_ptr->mGridLayout->setHorizontalSpacing(v); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @return + */ +int SAColorGridWidget::horizontalSpacing() const +{ + return d_ptr->mGridLayout->horizontalSpacing(); +} + +/** + * @brief 清除选中状态,这时没有颜色是选中的 + */ +void SAColorGridWidget::clearCheckedState() +{ + if (d_ptr->mButtonGroup->exclusive()) { + SAColorToolButton* btn = d_ptr->getCheckedButton(); + if (btn) { + d_ptr->mButtonGroup->setExclusive(false); + btn->setChecked(false); + d_ptr->mButtonGroup->setExclusive(true); + } + } else { + d_ptr->iterationColorBtns([](SAColorToolButton* btn) { + if (btn->isChecked()) { + btn->setChecked(false); + } + }); + } +} + +void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn) +{ + d_ptr->iterationColorBtns(fn); +} + +void SAColorGridWidget::setRowMinimumHeight(int row, int minSize) +{ + d_ptr->mGridLayout->setRowMinimumHeight(row, minSize); +} + +void SAColorGridWidget::setHorizontalSpacerToRight(bool on) +{ + if (on) { + QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1); + } else { + d_ptr->removeAt(0, d_ptr->mColumnCount); + } + d_ptr->mHorizontalSpacerToRight = on; +} + +void SAColorGridWidget::onButtonClicked(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorClicked(t->color()); + } +} + +void SAColorGridWidget::onButtonPressed(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorPressed(t->color()); + } +} + +void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorToggled(t->color(), on); + } +} + +QSize SAColorGridWidget::sizeHint() const +{ + return d_ptr->mGridLayout->sizeHint(); + // int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing(); + // int h = d_ptr->mIconSize.height(); + // if (d_ptr->mColumnCount > 0) { + // h *= d_ptr->mColumnCount; + // if (!d_ptr->mColors.empty()) { + // int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount)); + // if (r > 0) { + // w *= r; + // } + // } + // } + // return QSize(w, h); +} + +void SAColorGridWidget::onButtonReleased(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorReleased(t->color()); + } +} + +namespace SA +{ + +QList< QColor > getStandardColorList() +{ + static QList< QColor > s_standardColorList({ QColor(192, 0, 0), + QColor(255, 0, 0), + QColor(255, 192, 0), + QColor(255, 255, 0), + QColor(146, 208, 80), + QColor(0, 176, 80), + QColor(0, 176, 240), + QColor(0, 112, 192), + QColor(0, 32, 96), + QColor(112, 48, 160) }); + return s_standardColorList; +} +} + +/*** End of inlined file: SAColorGridWidget.cpp ***/ + +/*** Start of inlined file: SAColorPaletteGridWidget.cpp ***/ +// Qt +#include +#include +#include +#include +// SA + +class SAColorPaletteGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget) +public: + PrivateData(SAColorPaletteGridWidget* p); + // 生成color palette + QList< QColor > makeColorPalette(const QList< QColor >& clrList) const; + +public: + QList< int > mFactor { 180, 160, 140, 75, 50 }; ///< palette的比例因子,将调用QColor的lighter函数执行 + QVBoxLayout* mLayout { nullptr }; ///< 垂直布局 + SAColorGridWidget* mMainColorList { nullptr }; ///< 这个用于显示标准颜色 + SAColorGridWidget* mPaletteColorGrid { nullptr }; ///< 这个用于生成3行亮色,2行暗色的palette +}; + +SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p) +{ + mLayout = new QVBoxLayout(p); + p->setLayout(mLayout); + mMainColorList = new SAColorGridWidget(p); + mPaletteColorGrid = new SAColorGridWidget(p); + mLayout->addWidget(mMainColorList); + mLayout->addWidget(mPaletteColorGrid); + mLayout->setContentsMargins(1, 1, 1, 1); + mLayout->setSpacing(8); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + mMainColorList->setSizePolicy(sizePolicy); + mPaletteColorGrid->setSizePolicy(sizePolicy); + mMainColorList->setColumnCount(0); + mPaletteColorGrid->setVerticalSpacing(0); +} + +QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const +{ + QList< QColor > res; + for (int f : qAsConst(mFactor)) { + for (const QColor& c : qAsConst(clrList)) { + res.append(c.lighter(f)); + } + } + return res; +} +//============================================================== +// name +//============================================================== + +/** + * @brief 使用默认的标准颜色生成一个color palette + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(SA::getStandardColorList()); +} + +/** + * @brief 根据指定的颜色生成一个color palette + * @param cls + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par) + : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(cls); +} + +SAColorPaletteGridWidget::~SAColorPaletteGridWidget() +{ +} +void SAColorPaletteGridWidget::init() +{ + connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked); + connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(sizePolicy); + setColorIconSize(QSize(10, 10)); +} + +/** + * @brief 设置颜色系列,颜色系列的个数决定了主色卡的数量,palette色卡会根据factor进行生成 + * @param cls + */ +void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mMainColorList->setColumnCount(0); + d_ptr->mMainColorList->setColorList(cls); + d_ptr->mPaletteColorGrid->setColumnCount(cls.size()); + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls)); +} + +/** + * @brief 获取颜色系列 + * @return + */ +QList< QColor > SAColorPaletteGridWidget::colorList() const +{ + return d_ptr->mMainColorList->getColorList(); +} + +/** + * @brief 设置颜色深浅比例factor,factor决定了palette的行数,factor有5个,就有5行 + * + * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行,每个系数会对标准颜色执行QColor::lighter操作 + * @param factor + */ +void SAColorPaletteGridWidget::setFactor(const QList< int >& factor) +{ + d_ptr->mFactor = factor; + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList())); +} + +/** + * @brief 获取色卡 + * @return + */ +QList< int > SAColorPaletteGridWidget::factor() const +{ + return d_ptr->mFactor; +} + +/** + * @brief 设置颜色块的尺寸,默认为10*10 + * @param s + */ +void SAColorPaletteGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mMainColorList->setColorIconSize(s); + d_ptr->mPaletteColorGrid->setColorIconSize(s); +} + +QSize SAColorPaletteGridWidget::colorIconSize() const +{ + return d_ptr->mMainColorList->colorIconSize(); +} + +/** + * @brief 设置颜色块是否能被checked + * @param on + */ +void SAColorPaletteGridWidget::setColorCheckable(bool on) +{ + d_ptr->mMainColorList->setColorCheckable(on); + d_ptr->mPaletteColorGrid->setColorCheckable(on); +} + +bool SAColorPaletteGridWidget::isColorCheckable() const +{ + return d_ptr->mMainColorList->isColorCheckable(); +} + +void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c) +{ + d_ptr->mPaletteColorGrid->clearCheckedState(); + Q_EMIT colorClicked(c); +} + +void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c) +{ + d_ptr->mMainColorList->clearCheckedState(); + Q_EMIT colorClicked(c); +} + +/*** End of inlined file: SAColorPaletteGridWidget.cpp ***/ + +/*** Start of inlined file: SAColorToolButton.cpp ***/ +#include +#include +#include +#include +#include +#include +#include + +#define SAColorToolButton_DEBUG_PRINT 0 +class SAColorToolButton::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton) +public: + PrivateData(SAColorToolButton* p); + void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect); + QRect getButtonRect(const QStyleOptionToolButton& opt); + QRect getIndicatorRect(const QStyleOptionToolButton& opt); + QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt); + QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt); + void removeMenu(); + +public: + QColor mColor { Qt::white }; + int mSpacing { 2 }; ///< 间隔 + QMargins mMargins { 3, 3, 3, 3 }; ///< box + ColorToolButtonStyle mColorButtonStyle { WithColorMenu }; ///< 样式 + static int s_indicatorArrorWidth; ///< 菜单宽度 +}; + +int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8; + +SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p) +{ +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + // 确定文本区域 + textRect = QRect(); + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + colorRect = buttonRect; + iconRect = QRect(); + } else { + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + if (opt.iconSize.height() > 0) { + tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height()); // 等比例 + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + int x = (buttonRect.width() - tmpSize.width()) / 2; + iconRect = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + } +} + +/** + * @brief 纯文本情况,左边有5像素显示颜色 + * @param opt + * @param iconRect + * @param textRect + * @param colorRect 左边有5像素显示颜色 + */ +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + iconRect = QRect(); + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + colorSize = buttonRect.size().boundedTo(colorSize); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0); +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + // 说明没有icon + // 这时所有都是color + // iconRect=Null + colorSize = buttonRect.size().boundedTo(colorSize); + iconRect = QRect(); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0); + } else { // 有图标 + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + tmpSize.setWidth(tmpSize.height()); // 对于异形也设置为正方行 + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + iconRect = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0); + } +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + // 获取字体高度 + int textHeight = opt.fontMetrics.height(); + tmpSize = buttonRect.size().boundedTo(tmpSize); + if (opt.icon.isNull()) { + int totalHeight = textHeight + opt.iconSize.height() + mSpacing; + if (totalHeight < buttonRect.height()) { + // 足够高 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + } else { + // 空间不足 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + iconRect = QRect(); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + + } else { + // 有图标 + int colorHeight = tmpSize.height() / 4; + int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing; + if (totalHeight < buttonRect.height()) { + // 高度空间足够 + // 先布置icon + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + + } else { + // 空间不足 + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + } +} + +QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect) +{ + if (opt.icon.isNull()) { + return (QPixmap()); + } + // 有图标 + QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state)); + return (opt.icon.pixmap(iconRect.size(), mode, state)); +} + +/** + * @brief 获取按钮的矩形区域 + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt) +{ + QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr); + return btnRect.marginsRemoved(mMargins); +} + +/** + * @brief 获取ToolButtonMenu的区域 + * @note 注意,如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数,返回的是QRect() + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt) +{ + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr); + } + return QRect(); +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + return bflags; +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State mflags = opt.state & ~QStyle::State_Sunken; + if (mflags & QStyle::State_AutoRaise) { + if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) { + mflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + mflags |= QStyle::State_Sunken; + } + return mflags; +} + +void SAColorToolButton::PrivateData::removeMenu() +{ + QMenu* m = q_ptr->menu(); + if (m) { + q_ptr->setMenu(nullptr); + m->deleteLater(); + } +} + +//============================================================== +// SAColorToolButton +//============================================================== + +SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this)) +{ + setColorToolButtonStyle(WithColorMenu); + connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); +} + +SAColorToolButton::SAColorToolButton(ColorToolButtonStyle style, QWidget* parent) + : QToolButton(parent), d_ptr(new PrivateData(this)) +{ + setColorToolButtonStyle(style); + connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); +} + +SAColorToolButton::~SAColorToolButton() +{ +} + +/** + * @brief 获取颜色 + * @return + */ +QColor SAColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置Margins + * @param mg + */ +void SAColorToolButton::setMargins(const QMargins& mg) +{ + d_ptr->mMargins = mg; + repaint(); +} + +QMargins SAColorToolButton::margins() const +{ + return d_ptr->mMargins; +} + +/** + * @brief 绘制无颜色表示 + * @param p + * @param colorRect 绘制的区域 + */ +void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect) +{ + p->save(); + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + p->setPen(pen); + p->setRenderHint(QPainter::SmoothPixmapTransform, true); + p->setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + p->setPen(pen); + p->drawRect(colorRect); + p->restore(); +} + +/** + * @brief 设置颜色按钮的样式 + * @param s + */ +void SAColorToolButton::setColorToolButtonStyle(ColorToolButtonStyle s) +{ + if (NoColorMenu == s) { + d_ptr->removeMenu(); + } else { + createColorMenu(); + } + d_ptr->mColorButtonStyle = s; +} + +/** + * @brief 颜色按钮的样式 + * @return + */ +SAColorToolButton::ColorToolButtonStyle SAColorToolButton::colorToolButtonStyle() const +{ + return d_ptr->mColorButtonStyle; +} + +/** + * @brief 获取colorMenu + * + * @note 注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr + * @return SAColorMenu指针,如果没有,返回nullptr + */ +SAColorMenu* SAColorToolButton::colorMenu() const +{ + return qobject_cast< SAColorMenu* >(menu()); +} + +/** + * @brief 建立标准的颜色菜单 + * @return + */ +SAColorMenu* SAColorToolButton::createColorMenu() +{ + setPopupMode(QToolButton::InstantPopup); + SAColorMenu* m = new SAColorMenu(this); + m->enableNoneColorAction(true); + QAction* customColor = m->customColorAction(); + if (customColor) { + customColor->setIcon(QIcon(":/image/resource/define-color.svg")); + } + connect(m, &SAColorMenu::selectedColor, this, &SAColorToolButton::setColor); + setMenu(m); + return m; +} + +/** + * @brief 设置颜色 + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SAColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + repaint(); + Q_EMIT colorChanged(c); + } +} + +/** + * @brief 计算各个基本组件的位置 + * @param opt + * @param iconRect 图标区域 + * @param textRect 文本区域 + * @param colorRect 颜色区域 + */ +void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonTextOnly: + d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextBesideIcon: + d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextUnderIcon: + d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonFollowStyle: + case Qt::ToolButtonIconOnly: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + default: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + } +} + +void SAColorToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QStylePainter p(this); + + QStyleOptionToolButton opt; + + initStyleOption(&opt); + QRect iconRect, colorRect, textRect; + calcRect(opt, iconRect, textRect, colorRect); + paintButton(&p, opt); + // 绘制图标 + paintIcon(&p, iconRect, opt); + // 绘制文字 + paintText(&p, textRect, opt); + // 绘制颜色 + paintColor(&p, colorRect, d_ptr->mColor, opt); +} + +void SAColorToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); +} + +/** + * @brief sizeHint重载 + * @return + */ +QSize SAColorToolButton::sizeHint() const +{ + ensurePolished(); + QStyleOptionToolButton opt; + initStyleOption(&opt); + int w = 0, h = 0; + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) { + // 和文本无关 + w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } + opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); + } + //! Qt6.4 取消了QApplication::globalStrut + return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2)); +} + +void SAColorToolButton::onButtonClicked(bool checked) +{ + Q_EMIT colorClicked(color(), checked); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt) +{ + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this); + } + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + QStyleOption tool = opt; + tool.state = d_ptr->getButtonMenuStyleState(opt); + tool.rect = d_ptr->getIndicatorRect(opt); + // if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { + // style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this); + // } + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this); + } + + // 绘制focus + if (opt.state & QStyle::State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(opt); + fr.rect.adjust(3, 3, -3, -3); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0); + } + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this); + } +} + +/** + * @brief 绘制icon + * @param p + * @param iconRect + * @param opt icon信息从QStyleOptionToolButton获取 + */ +void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt) +{ + if (!iconRect.isNull()) { + QPixmap pm = d_ptr->createIconPixmap(opt, iconRect); + style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm); + } +} + +/** + * @brief 绘制文本 + * @param p + * @param textRect + * @param opt + */ +void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt) +{ + if (opt.text.isEmpty()) { + return; + } + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) { + return; + } + p->save(); + p->setFont(opt.font); + int alignment = Qt::TextShowMnemonic; + // 快捷键的下划线 + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + alignment |= Qt::AlignHCenter | Qt::AlignVCenter; + style()->drawItemText(p, + QStyle::visualRect(opt.direction, opt.rect, textRect), + alignment, + opt.palette, + opt.state & QStyle::State_Enabled, + opt.text, + QPalette::ButtonText); + p->restore(); +} + +/** + * @brief 绘制color + * @param p + * @param iconRect + * @param opt + */ +void SAColorToolButton::paintColor(QStylePainter* p, + const QRect& colorRect, + const QColor& color, + const QStyleOptionToolButton& opt) +{ + Q_UNUSED(opt); + if (colorRect.isNull()) { + return; + } + // 绘制颜色 + if (color.isValid()) { + p->fillRect(colorRect, color); + } else { + paintNoneColor(p, colorRect); + } +} + +/*** End of inlined file: SAColorToolButton.cpp ***/ + +// sa ribbon + +/*** Start of inlined file: SAFramelessHelper.cpp ***/ +#include +#include +#include +#include +#include +#include + +class SAPrivateFramelessWidgetData; + +/***** + * FramelessHelperPrivate + * 存储界面对应的数据集合,以及是否可移动、可缩放属性 + *****/ +class SAFramelessHelper::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SAFramelessHelper) + friend class SAPrivateFramelessWidgetData; + +public: + PrivateData(SAFramelessHelper* p); + QHash< QWidget*, SAPrivateFramelessWidgetData* > m_widgetDataHash; + bool m_bWidgetMovable { true }; + bool m_bWidgetResizable { true }; + bool m_bRubberBandOnResize { true }; + bool m_bRubberBandOnMove { true }; +}; + +SAFramelessHelper::PrivateData::PrivateData(SAFramelessHelper* p) : q_ptr(p) +{ +} + +/***** + * CursorPosCalculator + * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 + *****/ +class SAPrivateFramelessCursorPosCalculator +{ +public: + explicit SAPrivateFramelessCursorPosCalculator(); + void reset(); + void recalculate(const QPoint& globalMousePos, const QRect& frameRect); + +public: + bool mIsOnEdges { true }; + bool mIsOnLeftEdge { true }; + bool mIsOnRightEdge { true }; + bool mIsOnTopEdge { true }; + bool mIsOnBottomEdge { true }; + bool mIsOnTopLeftEdge { true }; + bool mIsOnBottomLeftEdge { true }; + bool mIsOnTopRightEdge { true }; + bool mIsOnBottomRightEdge { true }; + + static int s_borderWidth; + static int s_titleHeight; +}; + +int SAPrivateFramelessCursorPosCalculator::s_borderWidth = 5; +int SAPrivateFramelessCursorPosCalculator::s_titleHeight = 30; + +/***** CursorPosCalculator *****/ +SAPrivateFramelessCursorPosCalculator::SAPrivateFramelessCursorPosCalculator() +{ + reset(); +} + +void SAPrivateFramelessCursorPosCalculator::reset() +{ + mIsOnEdges = false; + mIsOnLeftEdge = false; + mIsOnRightEdge = false; + mIsOnTopEdge = false; + mIsOnBottomEdge = false; + mIsOnTopLeftEdge = false; + mIsOnBottomLeftEdge = false; + mIsOnTopRightEdge = false; + mIsOnBottomRightEdge = false; +} + +void SAPrivateFramelessCursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect) +{ + int globalMouseX = gMousePos.x(); + int globalMouseY = gMousePos.y(); + + int frameX = frameRect.x(); + int frameY = frameRect.y(); + + int frameWidth = frameRect.width(); + int frameHeight = frameRect.height(); + + mIsOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + s_borderWidth); + + mIsOnRightEdge = (globalMouseX >= frameX + frameWidth - s_borderWidth && globalMouseX <= frameX + frameWidth); + + mIsOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + s_borderWidth); + + mIsOnBottomEdge = (globalMouseY >= frameY + frameHeight - s_borderWidth && globalMouseY <= frameY + frameHeight); + + mIsOnTopLeftEdge = mIsOnTopEdge && mIsOnLeftEdge; + mIsOnBottomLeftEdge = mIsOnBottomEdge && mIsOnLeftEdge; + mIsOnTopRightEdge = mIsOnTopEdge && mIsOnRightEdge; + mIsOnBottomRightEdge = mIsOnBottomEdge && mIsOnRightEdge; + + mIsOnEdges = mIsOnLeftEdge || mIsOnRightEdge || mIsOnTopEdge || mIsOnBottomEdge; +} + +/***** + * WidgetData + * 更新鼠标样式、移动窗体、缩放窗体 + *****/ +class SAPrivateFramelessWidgetData +{ +public: + explicit SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget); + ~SAPrivateFramelessWidgetData(); + QWidget* widget(); + + // 处理鼠标事件-划过、按下、释放、移动 + bool handleWidgetEvent(QEvent* event); + + // 更新橡皮筋状态 + void updateRubberBandStatus(); + +private: + // 更新鼠标样式 + void updateCursorShape(const QPoint& gMousePos); + + // 重置窗体大小 + void resizeWidget(const QPoint& gMousePos); + + // 移动窗体 + void moveWidget(const QPoint& gMousePos); + + // 处理鼠标按下 + bool handleMousePressEvent(QMouseEvent* event); + + // 处理鼠标释放 + bool handleMouseReleaseEvent(QMouseEvent* event); + + // 处理鼠标移动 + bool handleMouseMoveEvent(QMouseEvent* event); + + // 处理鼠标离开 + bool handleLeaveEvent(QEvent* event); + + // 处理鼠标进入 + bool handleHoverMoveEvent(QHoverEvent* event); + + // 处理鼠标双击事件 + bool handleDoubleClickedMouseEvent(QMouseEvent* event); + +private: + SAFramelessHelper::PrivateData* d; + QRubberBand* m_pRubberBand; + QWidget* m_pWidget; + QPoint m_ptDragPos; + SAPrivateFramelessCursorPosCalculator m_pressedMousePos; + SAPrivateFramelessCursorPosCalculator m_moveMousePos; + bool m_bLeftButtonPressed; + bool m_bCursorShapeChanged; + bool m_bLeftButtonTitlePressed; + Qt::WindowFlags m_windowFlags; +}; + +/***** WidgetData *****/ +SAPrivateFramelessWidgetData::SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget) +{ + d = pd; + m_pWidget = pTopLevelWidget; + m_bLeftButtonPressed = false; + m_bCursorShapeChanged = false; + m_bLeftButtonTitlePressed = false; + m_pRubberBand = NULL; + + m_windowFlags = m_pWidget->windowFlags(); + m_pWidget->setMouseTracking(true); + m_pWidget->setAttribute(Qt::WA_Hover, true); + + updateRubberBandStatus(); +} + +SAPrivateFramelessWidgetData::~SAPrivateFramelessWidgetData() +{ + m_pWidget->setMouseTracking(false); + m_pWidget->setWindowFlags(m_windowFlags); + m_pWidget->setAttribute(Qt::WA_Hover, false); + + delete m_pRubberBand; + m_pRubberBand = NULL; +} + +QWidget* SAPrivateFramelessWidgetData::widget() +{ + return (m_pWidget); +} + +bool SAPrivateFramelessWidgetData::handleWidgetEvent(QEvent* event) +{ + switch (event->type()) { + case QEvent::MouseButtonPress: + return (handleMousePressEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::MouseButtonRelease: + return (handleMouseReleaseEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::MouseMove: + return (handleMouseMoveEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::Leave: + return (handleLeaveEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::HoverMove: + return (handleHoverMoveEvent(static_cast< QHoverEvent* >(event))); + + case QEvent::MouseButtonDblClick: + return (handleDoubleClickedMouseEvent(static_cast< QMouseEvent* >(event))); + + default: + break; + } + return (false); +} + +void SAPrivateFramelessWidgetData::updateRubberBandStatus() +{ + if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) { + if (NULL == m_pRubberBand) { + m_pRubberBand = new QRubberBand(QRubberBand::Rectangle); + } + } else { + delete m_pRubberBand; + m_pRubberBand = NULL; + } +} + +void SAPrivateFramelessWidgetData::updateCursorShape(const QPoint& gMousePos) +{ + if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) { + if (m_bCursorShapeChanged) { + m_pWidget->unsetCursor(); + } + return; + } + + m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry()); + + if (m_moveMousePos.mIsOnTopLeftEdge || m_moveMousePos.mIsOnBottomRightEdge) { + m_pWidget->setCursor(Qt::SizeFDiagCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.mIsOnTopRightEdge || m_moveMousePos.mIsOnBottomLeftEdge) { + m_pWidget->setCursor(Qt::SizeBDiagCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.mIsOnLeftEdge || m_moveMousePos.mIsOnRightEdge) { + m_pWidget->setCursor(Qt::SizeHorCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.mIsOnTopEdge || m_moveMousePos.mIsOnBottomEdge) { + m_pWidget->setCursor(Qt::SizeVerCursor); + m_bCursorShapeChanged = true; + } else { + if (m_bCursorShapeChanged) { + m_pWidget->unsetCursor(); + m_bCursorShapeChanged = false; + } + } +} + +void SAPrivateFramelessWidgetData::resizeWidget(const QPoint& gMousePos) +{ + QRect origRect; + + if (d->m_bRubberBandOnResize) { + origRect = m_pRubberBand->frameGeometry(); + } else { + origRect = m_pWidget->frameGeometry(); + } + + int left = origRect.left(); + int top = origRect.top(); + int right = origRect.right(); + int bottom = origRect.bottom(); + + origRect.getCoords(&left, &top, &right, &bottom); + + int minWidth = m_pWidget->minimumWidth(); + int minHeight = m_pWidget->minimumHeight(); + + if (m_pressedMousePos.mIsOnTopLeftEdge) { + left = gMousePos.x(); + top = gMousePos.y(); + } else if (m_pressedMousePos.mIsOnBottomLeftEdge) { + left = gMousePos.x(); + bottom = gMousePos.y(); + } else if (m_pressedMousePos.mIsOnTopRightEdge) { + right = gMousePos.x(); + top = gMousePos.y(); + } else if (m_pressedMousePos.mIsOnBottomRightEdge) { + right = gMousePos.x(); + bottom = gMousePos.y(); + } else if (m_pressedMousePos.mIsOnLeftEdge) { + left = gMousePos.x(); + } else if (m_pressedMousePos.mIsOnRightEdge) { + right = gMousePos.x(); + } else if (m_pressedMousePos.mIsOnTopEdge) { + top = gMousePos.y(); + } else if (m_pressedMousePos.mIsOnBottomEdge) { + bottom = gMousePos.y(); + } + + QRect newRect(QPoint(left, top), QPoint(right, bottom)); + + if (newRect.isValid()) { + if (minWidth > newRect.width()) { + if (left != origRect.left()) { + newRect.setLeft(origRect.left()); + } else { + newRect.setRight(origRect.right()); + } + } + if (minHeight > newRect.height()) { + if (top != origRect.top()) { + newRect.setTop(origRect.top()); + } else { + newRect.setBottom(origRect.bottom()); + } + } + + if (d->m_bRubberBandOnResize) { + m_pRubberBand->setGeometry(newRect); + } else { + m_pWidget->setGeometry(newRect); + } + } +} + +void SAPrivateFramelessWidgetData::moveWidget(const QPoint& gMousePos) +{ + if (d->m_bRubberBandOnMove) { + m_pRubberBand->move(gMousePos - m_ptDragPos); + } else { + m_pWidget->move(gMousePos - m_ptDragPos); + } +} + +bool SAPrivateFramelessWidgetData::handleMousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + m_bLeftButtonPressed = true; + m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.s_titleHeight; + + QRect frameRect = m_pWidget->frameGeometry(); + auto gp = SA_MOUSEEVENT_GLOBALPOS_POINT(event); + m_pressedMousePos.recalculate(gp, frameRect); + + m_ptDragPos = gp - frameRect.topLeft(); + + if (m_pressedMousePos.mIsOnEdges) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击边界不做任何处理 + return (false); + } + if (d->m_bRubberBandOnResize) { + m_pRubberBand->setGeometry(frameRect); + m_pRubberBand->show(); + return (true); + } + } else if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击标题栏不做任何处理 + return (false); + } + m_pRubberBand->setGeometry(frameRect); + m_pRubberBand->show(); + return (true); + } + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleMouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + m_bLeftButtonPressed = false; + m_bLeftButtonTitlePressed = false; + m_pressedMousePos.reset(); + if (m_pRubberBand && m_pRubberBand->isVisible()) { + m_pRubberBand->hide(); + m_pWidget->setGeometry(m_pRubberBand->geometry()); + return (true); + } + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleMouseMoveEvent(QMouseEvent* event) +{ + QPoint p = SA_MOUSEEVENT_GLOBALPOS_POINT(event); + if (m_bLeftButtonPressed) { + if (d->m_bWidgetResizable && m_pressedMousePos.mIsOnEdges) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击边界不做任何处理 + return (false); + } + resizeWidget(p); + return (true); + } else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) { + if (m_pWidget->isMaximized()) { + // 先求出窗口到鼠标的相对位置 + QRect normalGeometry = m_pWidget->normalGeometry(); + m_pWidget->showNormal(); + p.ry() -= 10; + p.rx() -= (normalGeometry.width() / 2); + m_pWidget->move(p); + // 这时要重置m_ptDragPos + m_ptDragPos = QPoint(normalGeometry.width() / 2, 10); + return (true); + } + moveWidget(p); + return (true); + } + return (false); + } else if (d->m_bWidgetResizable) { + updateCursorShape(p); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleLeaveEvent(QEvent* event) +{ + Q_UNUSED(event) + if (!m_bLeftButtonPressed) { + m_pWidget->unsetCursor(); + return (true); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleHoverMoveEvent(QHoverEvent* event) +{ + if (d->m_bWidgetResizable) { + updateCursorShape(m_pWidget->mapToGlobal(SA_HOVEREVENT_POS_POINT(event))); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleDoubleClickedMouseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + if (m_pWidget) { + SARibbonMainWindow* mainwindow = qobject_cast< SARibbonMainWindow* >(m_pWidget); + if (mainwindow) { + if (mainwindow->windowFlags() & Qt::WindowMaximizeButtonHint) { + // 在最大化按钮显示时才进行shownormal处理 + bool titlePressed = event->pos().y() < m_moveMousePos.s_titleHeight; + if (titlePressed) { + if (m_pWidget->isMaximized()) { + m_pWidget->showNormal(); + } else { + m_pWidget->showMaximized(); + } + return (true); + } + } + } + } + } + return (false); +} + +//=================================================== +// SAFramelessHelper +//=================================================== +SAFramelessHelper::SAFramelessHelper(QObject* parent) : QObject(parent), d_ptr(new SAFramelessHelper::PrivateData(this)) +{ + d_ptr->m_bWidgetMovable = true; + d_ptr->m_bWidgetResizable = true; + d_ptr->m_bRubberBandOnResize = false; + d_ptr->m_bRubberBandOnMove = false; + if (parent) { + QWidget* w = qobject_cast< QWidget* >(parent); + if (w) { + w->setWindowFlags(w->windowFlags() | Qt::FramelessWindowHint); + setWidgetMovable(true); // 设置窗体可移动 + setWidgetResizable(true); // 设置窗体可缩放 + setRubberBandOnMove(false); // 设置橡皮筋效果-可移动 + setRubberBandOnResize(true); // 设置橡皮筋效果-可缩放 + activateOn(w); // 激活当前窗体 + } + } +} + +SAFramelessHelper::~SAFramelessHelper() +{ + QList< QWidget* > keys = d_ptr->m_widgetDataHash.keys(); + int size = keys.size(); + + for (int i = 0; i < size; ++i) { + delete d_ptr->m_widgetDataHash.take(keys[ i ]); + } +} + +bool SAFramelessHelper::eventFilter(QObject* obj, QEvent* event) +{ + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::HoverMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::Leave: { + SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.value(static_cast< QWidget* >(obj)); + if (data) { + return (data->handleWidgetEvent(event)); + } + break; + } + + default: + break; + } + return (QObject::eventFilter(obj, event)); +} + +void SAFramelessHelper::activateOn(QWidget* topLevelWidget) +{ + if (!d_ptr->m_widgetDataHash.contains(topLevelWidget)) { + SAPrivateFramelessWidgetData* data = new SAPrivateFramelessWidgetData(d_ptr.get(), topLevelWidget); + d_ptr->m_widgetDataHash.insert(topLevelWidget, data); + + topLevelWidget->installEventFilter(this); + } +} + +void SAFramelessHelper::removeFrom(QWidget* topLevelWidget) +{ + SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.take(topLevelWidget); + + if (data) { + topLevelWidget->removeEventFilter(this); + delete data; + } +} + +void SAFramelessHelper::setRubberBandOnMove(bool movable) +{ + d_ptr->m_bRubberBandOnMove = movable; + QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); + + foreach (SAPrivateFramelessWidgetData* data, list) { + data->updateRubberBandStatus(); + } +} + +void SAFramelessHelper::setWidgetMovable(bool movable) +{ + d_ptr->m_bWidgetMovable = movable; +} + +void SAFramelessHelper::setWidgetResizable(bool resizable) +{ + d_ptr->m_bWidgetResizable = resizable; +} + +void SAFramelessHelper::setRubberBandOnResize(bool resizable) +{ + d_ptr->m_bRubberBandOnResize = resizable; + QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); + + foreach (SAPrivateFramelessWidgetData* data, list) { + data->updateRubberBandStatus(); + } +} + +void SAFramelessHelper::setBorderWidth(int width) +{ + if (width > 0) { + SAPrivateFramelessCursorPosCalculator::s_borderWidth = width; + } +} + +void SAFramelessHelper::setTitleHeight(int height) +{ + if (height > 0) { + SAPrivateFramelessCursorPosCalculator::s_titleHeight = height; + } +} + +bool SAFramelessHelper::widgetMovable() +{ + return (d_ptr->m_bWidgetMovable); +} + +bool SAFramelessHelper::widgetResizable() +{ + return (d_ptr->m_bWidgetResizable); +} + +bool SAFramelessHelper::rubberBandOnMove() +{ + return (d_ptr->m_bRubberBandOnMove); +} + +bool SAFramelessHelper::rubberBandOnResisze() +{ + return (d_ptr->m_bRubberBandOnResize); +} + +uint SAFramelessHelper::borderWidth() +{ + return (SAPrivateFramelessCursorPosCalculator::s_borderWidth); +} + +uint SAFramelessHelper::titleHeight() +{ + return (SAPrivateFramelessCursorPosCalculator::s_titleHeight); +} + +/*** End of inlined file: SAFramelessHelper.cpp ***/ + +/*** Start of inlined file: SARibbonApplicationButton.cpp ***/ +SARibbonApplicationButton::SARibbonApplicationButton(QWidget* parent) : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +} + +SARibbonApplicationButton::SARibbonApplicationButton(const QString& text, QWidget* parent) : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setText(text); +} + +SARibbonApplicationButton::SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent) + : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setIcon(icon); + setText(text); +} + +SARibbonApplicationButton::~SARibbonApplicationButton() +{ +} + +/*** End of inlined file: SARibbonApplicationButton.cpp ***/ + +/*** Start of inlined file: SARibbonSystemButtonBar.cpp ***/ +#include +#include +#include +#include +#include +#include + +// 为了避免使用此框架的app设置了全局的qpushbutton 的 qss样式影响此按钮,定义了一个类 + +/** + * @brief The SARibbonSystemButtonBar class + */ +class SARibbonSystemButtonBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonSystemButtonBar) +public: + SARibbonSystemToolButton* buttonClose { nullptr }; + SARibbonSystemToolButton* buttonMinimize { nullptr }; + SARibbonSystemToolButton* buttonMaximize { nullptr }; + int mCloseStretch { 4 }; + int mMaxStretch { 3 }; + int mMinStretch { 3 }; + int mWindowButtonWidth { 35 }; + int mTitleBarHeight { 28 }; + Qt::WindowFlags mFlags { Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint }; + SARibbonButtonGroupWidget* mButtonGroup; + +public: + PrivateData(SARibbonSystemButtonBar* p) : q_ptr(p) + { + mButtonGroup = new SARibbonButtonGroupWidget(p); + mButtonGroup->setObjectName("SASystemButtonGroup"); + } + + void setupMinimizeButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonMinimize) { + buttonMinimize->deleteLater(); + buttonMinimize = nullptr; + } + buttonMinimize = new SARibbonSystemToolButton(par); + buttonMinimize->setObjectName(QStringLiteral("SAMinimizeWindowButton")); + buttonMinimize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + buttonMinimize->show(); + par->connect(buttonMinimize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::minimizeWindow); + } else { + if (buttonMinimize) { + buttonMinimize->deleteLater(); + buttonMinimize = nullptr; + } + } + updateSize(); + } + + void setupMaximizeButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonMaximize) { + buttonMaximize->deleteLater(); + buttonMaximize = nullptr; + } + buttonMaximize = new SARibbonSystemToolButton(par); + buttonMaximize->setObjectName(QStringLiteral("SAMaximizeWindowButton")); + buttonMaximize->setCheckable(true); + buttonMaximize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + // buttonMaximize->setIconSize(buttonMaximize->size() * mIconscale); + buttonMaximize->show(); + par->connect(buttonMaximize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::maximizeWindow); + } else { + if (buttonMaximize) { + buttonMaximize->deleteLater(); + ; + buttonMaximize = nullptr; + } + } + updateSize(); + } + + void setupCloseButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonClose) { + buttonClose->deleteLater(); + buttonClose = nullptr; + } + buttonClose = new SARibbonSystemToolButton(par); + buttonClose->setObjectName(QStringLiteral("SACloseWindowButton")); + buttonClose->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + // buttonClose->setFlat(true); + par->connect(buttonClose, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::closeWindow); + // buttonClose->setIconSize(buttonClose->size() * mIconscale); + buttonClose->show(); + } else { + if (buttonClose) { + buttonClose->deleteLater(); + ; + buttonClose = nullptr; + } + } + updateSize(); + } + + void updateSize() + { + resizeElement(q_ptr->size()); + } + + void resizeElement(QSize size) + { + int x = size.width(); + if (buttonClose) { + int w = closeButtonWidthHint(); + x -= w; + buttonClose->setGeometry(x, 0, w, size.height()); + } + if (buttonMaximize) { + int w = maxButtonWidthHint(); + x -= w; + buttonMaximize->setGeometry(x, 0, w, size.height()); + } + if (buttonMinimize) { + int w = minButtonWidthHint(); + x -= w; + buttonMinimize->setGeometry(x, 0, w, size.height()); + } + if (mButtonGroup) { + mButtonGroup->setGeometry(0, 0, x, size.height()); + } + } + + int closeButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mCloseStretch * (3 * mWindowButtonWidth)) / t; + } + + int maxButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mMaxStretch * (3 * mWindowButtonWidth)) / t; + } + + int minButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mMinStretch * (3 * mWindowButtonWidth)) / t; + } + + QSize sizeHint() const + { + int height = mTitleBarHeight; + if (height < 20) { + height = 20; + } + QSize res(0, 0); + if (mButtonGroup) { + res = mButtonGroup->sizeHint(); + } + res.setHeight(height); + if (buttonClose) { + res.setWidth(res.width() + closeButtonWidthHint()); + } + if (buttonMaximize) { + res.setWidth(res.width() + maxButtonWidthHint()); + } + if (buttonMinimize) { + res.setWidth(res.width() + minButtonWidthHint()); + } + return res; + } +}; + +//=================================================== +// SARibbonSystemToolButton +//=================================================== +SARibbonSystemToolButton::SARibbonSystemToolButton(QWidget* p) : QToolButton(p) +{ + setAutoRaise(true); +} +//=================================================== +// SARibbonSystemButtonBar +//=================================================== +SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) +{ + updateWindowFlag(); +} + +/** + * @brief 构造函数,强制使用flags,而不是用parent的flags进行构造 + * @param parent + * @param flags + */ +SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags) + : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) +{ + d_ptr->mFlags = flags; + updateWindowFlag(); +} + +SARibbonSystemButtonBar::~SARibbonSystemButtonBar() +{ +} + +void SARibbonSystemButtonBar::setupMinimizeButton(bool on) +{ + d_ptr->setupMinimizeButton(on); +} + +void SARibbonSystemButtonBar::setupMaximizeButton(bool on) +{ + d_ptr->setupMaximizeButton(on); +} + +void SARibbonSystemButtonBar::setupCloseButton(bool on) +{ + d_ptr->setupCloseButton(on); +} + +void SARibbonSystemButtonBar::updateWindowFlag() +{ + QWidget* topedWidget = this; + // 找到最顶层窗口 + while (topedWidget->parentWidget()) { + topedWidget = topedWidget->parentWidget(); + } + Qt::WindowFlags flags = topedWidget->windowFlags(); + updateWindowFlag(flags); +} + +/** + * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 + * @param flags + */ +void SARibbonSystemButtonBar::updateWindowFlag(Qt::WindowFlags flags) +{ + d_ptr->mFlags = flags; + setupMinimizeButton(flags & Qt::WindowMinimizeButtonHint); + setupMaximizeButton(flags & Qt::WindowMaximizeButtonHint); + setupCloseButton(flags & Qt::WindowCloseButtonHint); +} + +/** + * @brief 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 + * @param close 关闭按钮比例 + * @param max 最大化按钮比例 + * @param min 最小化按钮比例 + */ +void SARibbonSystemButtonBar::setButtonWidthStretch(int close, int max, int min) +{ + d_ptr->mMaxStretch = max; + d_ptr->mMinStretch = min; + d_ptr->mCloseStretch = close; +} + +/** + * @brief 标题栏高度 + * + * 标题栏高度会影响sizeHint + * @param h + */ +void SARibbonSystemButtonBar::setWindowTitleHeight(int h) +{ + d_ptr->mTitleBarHeight = h; +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonSystemButtonBar::windowTitleHeight() const +{ + return d_ptr->mTitleBarHeight; +} + +/** + * @brief 系统按钮的宽度 + * @param w + */ +void SARibbonSystemButtonBar::setWindowButtonWidth(int w) +{ + d_ptr->mWindowButtonWidth = w; +} + +/** + * @brief 系统按钮的宽度 + * @param w + */ +int SARibbonSystemButtonBar::windowButtonWidth() const +{ + return d_ptr->mWindowButtonWidth; +} + +/** + * @brief 设置窗口状态(最大最小化按钮状态) + * @param s + */ +void SARibbonSystemButtonBar::setWindowStates(Qt::WindowStates s) +{ + if (d_ptr->buttonMaximize) { + bool on = s.testFlag(Qt::WindowMaximized); + d_ptr->buttonMaximize->setChecked(on); + d_ptr->buttonMaximize->setToolTip(on ? tr("Restore") : tr("Maximize")); + } +} + +/** + * @brief 此函数返回的flags仅包括 Qt::WindowCloseButtonHint,Qt::WindowMaximizeButtonHint,Qt::WindowMinimizeButtonHint + * 三个 + * + * @return + */ +Qt::WindowFlags SARibbonSystemButtonBar::windowButtonFlags() const +{ + Qt::WindowFlags f = Qt::Widget; // widget是000 + + if (d_ptr->mFlags & Qt::WindowCloseButtonHint) { + f |= Qt::WindowCloseButtonHint; + } + if (d_ptr->mFlags & Qt::WindowMaximizeButtonHint) { + f |= Qt::WindowMaximizeButtonHint; + } + if (d_ptr->mFlags & Qt::WindowMinimizeButtonHint) { + f |= Qt::WindowMinimizeButtonHint; + } + + return (f); +} + +QSize SARibbonSystemButtonBar::sizeHint() const +{ + return (d_ptr->sizeHint()); +} + +bool SARibbonSystemButtonBar::eventFilter(QObject* obj, QEvent* event) +{ + if (obj == parent()) { + // SARibbonMainWindow的事件 + if (event->type() == QEvent::Resize) { + SARibbonMainWindow* mainWindow = qobject_cast< SARibbonMainWindow* >(obj); + if (!mainWindow) { + // 所有事件都不消费 + return QFrame::eventFilter(obj, event); + } + SARibbonBar* ribbonBar = mainWindow->ribbonBar(); + if (!ribbonBar) { + // 所有事件都不消费 + return QFrame::eventFilter(obj, event); + } + const int th = ribbonBar->titleBarHeight(); + if (th != height()) { + setWindowTitleHeight(th); + } + QRect fr = mainWindow->geometry(); + QSize wgSizeHint = sizeHint(); + setGeometry(fr.width() - wgSizeHint.width(), 0, wgSizeHint.width(), wgSizeHint.height()); + // 把设置好的尺寸给ribbonbar + ribbonBar->setWindowButtonGroupSize(size()); + } else if (event->type() == QEvent::WindowStateChange) { + SARibbonMainWindow* mainWindow = qobject_cast< SARibbonMainWindow* >(obj); + if (!mainWindow) { + // 所有事件都不消费 + return QFrame::eventFilter(obj, event); + } + setWindowStates(mainWindow->windowState()); + } + } + return QFrame::eventFilter(obj, event); +} + +QAbstractButton* SARibbonSystemButtonBar::minimizeButton() const +{ + return d_ptr->buttonMinimize; +} + +QAbstractButton* SARibbonSystemButtonBar::maximizeButton() const +{ + return d_ptr->buttonMaximize; +} + +QAbstractButton* SARibbonSystemButtonBar::closeButton() const +{ + return d_ptr->buttonClose; +} + +void SARibbonSystemButtonBar::setIconSize(const QSize& ic) +{ + d_ptr->mButtonGroup->setIconSize(ic); +} + +QSize SARibbonSystemButtonBar::iconSize() const +{ + return d_ptr->mButtonGroup->iconSize(); +} + +QAction* SARibbonSystemButtonBar::addAction(QAction* a, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addAction(a, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle, + QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addAction(text, icon, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addMenu(menu, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addSeparator() +{ + return d_ptr->mButtonGroup->addSeparator(); +} + +QAction* SARibbonSystemButtonBar::addWidget(QWidget* w) +{ + return d_ptr->mButtonGroup->addWidget(w); +} + +void SARibbonSystemButtonBar::resizeEvent(QResizeEvent* e) +{ + Q_UNUSED(e); + d_ptr->resizeElement(size()); +} + +void SARibbonSystemButtonBar::closeWindow() +{ + if (parentWidget()) { + parentWidget()->close(); + } +} + +void SARibbonSystemButtonBar::minimizeWindow() +{ + if (parentWidget()) { + parentWidget()->showMinimized(); + } +} + +void SARibbonSystemButtonBar::maximizeWindow() +{ + QWidget* par = parentWidget(); + + if (par) { + if (par->isMaximized()) { + par->showNormal(); + } else { + par->showMaximized(); + } + } +} + +/*** End of inlined file: SARibbonSystemButtonBar.cpp ***/ + +/*** Start of inlined file: SARibbonToolButton.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @def 定义文字换行时2行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR 2.05 + +/** + * @def 定义文字不换行时单行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR 1.2 + +/** + * @def 定义小按钮的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR 1.4 + +/** + * @def 文本宽度估算时的宽度比高度系数 + * + * 超过此系数的宽度时,开始尝试换行,例如按钮高度为h,如果单行文本的宽度大于h*系数,则触发换行估算 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO 1.4 + +/** + * @def 开启此宏会打印一些常见信息 + */ +#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0 + +#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0 + +#if SARIBBONTOOLBUTTON_DEBUG_DRAW +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + p.setPen(Qt::red); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +#else +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) +#endif +namespace SA +{ + +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt) +{ + debug << "==============" << "\nQStyleOption(" << (QStyleOption)opt << ")" + << "\n QStyleOptionComplex:" + "\n subControls(" + << opt.subControls + << " ) " + "\n activeSubControls(" + << opt.activeSubControls + << "\n QStyleOptionToolButton" + "\n features(" + << opt.features + << ")" + "\n toolButtonStyle(" + << opt.toolButtonStyle << ")"; + + return (debug); +} +} + +//=================================================== +// SARibbonToolButtonProxyStyle +//=================================================== + +class SARibbonToolButtonProxyStyle : public QProxyStyle +{ +public: + void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override + { + if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight + || pe == PE_IndicatorArrowLeft) { + if (opt->rect.width() <= 1 || opt->rect.height() <= 1) + return; + + QRect r = opt->rect; + int size = qMin(r.height(), r.width()); + QPixmap pixmap; + qreal pixelRatio = p->device()->devicePixelRatio(); + int border = qRound(pixelRatio * (size / 4)); + int sqsize = qRound(pixelRatio * (2 * (size / 2))); + QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + QPainter imagePainter(&image); + + QPolygon a; + switch (pe) { + case PE_IndicatorArrowUp: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowDown: + a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowRight: + a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + case PE_IndicatorArrowLeft: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + default: + break; + } + + int bsx = 0; + int bsy = 0; + + if (opt->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget); + } + + QRect bounds = a.boundingRect(); + int sx = sqsize / 2 - bounds.center().x() - 1; + int sy = sqsize / 2 - bounds.center().y() - 1; + imagePainter.translate(sx + bsx, sy + bsy); + imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4)); + imagePainter.setBrush(Qt::NoBrush); + + if (!(opt->state & State_Enabled)) { + imagePainter.translate(1, 1); + imagePainter.setPen(QPen(opt->palette.light().color(), 1.4)); + imagePainter.drawPolyline(a); + imagePainter.translate(-1, -1); + imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4)); + } + + imagePainter.drawPolyline(a); + imagePainter.end(); + pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(pixelRatio); + + int xOffset = r.x() + (r.width() - size) / 2; + int yOffset = r.y() + (r.height() - size) / 2; + p->drawPixmap(xOffset, yOffset, pixmap); + } else { + QProxyStyle::drawPrimitive(pe, opt, p, widget); + } + } +}; + +//=================================================== +// SARibbonToolButton::PrivateData +//=================================================== + +class SARibbonToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton) +public: + PrivateData(SARibbonToolButton* p); + // 根据鼠标位置更新按钮的信息 + void updateStatusByMousePosition(const QPoint& pos); + // 更新绘图相关的尺寸 + void updateDrawRect(const QStyleOptionToolButton& opt); + // 更新SizeHint + void updateSizeHint(const QStyleOptionToolButton& opt); + // 计算涉及到的rect尺寸 + void calcDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 计算小按钮模式下的尺寸 + void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 计算大按钮模式下的尺寸 + void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况) + QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const; + // 判断是否有Indicator + bool hasIndicator(const QStyleOptionToolButton& opt) const; + // 计算sizehint + QSize calcSizeHint(const QStyleOptionToolButton& opt); + QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt); + QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt); + + // 计算文本绘制矩形的高度 + int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const; + // 估算一个最优的文本宽度 + int estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio = SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO, + int maxTrycount = 3); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + // 获取文字的对其方式 + int getTextAlignment() const; + // 确认文字是否确切要换行显示 + bool isTextNeedWrap() const; + // 仅仅对\n进行剔除,和QString::simplified不一样 + static QString simplified(const QString& str); + +public: + bool mMouseOnSubControl { false }; ///< 这个用于标记MenuButtonPopup模式下,鼠标在文本区域 + bool mMenuButtonPressed { false }; ///< 由于Indicator改变,因此hitButton不能用QToolButton的hitButton + bool mWordWrap { false }; ///< 标记是否文字换行 @default false + SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton }; + int mSpacing { 1 }; ///< 按钮和边框的距离 + int mIndicatorLen { 8 }; ///< Indicator的长度 + QRect mDrawIconRect; ///< 记录icon的绘制位置 + QRect mDrawTextRect; ///< 记录text的绘制位置 + QRect mDrawIndicatorArrowRect; ///< 记录IndicatorArrow的绘制位置 + QSize mSizeHint; ///< 保存计算好的sizehint + bool mIsTextNeedWrap { false }; ///< 标记文字是否需要换行显示 +public: + static bool s_enableWordWrap; ///< 在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 +}; + +// 静态参数初始化 +bool SARibbonToolButton::PrivateData::s_enableWordWrap = false; + +SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p) +{ + auto proxy = new SARibbonToolButtonProxyStyle(); + proxy->setParent(p); // take ownership to avoid memleak + p->setStyle(proxy); +} + +/** + * @brief 根据鼠标的位置更新状态,主要用于判断鼠标是否位于subcontrol + * + * 此函数主要应用在action menu模式下 + * @param pos + */ +void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos) +{ + bool isMouseOnSubControl(false); + if (SARibbonToolButton::LargeButton == mButtonType) { + isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos); + } else { + // 小按钮模式就和普通toolbutton一样 + isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos); + } + + if (mMouseOnSubControl != isMouseOnSubControl) { + mMouseOnSubControl = isMouseOnSubControl; + // 从icon变到text过程中刷新一次 + q_ptr->update(); + } +} + +/** + * @brief 更新绘图的几个关键尺寸 + * + * 包括: + * + * - DrawIconRect 绘制图标的矩形区域 + * + * - DrawTextRect 绘制文本的矩形区域 + * + * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域 + * + * @param opt + */ +void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt) +{ + if (!mSizeHint.isValid()) { + updateSizeHint(opt); + } + // 先更新IndicatorLen + mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr); + if (mIndicatorLen < 3) { + if (SARibbonToolButton::LargeButton == mButtonType) { + mIndicatorLen = 8; + } else { + mIndicatorLen = 12; // 小按钮模式下设置为10 + } + } + calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen); +} + +/** + * @brief 更新sizehint + * @param opt + */ +void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt) +{ + mSizeHint = calcSizeHint(opt); +} + +/** + * @brief 计算绘图的几个关键区域 + * @param opt + * @param iconRect 绘制图标的矩形区域 + * @param textRect 绘制文本的矩形区域 + * @param indicatorArrowRect 绘制菜单下箭头的矩形区域 + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + + } else { + calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + } +} + +/** + * @brief 计算小按钮模式下的绘制尺寸 + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = + QRect(opt.rect.right() - indicatorLen - spacing, iconRect.y(), indicatorLen, iconRect.height()); + } else { + iconRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 文本区域为空 + textRect = QRect(); + } break; + case Qt::ToolButtonTextOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height()); + } else { + textRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 绘图区域为空 + iconRect = QRect(); + } break; + default: { + bool hasInd = hasIndicator(opt); + // icon Beside和under都是一样的 + QRect buttonRect = q_ptr->rect(); + buttonRect.adjust(spacing, spacing, -spacing, -spacing); + // 先设置IconRect + if (opt.icon.isNull()) { + // 没有图标 + iconRect = QRect(); + } else { + QSize iconSize = adjustIconSize(buttonRect, opt.iconSize); + iconRect = + QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height())); + } + // 后设置TextRect + if (opt.text.isEmpty()) { + textRect = QRect(); + } else { + // 分有菜单和没菜单两种情况 + int adjx = iconRect.isValid() ? (iconRect.width() + spacing) + : 0; // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + if (hasInd) { + textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0); + } else { + textRect = buttonRect.adjusted(adjx, 0, 0, 0); // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + } + } + // 最后设置Indicator + if (hasInd) { + if (textRect.isValid()) { + indicatorArrowRect = + QRect(buttonRect.right() - indicatorLen + 1, textRect.y(), indicatorLen, textRect.height()); + } else if (iconRect.isValid()) { + indicatorArrowRect = + QRect(buttonRect.right() - indicatorLen + 1, iconRect.y(), indicatorLen, iconRect.height()); + } else { + indicatorArrowRect = buttonRect; + } + } else { + indicatorArrowRect = QRect(); + } + } + } +} + +/** + * @brief 计算大按钮模式下的绘制尺寸(普通) + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + //! 3行模式的图标比较大,文字换行情况下,indicator会动态调整 + // 先获取文字矩形的高度 + int textHeight = calcTextDrawRectHeight(opt); + bool hIndicator = hasIndicator(opt); + if (!hIndicator) { + // 没有菜单,把len设置为0 + indicatorLen = 0; + indicatorArrowRect = QRect(); + } + // 这里要判断文字是否要换行显示,换行显示的文字的indicatorArrowRect所处的位置不一样 + // 先布置textRect + if (isEnableWordWrap()) { + // 在换行模式下 + if (isTextNeedWrap()) { + // 如果文字的确换行,indicator放在最右边 + textRect = QRect( + spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing - indicatorLen, textHeight); + if (hIndicator) { + indicatorArrowRect = + QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2); + } + } else { + // 如果文字不需要换行,indicator在下板行 + textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing, textHeight); + if (hIndicator) { + int dy = textRect.height() / 2; + dy += (dy - indicatorLen) / 2; + indicatorArrowRect = QRect(textRect.x(), textRect.y() + dy, textRect.width(), indicatorLen); + } + } + } else { + // 文字不换行,indicator放在最右边 + int y = opt.rect.bottom() - spacing - textHeight; + if (hIndicator) { + // 先布置indicator + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight); + textRect = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight); + } else { + textRect = QRect(spacing, y, opt.rect.width() - 2 * spacing, textHeight); + } + } + // 剩下就是icon区域 + iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing); +} + +/** + * @brief 适应iconsize + * @param buttonRect + * @param originIconSize + * @return + */ +QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const +{ + QSize iconSize = originIconSize; + if (iconSize.height() > buttonRect.height()) { + // 说明图标的icon过大,要匹配到buttonRect + iconSize.setHeight(buttonRect.height()); + // 等比例设置宽度 + iconSize.setWidth(originIconSize.width() * iconSize.height() / originIconSize.height()); + } + return iconSize; +} + +/** + * @brief 判断是否有Indicator + * @param opt + * @return + */ +bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const +{ + return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu)); +} + +/** + * @brief 计算sizehint + * + * 此函数非常关键,因为所有尺寸计算都是基于原始的rect来的 + * @param opt + * @return + */ +QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt) +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + return calcLargeButtonSizeHint(opt); + } + return calcSmallButtonSizeHint(opt); +} + +QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0, h = 0; + + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + } break; + case Qt::ToolButtonTextOnly: { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w = textSize.width() + 2 * mSpacing; + h = textSize.height() + 2 * mSpacing; + } break; + default: { + // 先加入icon的尺寸 + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + // 再加入文本的长度 + if (!opt.text.isEmpty()) { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w += mSpacing; + w += textSize.width(); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } else { + // 没有文本的时候也要设置一下高度 + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " "); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } + } + } + if (hasIndicator(opt)) { + // 存在indicator的按钮,宽度尺寸要扩展 + w += mIndicatorLen; + } + if (w < 16) { + w = 16; + } + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(2, 2)); +} + +QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0; + int h = opt.fontMetrics.lineSpacing() * 4.8; // 3*1.6 + int minW = h * 0.75; // 最小宽度,在pannel里面的按钮,最小宽度要和icon适应 + + if (SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(q_ptr->parent())) { + // 对于建立在SARibbonPannel的基础上的大按钮,把高度设置为SARibbonPannel计算的大按钮高度 + h = pannel->largeButtonHeight(); + } + int textHeight = calcTextDrawRectHeight(opt); + // 估算字体的宽度作为宽度 + w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics); + w += (2 * mSpacing); + // 判断是否需要加上indicator + if (isEnableWordWrap() && isTextNeedWrap()) { + w += mIndicatorLen; + } + +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text + << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5 // + << "\n| | | |-textHeight=" << textHeight // + << "\n| | | |-mDrawIconRect=" << mDrawIconRect // + << "\n| | | |-minW=" << minW // + << "\n| | | |-w=" << w // + ; +#endif + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(minW, textHeight)); +} + +/** + * @brief 计算文本高度 + * @param opt + * @return + */ +int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR + opt.fontMetrics.leading(); + } else { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR; + } + } + // 小按钮 + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR; +} + +/** + * @brief 估算一个最优的文字尺寸,在可以换行的情况下会进行换行,且只会换一行 + * @param buttonHeight 按钮的高度 + * @param textDrawRectHeight 文本绘制的高度 + * @param fm QFontMetrics + * @param widthHeightRatio 宽高比,宽度/高度的比值,如果大于这个比值,则会进行尝试换行以获取更低的宽度 + * @param maxTrycount 尝试次数 + * @return + */ +int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio, + int maxTrycount) +{ + QSize textSize; + int space = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2; + int hintMaxWidth = buttonHeight * widthHeightRatio; ///< 建议的宽度 + if (isEnableWordWrap()) { + textSize = fm.size(Qt::TextShowMnemonic, text); + textSize.setWidth(textSize.width() + space); + + if (textSize.height() > fm.lineSpacing() * 1.1) { + //! 说明文字带有换行符,是用户手动换行,这种情况就直接返回字体尺寸,不进行估算 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textSize.width(); + } + + // 这时候需要估算文本的长度 + if (textSize.width() <= hintMaxWidth) { + // 范围合理,直接返回 + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + return textSize.width(); + } + + //! 大于宽高比尝试进行文字换行 + //! 这里先对文本长度逐渐加长估算,一直到和原来长度一致为止 + int trycount = 0; + int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; + // 对于英文字体,直接宽度减半是无法满足完全显示两行的,需要进行预估 + QRect textRect(0, 0, textSize.width(), textDrawRectHeight); + do { + //! 先计算两行文本的紧凑矩形 + //! 从一半开始逐渐递增 + //! 第1次为 w/2 + w/2 * (0/3) + //! 第2次为 w/2 + w/2 * (1/3) + //! 第3次为 w/2 + w/2 * (2/3) + textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount)); + textRect = fm.boundingRect(textRect, alignment, text); + if (textRect.height() <= (fm.lineSpacing() * 2)) { + // 保证在两行 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textRect.width(); + } + ++trycount; +#if SARIBBONTOOLBUTTON_DEBUG_DRAW + if (trycount > 1) { + qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount + << ",textRect=" << textRect; + } +#endif + } while (trycount < 3); + // 到这里说明前面的尝试失败,最终使用原始的长度 + return textSize.width(); + } + + //! 说明是不换行 + + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + // 文字不换行情况下,做simplified处理 + textSize = fm.size(Qt::TextShowMnemonic, simplified(text)); + textSize.setWidth(textSize.width() + space); + if (textSize.width() < hintMaxWidth) { + // 范围合理,直接返回 + return textSize.width(); + } + if (textSize.width() > q_ptr->maximumWidth()) { + // 超出了极限,就返回极限 + return q_ptr->maximumWidth(); + } + return hintMaxWidth; +} + +QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return (QPixmap()); + } + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // 添加高分屏支持 + QSize pxiampSize = iconsize - QSize(2, 2); + return opt.icon.pixmap(pxiampSize, mode, state); +} + +int SARibbonToolButton::PrivateData::getTextAlignment() const +{ + int alignment = Qt::TextShowMnemonic; + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + alignment |= Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter; // 换行的情况下,顶部对齐 + } else { + alignment |= Qt::AlignCenter; + } + } else { + alignment |= Qt::AlignCenter; + } + return alignment; +} + +/** + * @brief 确认文字是否确切要换行显示 + * @return + */ +bool SARibbonToolButton::PrivateData::isTextNeedWrap() const +{ + return mIsTextNeedWrap; +} + +/** + * @brief 仅仅对\n进行剔除 + * @param str + * @return + */ +QString SARibbonToolButton::PrivateData::simplified(const QString& str) +{ + QString res = str; + res.remove('\n'); + return res; +} +//=================================================== +// SARibbonToolButton +//=================================================== + +SARibbonToolButton::SARibbonToolButton(QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setDefaultAction(defaultAction); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::~SARibbonToolButton() +{ +} + +/** + * @brief 鼠标移动事件 + * + * 由于Ribbon的Indicator和正常的Toolbutton不一样,因此无法用QStyleOptionToolButton的activeSubControls的状态 + * + * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态 + * @param e + */ +void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e) +{ + d_ptr->updateStatusByMousePosition(e->pos()); + QToolButton::mouseMoveEvent(e); +} + +/** + * @brief SARibbonToolButton::mousePressEvent + * @param e + */ +void SARibbonToolButton::mousePressEvent(QMouseEvent* e) +{ + if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) { + d_ptr->updateStatusByMousePosition(e->pos()); + if (d_ptr->mMouseOnSubControl) { + d_ptr->mMenuButtonPressed = true; + showMenu(); + // showmenu结束后,在判断当前的鼠标位置是否是在subcontrol + d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos())); + return; + } + } + d_ptr->mMenuButtonPressed = false; + //! 注意这里要用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent + //! QToolButton的mousePressEvent主要是为了弹出菜单,这里弹出菜单的方式是不一样的,因此不能执行QToolButton的mousePressEvent + QAbstractButton::mousePressEvent(e); +} + +void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e) +{ + d_ptr->mMenuButtonPressed = false; + QToolButton::mouseReleaseEvent(e); +} + +void SARibbonToolButton::focusOutEvent(QFocusEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::focusOutEvent(e); +} + +void SARibbonToolButton::leaveEvent(QEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::leaveEvent(e); +} + +bool SARibbonToolButton::hitButton(const QPoint& pos) const +{ + if (QToolButton::hitButton(pos)) { + return (!d_ptr->mMenuButtonPressed); + } + return (false); +} + +/** + * @brief 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + * @param e + */ +void SARibbonToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); + updateRect(); +} + +/** + * @brief toolbutton的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行 + * @note m_sizeHint的刷新需要注意 + * @return + */ +QSize SARibbonToolButton::sizeHint() const +{ + // if (!d_ptr->mSizeHint.isValid()) { // 22是给与sizehint的最小值,如果小于这个值,重新计算一下 + // QStyleOptionToolButton opt; + // initStyleOption(&opt); + // d_ptr->updateSizeHint(opt); + // } +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::sizeHint"; +#endif + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateSizeHint(opt); + return d_ptr->mSizeHint; +} + +void SARibbonToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QPainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + // 在菜单弹出消失后,需要通过此方法取消掉鼠标停留 + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + opt.state &= ~QStyle::State_MouseOver; + } + } + paintButton(p, opt); + paintIcon(p, opt, d_ptr->mDrawIconRect); + paintText(p, opt, d_ptr->mDrawTextRect); + paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt) +{ + // QStyle::State_Sunken 代表按钮按下去了 + // QStyle::State_On 代表按钮按checked + // QStyle::State_MouseOver 代表当前鼠标位于按钮上面 + QStyleOption tool = opt; + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态 + // 先绘制一个鼠标不在按钮上的状态 + if (opt.state & QStyle::State_Sunken) { + tool.state &= ~QStyle::State_MouseOver; + } + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + // 针对MenuButtonPopup的ribbon样式的特殊绘制 + if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) { + if (opt.state & QStyle::State_MouseOver) { // 鼠标在按钮上才进行绘制 + if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) { // 按钮的菜单弹出时不做处理 + if (LargeButton == d_ptr->mButtonType) { // 大按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标显示为正常(就是鼠标不放上去的状态) + tool.rect = d_ptr->mDrawIconRect; + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + if (!tool.state.testFlag(QStyle::State_Sunken)) { + // State_Sunken说明此按钮正在按下,这时候,文本区域不需要绘制,只有在非按下状态才需要绘制 + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect); + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } else { // 小按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标和文字显示为正常 + tool.rect = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect); + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawIndicatorArrowRect; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } + } + } + // 绘制Focus + // if (opt.state & QStyle::State_HasFocus) { + // QStyleOptionFocusRect fr; + // fr.QStyleOption::operator=(opt); + // fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing); + // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this); + // } +} + +/** + * @brief 绘制图标 + * @param p + * @param opt + */ +void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (!iconDrawRect.isValid()) { + return; + } + + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect); +} + +/** + * @brief 绘制文本 + * @param p + * @param opt + */ +void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect) +{ + int alignment = d_ptr->getTextAlignment(); + + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + QString text; + if (isSmallRibbonButton()) { + text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + if (!isEnableWordWrap()) { + text = opt.fontMetrics.elidedText( + PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + text = opt.text; + } + } + //! 以下内容参考QCommonStyle.cpp + //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const + //! case CC_ToolButton: + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + QStyleOptionToolButton label = opt; + label.state = bflags; + style()->drawItemText( + &p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect); +} + +/** + * @brief 绘制Indicator + * @param p + * @param opt + */ +void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect) +{ + if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) { + return; + } + + QStyleOption tool = opt; + tool.rect = indicatorDrawRect; + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect); +} + +void SARibbonToolButton::drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget) +{ + QStyle::PrimitiveElement pe; + + switch (toolbutton->arrowType) { + case Qt::LeftArrow: + pe = QStyle::PE_IndicatorArrowLeft; + break; + + case Qt::RightArrow: + pe = QStyle::PE_IndicatorArrowRight; + break; + + case Qt::UpArrow: + pe = QStyle::PE_IndicatorArrowUp; + break; + + case Qt::DownArrow: + pe = QStyle::PE_IndicatorArrowDown; + break; + + default: + return; + } + QStyleOption arrowOpt = *toolbutton; + + arrowOpt.rect = rect; + style->drawPrimitive(pe, &arrowOpt, painter, widget); +} + +void SARibbonToolButton::actionEvent(QActionEvent* e) +{ + QToolButton::actionEvent(e); + updateRect(); +} + +/** + * @brief 按钮样式 + * @sa setButtonType + * @return + */ +SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const +{ + return (d_ptr->mButtonType); +} + +/** + * @brief 设置按钮样式 + * @note 设置按钮样式过程会调用setToolButtonStyle,如果要改变toolButtonStyle,如设置为Qt::ToolButtonIconOnly,需要在此函数之后设置 + * @param buttonType + */ +void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType) +{ + d_ptr->mButtonType = buttonType; + // 计算iconrect + // 根据字体计算文字的高度 + + if (LargeButton == buttonType) { + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + } else { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + } + + updateRect(); +} + +/** + * @brief 是否是小按钮 + * @return + */ +bool SARibbonToolButton::isSmallRibbonButton() const +{ + return (d_ptr->mButtonType == SmallButton); +} + +/** + * @brief 是否是大按钮 + * @return + */ +bool SARibbonToolButton::isLargeRibbonButton() const +{ + return (d_ptr->mButtonType == LargeButton); +} + +QSize SARibbonToolButton::minimumSizeHint() const +{ + return (sizeHint()); +} + +/** + * @brief 间距是几个重要矩形的间隔 + * @return + */ +int SARibbonToolButton::spacing() const +{ + return d_ptr->mSpacing; +} + +void SARibbonToolButton::updateRect() +{ + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateDrawRect(opt); +} + +/** + * @brief 设置在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 + * @param on + */ +void SARibbonToolButton::setEnableWordWrap(bool on) +{ + SARibbonToolButton::PrivateData::s_enableWordWrap = on; +} + +/** + * @brief 在lite模式下是否允许文字换行 + * @return + */ +bool SARibbonToolButton::isEnableWordWrap() +{ + return SARibbonToolButton::PrivateData::s_enableWordWrap; +} + +bool SARibbonToolButton::event(QEvent* e) +{ + switch (e->type()) { + case QEvent::WindowDeactivate: + d_ptr->mMouseOnSubControl = false; + break; + case QEvent::ActionChanged: + case QEvent::ActionRemoved: + case QEvent::ActionAdded: { + d_ptr->mMouseOnSubControl = false; + updateRect(); + } break; + default: + break; + } + + return (QToolButton::event(e)); +} + +void SARibbonToolButton::changeEvent(QEvent* e) +{ + if (e) { + if (e->type() == QEvent::FontChange) { + // 说明字体改变,需要重新计算和字体相关的信息 + updateRect(); + } + } + QToolButton::changeEvent(e); +} + +/*** End of inlined file: SARibbonToolButton.cpp ***/ + +/*** Start of inlined file: SARibbonColorToolButton.cpp ***/ +#include +#include +#include + +//=================================================== +// SARibbonColorToolButton::PrivateData +//=================================================== +const int c_ribbonbutton_color_height = 5; ///< 颜色块的高度 + +class SARibbonColorToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton) +public: + PrivateData(SARibbonColorToolButton* p); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + QIcon createColorIcon(const QColor& c, const QSize& size) const; + +public: + QColor mColor; ///< 记录颜色 + SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon }; ///< 颜色显示样式 + QIcon mOldIcon; ///< 记录旧的icon +}; + +SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p) +{ +} + +QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return QPixmap(); + } + // 有icon,在icon下方加入颜色 + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1); + QPixmap pixmap = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state); + QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4)); // 宽度上,颜色块多出2px + res.fill(Qt::transparent); + QPainter painter(&res); + int xpixmap = (res.width() - pixmap.width()) / 2; + int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height()) + / 2; // 这里要减去2而不是1,这样奇数偶数都不会影响 + int w = pixmap.width(); + int h = pixmap.height(); + QRect rpixmap = QRect(xpixmap, ypixmap, w, h); + painter.drawPixmap(rpixmap, pixmap); + QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1); + if (mColor.isValid()) { + painter.fillRect(colorRect, mColor); + } else { + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + painter.setPen(pen); + painter.drawRect(colorRect); + } + return res; +} + +QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const +{ + QPixmap res(size); + res.fill(Qt::transparent); + QPainter painter(&res); + if (c.isValid()) { + painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c); + } else { + QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2)); + pen.setColor(Qt::red); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1)); + } + return QIcon(res); +} + +//=================================================== +// SARibbonColorToolButton +//=================================================== + +SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent) + : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent) + : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::~SARibbonColorToolButton() +{ +} + +/** + * @brief 获取按钮维护的颜色 + * @return + */ +QColor SARibbonColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置颜色显示的样式 + * @param s + */ +void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s) +{ + if (d_ptr->mColorStyle == s) { + return; + } + d_ptr->mColorStyle = s; + if (ColorUnderIcon == s) { + setIcon(d_ptr->mOldIcon); + } else { + d_ptr->mOldIcon = icon(); + setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32))); + } + repaint(); +} + +/** + * @brief 颜色显示的样式 + * @return + */ +SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const +{ + return d_ptr->mColorStyle; +} + +/** + * @brief 建立标准的颜色菜单 + * @return + */ +SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu() +{ + setPopupMode(QToolButton::MenuButtonPopup); + SAColorMenu* m = new SAColorMenu(this); + m->enableNoneColorAction(true); + QAction* customColor = m->customColorAction(); + if (customColor) { + customColor->setIcon(QIcon(":/image/resource/define-color.svg")); + } + connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor); + setMenu(m); + + updateRect(); + return m; +} + +/** + * @brief 设置按钮的颜色 + * + * 此时会生成一个新的icon + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SARibbonColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + if (ColorFillToIcon == colorStyle()) { + setIcon(d_ptr->createColorIcon(c, QSize(32, 32))); + } + repaint(); + Q_EMIT colorChanged(c); + } +} + +void SARibbonColorToolButton::onButtonClicked(bool checked) +{ + Q_EMIT colorClicked(d_ptr->mColor, checked); +} + +/** + * @brief 重写paintIcon函数,把颜色加到icon下面 + * @param p + * @param opt + * @param iconDrawRect + */ +void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (ColorUnderIcon == colorStyle()) { + // 有icon + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + } else { + SARibbonToolButton::paintIcon(p, opt, iconDrawRect); + } +} + +/*** End of inlined file: SARibbonColorToolButton.cpp ***/ + +/*** Start of inlined file: SARibbonLineWidgetContainer.cpp ***/ +#include + +SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget* par) : QWidget(par), m_innerWidget(nullptr) +{ + m_labelPrefix = new QLabel(this); + m_labelSuffix = new QLabel(this); + QHBoxLayout* lay = new QHBoxLayout(); + + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + lay->addWidget(m_labelPrefix); + lay->addWidget(m_labelSuffix); + setLayout(lay); +} + +SARibbonLineWidgetContainer::~SARibbonLineWidgetContainer() +{ +} + +void SARibbonLineWidgetContainer::setWidget(QWidget* innerWidget) +{ + QHBoxLayout* lay = static_cast< QHBoxLayout* >(layout()); + + if (m_innerWidget) { + lay->replaceWidget(m_innerWidget, innerWidget); + } else { + lay->insertWidget(1, innerWidget); + } + m_innerWidget = innerWidget; +} + +void SARibbonLineWidgetContainer::setPrefix(const QString& str) +{ + m_labelPrefix->setText(str); +} + +void SARibbonLineWidgetContainer::setSuffix(const QString& str) +{ + m_labelSuffix->setText(str); +} + +QLabel* SARibbonLineWidgetContainer::labelPrefix() const +{ + return (m_labelPrefix); +} + +QLabel* SARibbonLineWidgetContainer::labelSuffix() const +{ + return (m_labelSuffix); +} + +/*** End of inlined file: SARibbonLineWidgetContainer.cpp ***/ + +/*** Start of inlined file: SARibbonActionsManager.cpp ***/ +#include +#include +#include +#include + +class SARibbonActionsManager::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager) +public: + PrivateData(SARibbonActionsManager* p); + void clear(); + + QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList + QMap< int, QString > mTagToName; ///< tag对应的名字 + QHash< QString, QAction* > mKeyToAction; ///< key对应action + QMap< QAction*, QString > mActionToKey; ///< action对应key + QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用 + int mSale; ///< 盐用于生成固定的id,在用户不主动设置key时,id基于msale生成,只要SARibbonActionsManager的调用registeAction顺序不变,生成的id都不变,因为它是基于自增实现的 +}; + +SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0) +{ +} + +void SARibbonActionsManager::PrivateData::clear() +{ + mTagToActions.clear(); + mTagToName.clear(); + mKeyToAction.clear(); + mActionToKey.clear(); + mTagToCategory.clear(); + mSale = 0; +} + +SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar) + : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this)) +{ + autoRegisteActions(bar); +} + +SARibbonActionsManager::~SARibbonActionsManager() +{ +} + +/** + * @brief 设置tag对应的名字,通过这个可以得到tag和文本的映射 + * @param tag + * @param name + * @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字 + */ +void SARibbonActionsManager::setTagName(int tag, const QString& name) +{ + d_ptr->mTagToName[ tag ] = name; +} + +/** + * @brief 获取tag对应的中文名字 + * @param tag + * @return + */ +QString SARibbonActionsManager::tagName(int tag) const +{ + return (d_ptr->mTagToName.value(tag, "")); +} + +/** + * @brief 移除tag + * @note 注意,这个函数非常耗时 + * @param tag + */ +void SARibbonActionsManager::removeTag(int tag) +{ + QList< QAction* > oldacts = actions(tag); + + // 开始移除 + d_ptr->mTagToActions.remove(tag); + d_ptr->mTagToName.remove(tag); + // 开始查找需要移出总表的action + QList< QAction* > needRemoveAct; + QList< QAction* > total; + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + total += i.value(); + } + for (QAction* a : qAsConst(oldacts)) { + if (!total.contains(a)) { + needRemoveAct.append(a); + } + } + // 从总表移除action + for (QAction* a : qAsConst(needRemoveAct)) { + auto i = d_ptr->mActionToKey.find(a); + if (i != d_ptr->mActionToKey.end()) { + d_ptr->mKeyToAction.remove(i.value()); + d_ptr->mActionToKey.erase(i); + } + } +} + +/** + * @brief 把action注册到管理器中,实现action的管理 + * @param act + * @param tag tag是可以按照位进行叠加,见 @ref ActionTag 如果 + * 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值, + * registeAction的tag是直接记录进去的,如果要多个标签并存,在registe之前先或好tag + * @param key key是action对应的key,一个key只对应一个action,是查找action的关键 + * ,默认情况为一个QString(),这时key是QAction的objectName + * @param enableEmit 控制是否发射@ref actionTagChanged 信号 + * @note 同一个action多次注册不同的tag可以通过tag索引到action,但通过action只能索引到最后一个注册的tag + * @note tag的新增会触发actionTagChanged信号 + */ +bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit) +{ + if (nullptr == act) { + return (false); + } + QString k = key; + + if (k.isEmpty()) { + k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName()); + } + if (d_ptr->mKeyToAction.contains(k)) { + qWarning() << "key: " + << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction"; + return (false); + } + d_ptr->mKeyToAction[ k ] = act; + d_ptr->mActionToKey[ act ] = k; + // 记录tag 对 action + bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号 + d_ptr->mTagToActions[ tag ].append(act); + // 绑定槽 + connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + if (isneedemit && enableEmit) { + // 说明新增tag + Q_EMIT actionTagChanged(tag, false); + } + return (true); +} + +/** + * @brief 取消action的注册 + * + * 如果tag对应的最后一个action被撤销,tag也将一块删除 + * @param act + * @note tag的删除会触发actionTagChanged信号 + * @note 如果action关联了多个tag,这些tag里的action都会被删除,对应的key也同理 + */ +void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit) +{ + if (nullptr == act) { + return; + } + // 绑定槽 + disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + removeAction(act, enableEmit); +} + +/** + * @brief 移除action + * + * 仅移除内存内容 + * @param act + * @param enableEmit + */ +void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit) +{ + QList< int > deletedTags; // 记录删除的tag,用于触发actionTagChanged + QMap< int, QList< QAction* > > tagToActions; ///< tag : QList + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + // 把不是act的内容转移到tagToActions和tagToActionKeys中,之后再和m_d里的替换 + auto tmpi = tagToActions.insert(i.key(), QList< QAction* >()); + int count = 0; + for (int j = 0; j < i.value().size(); ++j) { + if (i.value()[ j ] != act) { + tmpi.value().append(act); + ++count; + } + } + if (0 == count) { + // 说明这个tag没有内容 + tagToActions.erase(tmpi); + deletedTags.append(i.key()); + } + } + // 删除mKeyToAction + QString key = d_ptr->mActionToKey.value(act); + + d_ptr->mActionToKey.remove(act); + d_ptr->mKeyToAction.remove(key); + + // 置换 + d_ptr->mTagToActions.swap(tagToActions); + // 发射信号 + if (enableEmit) { + for (int tagdelete : qAsConst(deletedTags)) { + Q_EMIT actionTagChanged(tagdelete, true); + } + } +} + +/** + * @brief 等同actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::filter(int tag) +{ + return (actions(tag)); +} + +/** + * @brief 根据tag得到actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::actions(int tag) +{ + return (d_ptr->mTagToActions[ tag ]); +} + +const QList< QAction* > SARibbonActionsManager::actions(int tag) const +{ + return (d_ptr->mTagToActions[ tag ]); +} + +/** + * @brief 获取所有的标签 + * @return + */ +QList< int > SARibbonActionsManager::actionTags() const +{ + return (d_ptr->mTagToActions.keys()); +} + +/** + * @brief 通过key获取action + * @param key + * @return 如果没有key,返回nullptr + */ +QAction* SARibbonActionsManager::action(const QString& key) const +{ + return (d_ptr->mKeyToAction.value(key, nullptr)); +} + +/** + * @brief 通过action找到key + * @param act + * @return 如果找不到,返回QString() + */ +QString SARibbonActionsManager::key(QAction* act) const +{ + return (d_ptr->mActionToKey.value(act, QString())); +} + +/** + * @brief 返回所有管理的action数 + * @return + */ +int SARibbonActionsManager::count() const +{ + return (d_ptr->mKeyToAction.size()); +} + +/** + * @brief 返回所有管理的actions + * @return + */ +QList< QAction* > SARibbonActionsManager::allActions() const +{ + return (d_ptr->mKeyToAction.values()); +} + +/** + * @brief 自动加载SARibbonBar的action + * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object,找到action注册, + * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类 + * + * 此函数会把所有category下的action生成tag并注册,返回的QMap是记录了category对应的tag + * + * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action,但不在任何一个category下的作为NotInRibbonCategoryTag标签注册,默认名字会赋予not + * in ribbon, 可以通过@ref setTagName 改变 + * + * @param w + * @return + * @note 此函数的调用最好在category设置了标题后调用,因为会以category的标题作为标签的命名 + */ +QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar) +{ + QMap< int, SARibbonCategory* > res; + // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象,把所有action找到 + QWidget* parWidget = bar->parentWidget(); + QSet< QAction* > mainwindowActions; + if (parWidget) { + for (QObject* o : qAsConst(parWidget->children())) { + if (QAction* a = qobject_cast< QAction* >(o)) { + // 说明是action + if (!a->objectName().isEmpty()) { + mainwindowActions.insert(a); + } + } + } + } + // 开始遍历每个category,加入action + + if (nullptr == bar) { + // 非ribbon模式,直接退出 + return (res); + } + QSet< QAction* > categoryActions; + QList< SARibbonCategory* > categorys = bar->categoryPages(); + int tag = AutoCategoryDistinguishBeginTag; + + for (SARibbonCategory* c : qAsConst(categorys)) { + QList< SARibbonPannel* > pannels = c->pannelList(); + for (SARibbonPannel* p : qAsConst(pannels)) { + categoryActions += autoRegisteWidgetActions(p, tag, false); + } + setTagName(tag, c->categoryName()); + res[ tag ] = c; + ++tag; + } + // 找到不在功能区的actions + QSet< QAction* > notincategory = mainwindowActions - categoryActions; + + for (QAction* a : qAsConst(notincategory)) { + if (!a->objectName().isEmpty()) { + registeAction(a, NotInRibbonCategoryTag, a->objectName(), false); + } + } + if (notincategory.size() > 0) { + setTagName(NotInRibbonCategoryTag, tr("not in ribbon")); + } + for (auto i = res.begin(); i != res.end(); ++i) { + connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged); + } + d_ptr->mTagToCategory = res; + return (res); +} + +/** + * @brief 自动加载action + * @param w + * @param tag + * @param enableEmit + * @return 返回成功加入SARibbonActionsManager管理的action + */ +QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit) +{ + QSet< QAction* > res; + QList< QAction* > was = w->actions(); + + for (QAction* a : qAsConst(was)) { + if (res.contains(a) || a->objectName().isEmpty()) { + // 重复内容不重复加入 + // 没有object name不加入 + continue; + } + if (registeAction(a, tag, a->objectName(), enableEmit)) { + res.insert(a); + } + } + return (res); +} + +/** + * @brief 根据标题查找action + * @param text + * @return + */ +QList< QAction* > SARibbonActionsManager::search(const QString& text) +{ + QList< QAction* > res; + + if (text.isEmpty()) { + return (res); + } + QStringList kws = text.split(" "); + + if (kws.isEmpty()) { + kws.append(text); + } + QList< QAction* > acts = d_ptr->mActionToKey.keys(); + + for (const QString& k : qAsConst(kws)) { + for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) { + if (i.key()->text().contains(k, Qt::CaseInsensitive)) { + res.append(i.key()); + } + } + } + return (res); +} + +void SARibbonActionsManager::clear() +{ + d_ptr->clear(); +} + +/** + * @brief action 被delete时,将触发此槽把管理的action删除 + * @param o + * @note 这个函数不会触发actionTagChanged信号 + */ +void SARibbonActionsManager::onActionDestroyed(QObject* o) +{ + QAction* act = static_cast< QAction* >(o); + + removeAction(act, false); +} + +/** + * @brief autoRegisteActions函数会关联此槽,在标签内容改变时改变tag 对应 文本 + * @param title + */ +void SARibbonActionsManager::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender()); + + if (nullptr == c) { + return; + } + int tag = d_ptr->mTagToCategory.key(c, -1); + + if (tag == -1) { + return; + } + setTagName(tag, title); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonActionsModel +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +class SARibbonActionsManagerModel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel) +public: + PrivateData(SARibbonActionsManagerModel* p); + void updateRef(); + int count() const; + QAction* at(int index); + bool isNull() const; + +public: + SARibbonActionsManager* mMgr { nullptr }; + int mTag { SARibbonActionsManager::CommonlyUsedActionTag }; + QString mSeatchText; + QList< QAction* > mActions; +}; + +SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p) +{ +} + +void SARibbonActionsManagerModel::PrivateData::updateRef() +{ + if (isNull()) { + return; + } + if (!mSeatchText.isEmpty()) { + mActions = mMgr->search(mSeatchText); + } else { + mActions = mMgr->actions(mTag); + } +} + +int SARibbonActionsManagerModel::PrivateData::count() const +{ + if (isNull()) { + return (0); + } + return (mActions.size()); +} + +QAction* SARibbonActionsManagerModel::PrivateData::at(int index) +{ + if (isNull()) { + return (nullptr); + } + if (index >= count()) { + return (nullptr); + } + return (mActions.at(index)); +} + +bool SARibbonActionsManagerModel::PrivateData::isNull() const +{ + return (mMgr == nullptr); +} + +//=================================================== +// SARibbonActionsManagerModel +//=================================================== + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ +} + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ + setupActionsManager(m); +} + +SARibbonActionsManagerModel::~SARibbonActionsManagerModel() +{ +} + +int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { // 非顶层 + return (0); + } + // 顶层 + return (d_ptr->count()); +} + +QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + if (role != Qt::DisplayRole) { + return (QVariant()); + } + if (Qt::Horizontal == orientation) { + return (tr("action name")); + } + return (QVariant()); +} + +Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) { + return (Qt::NoItemFlags); + } + return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); +} + +QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const +{ + QAction* act = indexToAction(index); + + if (nullptr == act) { + return (QVariant()); + } + + switch (role) { + case Qt::DisplayRole: { + QString str = act->text(); + if (!str.isEmpty()) { + return str; + } + // 如果文本为空,一般是没有设置文本,或者是窗口,如果是窗口,那么用windowtitle作为文本 + if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) { + if (QWidget* w = wa->defaultWidget()) { + str = w->windowTitle(); + } + } + return str; + } + case Qt::DecorationRole: + return (act->icon()); + + default: + break; + } + return (QVariant()); +} + +void SARibbonActionsManagerModel::setFilter(int tag) +{ + d_ptr->mTag = tag; + update(); +} + +void SARibbonActionsManagerModel::update() +{ + beginResetModel(); + d_ptr->updateRef(); + endResetModel(); +} + +void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m) +{ + d_ptr->mMgr = m; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + d_ptr->mActions = m->filter(d_ptr->mTag); + connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + update(); +} + +void SARibbonActionsManagerModel::uninstallActionsManager() +{ + if (!d_ptr->isNull()) { + disconnect( + d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + d_ptr->mMgr = nullptr; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + } + update(); +} + +QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const +{ + if (!index.isValid()) { + return (nullptr); + } + if (index.row() >= d_ptr->count()) { + return (nullptr); + } + return (d_ptr->at(index.row())); +} + +/** + * @brief 查找 + * @param text + */ +void SARibbonActionsManagerModel::search(const QString& text) +{ + d_ptr->mSeatchText = text; + update(); +} + +void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete) +{ + if (isdelete && (tag == d_ptr->mTag)) { + d_ptr->mTag = SARibbonActionsManager::UnknowActionTag; + update(); + } else { + if (tag == d_ptr->mTag) { + update(); + } + } +} + +/*** End of inlined file: SARibbonActionsManager.cpp ***/ + +/*** Start of inlined file: SARibbonLineEdit.cpp ***/ +#include + +SARibbonLineEdit::SARibbonLineEdit(QWidget* parent) : QLineEdit(parent) +{ +} + +SARibbonLineEdit::SARibbonLineEdit(const QString& text, QWidget* parent) : QLineEdit(text, parent) +{ +} + +SARibbonLineEdit::~SARibbonLineEdit() +{ +} + +/*** End of inlined file: SARibbonLineEdit.cpp ***/ + +/*** Start of inlined file: SARibbonCheckBox.cpp ***/ +#include + +SARibbonCheckBox::SARibbonCheckBox(QWidget* parent) : QCheckBox(parent) +{ +} + +SARibbonCheckBox::SARibbonCheckBox(const QString& text, QWidget* parent) : QCheckBox(text, parent) +{ +} + +SARibbonCheckBox::~SARibbonCheckBox() +{ +} + +/*** End of inlined file: SARibbonCheckBox.cpp ***/ + +/*** Start of inlined file: SARibbonComboBox.cpp ***/ +SARibbonComboBox::SARibbonComboBox(QWidget* parent) : QComboBox(parent) +{ +} + +SARibbonComboBox::~SARibbonComboBox() +{ +} + +/*** End of inlined file: SARibbonComboBox.cpp ***/ + +/*** Start of inlined file: SARibbonButtonGroupWidget.cpp ***/ +#include +#include +#include +#include +#include +#include +#include + +//=================================================== +// SARibbonButtonGroupWidget::PrivateData +//=================================================== +class SARibbonButtonGroupWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonButtonGroupWidget) +public: + PrivateData(SARibbonButtonGroupWidget* p); + void init(); + void removeAction(QAction* a); + +public: + QSize mIconSize { 20, 20 }; +}; + +SARibbonButtonGroupWidget::PrivateData::PrivateData(SARibbonButtonGroupWidget* p) : q_ptr(p) +{ +} + +void SARibbonButtonGroupWidget::PrivateData::init() +{ + QHBoxLayout* layout = new QHBoxLayout(q_ptr); + // 上下保留一点间隙 + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + q_ptr->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); +} + +void SARibbonButtonGroupWidget::PrivateData::removeAction(QAction* a) +{ + QLayout* lay = q_ptr->layout(); + int c = lay->count(); + QList< QLayoutItem* > willRemoveItems; + for (int i = 0; i < c; ++i) { + QLayoutItem* item = lay->itemAt(i); + SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); + if (nullptr == btn) { + continue; + } + if (a == btn->defaultAction()) { + willRemoveItems.push_back(item); + } + } + // 从尾部删除 + for (auto i = willRemoveItems.rbegin(); i != willRemoveItems.rend(); ++i) { + lay->removeItem(*i); + } +} + +//=================================================== +// SARibbonButtonGroupWidget +//=================================================== + +SARibbonButtonGroupWidget::SARibbonButtonGroupWidget(QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonButtonGroupWidget::PrivateData(this)) +{ + d_ptr->init(); +} + +SARibbonButtonGroupWidget::~SARibbonButtonGroupWidget() +{ +} + +/** + * @brief 设置图标尺寸 + * @param iconSize + */ +void SARibbonButtonGroupWidget::setIconSize(const QSize& ic) +{ + d_ptr->mIconSize = ic; + iterate([ ic ](SARibbonControlButton* btn) -> bool { + btn->setIconSize(ic); + return true; + }); +} + +/** + * @brief 图标尺寸 + * @return + */ +QSize SARibbonButtonGroupWidget::iconSize() const +{ + return d_ptr->mIconSize; +} + +QAction* SARibbonButtonGroupWidget::addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle, + QToolButton::ToolButtonPopupMode popMode) +{ + SARibbonPannel::setActionToolButtonStyleProperty(a, buttonStyle); + SARibbonPannel::setActionToolButtonPopupModeProperty(a, popMode); + QWidget::addAction(a); + return (a); +} + +/** + * @brief 生成action + * @note action的所有权归SARibbonButtonGroupWidget + * @param text + * @param icon + * @param popMode + * @return + */ +QAction* SARibbonButtonGroupWidget::addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle, + QToolButton::ToolButtonPopupMode popMode) +{ + QAction* a = new QAction(icon, text, this); + addAction(a, buttonStyle, popMode); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + QAction* a = menu->menuAction(); + addAction(a, buttonStyle, popMode); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addSeparator() +{ + QAction* a = new QAction(this); + + a->setSeparator(true); + addAction(a); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addWidget(QWidget* w) +{ + QWidgetAction* a = new QWidgetAction(this); + + a->setDefaultWidget(w); + w->setAttribute(Qt::WA_Hover); + addAction(a); + return (a); +} + +SARibbonControlButton* SARibbonButtonGroupWidget::actionToRibbonControlToolButton(QAction* action) +{ + SARibbonControlButton* res = nullptr; + iterate([ &res, action ](SARibbonControlButton* btn) -> bool { + if (btn->defaultAction() == action) { + res = btn; + return false; // 返回false退出迭代 + } + return true; + }); + return (res); +} + +QSize SARibbonButtonGroupWidget::sizeHint() const +{ + return (layout()->sizeHint()); +} + +QSize SARibbonButtonGroupWidget::minimumSizeHint() const +{ + return (layout()->minimumSize()); +} + +/** + * @brief 此函数会遍历SARibbonButtonGroupWidget下的所有SARibbonControlButton,执行函数指针(bool(SARibbonControlButton*)),函数指针返回false则停止迭代 + * @param fp + * @return 中途迭代退出返回false + */ +bool SARibbonButtonGroupWidget::iterate(SARibbonButtonGroupWidget::FpButtonIterate fp) +{ + QLayout* lay = layout(); + int c = lay->count(); + for (int i = 0; i < c; ++i) { + auto item = lay->itemAt(i); + if (!item) { + continue; + } + SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); + if (!btn) { + continue; + } + if (!fp(btn)) { + return false; + } + } + return true; +} + +/** + * @brief 处理action的事件 + * + * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 + * ActionAdded时会生成窗口 + * @param e + */ +void SARibbonButtonGroupWidget::actionEvent(QActionEvent* e) +{ + QAction* a = e->action(); + if (!a) { + return; + } + + switch (e->type()) { + case QEvent::ActionAdded: { + QWidget* w = nullptr; + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(a)) { + widgetAction->setParent(this); + w = widgetAction->requestWidget(this); + if (w != nullptr) { + w->setAttribute(Qt::WA_LayoutUsesWidgetRect); + w->show(); + } + } else if (a->isSeparator()) { + SARibbonSeparatorWidget* sp = RibbonSubElementFactory->createRibbonSeparatorWidget(this); + w = sp; + } + // 不是widget,自动生成ButtonTyle + if (!w) { + SARibbonControlButton* button = RibbonSubElementFactory->createRibbonControlButton(this); + button->setAutoRaise(true); + button->setIconSize(d_ptr->mIconSize); + button->setFocusPolicy(Qt::NoFocus); + button->setDefaultAction(a); + // 属性设置 + QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(a); + button->setPopupMode(popMode); + Qt::ToolButtonStyle buttonStyle = SARibbonPannel::getActionToolButtonStyleProperty(a); + button->setToolButtonStyle(buttonStyle); + // 根据QAction的属性设置按钮的大小 + + connect(button, &SARibbonToolButton::triggered, this, &SARibbonButtonGroupWidget::actionTriggered); + w = button; + } + layout()->addWidget(w); + updateGeometry(); + } break; + + case QEvent::ActionChanged: { + // 让布局重新绘制 + layout()->invalidate(); + updateGeometry(); + } break; + + case QEvent::ActionRemoved: { + d_ptr->removeAction(e->action()); + updateGeometry(); + } break; + + default: + break; + } + QFrame::actionEvent(e); +} + +/*** End of inlined file: SARibbonButtonGroupWidget.cpp ***/ + +/*** Start of inlined file: SARibbonStackedWidget.cpp ***/ +#include +#include +#include +#include +#include + +/** + * @brief The SARibbonStackedWidgetPrivate class + */ +class SARibbonStackedWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonStackedWidget) +public: + QEventLoop* eventLoop { nullptr }; + +public: + PrivateData(SARibbonStackedWidget* p) : q_ptr(p) + { + } + + void init() + { + // Parent->setFocusPolicy(Qt::StrongFocus); + } +}; + +SARibbonStackedWidget::SARibbonStackedWidget(QWidget* parent) + : QStackedWidget(parent), d_ptr(new SARibbonStackedWidget::PrivateData(this)) +{ + d_ptr->init(); + setNormalMode(); +} + +SARibbonStackedWidget::~SARibbonStackedWidget() +{ + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + } +} + +void SARibbonStackedWidget::setPopupMode() +{ + setMouseTracking(true); + setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); + setFrameShape(QFrame::Panel); +} + +bool SARibbonStackedWidget::isPopupMode() const +{ + return (windowFlags() & Qt::Popup); +} + +void SARibbonStackedWidget::setNormalMode() +{ + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + d_ptr->eventLoop = nullptr; + } + setMouseTracking(false); + setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + setFrameShape(QFrame::NoFrame); +} + +bool SARibbonStackedWidget::isNormalMode() const +{ + return (!isPopupMode()); +} + +void SARibbonStackedWidget::exec() +{ + show(); + if (!isPopupMode()) { + d_ptr->eventLoop = nullptr; + return; + } + QEventLoop event; + + d_ptr->eventLoop = &event; + event.exec(); + d_ptr->eventLoop = nullptr; +} + +void SARibbonStackedWidget::moveWidget(int from, int to) +{ + QWidget* w = widget(from); + + removeWidget(w); + insertWidget(to, w); +} + +void SARibbonStackedWidget::hideEvent(QHideEvent* e) +{ + if (isPopupMode()) { + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + } + } + setFocus(); + Q_EMIT hidWindow(); + + QStackedWidget::hideEvent(e); +} + +void SARibbonStackedWidget::resizeEvent(QResizeEvent* e) +{ + QStackedWidget::resizeEvent(e); + for (int i = 0; i < count(); ++i) { + if (i == currentIndex()) { + continue; + } + QEvent* layE = new QEvent(QEvent::LayoutRequest); + QApplication::postEvent(widget(i), layE); + } +} + +/*** End of inlined file: SARibbonStackedWidget.cpp ***/ + +/*** Start of inlined file: SARibbonSeparatorWidget.cpp ***/ +#include +#include +#include +#include +#include + +SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent) +{ + setFrameShape(QFrame::VLine); + setFrameShadow(QFrame::Plain); + + if (QScreen* screen = QApplication::primaryScreen()) { + qreal dpr = screen->physicalDotsPerInch() / screen->logicalDotsPerInch(); + int scaledLineWidth = qRound(1.0 * dpr); // 假设基础 lineWidth 是 1 + setLineWidth(scaledLineWidth); + // qDebug() << "SARibbonSeparatorWidget:" << scaledLineWidth; + } else { + setLineWidth(1); + } +} + +SARibbonSeparatorWidget::~SARibbonSeparatorWidget() +{ +} + +/*** End of inlined file: SARibbonSeparatorWidget.cpp ***/ + +/*** Start of inlined file: SARibbonCtrlContainer.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +/** + * @brief The SARibbonCtrlContainerPrivate class + */ +class SARibbonCtrlContainer::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCtrlContainer) +public: + QWidget* mContainerWidget { nullptr }; + QHBoxLayout* mHorizontalLayout { nullptr }; + QLabel* mLabelPixmap { nullptr }; + QLabel* mLabelText { nullptr }; + QSize mIconSize { 24, 24 }; + QIcon mIcon; + PrivateData(SARibbonCtrlContainer* p) : q_ptr(p) + { + mHorizontalLayout = new QHBoxLayout(p); + mHorizontalLayout->setSpacing(0); + mHorizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + mHorizontalLayout->setContentsMargins(0, 0, 0, 0); + + mLabelPixmap = new QLabel(p); + mLabelPixmap->setObjectName(QString::fromUtf8("labelPixmap")); + mLabelPixmap->setAlignment(Qt::AlignCenter); + mHorizontalLayout->addWidget(mLabelPixmap); + + mLabelText = new QLabel(p); + mLabelText->setObjectName(QString::fromUtf8("labelText")); + mHorizontalLayout->addWidget(mLabelText); + } + + void setContainerWidget(QWidget* w) + { + if (mContainerWidget) { + // 原来有widget + QWidget* oldwidget = mContainerWidget; + takeContainerWidget(oldwidget); + oldwidget->deleteLater(); + } + mContainerWidget = w; + if (!w) { + return; + } + if (w->parent() != q_ptr) { + w->setParent(q_ptr); + } + // ContainerWidget永远在最右边 + mHorizontalLayout->insertWidget(mHorizontalLayout->count(), w); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + w->setSizePolicy(sizePolicy); + } + + void takeContainerWidget(QWidget* w) + { + int i = mHorizontalLayout->indexOf(w); + if (i >= 0) { + QLayoutItem* item = mHorizontalLayout->takeAt(i); + delete item; + } + } +}; + +//=================================================== +// SARibbonCtrlContainer +//=================================================== +SARibbonCtrlContainer::SARibbonCtrlContainer(QWidget* parent) + : QWidget(parent), d_ptr(new SARibbonCtrlContainer::PrivateData(this)) +{ +} + +SARibbonCtrlContainer::~SARibbonCtrlContainer() +{ +} + +QWidget* SARibbonCtrlContainer::containerWidget() +{ + return (d_ptr->mContainerWidget); +} + +const QWidget* SARibbonCtrlContainer::containerWidget() const +{ + return (d_ptr->mContainerWidget); +} + +void SARibbonCtrlContainer::setEnableShowIcon(bool b) +{ + d_ptr->mLabelPixmap->setVisible(b); +} + +void SARibbonCtrlContainer::setEnableShowTitle(bool b) +{ + d_ptr->mLabelText->setVisible(b); +} + +/** + * @brief 判断是否存在容器窗口 + * @return + */ +bool SARibbonCtrlContainer::hasContainerWidget() const +{ + return (d_ptr->mContainerWidget != nullptr); +} + +/** + * @brief 设置图标 + * @param i + */ +void SARibbonCtrlContainer::setIcon(const QIcon& i) +{ + d_ptr->mIcon = i; + QPixmap pixmap = i.pixmap(d_ptr->mIconSize); + d_ptr->mLabelPixmap->setPixmap(pixmap); +} + +void SARibbonCtrlContainer::setIcon(const QPixmap& pixmap) +{ + d_ptr->mLabelPixmap->setPixmap(pixmap); +} + +/** + * @brief 获取图标 + * @return + */ +QIcon SARibbonCtrlContainer::icon() const +{ + return d_ptr->mIcon; +} + +/** + * @brief 设置文字 + * @param t + */ +void SARibbonCtrlContainer::setText(const QString& t) +{ + d_ptr->mLabelText->setText(t); +} + +/** + * @brief 获取文字 + * @return + */ +QString SARibbonCtrlContainer::text() const +{ + return d_ptr->mLabelText->text(); +} + +void SARibbonCtrlContainer::setContainerWidget(QWidget* w) +{ + d_ptr->setContainerWidget(w); +} + +/** + * @brief 获取显示icon的窗口 + * @return + */ +QWidget* SARibbonCtrlContainer::iconWidget() const +{ + return d_ptr->mLabelPixmap; +} + +/*** End of inlined file: SARibbonCtrlContainer.cpp ***/ + +/*** Start of inlined file: SARibbonQuickAccessBar.cpp ***/ +#include + +/** + * @brief The SARibbonQuickAccessBarPrivate class + */ +class SARibbonQuickAccessBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonQuickAccessBar) +public: + PrivateData(SARibbonQuickAccessBar* p); + +public: + SARibbonButtonGroupWidget* mGroupWidget { nullptr }; +}; + +SARibbonQuickAccessBar::PrivateData::PrivateData(SARibbonQuickAccessBar* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonQuickAccessBar +//=================================================== +SARibbonQuickAccessBar::SARibbonQuickAccessBar(QWidget* parent) + : SARibbonCtrlContainer(parent), d_ptr(new SARibbonQuickAccessBar::PrivateData(this)) +{ + d_ptr->mGroupWidget = new SARibbonButtonGroupWidget(this); + d_ptr->mGroupWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + setContainerWidget(d_ptr->mGroupWidget); +} + +SARibbonQuickAccessBar::~SARibbonQuickAccessBar() +{ +} + +void SARibbonQuickAccessBar::addSeparator() +{ + d_ptr->mGroupWidget->addSeparator(); +} + +void SARibbonQuickAccessBar::addAction(QAction* act, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + d_ptr->mGroupWidget->addAction(act, buttonStyle, popMode); +} + +void SARibbonQuickAccessBar::addWidget(QWidget* w) +{ + d_ptr->mGroupWidget->addWidget(w); +} + +void SARibbonQuickAccessBar::addMenu(QMenu* m, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + d_ptr->mGroupWidget->addMenu(m, buttonStyle, popMode); +} + +/** + * @brief 获取内部管理的ButtonGroupWidget + * @return + */ +SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() +{ + return d_ptr->mGroupWidget; +} + +/** + * @brief 获取内部管理的ButtonGroupWidget + * @return + */ +const SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() const +{ + return d_ptr->mGroupWidget; +} + +/** + * @brief 图标尺寸 + * @param s + */ +void SARibbonQuickAccessBar::setIconSize(const QSize& s) +{ + d_ptr->mGroupWidget->setIconSize(s); +} + +/** + * @brief SARibbonQuickAccessBar::iconSize + * @return + */ +QSize SARibbonQuickAccessBar::iconSize() const +{ + return d_ptr->mGroupWidget->iconSize(); +} + +/*** End of inlined file: SARibbonQuickAccessBar.cpp ***/ + +/*** Start of inlined file: SARibbonTabBar.cpp ***/ +#include +#include + +SARibbonTabBar::SARibbonTabBar(QWidget* parent) : QTabBar(parent), m_tabMargin(6, 0, 0, 0) +{ + setExpanding(false); +} + +SARibbonTabBar::~SARibbonTabBar() +{ +} + +const QMargins& SARibbonTabBar::tabMargin() const +{ + return (m_tabMargin); +} + +void SARibbonTabBar::setTabMargin(const QMargins& tabMargin) +{ + m_tabMargin = tabMargin; +} + +/** + * @brief tab的尺寸预估 + * + * 有别于系统默认的tabbar,SARibbonTabBar的tab高度和tabbar高度一致,且不考虑纵向分布情况 + * @param index + * @return + */ +QSize SARibbonTabBar::tabSizeHint(int index) const +{ + if (index < 0) { + return QSize(); + } + QStyleOptionTab opt; + initStyleOption(&opt, index); + int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this); + const QFontMetrics fm = fontMetrics(); + + int widgetWidth = 0; + int padding = 0; + if (!opt.leftButtonSize.isEmpty()) { + padding += 4; + widgetWidth += opt.leftButtonSize.width(); + } + if (!opt.rightButtonSize.isEmpty()) { + padding += 4; + widgetWidth += opt.rightButtonSize.width(); + } + if (!opt.icon.isNull()) { + padding += 4; + } + const int textWidth = fm.size(Qt::TextShowMnemonic, opt.text).width(); + QSize csz = QSize(textWidth + opt.iconSize.width() + hframe + widgetWidth + padding, height()); + return style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this); +} + +/*** End of inlined file: SARibbonTabBar.cpp ***/ + +/*** Start of inlined file: SARibbonControlButton.cpp ***/ +SARibbonControlButton::SARibbonControlButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonControlButton::~SARibbonControlButton() +{ +} +SARibbonControlToolButton::SARibbonControlToolButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonControlToolButton::~SARibbonControlToolButton() +{ +} + +/*** End of inlined file: SARibbonControlButton.cpp ***/ + +/*** Start of inlined file: SARibbonMenu.cpp ***/ +#include + +SARibbonMenu::SARibbonMenu(QWidget* parent) : QMenu(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +SARibbonMenu::SARibbonMenu(const QString& title, QWidget* parent) : QMenu(title, parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +SARibbonMenu::~SARibbonMenu() +{ +} + +QAction* SARibbonMenu::addRibbonMenu(SARibbonMenu* menu) +{ + return (QMenu::addMenu(menu)); +} + +SARibbonMenu* SARibbonMenu::addRibbonMenu(const QString& title) +{ + SARibbonMenu* menu = new SARibbonMenu(title, this); + + return (menu); +} + +SARibbonMenu* SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title) +{ + SARibbonMenu* menu = new SARibbonMenu(title, this); + + menu->setIcon(icon); + return (menu); +} + +QAction* SARibbonMenu::addWidget(QWidget* w) +{ + QWidgetAction* action = new QWidgetAction(this); + + action->setDefaultWidget(w); + addAction(action); + return (action); +} + +/*** End of inlined file: SARibbonMenu.cpp ***/ + +/*** Start of inlined file: SARibbonPannelOptionButton.cpp ***/ +#include + +SARibbonPannelOptionButton::SARibbonPannelOptionButton(QWidget* parent) : QToolButton(parent) +{ + setAutoRaise(true); + setCheckable(false); + setToolButtonStyle(Qt::ToolButtonIconOnly); + setIconSize(QSize(10, 10)); + static QIcon s_default_icon = QIcon(":/image/resource/ribbonPannelOptionButton.png"); + setIcon(s_default_icon); +} + +SARibbonPannelOptionButton::~SARibbonPannelOptionButton() +{ +} + +/*** End of inlined file: SARibbonPannelOptionButton.cpp ***/ + +/*** Start of inlined file: SARibbonPannelItem.cpp ***/ +SARibbonPannelItem::SARibbonPannelItem(QWidget* widget) + : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large) +{ +} + +SARibbonPannelItem::~SARibbonPannelItem() +{ +} + +bool SARibbonPannelItem::isEmpty() const +{ + return (action == nullptr || !action->isVisible()); +} + +/*** End of inlined file: SARibbonPannelItem.cpp ***/ + +/*** Start of inlined file: SARibbonPannelLayout.cpp ***/ +#include +#include + +#define SARibbonPannelLayout_DEBUG_PRINT 1 +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true) +{ + setSpacing(1); + SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p); + + if (!tb) { + return; + } +} + +SARibbonPannelLayout::~SARibbonPannelLayout() +{ + // 参考QToolBarLayout + while (!mItems.isEmpty()) { + SARibbonPannelItem* item = mItems.takeFirst(); + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) { + if (item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } + } + delete item; + } +} + +/** + * @brief 通过action查找索引,用于actionEvent添加action用 + * @param action + * @return 没有查到返回-1 + */ +int SARibbonPannelLayout::indexByAction(QAction* action) const +{ + for (int i = 0; i < mItems.count(); ++i) { + if (mItems.at(i)->action == action) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取ribbonpannel + * @return + */ +SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const +{ + return qobject_cast< SARibbonPannel* >(parentWidget()); +} + +void SARibbonPannelLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead"); + return; +} + +/** + * @brief SARibbonPannel主要通过此函数来添加action + * @param act + * @param rp 布局策略 + */ +void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp) +{ + index = qMax(0, index); + index = qMin(mItems.count(), index); + SARibbonPannelItem* item = createItem(act, rp); + + if (item) { + mItems.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); + } +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannelLayout::setOptionAction(QAction* action) +{ + SARibbonPannel* p = ribbonPannel(); + if (!p) { + return; + } + if (action) { + // 创建option action + if (nullptr == mOptionActionBtn) { + mOptionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p); + QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered); + // 确保m_optionActionBtn在label之上 + if (mTitleLabel) { + mTitleLabel->stackUnder(mOptionActionBtn); + } + } + mOptionActionBtn->setDefaultAction(action); + if (action->icon().isNull()) { + mOptionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); + } + // 标记需要重新计算尺寸 + invalidate(); + } else { + // 取消option action + if (mOptionActionBtn) { + mOptionActionBtn->hide(); + mOptionActionBtn->deleteLater(); + mOptionActionBtn = nullptr; + // 标记需要重新计算尺寸 + invalidate(); + } + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannelLayout::isHaveOptionAction() const +{ + return (mOptionActionBtn != nullptr); +} + +QLayoutItem* SARibbonPannelLayout::itemAt(int index) const +{ + if ((index < 0) || (index >= mItems.count())) { + return (nullptr); + } + return (mItems.at(index)); +} + +QLayoutItem* SARibbonPannelLayout::takeAt(int index) +{ + if ((index < 0) || (index >= mItems.count())) { + return (nullptr); + } + SARibbonPannelItem* item = mItems.takeAt(index); + + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action); + + if ((widgetAction != 0) && item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } else { + // destroy the QToolButton/QToolBarSeparator + item->widget()->hide(); + item->widget()->deleteLater(); + } + + invalidate(); + return (item); +} + +int SARibbonPannelLayout::count() const +{ + return (mItems.count()); +} + +bool SARibbonPannelLayout::isEmpty() const +{ + + return (mItems.isEmpty()); +} + +void SARibbonPannelLayout::invalidate() +{ + mDirty = true; + QLayout::invalidate(); +} + +Qt::Orientations SARibbonPannelLayout::expandingDirections() const +{ + return (Qt::Horizontal); +} + +QSize SARibbonPannelLayout::minimumSize() const +{ + return (mSizeHint); +} + +QSize SARibbonPannelLayout::sizeHint() const +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint; + } +#endif + return (mSizeHint); +} + +/** + * @brief 通过action获取SARibbonPannelItem + * @param action + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const +{ + int index = indexByAction(action); + + if (index >= 0) { + return (mItems[ index ]); + } + return (nullptr); +} + +/** + * @brief 获取最后一个添加的item + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::lastItem() const +{ + if (mItems.isEmpty()) { + return (nullptr); + } + return (mItems.last()); +} + +/** + * @brief 获取最后生成的窗口 + * @return 如果无窗口或者item为空,返回nullptr + */ +QWidget* SARibbonPannelLayout::lastWidget() const +{ + SARibbonPannelItem* item = lastItem(); + + if (item) { + return (item->widget()); + } + return (nullptr); +} + +/** + * @brief 移动两个item + * @param from + * @param to + * @note 移动完后所有都失效,需要重新布局 + */ +void SARibbonPannelLayout::move(int from, int to) +{ + if (from == to) { + return; + } + if (to < 0) { + to = 0; + } + if (to >= count()) { + to = count() - 1; + } + mItems.move(from, to); + invalidate(); +} + +/** + * @brief 判断是否需要重新布局 + * @return + */ +bool SARibbonPannelLayout::isDirty() const +{ + return (mDirty); +} + +void SARibbonPannelLayout::updateGeomArray() +{ + updateGeomArray(geometry()); +} + +/** + * @brief 布局所有action + */ +void SARibbonPannelLayout::doLayout() +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName(); + } +#endif + if (isDirty()) { + updateGeomArray(); + } + QList< QWidget* > showWidgets, hideWidgets; + SARibbonPannel* pannel = ribbonPannel(); + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + } else { + // 在category发现item->setGeometry有点奇怪的现象,这里统一使用item->widget->setgeo + // item->setGeometry(item->itemWillSetGeometry); + if (item->widget()) { + item->widget()->setGeometry(item->itemWillSetGeometry); + } + showWidgets << item->widget(); + } + } + + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) + w->show(); + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) + w->hide(); + } + // 布局label + if (mTitleLabel) { + if (isEnableShowPannelTitle()) { + mTitleLabel->setGeometry(mTitleLabelGeometry); + if (!mTitleLabel->isVisibleTo(pannel)) { + mTitleLabel->show(); + } + } else { + if (mTitleLabel->isVisibleTo(pannel)) { + mTitleLabel->hide(); + } + } + } + // 布局m_optionActionBtn + if (mOptionActionBtn) { + mOptionActionBtn->setGeometry(mOptionActionBtnGeometry); + mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height())); + } +} + +/** + * @brief 把action转换为item + * + * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action) + * + * 对于普通QAction,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考SARibbonPannelItem::RowProportion, + * @param action + * @param rp 行高占比情况 + * @return 转换的SARibbonPannelItem + * @note 每个SARibbonPannelItem最终都会携带一个widget,传入的是QWidgetAction的话,会直接使用QWidgetAction带的widget, + * 否则会内部生成一个SARibbonToolButton + * + */ +SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + bool customWidget = false; + QWidget* widget = nullptr; + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + // 在没有pannel这个函数会返回nullptr + return (nullptr); + } + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) { + widget = widgetAction->requestWidget(pannel); + if (widget != nullptr) { + widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); + customWidget = true; // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton + } + } else if (action->isSeparator()) { + SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel); + widget = sep; + } + // 不是widget,自动生成SARibbonToolbutton + if (!widget) { + SARibbonToolButton::RibbonButtonType buttonType = + ((rp == SARibbonPannelItem::Large) ? SARibbonToolButton::LargeButton : SARibbonToolButton::SmallButton); + + SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel); + button->setFocusPolicy(Qt::NoFocus); + button->setButtonType(buttonType); + button->setDefaultAction(action); + button->setIconSize(mDefaultToolButtonIconSize); + // 属性设置 + QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action); + button->setPopupMode(popMode); + // 根据QAction的属性设置按钮的大小 + + QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered); + widget = button; + } + // 这时总会有widget + widget->hide(); + SARibbonPannelItem* result = new SARibbonPannelItem(widget); + + result->rowProportion = rp; + result->customWidget = customWidget; + result->action = action; + return (result); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonPannelLayout::updateGeomArray(const QRect& setrect) +{ + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + return; + } + + const int height = setrect.height(); + const QMargins& mag = contentsMargins(); + const int spacing = this->spacing(); + const int spacingRow = 1; // 高度间距,也就是行间距,不同行之间的距离 + int x = mag.left(); + const int yBegin = mag.top(); + int titleH = (mTitleHeight >= 0) ? mTitleHeight : 0; // 防止负数影响 + int titleSpace = (mTitleHeight >= 0) ? mTitleSpace : 0; // 对于没有标题的情况,spacing就不生效 + if (!isEnableShowPannelTitle()) { + titleH = 0; + titleSpace = 0; + } + // 获取pannel的布局模式 3行或者2行 + // rowcount 是ribbon的行,有2行和3行两种 + const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2; + // largeHeight是对应large占比的高度 + const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace; + const int yTitleBegin = height - mag.bottom() - titleH; + mLargeHeight = largeHeight; + // 计算smallHeight的高度 + const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount; + // Medium行的y位置 + const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3)); + const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow) + : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight); + // Small行的y位置 + const int ySmallRow0 = yBegin; + const int ySmallRow1 = yBegin + smallHeight + spacingRow; + const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow); + // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行, + // item->rowIndex主要用于SARibbonPannelItem::Medium + short row = 0; + int column = 0; + // 记录每列最大的宽度 + int columMaxWidth = 0; + // 记录总宽度 + int totalWidth = 0; + + int itemCount = mItems.count(); + +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + QString debug_print__log__; +#endif + // 本列第一、二行占比 + SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None; + SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item + for (int i = 0; i < itemCount; ++i) { + SARibbonPannelItem* item = mItems.at(i); + if (item->isEmpty()) { + // 如果是hide就直接跳过 + item->rowIndex = -1; + item->columnIndex = -1; + continue; + } + + QSize hint = item->sizeHint(); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) { + auto ss__ = tb->sizeHint(); + debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n") + .arg(i) + .arg(ss__.width()) + .arg(ss__.height()) + .arg(tb->text()); + } +#endif + Qt::Orientations exp = item->expandingDirections(); + if (item->widget()) { + // 有窗口是水平扩展,则标记为扩展 + if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { + mExpandFlag = true; + } + } + SARibbonPannelItem::RowProportion rp = item->rowProportion; + if (SARibbonPannelItem::None == rp) { + // 为定义行占比但是垂直扩展,就定义为Large占比,否则就是small占比 + if (exp & Qt::Vertical) { + rp = SARibbonPannelItem::Large; + } else { + rp = SARibbonPannelItem::Small; + } + } + // 开始根据占比和layoutmode来布局 + switch (rp) { + case SARibbonPannelItem::Large: { + // !!在Large,如果不是处于新列的第一行,就需要进行换列处理 + // 把large一直设置在下一列的开始 + if (row != 0) { + x += (columMaxWidth + spacing); + ++column; + } + // + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight); + columMaxWidth = hint.width(); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } break; + + case SARibbonPannelItem::Medium: { + // 2行模式下Medium和small等价 + if (2 == rowCount) { + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 3行模式 + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + // x不变 + } else if (1 == row) { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium + // 这时需要先换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + ++column; + // 换列后此时等价于0 == row + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + } + } + } break; + + case SARibbonPannelItem::Small: { + if (0 == row) { + // 第一行 + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Small; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else if (1 == row) { + // 第二行 + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight); + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + } + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 这里要看两行还是三行,确定是否要换列 + if (2 == rowCount) { + // 两行模式,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 三行模式,继续增加行数 + row = 2; + // x不变 + } + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 第三行 + item->rowIndex = 2; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } break; + + default: + // 不可能出现 + break; + } + lastGeomItem = item; + } + // 最后一个元素,更新列数 + // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常 + if (lastGeomItem) { // 最后一个元素,更新totalWidth + if (lastGeomItem->columnIndex != column) { + // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引 + mColumnCount = column; + // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth要减去已经加入的spacing + totalWidth = x - spacing + mag.right(); + } else { + // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1 + mColumnCount = column + 1; + // 由于最后一个元素未触发换列,需要计算totalWidth + totalWidth = x + columMaxWidth + mag.right(); + } + } + + // 在设置完所有窗口后,再设置扩展属性的窗口 + if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) { + // 说明可以设置扩展属性的窗口 + recalcExpandGeomArray(setrect); + } + // 布局label + bool isTitleWidthThanPannel = false; + if (isEnableShowPannelTitle()) { + mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH); + // 这里要确认标题宽度是否大于totalWidth,如果大于,则要把标题的宽度作为totalwidth + QFontMetrics fm = mTitleLabel->fontMetrics(); + int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName()); + textWidth += 4; + if (totalWidth < textWidth) { + totalWidth = textWidth; + isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度 + } + } + // 布局optionActionButton + + if (isHaveOptionAction()) { + QSize optBtnSize = optionActionButtonSize(); + if (isEnableShowPannelTitle()) { + // 有标题 + mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(), + mTitleLabelGeometry.y(), + mTitleLabelGeometry.height(), + mTitleLabelGeometry.height()); + + // 特殊情况,如果pannel的标题长度大于totalWidth,那么说明totalWidth比较短 + // 这时候,optionActionBtn的宽度要加上到标题宽度上 + if (isTitleWidthThanPannel) { + // 由于文字是居中对齐,因此要扩展2个按钮的宽度 + totalWidth += (2 * titleH); + } + } else { + // 无标题 + mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(), + setrect.bottom() - optBtnSize.height() - mag.bottom(), + optBtnSize.width(), + optBtnSize.height()); + totalWidth += optBtnSize.width(); + } + } + // 刷新sizeHint + int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH); + this->mSizeHint = QSize(totalWidth, heightHint); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName() + << "\n| | |-size hint =" << this->m_sizeHint // + << "\n| | |-totalWidth=" << totalWidth // + << "\n| | |-last x=" << x // + << "\n| | |-columMaxWidth=" << columMaxWidth // + << "\n| | |-spacing=" << spacing // + << "\n| | |-mag=" << mag // + << "\n| | |-largeHeight=" << largeHeight // + << "\n| | |-smallHeight=" << smallHeight // + ; + qDebug().noquote() << debug_print__log__; +#endif +} + +void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect) +{ + // 计算能扩展的尺寸 + int expandwidth = setrect.width() - this->mSizeHint.width(); + + if (expandwidth <= 0) { + // 没有必要设置 + return; + } + // 列扩展信息 + struct _columnExpandInfo + { + int oldColumnWidth = 0; ///< 原来的列宽 + int columnMaximumWidth = -1; ///< 列的最大宽度 + int columnExpandedWidth = 0; ///< 扩展后列的宽度 + QList< SARibbonPannelItem* > expandItems; + }; + // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展 + QMap< int, _columnExpandInfo > columnExpandInfo; + + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) { + // 只获取可见的 + QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex); + if (i == columnExpandInfo.end()) { + i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo()); + } + i.value().expandItems.append(item); + } + } + if (columnExpandInfo.size() <= 0) { + // 没有需要扩展的就退出 + return; + } + // 获取完可扩展的列和控件后,计算对应的列的尺寸 + // 计算能扩展的尺寸 + int oneColCanexpandWidth = expandwidth / columnExpandInfo.size(); + + for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) { + int& oldColumnWidth = i.value().oldColumnWidth; + int& columnMaximumWidth = i.value().columnMaximumWidth; + this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth); + if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) { + // 如果小于0说明没有这个列,这种属于异常,删除继续 + // oldColumnWidth > columnMaximumWidth也是异常 + i = columnExpandInfo.erase(i); + continue; + } + // 开始调整 + int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了 + if (colwidth >= columnMaximumWidth) { + // 过最大宽度要求 + i.value().columnExpandedWidth = columnMaximumWidth; + } else { + i.value().columnExpandedWidth = colwidth; + } + ++i; + } + // 从新调整尺寸 + // 由于会涉及其他列的变更,因此需要所有都遍历一下 + for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) { + int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth; + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if (item->isEmpty() || (item->columnIndex < i.key())) { + // 之前的列不用管 + continue; + } + if (item->columnIndex == i.key()) { + // 此列的扩展 + if (i.value().expandItems.contains(item)) { + // 此列需要扩展的item才扩展尺寸 + item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth); + } else { + // 此列不扩展的模块保持原来的尺寸 + continue; + } + } else { + // 后面的移动 + item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen); + } + } + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect + << ") pannelName=" << ribbonPannel()->pannelName() // + << ",expandwidth=" << expandwidth // + ; +#endif +} + +/** + * @brief 根据列数,计算窗口的宽度,以及最大宽度 + * @param colindex + * @param width 如果传入没有这个列,返回-1 + * @param maximum 如果传入没有这个列,返回-1 + */ +void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const +{ + width = -1; + maximum = -1; + for (SARibbonPannelItem* item : mItems) { + if (!item->isEmpty() && (item->columnIndex == colindex)) { + width = qMax(width, item->itemWillSetGeometry.width()); + maximum = qMax(maximum, item->widget()->maximumWidth()); + } + } +} + +SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const +{ + return mTitleLabel; +} + +void SARibbonPannelLayout::setToolButtonIconSize(const QSize& s) +{ + mDefaultToolButtonIconSize = s; +} + +QSize SARibbonPannelLayout::toolButtonIconSize() const +{ + return mDefaultToolButtonIconSize; +} + +/** + * @brief 获取optionAction 按钮尺寸 + * @return + */ +QSize SARibbonPannelLayout::optionActionButtonSize() const +{ + return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight)); +} + +void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel) +{ + mTitleLabel = newTitleLabel; + // 确保m_optionActionBtn在label之上 + if (mOptionActionBtn) { + if (mTitleLabel) { + mTitleLabel->stackUnder(mOptionActionBtn); + } + } +} + +/** + * @brief 标题区域和按钮的间隔 + * @return + */ +int SARibbonPannelLayout::pannelTitleSpace() const +{ + return mTitleSpace; +} + +/** + * @brief 设置标题区域和按钮的间隔 + * @param newTitleSpace + */ +void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace) +{ + if (mTitleSpace == newTitleSpace) { + return; + } + mTitleSpace = newTitleSpace; + invalidate(); +} + +/** + * @brief 标题高度 + * @return + */ +int SARibbonPannelLayout::pannelTitleHeight() const +{ + return mTitleHeight; +} + +/** + * @brief 设置标题高度 + * @param newTitleHeight + */ +void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight) +{ + if (mTitleHeight == newTitleHeight) { + return; + } + mTitleHeight = newTitleHeight; + invalidate(); +} + +/** + * @brief 判断是否存在标题 + * @return + */ +bool SARibbonPannelLayout::isEnableShowPannelTitle() const +{ + return mEnableShowTitle; +} + +/** + * @brief 设置显示标题 + * @param on + */ +void SARibbonPannelLayout::setEnableShowPannelTitle(bool on) +{ + if (mEnableShowTitle == on) { + return; + } + mEnableShowTitle = on; + invalidate(); +} + +/** + * @brief 大按钮的高度 + * @return + */ +int SARibbonPannelLayout::largeButtonHeight() const +{ + return mLargeHeight; +} + +void SARibbonPannelLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")======="; +#endif + QLayout::setGeometry(rect); + mDirty = false; + updateGeomArray(rect); + doLayout(); +} + +/*** End of inlined file: SARibbonPannelLayout.cpp ***/ + +/*** Start of inlined file: SARibbonPannel.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SARibbonPannel_DEBUG_PRINT +#define SARibbonPannel_DEBUG_PRINT 0 +#endif + +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) +//=============================================================== +// SARibbonPannelLabel +//=============================================================== + +SARibbonPannelLabel::SARibbonPannelLabel(QWidget* parent) : QLabel(parent) +{ +} + +//=============================================================== +// SARibbonPannel::PrivateData +//=============================================================== + +class SARibbonPannel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonPannel) +public: + PrivateData(SARibbonPannel* p); + // 根据m_pannelLayoutMode返回gridLayout应该增加的行数 + int rowadded(); + void createLayout(); + // 获取layout + SARibbonPannelLayout* pannelLayout() const; + // 返回最后一个添加的action对应的button,前提是最后一个是toolbutton,否则返回nullptr + SARibbonToolButton* lastAddActionButton(); + // 重置labe的字体,这个主要是为了让pannel的标题字体能适应标题高度 + void resetTitleLabelFont(); + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + +public: + bool m_isCanCustomize { true }; ///< 记录是否可自定义 + SARibbonPannel::PannelLayoutMode m_pannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< pannel的布局模式,默认为3行模式ThreeRowMode + SARibbonPannelOptionButton* m_optionActionButton { nullptr }; ///< 标题栏的y距离 + SARibbonPannelLabel* m_label { nullptr }; +}; + +SARibbonPannel::PrivateData::PrivateData(SARibbonPannel* p) : q_ptr(p) +{ + createLayout(); +} + +int SARibbonPannel::PrivateData::rowadded() +{ + switch (m_pannelLayoutMode) { + case SARibbonPannel::ThreeRowMode: + return (2); + + case SARibbonPannel::TwoRowMode: + return (3); + + default: + break; + } + return (2); +} + +void SARibbonPannel::PrivateData::createLayout() +{ + m_label = new SARibbonPannelLabel(q_ptr); + m_label->setAlignment(Qt::AlignCenter); + SARibbonPannelLayout* lay = new SARibbonPannelLayout(q_ptr); + lay->setPannelTitleLabel(m_label); + lay->setSpacing(2); + lay->setContentsMargins(2, 2, 2, 2); +} + +SARibbonPannelLayout* SARibbonPannel::PrivateData::pannelLayout() const +{ + return qobject_cast< SARibbonPannelLayout* >(q_ptr->layout()); +} + +SARibbonToolButton* SARibbonPannel::PrivateData::lastAddActionButton() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (qobject_cast< SARibbonToolButton* >(lay->lastWidget())); + } + return (nullptr); +} + +/** + * @brief 重置label的字体 + * + * @note 此函数必须在布局设置后调用 + */ +void SARibbonPannel::PrivateData::resetTitleLabelFont() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + int h = lay->pannelTitleHeight(); + QFont f = q_ptr->font(); + f.setPixelSize(h * 0.8); + if (m_label) { + m_label->setFont(f); + } + } +} + +QString SARibbonPannel::PrivateData::pannelName() const +{ + if (m_label) { + return m_label->text(); + } + return (QString()); +} + +void SARibbonPannel::PrivateData::setPannelName(const QString& title) +{ + if (m_label) { + m_label->setText(title); + } +} + +//================================================== +// SARibbonPannel +//================================================== + +SARibbonPannel::SARibbonPannel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); +} + +SARibbonPannel::SARibbonPannel(const QString& name, QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); + setPannelName(name); +} + +SARibbonPannel::~SARibbonPannel() +{ +} + +/** + * @brief 把action的行属性设置进action中,action自身携带了行属性 + * @param action + * @param rp + */ +void SARibbonPannel::setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp)); +} + +/** + * @brief 获取action的行属性 + * @param action + * @return + */ +SARibbonPannelItem::RowProportion SARibbonPannel::getActionRowProportionProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok); + + if (isok) { + return (static_cast< SARibbonPannelItem::RowProportion >(r)); + } + return (SARibbonPannelItem::Large); +} + +/** + * @brief 设置action的ToolButtonPopupMode属性 + * @param action + * @param popMode + */ +void SARibbonPannel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode)); +} + +/** + * @brief 获取action的ToolButtonPopupMode属性 + * @param action + * @return + */ +QToolButton::ToolButtonPopupMode SARibbonPannel::getActionToolButtonPopupModeProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok); + + if (isok) { + return (static_cast< QToolButton::ToolButtonPopupMode >(r)); + } + return (QToolButton::InstantPopup); +} + +/** + * @brief 设置action的ToolButtonStyle属性 + * @param action + * @param buttonStyle + */ +void SARibbonPannel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle)); +} + +/** + * @brief 获取action的ToolButtonStyle属性 + * @param action + * @return + */ +Qt::ToolButtonStyle SARibbonPannel::getActionToolButtonStyleProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok); + + if (isok) { + return (static_cast< Qt::ToolButtonStyle >(r)); + } + return (Qt::ToolButtonIconOnly); +} + +/** + * @brief 添加action + * + * action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + * @param action + * @param rp 指定action的行占比 + * @param popMode 菜单弹出样式 + */ +void SARibbonPannel::addAction(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + setActionRowProportionProperty(action, rp); + addAction(action); +} + +/** + * @brief 添加一个action + * @param act + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + */ +void SARibbonPannel::addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(act); + setActionRowProportionProperty(act, rp); + setActionToolButtonPopupModeProperty(act, popMode); + addAction(act); +} + +/** + @brief 添加大图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addLargeAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Large); +} + +/** + @brief 在三栏模式下,强制加为2栏action + @note 在两行模式下,Medium和Small等价 + 主要应用在ThreeRowMode下 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addMediumAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Medium); +} + +/** + @brief 添加小图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addSmallAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Large); +} + +void SARibbonPannel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Medium); +} + +/** + * @brief 生成并添加一个action + * + * 如果不对此action做操作,SARibbonPannel将管理此action + * + * @note action的父对象将设置为SARibbonPannel,SARibbonPannel在删除时将会删除子对象,会把这个action也删除, + * 如果不想此action也删除,需要对action重新设置父对象 + * + * @param text action的文字 + * @param icon action的图标 + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + * @return 返回添加的action + */ +QAction* SARibbonPannel::addAction(const QString& text, + const QIcon& icon, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp) +{ + QAction* action = new QAction(icon, text, this); + addAction(action, popMode, rp); + return (action); +} + +/** + * @brief 添加一个普通菜单 + * @param menu + * @param rp + * @param popMode,菜单弹出模式,默认InstantPopup模式 + * @return + */ +void SARibbonPannel::addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(menu); + QAction* action = menu->menuAction(); + action->setIcon(menu->icon()); + action->setText(menu->title()); + action->setObjectName("action." + menu->objectName()); + addAction(action, popMode, rp); +} + +void SARibbonPannel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Large, popMode); +} + +void SARibbonPannel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Small, popMode); +} + +/** + * @brief 添加窗口 + * + * @param w + * @param rp + * @return 返回action(QWidgetAction) + * @note SARibbonPannel并不会管理此窗口内存,在delete SARibbonPannel时,此窗口如果父对象不是SARibbonPannel将不会被删除 + */ +QAction* SARibbonPannel::addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp) +{ + QWidgetAction* action = new QWidgetAction(this); + + action->setDefaultWidget(w); + action->setIcon(w->windowIcon()); + action->setText(w->windowTitle()); + action->setObjectName("action." + w->objectName()); + w->setAttribute(Qt::WA_Hover); + setActionRowProportionProperty(action, rp); + addAction(action); + return (action); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addSmallWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Small)); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return + */ +QAction* SARibbonPannel::addMediumWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Medium)); +} + +/** + * @brief 添加窗口,占用所有行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addLargeWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Large)); +} + +/** + * @brief SARibbonPannel::addGallery + * @return + * @note SARibbonPannel将拥有SARibbonGallery的管理权 + */ +SARibbonGallery* SARibbonPannel::addGallery(bool expanding) +{ + SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this); + + addWidget(gallery, SARibbonPannelItem::Large); + if (expanding) { + setExpanding(expanding); + } + return (gallery); +} + +/** + * @brief 添加分割线 + * @param top 上边距 @default 6 + * @param bottom 下边距 @default 6 + */ +QAction* SARibbonPannel::addSeparator() +{ + QAction* action = new QAction(this); + + action->setSeparator(true); + setActionRowProportionProperty(action, SARibbonPannelItem::Large); + addAction(action); + return (action); +} + +/** + * @brief 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + * @param action + * @return 如果action没有对应的button,就返回nullptr + */ +SARibbonToolButton* SARibbonPannel::actionToRibbonToolButton(QAction* action) +{ +#if 0 + SARibbonPannelLayout* lay = qobject_cast< SARibbonPannelLayout* >(layout()); + + if (lay) { + int index = lay->indexOf(action); + if (index == -1) { + return (nullptr); + } + QLayoutItem* item = lay->takeAt(index); + SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(item ? item->widget() : nullptr); + return (btn); + } + return (nullptr); +#else + for (auto obj : qAsConst(children())) { + if (obj->isWidgetType()) { + if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) { + if (btn->defaultAction() == action) { + return btn; + } + } + } + } + return (nullptr); +#endif +} + +/** + * @brief 获取pannel下面的所有toolbutton + * @return + */ +QList< SARibbonToolButton* > SARibbonPannel::ribbonToolButtons() const +{ + const QObjectList& objs = children(); + QList< SARibbonToolButton* > res; + + for (QObject* o : objs) { + SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o); + if (b) { + res.append(b); + } + } + return (res); +} + +/** + * @brief 设置PannelLayoutMode + * @param mode + */ +void SARibbonPannel::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode mode) +{ + if (d_ptr->m_pannelLayoutMode == mode) { + return; + } + d_ptr->m_pannelLayoutMode = mode; + updateItemGeometry(); +} + +SARibbonPannel::PannelLayoutMode SARibbonPannel::pannelLayoutMode() const +{ + return (d_ptr->m_pannelLayoutMode); +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannel::setOptionAction(QAction* action) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setOptionAction(action); + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannel::isHaveOptionAction() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isHaveOptionAction(); + } + return false; +} + +QSize SARibbonPannel::sizeHint() const +{ + int shWidth = 500; + int shHeight = 100; + if (QLayout* lay = layout()) { + QSize laySize = layout()->sizeHint(); + shWidth = laySize.width(); + shHeight = laySize.height(); + } + return QSize(shWidth, shHeight); +} + +QSize SARibbonPannel::minimumSizeHint() const +{ + return (layout()->minimumSize()); +} + +void SARibbonPannel::setToolButtonIconSize(const QSize& s) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setToolButtonIconSize(s); + } + // 对已经管理的QToolButton设置为iconsize + iterate([ s ](SARibbonToolButton* t) -> bool { + t->setIconSize(s); + return true; + }); +} + +QSize SARibbonPannel::toolButtonIconSize() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->toolButtonIconSize(); + } + return QSize(); +} + +/** + * @brief 遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 + * @param fp + * @return 返回false说明迭代中途退出,返回true则表示迭代完全 + */ +bool SARibbonPannel::iterate(SARibbonPannel::FpRibbonToolButtonIterate fp) +{ + const QObjectList& ols = children(); + for (QObject* obj : ols) { + if (SARibbonToolButton* t = qobject_cast< SARibbonToolButton* >(obj)) { + if (!fp(t)) { + return false; + } + } + } + return true; +} + +/** + * @brief 把pannel设置为扩展模式,此时会撑大水平区域 + * @param isExpanding + */ +void SARibbonPannel::setExpanding(bool isExpanding) +{ + setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +/** + * @brief 判断此pannel是否为(水平)扩展模式 + * @return 是扩展模式返回true + */ +bool SARibbonPannel::isExpanding() const +{ + QSizePolicy sp = sizePolicy(); + + return (sp.horizontalPolicy() == QSizePolicy::Expanding); +} + +/** + @brief 设置标题栏的高度 + @param h + */ +void SARibbonPannel::setTitleHeight(int h) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setPannelTitleHeight(h); + } +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonPannel::titleHeight() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->pannelTitleHeight(); + } + return -1; +} + +/** + * @brief 判断是否显示标题,只有标题的高度被设置,才会显示标题 + * @return + */ +bool SARibbonPannel::isEnableShowTitle() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isEnableShowPannelTitle(); + } + return false; +} + +/** + * @brief 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + * @param on + */ +void SARibbonPannel::setEnableShowTitle(bool on) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->setEnableShowPannelTitle(on); + } +} + +/** + * @brief action对应的布局index,此操作一般用于移动,其他意义不大 + * @param act + * @return 没有查到返回-1 + */ +int SARibbonPannel::actionIndex(QAction* act) const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->indexByAction(act)); + } + return (-1); +} + +/** + * @brief 移动action + * @param from 要移动action的位置,通过@ref actionIndex 获取 + * @param to 要移动的位置 + */ +void SARibbonPannel::moveAction(int from, int to) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->move(from, to)); + } + updateGeometry(); // 通知layout进行重新布局 +} + +/** + * @brief 判断是否可以自定义 + * @return + */ +bool SARibbonPannel::isCanCustomize() const +{ + return (d_ptr->m_isCanCustomize); +} + +/** + * @brief 设置是否可以自定义 + * @param b + */ +void SARibbonPannel::setCanCustomize(bool b) +{ + d_ptr->m_isCanCustomize = b; +} + +/** + * @brief pannel名称 + * @return + */ +QString SARibbonPannel::pannelName() const +{ + return (d_ptr->pannelName()); +} + +/** + * @brief pannel的名称 + * @param title 标题 + * @note 此函数会触发@sa pannelNameChanged 信号 + */ +void SARibbonPannel::setPannelName(const QString& title) +{ + QString oldName = pannelName(); + if (oldName != title) { + d_ptr->setPannelName(title); + Q_EMIT pannelNameChanged(title); + } +} + +/** + * @brief 获取大图标的高度 + * @return + */ +int SARibbonPannel::largeButtonHeight() const +{ + const QMargins& mag = contentsMargins(); + return height() - mag.top() - mag.bottom() - titleHeight(); +} + +/** + * @brief 获取pannel layout + * @return + */ +SARibbonPannelLayout* SARibbonPannel::pannelLayout() const +{ + return d_ptr->pannelLayout(); +} + +/** + * @brief 更新布局 + */ +void SARibbonPannel::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonPannel updateItemGeometry,pannelName=" << pannelName(); +#endif + // 此函数需要添加,否则SARibbonBar::setEnableWordWrap无法刷新按钮 + resetToolButtonSize(); + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->updateGeomArray(); + } +} + +/** + @brief 获取category指针,如果没有parent,或者不在category管理,返回nullptr + @return + */ +SARibbonCategory* SARibbonPannel::category() const +{ + return qobject_cast< SARibbonCategory* >(parent()); +} + +/** + @brief 获取ribbonBar指针,如果没有返回nullptr + @return + */ +SARibbonBar* SARibbonPannel::ribbonBar() const +{ + if (SARibbonCategory* c = category()) { + return c->ribbonBar(); + } + return nullptr; +} + +/** + * @brief 设置按钮之间的间隔 + * @param n + */ +void SARibbonPannel::setSpacing(int n) +{ + if (auto lay = layout()) { + lay->setSpacing(n); + } +} + +/** + * @brief 按钮之间的间隔 + * @return + */ +int SARibbonPannel::spacing() const +{ + if (auto lay = layout()) { + return lay->spacing(); + } + return 0; +} + +/** + * @brief pannel高度推荐 + * @param fm + * @param layMode + * @param pannelTitleHeight + * @return + */ +int SARibbonPannel::pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight) +{ + int textH = fm.lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 + switch (layMode) { + case SARibbonPannel::ThreeRowMode: { + // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 + return textH * 4.8 + pannelTitleHeight; + } break; + case SARibbonPannel::TwoRowMode: { + // 3=2*1.6 + return textH * 3.2 + pannelTitleHeight; + } break; + default: { + qWarning() << "unknow SARibbonPannel::PannelLayoutMode:" << layMode; + } + } + return (textH * 4.8 + pannelTitleHeight); +} + +/** + * @brief 重置按钮的尺寸,在布局改变后(尤其高度变更),按钮的尺寸需要手动变更 + */ +void SARibbonPannel::resetToolButtonSize() +{ + QList< SARibbonToolButton* > btns = ribbonToolButtons(); + + for (SARibbonToolButton* b : qAsConst(btns)) { + if ((nullptr == b)) { + continue; + } + b->updateRect(); + } +} + +bool SARibbonPannel::event(QEvent* e) +{ +#if SA_DEBUG_PRINT_EVENT + if (e->type() != QEvent::Paint) { + qDebug() << "SARibbonPannel event(" << e->type() << "),name=" << pannelName(); + } +#endif + // if (SARibbonPannelLayout* lay = pannelLayout()) { + // if (lay->isDirty() && e->type() == QEvent::LayoutRequest) { + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + // lay->m_dirty = false; + // } + // } + return QWidget::event(e); +} + +/** + * @brief 处理action的事件 + * + * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 + * + * ActionAdded时向布局请求,添加action,布局中同时触发了@ref SARibbonPannelLayout::createItem 函数 + * 此函数用于生成窗口,例如QRibbonToolButton + * + * ActionChanged时会让布局重新计算尺寸,并向category请求重新布局,有可能category的所有要重新调整尺寸 + * + * ActionRemoved会移除布局管理的QLayoutItem,并进行内存清理,这时窗口也会隐藏,同时销毁 + * + * @param e + * @note 所有action事件都会向category请求重新布局 + * + */ +void SARibbonPannel::actionEvent(QActionEvent* e) +{ + QAction* action = e->action(); + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action); + + switch (e->type()) { + case QEvent::ActionAdded: { + SARibbonPannelLayout* lay = pannelLayout(); + if (nullptr != widgetAction) { + if (widgetAction->parent() != this) { + widgetAction->setParent(this); + } + } + int index = layout()->count(); + if (e->before()) { + // 说明是插入 + index = lay->indexByAction(e->before()); + if (-1 == index) { + index = layout()->count(); // 找不到的时候就插入到最后 + } + } + lay->insertAction(index, action, getActionRowProportionProperty(action)); + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + case QEvent::ActionChanged: { + // 让布局重新绘制 + layout()->invalidate(); + + // updateGeometry(); + // 由于pannel的尺寸发生变化,需要让category也调整 + if (QWidget* parw = parentWidget()) { + if (QLayout* pl = parw->layout()) { + pl->invalidate(); + } + //! 强制发送一个resizeevent,让Category能重绘,如果没有这个函数,发现Category的layout虽然设置了invalidate(标记缓存失效) + //! 但并没有按顺序在pannel尺寸更新后更新Category的尺寸,导致有些pannel的尺寸识别出现异常 + //! 重打印信息上看,pannel的尺寸有进行更新,category的尺寸也进行了更新,但更新的次数和调用invalidate的次数不一样,需要手动触发ResizeEvent + //! 尝试过调用QEvent::LayoutRequest没有效果: + //! @code + //! QEvent* el = new QEvent(QEvent::LayoutRequest); + //! QApplication::postEvent(parw, el); + //! @endcode + //! + //! 调用parw->updateGeometry();也没有效果,目前看使用resizeevent是最有效果的 + //! + QResizeEvent* ersize = new QResizeEvent(parw->size(), QSize()); + QApplication::postEvent(parw, ersize); + } + } break; + + case QEvent::ActionRemoved: { + SARibbonPannelLayout* lay = pannelLayout(); + action->disconnect(this); + int index = lay->indexByAction(action); + if (index != -1) { + QLayoutItem* item = lay->takeAt(index); + delete item; + } + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + default: + break; + } +} + +/** + * @brief 追踪字体改变事件,把新字体设置到childwidget中 + * @param e + */ +void SARibbonPannel::changeEvent(QEvent* e) +{ + if (nullptr == e) { + return; + } + if (e->type() == QEvent::FontChange) { + QFont f = font(); + QList< QWidget* > listWidgets = findChildren< QWidget* >(); + for (QWidget* w : listWidgets) { + w->setFont(f); + } + if (QLayout* lay = layout()) { + lay->invalidate(); + } + d_ptr->resetTitleLabelFont(); + } + QWidget::changeEvent(e); +} + +/** + * @brief 获取布局item + * @return + */ +const QList< SARibbonPannelItem* >& SARibbonPannel::ribbonPannelItem() const +{ + return (pannelLayout()->mItems); +} + +/** + * @brief 获取最近添加的按钮 + * @return 刚才添加的按钮的指针 + */ +SARibbonToolButton* SARibbonPannel::lastAddActionButton() +{ + return d_ptr->lastAddActionButton(); +} + +/*** End of inlined file: SARibbonPannel.cpp ***/ + +/*** Start of inlined file: SARibbonCategory.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// \brief ribbon页的代理类 +/// 如果需要修改重绘SARibbonCategory,可以通过设置SARibbonCategory::setProxy +/// +class SARibbonCategory::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCategory) +public: + PrivateData(SARibbonCategory* p); + + SARibbonPannel* addPannel(const QString& title); + SARibbonPannel* insertPannel(const QString& title, int index); + void addPannel(SARibbonPannel* pannel); + void insertPannel(int index, SARibbonPannel* pannel); + + // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 + bool takePannel(SARibbonPannel* pannel); + + // 移除Pannel,Category会直接回收SARibbonPannel内存 + bool removePannel(SARibbonPannel* pannel); + SARibbonCategory* ribbonCategory(); + const SARibbonCategory* ribbonCategory() const; + + // 返回所有的Pannel + QList< SARibbonPannel* > pannelList(); + + // 更新item的布局,此函数会调用doItemLayout + void updateItemGeometry(); + + void doWheelEvent(QWheelEvent* event); + // 初始化 + void init(SARibbonCategory* c); + +public: + bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 + int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 + bool mIsContextCategory { false }; ///< 标记是否是上下文标签 + bool mIsCanCustomize { true }; ///< 标记是否可以自定义 + int mPannelSpacing { 0 }; ///< pannel的spacing + QSize mPannelToolButtonSize { 22, 22 }; ///< 记录pannel的默认图标大小 + SARibbonPannel::PannelLayoutMode mDefaultPannelLayoutMode { SARibbonPannel::ThreeRowMode }; +}; +SARibbonCategory::PrivateData::PrivateData(SARibbonCategory* p) : q_ptr(p) +{ +} + +SARibbonPannel* SARibbonCategory::PrivateData::addPannel(const QString& title) +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + SARibbonPannel* p = insertPannel(title, lay->pannelCount()); + return p; + } + return nullptr; +} + +SARibbonPannel* SARibbonCategory::PrivateData::insertPannel(const QString& title, int index) +{ + SARibbonPannel* pannel = RibbonSubElementFactory->createRibbonPannel(ribbonCategory()); + + pannel->setPannelName(title); + pannel->setObjectName(title); + insertPannel(index, pannel); + return (pannel); +} + +void SARibbonCategory::PrivateData::addPannel(SARibbonPannel* pannel) +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + insertPannel(lay->pannelCount(), pannel); + } +} + +/** + * @brief 插入pannel到layout + * + * 所有的添加操作最终会调用此函数 + * @param index + * @param pannel + */ +void SARibbonCategory::PrivateData::insertPannel(int index, SARibbonPannel* pannel) +{ + if (nullptr == pannel) { + return; + } + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (nullptr == lay) { + return; + } + if (pannel->parentWidget() != q_ptr) { + pannel->setParent(q_ptr); + } + // 同步状态 + pannel->setEnableShowTitle(mEnableShowPannelTitle); + pannel->setTitleHeight(mPannelTitleHeight); + pannel->setPannelLayoutMode(mDefaultPannelLayoutMode); + pannel->setSpacing(mPannelSpacing); + pannel->setToolButtonIconSize(mPannelToolButtonSize); + index = qMax(0, index); + index = qMin(lay->pannelCount(), index); + lay->insertPannel(index, pannel); + pannel->setVisible(true); + + QObject::connect(pannel, &SARibbonPannel::actionTriggered, ribbonCategory(), &SARibbonCategory::actionTriggered); +} + +bool SARibbonCategory::PrivateData::takePannel(SARibbonPannel* pannel) +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (nullptr == lay) { + return false; + } + return lay->takePannel(pannel); +} + +bool SARibbonCategory::PrivateData::removePannel(SARibbonPannel* pannel) +{ + if (takePannel(pannel)) { + pannel->hide(); + pannel->deleteLater(); + return (true); + } + return (false); +} + +QList< SARibbonPannel* > SARibbonCategory::PrivateData::pannelList() +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + return lay->pannelList(); + } + return QList< SARibbonPannel* >(); +} + +SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() +{ + return (q_ptr); +} + +const SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() const +{ + return (q_ptr); +} + +void SARibbonCategory::PrivateData::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory::PrivateData::updateItemGeometry,categoryName=" << q_ptr->categoryName(); +#endif + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (!lay) { + return; + } + const QList< SARibbonPannel* > pannels = lay->pannelList(); + for (auto pannel : pannels) { + pannel->updateItemGeometry(); + } + lay->updateGeometryArr(); + return; +} + +void SARibbonCategory::PrivateData::doWheelEvent(QWheelEvent* event) +{ + SARibbonCategoryLayout* lay = q_ptr->categoryLayout(); + if (nullptr == lay) { + return; + } + QSize contentSize = lay->categoryContentSize(); + // 求总宽 + int totalWidth = lay->categoryTotalWidth(); + + if (totalWidth > contentSize.width()) { + // 这个时候滚动有效 + int scrollpix = 40; + // Qt6 取消了QWheelEvent::delta函数 + // 是要下面方法可兼容qt5/6 + QPoint numPixels = event->pixelDelta(); + QPoint numDegrees = event->angleDelta() / 8; + if (!numPixels.isNull()) { + if (numDegrees.y() < 0) { + scrollpix = -scrollpix; + } + } else if (!numDegrees.isNull()) { + if (numDegrees.y() < 0) { + scrollpix = -scrollpix; + } + } + lay->scroll(scrollpix); + } else { + // 这时候无需处理事件,把滚动事件上发让父级也能接收 + event->ignore(); + // 如滚动过就还原 + if (lay->isScrolled()) { + lay->scroll(0); + } + } +} + +void SARibbonCategory::PrivateData::init(SARibbonCategory* c) +{ + c->setLayout(new SARibbonCategoryLayout(c)); + c->connect(c, &SARibbonCategory::windowTitleChanged, c, &SARibbonCategory::categoryNameChanged); +} + +//---------------------------------------------------- +// SARibbonCategory +//---------------------------------------------------- + +SARibbonCategory::SARibbonCategory(QWidget* p) : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) +{ + d_ptr->init(this); +} + +SARibbonCategory::SARibbonCategory(const QString& name, QWidget* p) + : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) +{ + setCategoryName(name); + d_ptr->init(this); +} + +SARibbonCategory::~SARibbonCategory() +{ +} + +/** + * @brief category的名字,等同windowTitle函数 + * @return + */ +QString SARibbonCategory::categoryName() const +{ + return (windowTitle()); +} + +/** + * @brief 设置category名字,等同setWindowTitle + * @param title + */ +void SARibbonCategory::setCategoryName(const QString& title) +{ + setWindowTitle(title); +} + +bool SARibbonCategory::event(QEvent* e) +{ +#if SA_DEBUG_PRINT_EVENT + if (e->type() != QEvent::Paint) { + qDebug() << "SARibbonCategory event(" << e->type() << "),name=" << categoryName(); + } +#endif + return QWidget::event(e); +} + +/** + * @brief pannel的模式 + * @return + */ +SARibbonPannel::PannelLayoutMode SARibbonCategory::pannelLayoutMode() const +{ + return (d_ptr->mDefaultPannelLayoutMode); +} + +/** + * @brief 设置pannel的模式 + * + * 在@ref SARibbonBar 调用@ref SARibbonBar::setRibbonStyle 函数时,会对所有的SARibbonCategory调用此函数 + * 把新的SARibbonPannel::PannelLayoutMode设置进去 + * @param m + */ +void SARibbonCategory::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) +{ + d_ptr->mDefaultPannelLayoutMode = m; + iterate([ m ](SARibbonPannel* p) -> bool { + p->setPannelLayoutMode(m); + return true; + }); + updateItemGeometry(); +} + +/** + * @brief 添加pannel + * + * @note pannel的所有权由SARibbonCategory来管理,请不要在外部对其进行销毁 + * @param title pannel的标题,在office/wps的三行模式下会显示在pannel的下方 + * @return 返回生成的@ref SARibbonPannel 指针 + * @see 对SARibbonPannel的其他操作,参考 @ref SARibbonCategory::takePannel + */ +SARibbonPannel* SARibbonCategory::addPannel(const QString& title) +{ + return (d_ptr->addPannel(title)); +} + +/** + * @brief 添加pannel + * @param pannel pannel的所有权SARibbonCategory来管理 + */ +void SARibbonCategory::addPannel(SARibbonPannel* pannel) +{ + d_ptr->addPannel(pannel); +} + +/** + * @brief qt designer专用 + * @param pannel + */ +void SARibbonCategory::addPannel(QWidget* pannel) +{ + SARibbonPannel* p = qobject_cast< SARibbonPannel* >(pannel); + if (p) { + addPannel(p); + } +} + +/** + * @brief 新建一个pannel,并插入到index位置 + * @param title pannel的title + * @param index 插入的位置,如果index超出category里pannel的个数,将插入到最后 + * @return 返回生成的@ref SARibbonPannel 指针 + * @note 如果 + */ +SARibbonPannel* SARibbonCategory::insertPannel(const QString& title, int index) +{ + return (d_ptr->insertPannel(title, index)); +} + +/** + * @brief 通过名字查找pannel + * @param title + * @return 如果有重名,只会返回第一个符合条件的 + */ +SARibbonPannel* SARibbonCategory::pannelByName(const QString& title) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByName(title); + } + return nullptr; +} + +/** + * @brief 通过ObjectName查找pannel + * @param objname + * @return + */ +SARibbonPannel* SARibbonCategory::pannelByObjectName(const QString& objname) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByObjectName(objname); + } + return nullptr; +} + +/** + * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr + * @param index + * @return 如果超过索引范围,会返回nullptr + */ +SARibbonPannel* SARibbonCategory::pannelByIndex(int index) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByIndex(index); + } + return nullptr; +} + +/** + * @brief 查找pannel对应的索引 + * @param p + * @return 如果找不到,返回-1 + */ +int SARibbonCategory::pannelIndex(SARibbonPannel* p) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelIndex(p); + } + return -1; +} + +/** + * @brief 移动一个Pannel从from index到to index + * @param from 要移动pannel的index + * @param to 要移动到的位置 + */ +void SARibbonCategory::movePannel(int from, int to) +{ + if (from == to) { + return; + } + if (to < 0) { + to = 0; + } + if (to >= pannelCount()) { + to = pannelCount() - 1; + } + if (SARibbonCategoryLayout* lay = categoryLayout()) { + lay->movePannel(from, to); + } +} + +/** + * @brief 把pannel脱离SARibbonCategory的管理 + * @param pannel 需要提取的pannel + * @return 成功返回true,否则返回false + */ +bool SARibbonCategory::takePannel(SARibbonPannel* pannel) +{ + return (d_ptr->takePannel(pannel)); +} + +/** + * @brief 移除Pannel,Category会直接回收SARibbonPannel内存 + * @param pannel 需要移除的pannel + * @note 移除后pannel为野指针,一般操作完建议把pannel指针设置为nullptr + * + * 此操作等同于: + * + * @code + * SARibbonPannel* pannel; + * ... + * category->takePannel(pannel); + * pannel->hide(); + * pannel->deleteLater(); + * @endcode + */ +bool SARibbonCategory::removePannel(SARibbonPannel* pannel) +{ + return (d_ptr->removePannel(pannel)); +} + +/** + * @brief 移除pannel + * @param index pannel的索引,如果超出会返回false + * @return 成功返回true + */ +bool SARibbonCategory::removePannel(int index) +{ + SARibbonPannel* p = pannelByIndex(index); + + if (nullptr == p) { + return (false); + } + return (removePannel(p)); +} + +/** + * @brief 返回Category下的所有pannel + * @return + */ +QList< SARibbonPannel* > SARibbonCategory::pannelList() const +{ + return (d_ptr->pannelList()); +} + +QSize SARibbonCategory::sizeHint() const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->sizeHint(); + } + return QSize(1000, 100); +} + +/** + * @brief 如果是ContextCategory,此函数返回true + * @return + */ +bool SARibbonCategory::isContextCategory() const +{ + return (d_ptr->mIsContextCategory); +} + +/** + * @brief 返回pannel的个数 + * @return + */ +int SARibbonCategory::pannelCount() const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelCount(); + } + return -1; +} + +/** + * @brief 判断是否可以自定义 + * @return + */ +bool SARibbonCategory::isCanCustomize() const +{ + return (d_ptr->mIsCanCustomize); +} + +/** + * @brief 设置是否可以自定义 + * @param b + */ +void SARibbonCategory::setCanCustomize(bool b) +{ + d_ptr->mIsCanCustomize = b; +} + +/** + * @brief pannel标题栏的高度 + * @return + */ +int SARibbonCategory::pannelTitleHeight() const +{ + return d_ptr->mPannelTitleHeight; +} +/** + * @brief 设置pannel的高度 + * @param h + */ +void SARibbonCategory::setPannelTitleHeight(int h) +{ + d_ptr->mPannelTitleHeight = h; + iterate([ h ](SARibbonPannel* p) -> bool { + p->setTitleHeight(h); + return true; + }); +} + +/** + * @brief 是否pannel显示标题栏 + * @return + */ +bool SARibbonCategory::isEnableShowPannelTitle() const +{ + return d_ptr->mEnableShowPannelTitle; +} + +/** + * @brief 设置显示pannel标题 + * @param on + */ +void SARibbonCategory::setEnableShowPannelTitle(bool on) +{ + d_ptr->mEnableShowPannelTitle = on; + iterate([ on ](SARibbonPannel* p) -> bool { + p->setEnableShowTitle(on); + return true; + }); +} + +/** + * @brief 获取对应的ribbonbar + * @return 如果没有加入ribbonbar的管理,此值为null + */ +SARibbonBar* SARibbonCategory::ribbonBar() const +{ + // 第一个par是stackwidget + if (QWidget* par = parentWidget()) { + // 理论此时是ribbonbar + par = par->parentWidget(); + while (par) { + if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(par)) { + return ribbon; + } + par = par->parentWidget(); + } + } + return nullptr; +} + +/** + * @brief 刷新category的布局,适用于改变ribbon的模式之后调用 + */ +void SARibbonCategory::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory name=" << categoryName() << " updateItemGeometry"; +#endif + d_ptr->updateItemGeometry(); +} + +/** + * @brief 此函数会遍历Category下的所有pannel,执行函数指针 + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonCategory::iterate(FpPannelIterate fp) +{ + const QList< SARibbonPannel* > ps = pannelList(); + for (SARibbonPannel* p : ps) { + if (!fp(p)) { + return false; + } + } + return true; +} + +/** + @brief 设置Category的对齐方式 + @param al + */ +void SARibbonCategory::setCategoryAlignment(SARibbonAlignment al) +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); + if (lay) { + lay->setCategoryAlignment(al); + } +} + +/** + @brief Category的对齐方式 + @return + */ +SARibbonAlignment SARibbonCategory::categoryAlignment() const +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); + if (lay) { + return lay->categoryAlignment(); + } + return SARibbonAlignment::AlignLeft; +} + +/** + * @brief 设置pannel的spacing + * @param n + */ +void SARibbonCategory::setPannelSpacing(int n) +{ + d_ptr->mPannelSpacing = n; + iterate([ n ](SARibbonPannel* pannel) -> bool { + if (pannel) { + pannel->setSpacing(n); + } + return true; + }); +} + +/** + * @brief pannel的spacing + * @return + */ +int SARibbonCategory::pannelSpacing() const +{ + return d_ptr->mPannelSpacing; +} + +/** + * @brief 设置pannel按钮的icon尺寸,large action不受此尺寸影响 + * @param s + */ +void SARibbonCategory::setPannelToolButtonIconSize(const QSize& s) +{ + d_ptr->mPannelToolButtonSize = s; + iterate([ s ](SARibbonPannel* pannel) -> bool { + if (pannel) { + pannel->setToolButtonIconSize(s); + } + return true; + }); +} + +/** + * @brief pannel按钮的icon尺寸,large action不受此尺寸影响 + * @return + */ +QSize SARibbonCategory::pannelToolButtonIconSize() const +{ + return d_ptr->mPannelToolButtonSize; +} + +/** + * @brief 在超出边界情况下,滚轮可滚动pannel + * @param event + */ +void SARibbonCategory::wheelEvent(QWheelEvent* event) +{ + d_ptr->doWheelEvent(event); +} + +void SARibbonCategory::changeEvent(QEvent* event) +{ + switch (event->type()) { + case QEvent::StyleChange: { + if (layout()) { +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory changeEvent(StyleChange),categoryName=" << categoryName(); +#endif + layout()->invalidate(); + } + } break; + case QEvent::FontChange: { +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory changeEvent(FontChange),categoryName=" << categoryName(); +#endif + QFont f = font(); + iterate([ f ](SARibbonPannel* p) -> bool { + p->setFont(f); + return true; + }); + if (layout()) { + layout()->invalidate(); + } + } break; + default: + break; + }; + return QWidget::changeEvent(event); +} + +/** + * @brief 标记这个是上下文标签 + * @param isContextCategory + */ +void SARibbonCategory::markIsContextCategory(bool isContextCategory) +{ + d_ptr->mIsContextCategory = isContextCategory; +} + +/** + * @brief 获取SARibbonCategoryLayoutlayout + * @return + */ +SARibbonCategoryLayout* SARibbonCategory::categoryLayout() const +{ + return qobject_cast< SARibbonCategoryLayout* >(layout()); +} + +//=================================================== +// SARibbonCategoryScrollButton +//=================================================== +SARibbonCategoryScrollButton::SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p) : QToolButton(p) +{ + setArrowType(arr); +} + +SARibbonCategoryScrollButton::~SARibbonCategoryScrollButton() +{ +} + +/*** End of inlined file: SARibbonCategory.cpp ***/ + +/*** Start of inlined file: SARibbonCategoryLayout.cpp ***/ +#include + +#include +#include + +#ifndef SARibbonCategoryLayout_DEBUG_PRINT +#define SARibbonCategoryLayout_DEBUG_PRINT 1 +#endif +/** + * @brief The SARibbonCategoryLayoutPrivate class + */ +class SARibbonCategoryLayout::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout) +public: + PrivateData(SARibbonCategoryLayout* p); + // 计算所有元素的sizehint总宽度 + int totalSizeHintWidth() const; + +public: + bool mDirty { true }; + bool mIsRightScrollBtnShow { false }; ///< 标记右滚动按钮是否需要显示 + bool mIsLeftScrollBtnShow { false }; ///< 标记左滚动按钮是否需要显示 + SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + SARibbonCategoryScrollButton* mRightScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + int mTotalWidth { 0 }; + int mXBase { 0 }; + QSize mSizeHint; + QSize mMinSizeHint; + QList< SARibbonCategoryLayoutItem* > mItemList; + SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 +}; + +//============================================================= +// SARibbonCategoryLayoutPrivate +//============================================================= + +SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p) +{ +} + +/** + * @brief 计算所有元素的SizeHint宽度总和 + * @return + */ +int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const +{ + int total = 0; + QMargins mag = q_ptr->contentsMargins(); +#if SA_DEBUG_PRINT_SIZE_HINT + int debug_i__ = 0; + QString debug_totalSizeHintWidth__; +#endif + if (!mag.isNull()) { + total += (mag.left() + mag.right()); + } + // 先计算总长 + for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) { + if (item->isEmpty()) { +// 如果是hide就直接跳过 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += + QString(" [%1](%2)is empty skip\n").arg(debug_i__).arg(item->toPannelWidget()->pannelName()); +#endif + continue; + } + // 这里要使用widget()->sizeHint(),因为pannel的标题会影总体布局,此处需要修改 + // TODO + QSize pannelSize = item->widget()->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + total += pannelSize.width(); + total += SeparatorSize.width(); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += QString("|-[%1]pannelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n") + .arg(debug_i__) + .arg(pannelSize.width()) + .arg(pannelSize.height()) + .arg(SeparatorSize.width()) + .arg(SeparatorSize.height()) + .arg(item->toPannelWidget()->pannelName()); +#endif + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total; + qDebug().noquote() << debug_totalSizeHintWidth__; +#endif + return (total); +} + +//============================================================= +// SARibbonCategoryLayout +//============================================================= + +SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent) + : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this)) +{ + setContentsMargins(1, 1, 1, 1); + d_ptr->mLeftScrollBtn = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent); + d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent); + d_ptr->mLeftScrollBtn->setVisible(false); + d_ptr->mRightScrollBtn->setVisible(false); + connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked); + connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked); +} + +SARibbonCategoryLayout::~SARibbonCategoryLayout() +{ + while (auto item = takePannelItem(0)) { + delete item; + } +} + +SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const +{ + return (qobject_cast< SARibbonCategory* >(parentWidget())); +} + +void SARibbonCategoryLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPannel instead"); + invalidate(); +} + +/** + * @brief 返回pannel的layout + * @param index 索引 + * @return + * @note 注意,pannel是和分割线一起的,但这个只返回一个pannel对应的layout + */ +QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const +{ + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr); + + return (item); +} + +/** + * @brief 提取layout + * @param index + * @return + */ +QLayoutItem* SARibbonCategoryLayout::takeAt(int index) +{ + QLayoutItem* r = takePannelItem(index); + invalidate(); + return r; +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(int index) +{ + if ((index >= 0) && (index < d_ptr->mItemList.size())) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index); + if (item->widget()) { + item->widget()->hide(); + } + if (item->separatorWidget) { + item->separatorWidget->hide(); + } + return (item); + } + return (nullptr); +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(SARibbonPannel* pannel) +{ + for (int i = 0; i < d_ptr->mItemList.size(); ++i) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ]; + if (item->widget() == pannel) { + return (takePannelItem(i)); + } + } + return (nullptr); +} + +/** + * @brief 移除pannel,对应的分割线也会删除 + * @param pannel + * @return + */ +bool SARibbonCategoryLayout::takePannel(SARibbonPannel* pannel) +{ + SARibbonCategoryLayoutItem* i = takePannelItem(pannel); + if (i) { + SARibbonSeparatorWidget* sp = i->separatorWidget; + if (sp) { + sp->deleteLater(); + } + delete i; + invalidate(); + return true; + } + return false; +} + +int SARibbonCategoryLayout::count() const +{ + return (d_ptr->mItemList.size()); +} + +void SARibbonCategoryLayout::addPannel(SARibbonPannel* pannel) +{ + insertPannel(d_ptr->mItemList.count(), pannel); +} + +/** + * @brief 插入一个pannel + * @param index 索引 + * @param pannel + * @return 返回对应的分割线SARibbonSeparatorWidget + * @note 在SARibbonCategoryLayout的布局中,一个pannel会携带一个分割线 + */ +void SARibbonCategoryLayout::insertPannel(int index, SARibbonPannel* pannel) +{ + index = qMax(0, index); + index = qMin(d_ptr->mItemList.count(), index); + SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(pannel); + + // 分割线 + item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget()); + // 插入list中 + d_ptr->mItemList.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); +} + +QSize SARibbonCategoryLayout::sizeHint() const +{ + if (d_ptr->mSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mSizeHint); +} + +QSize SARibbonCategoryLayout::minimumSize() const +{ + if (d_ptr->mMinSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mMinSizeHint); +} + +/** + * @brief SARibbonCategory充满整个stacked widget + * @return + */ +Qt::Orientations SARibbonCategoryLayout::expandingDirections() const +{ + return (Qt::Horizontal | Qt::Vertical); +} + +void SARibbonCategoryLayout::invalidate() +{ + d_ptr->mDirty = true; + QLayout::invalidate(); +} +/** + * @brief category的内容尺寸(把margins减去) + * @return + */ +QSize SARibbonCategoryLayout::categoryContentSize() const +{ + SARibbonCategory* category = ribbonCategory(); + QSize s = category->size(); + QMargins mag = contentsMargins(); + if (!mag.isNull()) { + s.rheight() -= (mag.top() + mag.bottom()); + s.rwidth() -= (mag.right() + mag.left()); + } + return (s); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonCategoryLayout::updateGeometryArr() +{ + SARibbonCategory* category = ribbonCategory(); + if (nullptr == category) { + return; + } + int categoryWidth = category->width(); + QMargins mag = contentsMargins(); + int height = category->height(); + int y = 0; + + if (!mag.isNull()) { + y = mag.top(); + height -= (mag.top() + mag.bottom()); + // categoryWidth不能把mag减去,减去后会导致categoryWidth不是实际的categoryWidth + // categoryWidth -= (mag.right() + mag.left()); + } + // total 是总宽,不是x坐标系,x才是坐标系 + int total = d_ptr->totalSizeHintWidth(); + + // 扩展的宽度 + int expandWidth = 0; + +// 如果total < categoryWidth,m_d->mXBase可以设置为0 +// 判断是否超过总长度 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout::updateGeometryArr" << "\n|-category name=" << category->categoryName() // + << "\n|-category height=" << height // + << "\n|-totalSizeHintWidth=" << total // + << "\n|-y=" << y // + << "\n|-expandWidth:" << expandWidth // + << "\n|-mag=" << mag; +#endif + if (total > categoryWidth) { + // 超过总长度,需要显示滚动按钮 + if (0 == d_ptr->mXBase) { + // 已经移动到最左,需要可以向右移动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = false; + } else if (d_ptr->mXBase <= (categoryWidth - total)) { + // 已经移动到最右,需要可以向左移动 + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = true; + } else { + // 移动到中间两边都可以动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = true; + } + } else { + // 说明total 小于 categoryWidth + // 记录可以扩展的数量 + int canExpandingCount = 0; + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = false; + // 这个是避免一开始totalWidth > categorySize.width(),通过滚动按钮调整了m_d->mBaseX + // 随之调整了窗体尺寸,调整后totalWidth < categorySize.width()导致category在原来位置 + // 无法显示,必须这里把mBaseX设置为0 + + d_ptr->mXBase = 0; + // + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = qobject_cast< SARibbonPannel* >(item->widget())) { + if (p->isExpanding()) { + // pannel可扩展 + ++canExpandingCount; + } + } + } + // 计算可扩展的宽度 + if (canExpandingCount > 0) { + expandWidth = (categoryWidth - total) / canExpandingCount; + } else { + expandWidth = 0; + } + } + int x = d_ptr->mXBase; + if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) { + // 如果是居中对齐,同时没有伸缩的pannel,同时总宽度没有超过category的宽度 + x = (categoryWidth - total) / 2; + } + total = 0; // total重新计算 + // 先按照sizeHint设置所有的尺寸 + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + // 如果是hide就直接跳过 + if (item->separatorWidget) { + // pannel hide分割线也要hide + item->separatorWidget->hide(); + } + item->mWillSetGeometry = QRect(0, 0, 0, 0); + item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0); + continue; + } + SARibbonPannel* p = item->toPannelWidget(); + if (nullptr == p) { + qDebug() << "unknow widget in SARibbonCategoryLayout"; + continue; + } + // p->layout()->update(); + QSize pannelSize = p->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + if (p->isExpanding()) { + // 可扩展,就把pannel扩展到最大 + pannelSize.setWidth(pannelSize.width() + expandWidth); + } + int w = pannelSize.width(); + + item->mWillSetGeometry = QRect(x, y, w, height); + x += w; + total += w; + w = SeparatorSize.width(); + item->mWillSetSeparatorGeometry = QRect(x, y, w, height); + x += w; + total += w; + } + d_ptr->mTotalWidth = total; + d_ptr->mSizeHint = QSize(d_ptr->mTotalWidth, height); + d_ptr->mMinSizeHint = QSize(categoryWidth, height); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint + << ",Category name=" << category->categoryName(); +#endif +} + +/** + * @brief 执行layout调整 + */ +void SARibbonCategoryLayout::doLayout() +{ + if (d_ptr->mDirty) { + updateGeometryArr(); + } + SARibbonCategory* category = ribbonCategory(); + // 两个滚动按钮的位置永远不变 + d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height()); + d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height()); + QList< QWidget* > showWidgets, hideWidgets; +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + int debug_i__(0); + qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName(); +#endif + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + if (item->separatorWidget) { + hideWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() << ",will hide"; + ++debug_i__; +#endif + } else { + //! 这里不能用item->setGeometry(item->mWillSetGeometry);这样会得到一个很奇怪的显示效果 + //! 就是窗口的最左边不会移出去,而是把最右边压缩, + item->widget()->setGeometry(item->mWillSetGeometry); + // item->widget()->setFixedSize(item->mWillSetGeometry.size()); + // item->widget()->move(item->mWillSetGeometry.topLeft()); + // item->setGeometry(item->mWillSetGeometry); + showWidgets << item->widget(); + if (item->separatorWidget) { + item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry); + showWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() + << "),willSetGeometry:" << item->mWillSetGeometry + << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry; + ++debug_i__; +#endif + } + } + + d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow); + d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow); + if (d_ptr->mIsRightScrollBtnShow) { + d_ptr->mRightScrollBtn->raise(); + } + if (d_ptr->mIsLeftScrollBtnShow) { + d_ptr->mLeftScrollBtn->raise(); + } + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) { + w->show(); + } + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) { + w->hide(); + } + } +} + +/** + * @brief 返回所有pannels + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannels() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + SARibbonPannel* p = item->toPannelWidget(); + res.append(p); + } + return (res); +} + +/** + * @brief 通过ObjectName查找pannel + * @param objname + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByObjectName(const QString& objname) const +{ + for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->objectName() == objname) { + return pannel; + } + } + } + return nullptr; +} + +/** + * @brief 通过名字查找pannel + * @param title + * @return 如果有重名,只会返回第一个符合条件的 + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByName(const QString& pannelname) const +{ + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->pannelName() == pannelname) { + return (pannel); + } + } + } + return (nullptr); +} + +/** + * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr + * @param i + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByIndex(int i) const +{ + if (i >= 0 && i < d_ptr->mItemList.size()) { + return d_ptr->mItemList[ i ]->toPannelWidget(); + } + return nullptr; +} + +/** + * @brief 移动pannel + * @param from + * @param to + */ +void SARibbonCategoryLayout::movePannel(int from, int to) +{ + d_ptr->mItemList.move(from, to); + doLayout(); +} + +/** + * @brief 返回pannel的个数 + * @return + */ +int SARibbonCategoryLayout::pannelCount() const +{ + return d_ptr->mItemList.size(); +} + +/** + * @brief 查找pannel对应的索引 + * @param p + * @return 如果找不到,返回-1 + */ +int SARibbonCategoryLayout::pannelIndex(SARibbonPannel* p) const +{ + int c = pannelCount(); + + for (int i = 0; i < c; ++i) { + if (d_ptr->mItemList[ i ]->toPannelWidget() == p) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取所有的pannel + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannelList() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = i->toPannelWidget()) { + res.append(p); + } + } + return (res); +} + +/** + * @brief 执行滚动 + * @param px + */ +void SARibbonCategoryLayout::scroll(int px) +{ + QSize contentSize = categoryContentSize(); + d_ptr->mXBase += px; + if (d_ptr->mXBase > 0) { + d_ptr->mXBase = 0; + } else if ((d_ptr->mXBase + d_ptr->mTotalWidth) < contentSize.width()) { + d_ptr->mXBase = contentSize.width() - d_ptr->mTotalWidth; + } + invalidate(); +} + +/** + * @brief 判断是否滚动过 + * @return + */ +bool SARibbonCategoryLayout::isScrolled() const +{ + return (d_ptr->mXBase != 0); +} + +/** + * @brief 这个宽度是实际内容的宽度,有可能大于size.width,也有可能小于 + * @return + */ +int SARibbonCategoryLayout::categoryTotalWidth() const +{ + return d_ptr->mTotalWidth; +} + +/** + @brief 设置Category的对齐方式 + + 居中对齐会让pannel以居中进行对齐 + @param al + */ +void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al) +{ + d_ptr->mCategoryAlignment = al; +} + +/** + @brief Category的对齐方式 + @return + */ +SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const +{ + return d_ptr->mCategoryAlignment; +} + +void SARibbonCategoryLayout::onLeftScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase + width; + if (tmp > 0) { + tmp = 0; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::onRightScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase - width; + if (tmp < (width - totalWidth)) { + tmp = width - totalWidth; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName() + << ")======="; +#endif + QLayout::setGeometry(rect); + d_ptr->mDirty = false; + updateGeometryArr(); + doLayout(); +} +//============================================================= +// SARibbonCategoryLayoutItem +//============================================================= + +SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPannel* w) : QWidgetItem(w) +{ + separatorWidget = nullptr; +} + +SARibbonCategoryLayoutItem::~SARibbonCategoryLayoutItem() +{ +} + +SARibbonPannel* SARibbonCategoryLayoutItem::toPannelWidget() +{ + return qobject_cast< SARibbonPannel* >(widget()); +} + +/*** End of inlined file: SARibbonCategoryLayout.cpp ***/ + +/*** Start of inlined file: SARibbonContextCategory.cpp ***/ +#include +#include + +#include + +/** + * @brief The SARibbonCategoryData class + */ +class SAPrivateRibbonCategoryData +{ +public: + SARibbonCategory* categoryPage; +}; + +/** + * @brief The SARibbonContextCategoryPrivate class + */ +class SARibbonContextCategory::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory) +public: + PrivateData(SARibbonContextCategory* p); + +public: + QList< SAPrivateRibbonCategoryData > mCategoryDataList; + QVariant mContextID; + QColor mContextColor; + QString mContextTitle; +}; +SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonContextCategory +//=================================================== +SARibbonContextCategory::SARibbonContextCategory(QWidget* parent) + : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this)) +{ +} + +SARibbonContextCategory::~SARibbonContextCategory() +{ +} + +/** + * @brief 添加标签 + * @param title 标签名字 + */ +SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget()); + category->setCategoryName(title); + addCategoryPage(category); + return (category); +} + +/** + * @brief 添加标签 + * @param page + */ +void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category) +{ + if (isHaveCategory(category)) { + // cn:SARibbonContextCategory已经持有标签:%1,将跳过 + qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName()); + return; + } + category->markIsContextCategory(true); + connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged); + SAPrivateRibbonCategoryData catData; + catData.categoryPage = category; + d_ptr->mCategoryDataList.append(catData); + category->installEventFilter(this); + Q_EMIT categoryPageAdded(category); +} + +int SARibbonContextCategory::categoryCount() const +{ + return (d_ptr->mCategoryDataList.count()); +} + +void SARibbonContextCategory::setId(const QVariant& id) +{ + d_ptr->mContextID = id; +} + +QVariant SARibbonContextCategory::id() const +{ + return (d_ptr->mContextID); +} + +void SARibbonContextCategory::setContextColor(const QColor color) +{ + d_ptr->mContextColor = color; +} + +QColor SARibbonContextCategory::contextColor() const +{ + return (d_ptr->mContextColor); +} + +QWidget* SARibbonContextCategory::parentWidget() const +{ + return (qobject_cast< QWidget* >(parent())); +} + +bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e) +{ + if (nullptr == watched) { + return (false); + } + switch (e->type()) { + case QEvent::Close: { + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched); + if (c) { +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + qDebug() << " -----------> close event"; +#endif + takeCategory(c); + } + } break; + + default: + break; + } + return (false); +} + +/** + * @brief 获取上下文标签的标题 + * @return + */ +QString SARibbonContextCategory::contextTitle() const +{ + return (d_ptr->mContextTitle); +} + +/** + * @brief 设置上下文标签的标题,标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle + * @param contextTitle + * @note 此函数会触发信号@sa contextTitleChanged + */ +void SARibbonContextCategory::setContextTitle(const QString& contextTitle) +{ + d_ptr->mContextTitle = contextTitle; + Q_EMIT contextTitleChanged(contextTitle); +} + +/** + * @brief 获取对应的tab页 + * @param index + * @return + */ +SARibbonCategory* SARibbonContextCategory::categoryPage(int index) +{ + return (d_ptr->mCategoryDataList[ index ].categoryPage); +} + +/** + * @brief 获取所有的SARibbonCategory* + * @return + */ +QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const +{ + QList< SARibbonCategory* > res; + + for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->mCategoryDataList)) { + res.append(c.categoryPage); + } + return (res); +} + +/** + * @brief 移除这个category,这时SARibbonContextCategory不再管理这个category + * @param category + * @return 成功移除返回true + */ +bool SARibbonContextCategory::takeCategory(SARibbonCategory* category) +{ + for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { + if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { + d_ptr->mCategoryDataList.takeAt(i); + return (true); + } + } + return (false); +} + +/** + * @brief 判断上下文是否维护了此SARibbonCategory + * @param category + * @return + */ +bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const +{ + for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { + if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { + return (true); + } + } + return (false); +} + +void SARibbonContextCategory::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender()); + if (category) { + Q_EMIT categoryTitleChanged(category, title); + } +} + +/*** End of inlined file: SARibbonContextCategory.cpp ***/ + +/*** Start of inlined file: SARibbonGalleryItem.cpp ***/ +SARibbonGalleryItem::SARibbonGalleryItem() : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr) +{ +} + +SARibbonGalleryItem::SARibbonGalleryItem(const QString& text, const QIcon& icon) + : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), mAction(nullptr) +{ + setText(text); + setIcon(icon); + setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); +} + +SARibbonGalleryItem::SARibbonGalleryItem(QAction* act) : mFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable) +{ + setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); + setAction(act); +} + +SARibbonGalleryItem::~SARibbonGalleryItem() +{ +} + +void SARibbonGalleryItem::setData(int role, const QVariant& data) +{ + mDatas[ role ] = data; +} + +QVariant SARibbonGalleryItem::data(int role) const +{ + if (mAction) { + switch (role) { + case Qt::DisplayRole: + return (mAction->text()); + + case Qt::ToolTipRole: + return (mAction->toolTip()); + + case Qt::DecorationRole: + return (mAction->icon()); + default: + break; + } + } + return (mDatas.value(role)); +} + +void SARibbonGalleryItem::setText(const QString& text) +{ + setData(Qt::DisplayRole, text); +} + +QString SARibbonGalleryItem::text() const +{ + if (mAction) { + return (mAction->text()); + } + return (data(Qt::DisplayRole).toString()); +} + +void SARibbonGalleryItem::setToolTip(const QString& text) +{ + setData(Qt::ToolTipRole, text); +} + +QString SARibbonGalleryItem::toolTip() const +{ + if (mAction) { + return (mAction->toolTip()); + } + return (data(Qt::ToolTipRole).toString()); +} + +void SARibbonGalleryItem::setIcon(const QIcon& ico) +{ + setData(Qt::DecorationRole, ico); +} + +QIcon SARibbonGalleryItem::icon() const +{ + if (mAction) { + return (mAction->icon()); + } + return (qvariant_cast< QIcon >(data(Qt::DecorationRole))); +} + +bool SARibbonGalleryItem::isSelectable() const +{ + return (mFlags & Qt::ItemIsSelectable); +} + +void SARibbonGalleryItem::setSelectable(bool isSelectable) +{ + if (isSelectable) { + mFlags |= Qt::ItemIsSelectable; + } else { + mFlags = (mFlags & (~Qt::ItemIsSelectable)); + } +} + +bool SARibbonGalleryItem::isEnable() const +{ + if (mAction) { + return (mAction->isEnabled()); + } + return (mFlags & Qt::ItemIsEnabled); +} + +void SARibbonGalleryItem::setEnable(bool isEnable) +{ + if (mAction) { + mAction->setEnabled(isEnable); + } + + if (isEnable) { + mFlags |= Qt::ItemIsEnabled; + } else { + mFlags = (mFlags & (~Qt::ItemIsEnabled)); + } +} + +void SARibbonGalleryItem::setFlags(Qt::ItemFlags flag) +{ + mFlags = flag; + if (mAction) { + mAction->setEnabled(flag & Qt::ItemIsEnabled); + } +} + +Qt::ItemFlags SARibbonGalleryItem::flags() const +{ + return (mFlags); +} + +void SARibbonGalleryItem::setAction(QAction* act) +{ + mAction = act; + if (nullptr == mAction) { + return; + } + if (act->isEnabled()) { + mFlags |= Qt::ItemIsEnabled; + } else { + mFlags = (mFlags & (~Qt::ItemIsEnabled)); + } +} + +QAction* SARibbonGalleryItem::action() +{ + return (mAction); +} + +void SARibbonGalleryItem::setTextAlignment(Qt::Alignment a) +{ + setData(Qt::TextAlignmentRole, (int)a); +} + +Qt::Alignment SARibbonGalleryItem::textAlignment() const +{ + return qvariant_cast< Qt::Alignment >(data(Qt::TextAlignmentRole)); +} + +/*** End of inlined file: SARibbonGalleryItem.cpp ***/ + +/*** Start of inlined file: SARibbonGalleryGroup.cpp ***/ +#include +#include +#include +#include + +/** + * @brief The SARibbonGalleryGroupPrivate class + */ +class SARibbonGalleryGroup::PrivateData +{ +public: + SARibbonGalleryGroup* q_ptr; + QString mGroupTitle; + SARibbonGalleryGroup::GalleryGroupStyle mPreStyle { SARibbonGalleryGroup::IconWithText }; + SARibbonGalleryGroup::DisplayRow mDisplayRow { SARibbonGalleryGroup::DisplayOneRow }; + bool mBlockRecalc { false }; + int mGridMinimumWidth { 0 }; ///< grid最小宽度 + int mGridMaximumWidth { 0 }; ///< grid最大宽度 + QActionGroup* mActionGroup { nullptr }; ///< 所有GalleryGroup管理的actions都由这个actiongroup管理 +public: + PrivateData(SARibbonGalleryGroup* p) : q_ptr(p) + { + mActionGroup = new QActionGroup(p); + p->connect(mActionGroup, &QActionGroup::triggered, p, &SARibbonGalleryGroup::triggered); + p->connect(mActionGroup, &QActionGroup::hovered, p, &SARibbonGalleryGroup::hovered); + } +}; + +//=================================================== +// SARibbonGalleryGroupItemDelegate +//=================================================== + +SARibbonGalleryGroupItemDelegate::SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent) + : QStyledItemDelegate(parent), mGroup(group) +{ +} + +SARibbonGalleryGroupItemDelegate::~SARibbonGalleryGroupItemDelegate() +{ +} + +void SARibbonGalleryGroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + if (nullptr == mGroup) { + return; + } + switch (mGroup->galleryGroupStyle()) { + case SARibbonGalleryGroup::IconWithText: + paintIconWithText(painter, option, index); + break; + case SARibbonGalleryGroup::IconWithWordWrapText: + paintIconWithTextWordWrap(painter, option, index); + break; + case SARibbonGalleryGroup::IconOnly: + paintIconOnly(painter, option, index); + break; + default: + paintIconWithText(painter, option, index); + break; + } +} + +void SARibbonGalleryGroupItemDelegate::paintIconOnly(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QStyle* style = mGroup->style(); + int sp = mGroup->spacing(); + sp += 3; + painter->save(); + painter->setClipRect(option.rect); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, mGroup); + // draw the icon + QRect iconRect = option.rect; + + iconRect.adjust(sp, sp, -sp, -sp); + QIcon ico = qvariant_cast< QIcon >(index.data(Qt::DecorationRole)); + + ico.paint(painter, iconRect, Qt::AlignCenter, QIcon::Normal, QIcon::On); + painter->restore(); +} + +void SARibbonGalleryGroupItemDelegate::paintIconWithText(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QStyledItemDelegate::paint(painter, option, index); +} + +void SARibbonGalleryGroupItemDelegate::paintIconWithTextWordWrap(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QStyledItemDelegate::paint(painter, option, index); +} + +QSize SARibbonGalleryGroupItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + Q_UNUSED(index); + Q_UNUSED(option); + return mGroup->gridSize(); +} + +//=================================================== +// SARibbonGalleryGroupModel +//=================================================== + +SARibbonGalleryGroupModel::SARibbonGalleryGroupModel(QObject* parent) : QAbstractListModel(parent) +{ +} + +SARibbonGalleryGroupModel::~SARibbonGalleryGroupModel() +{ + clear(); +} + +int SARibbonGalleryGroupModel::rowCount(const QModelIndex& parent) const +{ + return (parent.isValid() ? 0 : mItems.size()); +} + +Qt::ItemFlags SARibbonGalleryGroupModel::flags(const QModelIndex& index) const +{ + if (!index.isValid() || (index.row() >= mItems.size())) { + return (Qt::NoItemFlags); + } + return (mItems.at(index.row())->flags()); +} + +QVariant SARibbonGalleryGroupModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || (index.row() >= mItems.count())) { + return (QVariant()); + } + return (mItems.at(index.row())->data(role)); +} + +QModelIndex SARibbonGalleryGroupModel::index(int row, int column, const QModelIndex& parent) const +{ + if (hasIndex(row, column, parent)) { + return (createIndex(row, column, mItems.at(row))); + } + return (QModelIndex()); +} + +bool SARibbonGalleryGroupModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (!index.isValid() || (index.row() >= mItems.count())) { + return (false); + } + + mItems.at(index.row())->setData(role, value); + return (true); +} + +void SARibbonGalleryGroupModel::clear() +{ + beginResetModel(); + for (int i = 0; i < mItems.count(); ++i) { + if (mItems.at(i)) { + delete mItems.at(i); + } + } + mItems.clear(); + endResetModel(); +} + +SARibbonGalleryItem* SARibbonGalleryGroupModel::at(int row) const +{ + return (mItems.value(row)); +} + +void SARibbonGalleryGroupModel::insert(int row, SARibbonGalleryItem* item) +{ + beginInsertRows(QModelIndex(), row, row); + mItems.insert(row, item); + endInsertRows(); +} + +SARibbonGalleryItem* SARibbonGalleryGroupModel::take(int row) +{ + if ((row < 0) || (row >= mItems.count())) { + return (0); + } + + beginRemoveRows(QModelIndex(), row, row); + SARibbonGalleryItem* item = mItems.takeAt(row); + + endRemoveRows(); + return (item); +} + +void SARibbonGalleryGroupModel::append(SARibbonGalleryItem* item) +{ + beginInsertRows(QModelIndex(), mItems.count(), mItems.count() + 1); + mItems.append(item); + endInsertRows(); +} + +//=================================================== +// SARibbonGalleryGroup +//=================================================== +SARibbonGalleryGroup::SARibbonGalleryGroup(QWidget* w) + : QListView(w), d_ptr(new SARibbonGalleryGroup::PrivateData(this)) +{ + setViewMode(QListView::IconMode); + setResizeMode(QListView::Adjust); + setSelectionRectVisible(true); + setUniformItemSizes(true); + setSpacing(1); + setItemDelegate(new SARibbonGalleryGroupItemDelegate(this, this)); + connect(this, &QAbstractItemView::clicked, this, &SARibbonGalleryGroup::onItemClicked); + SARibbonGalleryGroupModel* m = new SARibbonGalleryGroupModel(this); + setModel(m); +} + +SARibbonGalleryGroup::~SARibbonGalleryGroup() +{ +} + +/** + * @brief 是否禁止计算 + * @param on + */ +void SARibbonGalleryGroup::setRecalcGridSizeBlock(bool on) +{ + d_ptr->mBlockRecalc = on; +} + +bool SARibbonGalleryGroup::isRecalcGridSizeBlock() const +{ + return d_ptr->mBlockRecalc; +} + +/** + * @brief 重新计算grid和icon的尺寸 + */ +void SARibbonGalleryGroup::recalcGridSize() +{ + recalcGridSize(height()); +} + +void SARibbonGalleryGroup::recalcGridSize(int galleryHeight) +{ + if (isRecalcGridSizeBlock()) { + return; + } + // 首先通过DisplayRow计算GridSize + int dr = static_cast< int >(displayRow()); + if (dr < 1) { + dr = 1; + } else if (dr > 3) { + dr = 3; + } + int h = galleryHeight / dr; + if (h <= 1) { + h = galleryHeight; + } + int w = h; + if (gridMinimumWidth() > 0) { + if (w < gridMinimumWidth()) { + w = gridMinimumWidth(); + } + } + if (gridMaximumWidth() > 0) { + if (w > gridMaximumWidth()) { + w = gridMaximumWidth(); + } + } + setGridSize(QSize(w, h)); + // 在通过GalleryGroupStyle确定icon的尺寸 + const int shiftpix = + 4; // 这个是移动像素,qt在鼠标移动到图标上时会移动一下,给用户明确的动态,导致如果布局很满会超出显示范围,因此要在此基础上缩放一点 + switch (galleryGroupStyle()) { + case IconWithText: { + int textHeight = fontMetrics().lineSpacing(); + int iconHeight = h - textHeight - 2 * spacing() - shiftpix; + if (iconHeight > 0) { + setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight)); + } else { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + } + break; + } + case IconWithWordWrapText: { + int textHeight = fontMetrics().lineSpacing() * 2; + int iconHeight = h - textHeight; + if (iconHeight > 0) { + setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight - 2 * spacing() - shiftpix)); + } else { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + } + break; + } + case IconOnly: { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + break; + } + default: { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + break; + } + } +#if 0 + qDebug() << "SARibbonGalleryGroup::recalcGridSize(" << galleryHeight << "): gridSize=" << gridSize() + << " iconSize=" << iconSize(); +#endif +} + +/// +/// \brief 设置默认的预设样式 +/// \param style +/// +void SARibbonGalleryGroup::setGalleryGroupStyle(SARibbonGalleryGroup::GalleryGroupStyle style) +{ + d_ptr->mPreStyle = style; + if (style == IconWithWordWrapText) { + setWordWrap(true); + } + recalcGridSize(); +} + +SARibbonGalleryGroup::GalleryGroupStyle SARibbonGalleryGroup::galleryGroupStyle() const +{ + return d_ptr->mPreStyle; +} + +void SARibbonGalleryGroup::addItem(const QString& text, const QIcon& icon) +{ + if (nullptr == groupModel()) { + return; + } + addItem(new SARibbonGalleryItem(text, icon)); +} + +/** + * @brief 添加一个条目 + * + * @param item 条目的内存所有权归属SARibbonGalleryGroup管理 + */ +void SARibbonGalleryGroup::addItem(SARibbonGalleryItem* item) +{ + if (nullptr == groupModel()) { + return; + } + groupModel()->append(item); +} + +void SARibbonGalleryGroup::addActionItem(QAction* act) +{ + if (nullptr == groupModel()) { + return; + } + d_ptr->mActionGroup->addAction(act); + groupModel()->append(new SARibbonGalleryItem(act)); +} + +void SARibbonGalleryGroup::addActionItemList(const QList< QAction* >& acts) +{ + SARibbonGalleryGroupModel* model = groupModel(); + + if (nullptr == model) { + return; + } + for (QAction* a : acts) { + d_ptr->mActionGroup->addAction(a); + } + for (int i = 0; i < acts.size(); ++i) { + model->append(new SARibbonGalleryItem(acts[ i ])); + } +} + +/// +/// \brief 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 +/// +void SARibbonGalleryGroup::setupGroupModel() +{ + setModel(new SARibbonGalleryGroupModel(this)); +} + +SARibbonGalleryGroupModel* SARibbonGalleryGroup::groupModel() +{ + return (qobject_cast< SARibbonGalleryGroupModel* >(model())); +} + +void SARibbonGalleryGroup::setGroupTitle(const QString& title) +{ + d_ptr->mGroupTitle = title; + Q_EMIT groupTitleChanged(d_ptr->mGroupTitle); +} + +QString SARibbonGalleryGroup::groupTitle() const +{ + return (d_ptr->mGroupTitle); +} + +void SARibbonGalleryGroup::selectByIndex(int i) +{ + SARibbonGalleryGroupModel* model = groupModel(); + + if (nullptr == model) { + return; + } + QModelIndex ind = model->index(i, 0, QModelIndex()); + QItemSelectionModel* selmodel = selectionModel(); + + if (selmodel) { + selmodel->select(ind, QItemSelectionModel::SelectCurrent); + } +} + +/** + * @brief 设置显示的行数 + * @param r + */ +void SARibbonGalleryGroup::setDisplayRow(DisplayRow r) +{ + d_ptr->mDisplayRow = r; + recalcGridSize(); +} + +/** + * @brief Gallery显示的行数 + * @return + */ +SARibbonGalleryGroup::DisplayRow SARibbonGalleryGroup::displayRow() const +{ + return d_ptr->mDisplayRow; +} + +/** + * @brief 设置grid最小的宽度,默认为0(不限制) + * @param w + */ +void SARibbonGalleryGroup::setGridMinimumWidth(int w) +{ + d_ptr->mGridMinimumWidth = w; +} + +/** + * @brief grid最小的宽度,默认为0(不限制) + * @return + */ +int SARibbonGalleryGroup::gridMinimumWidth() const +{ + return d_ptr->mGridMinimumWidth; +} + +/** + * @brief 设置grid最大的宽度,默认为0(不限制) + * @param w + */ +void SARibbonGalleryGroup::setGridMaximumWidth(int w) +{ + d_ptr->mGridMaximumWidth = w; +} + +/** + * @brief grid最大的的宽度,默认为0(不限制) + * @param w + */ +int SARibbonGalleryGroup::gridMaximumWidth() const +{ + return d_ptr->mGridMaximumWidth; +} + +/** + * @brief 获取SARibbonGalleryGroup管理的actiongroup + * @return + */ +QActionGroup* SARibbonGalleryGroup::actionGroup() const +{ + return d_ptr->mActionGroup; +} + +void SARibbonGalleryGroup::onItemClicked(const QModelIndex& index) +{ + if (index.isValid()) { + SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); + if (item) { + QAction* act = item->action(); + if (act) { + act->activate(QAction::Trigger); + } + } + } +} + +void SARibbonGalleryGroup::onItemEntered(const QModelIndex& index) +{ + if (index.isValid()) { + SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); + if (item) { + QAction* act = item->action(); + if (act) { + act->activate(QAction::Hover); + } + } + } +} + +/*** End of inlined file: SARibbonGalleryGroup.cpp ***/ + +/*** Start of inlined file: SARibbonGallery.cpp ***/ +#include +#include +#define ICON_ARROW_UP QIcon(":/image/resource/ArrowUp.png") +#define ICON_ARROW_DOWN QIcon(":/image/resource/ArrowDown.png") +#define ICON_ARROW_MORE QIcon(":/image/resource/ArrowMore.png") +#include +#include +#include +#include +#include + +#include + +/** + * @brief The SARibbonGalleryPrivate class + */ +class SARibbonGallery::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery) +public: + static int sGalleryButtonMaximumWidth; + SARibbonGalleryButton* mButtonUp { nullptr }; + SARibbonGalleryButton* mButtonDown { nullptr }; + SARibbonGalleryButton* mButtonMore { nullptr }; +#if 0 + SARibbonMenu *popupWidget; +#else + SARibbonGalleryViewport* mPopupWidget { nullptr }; +#endif + SARibbonGalleryGroup* mViewportGroup { nullptr }; + QBoxLayout* mButtonLayout { nullptr }; + QBoxLayout* mLayout { nullptr }; + PrivateData(SARibbonGallery* p) : q_ptr(p) + { + } + + void init() + { + mButtonUp = new SARibbonGalleryButton(q_ptr); + mButtonDown = new SARibbonGalleryButton(q_ptr); + mButtonMore = new SARibbonGalleryButton(q_ptr); + mButtonUp->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonDown->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonMore->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp")); + mButtonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown")); + mButtonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore")); + mButtonUp->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonDown->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonMore->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonUp->setIcon(ICON_ARROW_UP); + mButtonDown->setIcon(ICON_ARROW_DOWN); + mButtonMore->setIcon(ICON_ARROW_MORE); + q_ptr->connect(mButtonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp); + q_ptr->connect(mButtonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown); + q_ptr->connect(mButtonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail); + // 信号转发 + q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered); + mPopupWidget = nullptr; + mViewportGroup = nullptr; + mButtonLayout = new QBoxLayout(QBoxLayout::TopToBottom); + mButtonLayout->setSpacing(0); + mButtonLayout->setContentsMargins(0, 0, 0, 0); + mButtonLayout->addWidget(mButtonUp); + mButtonLayout->addWidget(mButtonDown); + mButtonLayout->addWidget(mButtonMore); + mLayout = new QBoxLayout(QBoxLayout::RightToLeft); + mLayout->setSpacing(0); + mLayout->setContentsMargins(0, 0, 0, 0); + mLayout->addLayout(mButtonLayout); + mLayout->addStretch(); + q_ptr->setLayout(mLayout); + } + + bool isValid() const + { + return (q_ptr != nullptr); + } + + void createPopupWidget() + { + if (nullptr == mPopupWidget) { +#if 0 + popupWidget = new SARibbonMenu(Parent); +#else + mPopupWidget = new SARibbonGalleryViewport(q_ptr); +#endif + } + } + + void setViewPort(SARibbonGalleryGroup* v) + { + if (nullptr == mViewportGroup) { + mViewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr); + mLayout->addWidget(mViewportGroup, 1); + } + mViewportGroup->setRecalcGridSizeBlock(true); + mViewportGroup->setGalleryGroupStyle(v->galleryGroupStyle()); + mViewportGroup->setDisplayRow(v->displayRow()); + mViewportGroup->setSpacing(v->spacing()); + mViewportGroup->setGridMaximumWidth(v->gridMaximumWidth()); + mViewportGroup->setGridMinimumWidth(v->gridMinimumWidth()); + mViewportGroup->setRecalcGridSizeBlock(false); + mViewportGroup->recalcGridSize(mViewportGroup->height()); + mViewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + mViewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + mViewportGroup->setModel(v->model()); + mViewportGroup->show(); + } +}; + +// 静态变量初始化 + +/** + * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth + */ +int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15; + +//=================================================== +// SARibbonGalleryButton +//=================================================== + +SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonGalleryButton::~SARibbonGalleryButton() +{ +} +//=================================================== +// SARibbonGalleryViewport +//=================================================== + +SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent) +{ + setWindowFlags(Qt::Popup); + QPalette pl = palette(); + pl.setBrush(QPalette::Window, pl.brush(QPalette::Base)); + setPalette(pl); + m_layout = new QVBoxLayout(this); + m_layout->setSpacing(0); + m_layout->setContentsMargins(0, 0, 0, 0); +} + +/** + * @brief 添加窗口不带标题 + * @param w + */ +void SARibbonGalleryViewport::addWidget(QWidget* w) +{ + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); +} + +/** + * @brief 添加窗口,带标题 + * @param w + * @param title + */ +void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title) +{ + QLabel* label = new QLabel(this); + label->setText(title); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(label); + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); + _widgetToTitleLable[ w ] = label; +} + +/** + * @brief 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + * @param g + * @return 如果没有管理group,将返回nullptr + */ +QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w) +{ + return _widgetToTitleLable.value(w, nullptr); +} + +/** + * @brief 移除窗口 + * @param w + */ +void SARibbonGalleryViewport::removeWidget(QWidget* w) +{ + QLabel* label = titleLabel(w); + if (label) { + m_layout->removeWidget(label); + } + m_layout->removeWidget(w); +} + +/** + * @brief widget的标题改变 + * @param g + * @param title + */ +void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title) +{ + QLabel* l = titleLabel(w); + if (l) { + l->setText(title); + } +} + +//=================================================== +// SARibbonGallery +//=================================================== + +SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this)) +{ + d_ptr->init(); + setFrameShape(QFrame::Box); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumWidth(200); +} + +SARibbonGallery::~SARibbonGallery() +{ +} + +QSize SARibbonGallery::sizeHint() const +{ + return (QSize(100, 62)); +} + +/** + * @brief 获取一个空白SARibbonGalleryGroup + * @return + */ +SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup() +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + addGalleryGroup(group); + return (group); +} + +/** + * @brief 添加一组SARibbonGalleryGroup + * @param group + */ +void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group) +{ + group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + SARibbonGalleryViewport* viewport = ensureGetPopupViewPort(); + viewport->addWidget(group, group->groupTitle()); + connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked); + connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) { + viewport->onTitleChanged(group, t); + }); + connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered); + connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered); + setCurrentViewGroup(group); +} + +/** + * @brief 添加一组actions + * @param title actions组的名字 + * @param actions + * @return 返回SARibbonGalleryGroup,用户可以通过修改SARibbonGalleryGroup属性控制其显示方法 + */ +SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions) +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + if (!title.isEmpty()) { + group->setGroupTitle(title); + } + group->addActionItemList(actions); + addGalleryGroup(group); + return (group); +} + +void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group) +{ + d_ptr->setViewPort(group); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const +{ + return (d_ptr->mViewportGroup); +} + +/** + * @brief 获取弹出窗口 + * @return + */ +SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const +{ + return d_ptr->mPopupWidget; +} + +/** + * @brief 设置最右边三个控制按钮的最大宽度(默认15) + * @param w + */ +void SARibbonGallery::setGalleryButtonMaximumWidth(int w) +{ + SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w; +} + +/** + * @brief 上翻页 + */ +void SARibbonGallery::pageDown() +{ + if (d_ptr->mViewportGroup) { + QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v += vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 下翻页 + */ +void SARibbonGallery::pageUp() +{ + if (d_ptr->mViewportGroup) { + QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v -= vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + */ +void SARibbonGallery::showMoreDetail() +{ + if (nullptr == d_ptr->mPopupWidget) { + return; + } + QSize popupMenuSize = d_ptr->mPopupWidget->sizeHint(); + QPoint start = mapToGlobal(QPoint(0, 0)); + + int width = d_ptr->mViewportGroup->width(); // viewport + + width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); // scrollbar + d_ptr->mPopupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height()); + d_ptr->mPopupWidget->show(); +} + +void SARibbonGallery::onItemClicked(const QModelIndex& index) +{ + QObject* obj = sender(); + SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj); + if (group) { + setCurrentViewGroup(group); + SARibbonGalleryGroup* curGroup = currentViewGroup(); + curGroup->scrollTo(index); + curGroup->setCurrentIndex(index); + } +} + +void SARibbonGallery::onTriggered(QAction* action) +{ + Q_UNUSED(action); + // 点击后关闭弹出窗口 + if (d_ptr->mPopupWidget) { + if (d_ptr->mPopupWidget->isVisible()) { + d_ptr->mPopupWidget->hide(); + } + } +} + +SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort() +{ + if (nullptr == d_ptr->mPopupWidget) { + d_ptr->createPopupWidget(); + } + return (d_ptr->mPopupWidget); +} + +void SARibbonGallery::resizeEvent(QResizeEvent* event) +{ + QFrame::resizeEvent(event); + // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸 + int h = layout()->contentsRect().height(); + if (d_ptr->mViewportGroup) { + h = d_ptr->mViewportGroup->height(); + d_ptr->mViewportGroup->recalcGridSize(); + } + if (d_ptr->mPopupWidget) { + QLayout* lay = d_ptr->mPopupWidget->layout(); + if (!lay) { + return; + } + int c = lay->count(); + for (int i = 0; i < c; ++i) { + QLayoutItem* item = lay->itemAt(i); + if (!item) { + continue; + } + QWidget* w = item->widget(); + if (!w) { + continue; + } + SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w); + if (!g) { + continue; + } + g->recalcGridSize(h); + } + } +} + +void SARibbonGallery::paintEvent(QPaintEvent* event) +{ + QFrame::paintEvent(event); +} + +/*** End of inlined file: SARibbonGallery.cpp ***/ + +/*** Start of inlined file: SARibbonBar.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashDotDotLine); \ + _pen.setColor(QColor(219, 26, 59)); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +class _SAContextCategoryManagerData +{ +public: + SARibbonContextCategory* contextCategory; + QList< int > tabPageIndex; + bool operator==(const SARibbonContextCategory* contextPage) + { + return (this->contextCategory == contextPage); + } +}; + +/** + * @todo 此处要修改,此方式容易异常 + */ +class _SARibbonTabData +{ +public: + SARibbonCategory* category; + int index; + _SARibbonTabData() : category(nullptr), index(-1) + { + } +}; +Q_DECLARE_METATYPE(_SARibbonTabData) + +class SARibbonBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonBar) +public: + QPointer< QAbstractButton > mApplicationButton; + QPointer< SARibbonTabBar > mRibbonTabBar; + QPointer< SARibbonStackedWidget > mStackedContainerWidget; + QPointer< SARibbonButtonGroupWidget > mRightButtonGroup; ///< 在tab bar右边的按钮群 + QPointer< SARibbonQuickAccessBar > mQuickAccessBar; ///< 快速响应栏 + QAction* mMinimumCategoryButtonAction { nullptr }; ///< 隐藏面板按钮action + QList< _SAContextCategoryManagerData > mCurrentShowingContextCategory; + QList< SARibbonContextCategory* > mContextCategoryList; ///< 存放所有的上下文标签 + QList< _SARibbonTabData > mHidedCategory; + int mIconRightBorderPosition { 1 }; ///< 标题栏x值得最小值,在有图标和快捷启动按钮,此值都需要变化 + SARibbonBar::RibbonStyles mRibbonStyle { SARibbonBar::RibbonStyleLooseThreeRow }; ///< ribbon的风格 + SARibbonBar::RibbonMode mCurrentRibbonMode { SARibbonBar::NormalRibbonMode }; ///< 记录当前模式 + QSize mWindowButtonSize; ///< 由SARibbonMainWindow告诉的windowbutton的尺寸 + QList< QColor > mContextCategoryColorList; ///< contextCategory的色系 + int mContextCategoryColorListIndex { -1 }; ///< 记录contextCategory色系索引 + QColor mTitleTextColor; ///< 标题文字颜色,默认无效,无效的情况下和SARibbonBar的qss:color属性一致 + QColor mTabBarBaseLineColor { QColor(186, 201, 219) }; ///< tabbar 底部会绘制一条线条,定义线条颜色 + QColor mContextCategoryTitleTextColor { Qt::black }; ///< 记录Context category的标题字体颜色 + Qt::Alignment mTitleAligment { Qt::AlignCenter }; ///< 标题对齐方式 + bool mIsTitleVisible { true }; ///< 标题是否显示 + SARibbonAlignment mRibbonAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 + SARibbonPannel::PannelLayoutMode mDefaulePannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< 默认的PannelLayoutMode + bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 + bool mIsTabOnTitle { false }; ///< 是否tab在标题栏上 + int mTitleBarHeight { 30 }; ///< 标题栏高度 + int mTabBarHeight { 28 }; ///< tabbar高度 + int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 + int mCategoryHeight { 60 }; ///< Category的高度 + int mPannelSpacing { 0 }; ///< pannel的spacing + QSize mPannelToolButtonSize { 22, 22 }; ///< 记录pannel的默认图标大小 + std::unique_ptr< int > mUserDefTitleBarHeight; ///< 用户定义的标题栏高度,正常不使用用户设定的高度,而是使用自动计算的高度 + std::unique_ptr< int > mUserDefTabBarHeight; ///< 用户定义的tabbar高度,正常不使用用户设定的高度,而是使用自动计算的高度 + std::unique_ptr< int > mUserDefCategoryHeight; ///< 用户定义的Category的高度,正常不使用用户设定的高度,而是使用自动计算的高度 +public: + PrivateData(SARibbonBar* par) : q_ptr(par) + { + mContextCategoryColorList = SARibbonBar::defaultContextCategoryColorList(); + } + void init(); + int systemTabBarHeight() const; + // 计算tabbar高度 + int calcTabBarHeight(); + // 根据字体信息计算标题栏高度 + int calcTitleBarHeight(); + // 根据字体信息计算category的高度 + int calcCategoryHeight(); + // 计算tabbar高度 + static int + calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode); + // 获取当前最小模式下的高度 + int getCurrentMinimumModeMainBarHeight() const; + // 获取当前正常模式下的高度 + int getCurrentNormalModeMainBarHeight() const; + // 重置尺寸 + void resetSize(); + // 获取标题栏高度 + int titleBarHeight() const; + // tabbar 高度 + int tabBarHeigth() const; + // category高度 + int categoryHeight() const; + // 更新推荐的尺寸值 + void updateHintSize(); + + void setApplicationButton(QAbstractButton* btn); + + bool isContainContextCategoryInList(SARibbonContextCategory* contextCategory); + + void setMinimumMode(); + + void setNormalMode(); + + QColor getContextCategoryColor(); + + void updateTabData(); + + /** + * @brief 通过输入高度计算iconSize + * @param h + * @return + */ + static QSize calcIconSizeByHeight(int h); +}; + +void SARibbonBar::PrivateData::init() +{ + mApplicationButton = RibbonSubElementFactory->createRibbonApplicationButton(q_ptr); + q_ptr->connect(mApplicationButton.data(), &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); + mRibbonTabBar = RibbonSubElementFactory->createRibbonTabBar(q_ptr); + mRibbonTabBar->setObjectName(QStringLiteral("objSARibbonTabBar")); + mRibbonTabBar->setDrawBase(false); + q_ptr->connect(mRibbonTabBar.data(), &QTabBar::currentChanged, q_ptr, &SARibbonBar::onCurrentRibbonTabChanged); + q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabClicked); + q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarDoubleClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabDoubleClicked); + q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabMoved, q_ptr, &SARibbonBar::onTabMoved); + // + mStackedContainerWidget = RibbonSubElementFactory->createRibbonStackedWidget(q_ptr); + mStackedContainerWidget->setObjectName(QStringLiteral("objSARibbonStackedContainerWidget")); + q_ptr->connect(mStackedContainerWidget.data(), &SARibbonStackedWidget::hidWindow, q_ptr, &SARibbonBar::onStackWidgetHided); + // 捕获事件,在popmode时必须用到 + mStackedContainerWidget->installEventFilter(q_ptr); + // + mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(q_ptr); + mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); + mQuickAccessBar->setIcon(q_ptr->windowIcon()); + // + mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(q_ptr); + // + setNormalMode(); +} + +int SARibbonBar::PrivateData::systemTabBarHeight() const +{ + return q_ptr->style()->pixelMetric(QStyle::PM_TabBarBaseHeight) + + q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabHSpace) + + q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabOverlap); +} + +/** + * @brief 估算tabbar的高度 + * @param fm + * @return + */ +int SARibbonBar::PrivateData::calcTabBarHeight() +{ + int defaultHeight = systemTabBarHeight(); + int fontHeight = q_ptr->fontMetrics().lineSpacing(); // 不要用height,像宋体这种字体,height=12,lineSpacing=14,有些就无法显示 + int defaultHeight2 = fontHeight * 1.6; + if (defaultHeight2 < fontHeight + 10) { + defaultHeight2 = fontHeight + 10; // 主要为了满足office2021主题,tab下有个4px的横杠 + } + int r = qMax(defaultHeight, defaultHeight2); + if (r < 20) { + r = 20; + } + return r; +} + +/** + * @brief 估算标题栏的高度 + * @param fm + * @return + */ +int SARibbonBar::PrivateData::calcTitleBarHeight() +{ + int defaultHeight = q_ptr->style()->pixelMetric(QStyle::PM_TitleBarHeight); + int defaultHeight2 = q_ptr->fontMetrics().height() * 1.8; + int r = qMax(defaultHeight, defaultHeight2); + if (r < 25) { + r = 25; + } + return r; +} + +/** + * @brief 估算category的高度 + * @note 经过对照,1.6行高和office的高度比较接近 + * @param fm + * @param s + * @return + */ +int SARibbonBar::PrivateData::calcCategoryHeight() +{ + int textH = q_ptr->fontMetrics().lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 + if (SARibbonPannel::ThreeRowMode == mDefaulePannelLayoutMode) { + // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 + return textH * 4.8 + mPannelTitleHeight; + } else { + // 3=2*1.6 + return textH * 3.2 + mPannelTitleHeight; + } + return (textH * 4.8 + mPannelTitleHeight); +} + +/** + * @brief 计算总高 + * + * @note 此总高是普通模式下的总高 + * + * @param tabHegith + * @param titleHeight + * @param categoryHeight + * @return + */ +int SARibbonBar::PrivateData::calcMainBarHeight(int tabHegith, + int titleHeight, + int categoryHeight, + bool tabOnTitle, + SARibbonBar::RibbonMode rMode) +{ + if (rMode == MinimumRibbonMode) { + // 最小模式,没有categoryHeight + if (tabOnTitle) { + return titleHeight; + } else { + return titleHeight + tabHegith; + } + } else { + if (tabOnTitle) { + return titleHeight + categoryHeight; + } else { + return tabHegith + titleHeight + categoryHeight; + } + } + return tabHegith + titleHeight + categoryHeight; +} + +int SARibbonBar::PrivateData::getCurrentMinimumModeMainBarHeight() const +{ + return calcMainBarHeight( + tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::MinimumRibbonMode); +} + +int SARibbonBar::PrivateData::getCurrentNormalModeMainBarHeight() const +{ + return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::NormalRibbonMode); +} + +/** + * @brief 重新计算尺寸 + */ +void SARibbonBar::PrivateData::resetSize() +{ + updateHintSize(); + const int th = tabBarHeigth(); + int mainBarHeight = calcMainBarHeight(th, titleBarHeight(), categoryHeight(), mIsTabOnTitle, mCurrentRibbonMode); + // 处于最小模式下时,bar的高度为tabbar的bottom,这个调整必须在resize event之后 + q_ptr->setFixedHeight(mainBarHeight); +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonBar::PrivateData::titleBarHeight() const +{ + if (mUserDefTitleBarHeight) { + return *mUserDefTitleBarHeight; + } + return mTitleBarHeight; +} + +/** + * @brief tab高度 + * @return + */ +int SARibbonBar::PrivateData::tabBarHeigth() const +{ + if (mUserDefTabBarHeight) { + return *mUserDefTabBarHeight; + } + return mTabBarHeight; +} + +/** + * @brief category高度 + * @return + */ +int SARibbonBar::PrivateData::categoryHeight() const +{ + if (mUserDefCategoryHeight) { + return *mUserDefCategoryHeight; + } else { + return mCategoryHeight; + } +} + +/** + * @brief 更新推荐的尺寸值 + */ +void SARibbonBar::PrivateData::updateHintSize() +{ + mTitleBarHeight = calcTitleBarHeight(); + // mTabBarHeight有大于0的值说明用户设置了,就使用用户设置的值 + mTabBarHeight = calcTabBarHeight(); + mCategoryHeight = calcCategoryHeight(); +} + +void SARibbonBar::PrivateData::setApplicationButton(QAbstractButton* btn) +{ + if (mApplicationButton) { + mApplicationButton->hide(); + mApplicationButton->deleteLater(); + } + if (btn) { + if (btn->parent() != q_ptr) { + btn->setParent(q_ptr); + } + btn->move(0, q_ptr->titleBarHeight()); + btn->show(); + q_ptr->connect(btn, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); + } + mApplicationButton = btn; +} + +bool SARibbonBar::PrivateData::isContainContextCategoryInList(SARibbonContextCategory* contextCategory) +{ + for (int i = 0; i < mCurrentShowingContextCategory.size(); ++i) { + if (mCurrentShowingContextCategory[ i ] == contextCategory) { + return (true); + } + } + return (false); +} + +void SARibbonBar::PrivateData::setMinimumMode() +{ + mCurrentRibbonMode = SARibbonBar::MinimumRibbonMode; + mStackedContainerWidget->setPopupMode(); + mStackedContainerWidget->setFocusPolicy(Qt::NoFocus); + mStackedContainerWidget->clearFocus(); + mRibbonTabBar->setFocus(); + mStackedContainerWidget->hide(); + resetSize(); +} + +void SARibbonBar::PrivateData::setNormalMode() +{ + mCurrentRibbonMode = SARibbonBar::NormalRibbonMode; + mStackedContainerWidget->setNormalMode(); + mStackedContainerWidget->setFocus(); + mStackedContainerWidget->show(); + resetSize(); +} + +QColor SARibbonBar::PrivateData::getContextCategoryColor() +{ + if (mContextCategoryColorList.isEmpty()) { + mContextCategoryColorListIndex = -1; + return (QColor()); + } + ++mContextCategoryColorListIndex; + if ((mContextCategoryColorListIndex >= mContextCategoryColorList.size()) || (mContextCategoryColorListIndex < 0)) { + mContextCategoryColorListIndex = 0; + } + return (mContextCategoryColorList.at(mContextCategoryColorListIndex)); +} + +void SARibbonBar::PrivateData::updateTabData() +{ + int tabcount = mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + p.index = i; + mRibbonTabBar->setTabData(i, QVariant::fromValue(p)); + } + } + // 刷新完tabdata信息也要接着刷新ContextCategory信息 + for (_SAContextCategoryManagerData& cd : mCurrentShowingContextCategory) { + cd.tabPageIndex.clear(); + for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { + SARibbonCategory* category = cd.contextCategory->categoryPage(i); + for (int t = 0; t < tabcount; ++t) { + QVariant v = mRibbonTabBar->tabData(t); + if (v.isValid()) { + _SARibbonTabData d = v.value< _SARibbonTabData >(); + if (d.category == category) { + cd.tabPageIndex.append(t); + } + } else { + cd.tabPageIndex.append(-1); + } + } + } + } +} + +QSize SARibbonBar::PrivateData::calcIconSizeByHeight(int h) +{ + if (h - 8 >= 20) { + return QSize(h - 8, h - 8); + } + return QSize(h - 4, h - 4); +} + +//=================================================== +// SARibbonBar +//=================================================== + +/** + * @brief SARibbonBar构造函数 + * @param parent + */ +SARibbonBar::SARibbonBar(QWidget* parent) : QMenuBar(parent), d_ptr(new SARibbonBar::PrivateData(this)) +{ + d_ptr->init(); + ensurePolished(); + setNativeMenuBar(false); + // #ifdef Q_OS_MACOS + // setNativeMenuBar(false); + // #endif + // #ifdef Q_OS_LINUX + // setNativeMenuBar(false); + // #endif + if (parent) { + connect(parent, &QWidget::windowTitleChanged, this, &SARibbonBar::onWindowTitleChanged); + connect(parent, &QWidget::windowIconChanged, this, &SARibbonBar::onWindowIconChanged); + } + setRibbonStyle(RibbonStyleLooseThreeRow); +} + +SARibbonBar::~SARibbonBar() +{ +} + +/** + * @brief 判断样式是否为2行 + * @param s + * @return 2行返回true,返回false代表当前是3行 + */ +bool SARibbonBar::isTwoRowStyle(SARibbonBar::RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleTwoRow); +} + +/** + * @brief 判断样式是否为3行 + * @param s + * @return 3行返回true,返回false代表当前是2行 + */ +bool SARibbonBar::isThreeRowStyle(RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleThreeRow); +} + +/** + * @brief 判断是否是宽松样式 + * @param s + * @return 宽松样式(带标题栏)返回true,否则就是紧凑样式 + */ +bool SARibbonBar::isLooseStyle(SARibbonBar::RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleLoose); +} + +/** + * @brief 判断是否是紧凑样式 + * @param s + * @return 紧凑样式(不带标题栏)返回true,否则就是宽松样式 + */ +bool SARibbonBar::isCompactStyle(RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleCompact); +} + +/** + * @brief 获取版本信息 + * @return {SA_RIBBON_BAR_VERSION_MAJ}.{SA_RIBBON_BAR_VERSION_MIN}.{SA_RIBBON_BAR_VERSION_PAT} + */ +QString SARibbonBar::versionString() +{ + return QString("%1.%2.%3").arg(SA_RIBBON_BAR_VERSION_MAJ).arg(SA_RIBBON_BAR_VERSION_MIN).arg(SA_RIBBON_BAR_VERSION_PAT); +} + +/** + @brief 获取默认的上下文标签颜色列表 + @return + */ +QList< QColor > SARibbonBar::defaultContextCategoryColorList() +{ + QList< QColor > res; + res // + << QColor(206, 232, 252) // 蓝 + << QColor(253, 238, 179) // 黄 + << QColor(212, 255, 174) // 绿 + << QColor(255, 196, 214) // 红 + << QColor(255, 216, 153) // 橙 + << QColor(255, 224, 243) // 玫红 + ; + return res; +} + +/** + * @brief 提供高分屏的支持静态函数 + * + * @note 此函数需要在main函数,QApplication生成之前调用 + * @code + * int main(int argc, char* argv[]){ + * SARibbonBar::initHighDpi(); + * QApplication a(argc, argv); + * ... + * } + * @endcode + */ +void SARibbonBar::initHighDpi() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + // 在有些时候,无法显示分割线,是由于PassThrough导致的 + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif +} + +/** + * @brief 返回applicationButton + * @return 默认的applicationButton是@ref SARibbonApplicationButton 生成,通过@ref setApplicationButton 可设置为其他button + */ +QAbstractButton* SARibbonBar::applicationButton() +{ + return (d_ptr->mApplicationButton); +} + +/** + * @brief 设置applicationButton,如果想隐藏,可以传入nullptr + * + * 默认会有一个SARibbonApplicationButton,如果想取消,可传入nullptr,或者自定义的button也可以传入 + * + * @note applicationButton的所有权归SARibbonBar所有,不要在外部对applicationButton进行delete操作 + * @param btn applicationButton指针,可以传入@ref SARibbonApplicationButton, + * SA已经对SARibbonApplicationButton进行了样式设置 + */ +void SARibbonBar::setApplicationButton(QAbstractButton* btn) +{ + d_ptr->setApplicationButton(btn); + if (btn) { + if (btn->objectName().isEmpty()) { + btn->setObjectName(QStringLiteral("SARibbonApplicationButton")); + } + btn->setVisible(true); + // btn->setGeometry(applicationButtonGeometry()); + } + // 无论设置为什么都触发resize + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 返回tabbar + * @return SARibbonTabBar指针 + * @sa SARibbonTabBar + */ +SARibbonTabBar* SARibbonBar::ribbonTabBar() +{ + return (d_ptr->mRibbonTabBar); +} + +/** + * @brief 添加一个标签 + * 如果需要删除,直接delete即可,SARibbonBar会对其进行处理 + * @param title 标签名字,默认情况下SARibbonCategory的object name也被设置为title + * @return 返回一个窗口容器,在Category里可以添加其他控件 + * @sa SARibbonCategory + */ +SARibbonCategory* SARibbonBar::addCategoryPage(const QString& title) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); + + category->setObjectName(title); + category->setCategoryName(title); + addCategoryPage(category); + return (category); +} + +/** + * @brief 添加一个标签 + * @param category + */ +void SARibbonBar::addCategoryPage(SARibbonCategory* category) +{ + if (nullptr == category) { + return; + } + int index = d_ptr->mRibbonTabBar->count(); + insertCategoryPage(category, index); +} + +/** + * @brief qtdesigner专用 + * @param category + */ +void SARibbonBar::addCategoryPage(QWidget* category) +{ + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(category); + + if (c) { + addCategoryPage(c); + } +} + +/** + * @brief 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 + * @param title category的标题 + * @param index category的位置 + * @return + */ +SARibbonCategory* SARibbonBar::insertCategoryPage(const QString& title, int index) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); + + category->setObjectName(title); + category->setCategoryName(title); + insertCategoryPage(category, index); + return (category); +} + +/** + * @brief 插入一个category + * @param category SARibbonCategory指针 + * @param index 插入的位置,如果超出范围,将默认插入到最后 + */ +void SARibbonBar::insertCategoryPage(SARibbonCategory* category, int index) +{ + if (nullptr == category) { + return; + } + category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); + category->setPannelSpacing(d_ptr->mPannelSpacing); + category->setPannelToolButtonIconSize(d_ptr->mPannelToolButtonSize); + int i = d_ptr->mRibbonTabBar->insertTab(index, category->categoryName()); + + _SARibbonTabData tabdata; + + tabdata.category = category; + tabdata.index = i; + d_ptr->mRibbonTabBar->setTabData(i, QVariant::fromValue(tabdata)); + d_ptr->mStackedContainerWidget->insertWidget(index, category); + // 更新index信息 + d_ptr->updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); + + connect(category, &QWidget::windowTitleChanged, this, &SARibbonBar::onCategoryWindowTitleChanged); + connect(category, &SARibbonCategory::actionTriggered, this, &SARibbonBar::actionTriggered); +} + +/** + * @brief 通过名字查找Category + * @param title Category的名字,既标签的标题 + * @return 如果没有找到,将返回nullptr,如果有重名,将返回第一个查询到的名字,因此,尽量避免重名标签 + * @note 由于翻译等原因,可能title会变化,因此如果想通过固定内容查找category,应该使用 @ref categoryByObjectName + * @see categoryByObjectName + */ +SARibbonCategory* SARibbonBar::categoryByName(const QString& title) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (w->categoryName() == title) { + return (w); + } + } + } + return (nullptr); +} + +/** + * @brief 通过ObjectName查找Category + * @param objname + * @return 如果没有找到,将返回nullptr,如果有同样的ObjectName,将返回第一个查询到的名字,因此,尽量避免ObjectName重名 + * @see categoryByName + */ +SARibbonCategory* SARibbonBar::categoryByObjectName(const QString& objname) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (w->objectName() == objname) { + return (w); + } + } + } + return (nullptr); +} + +/** + * @brief 通过索引找到category,如果超过索引范围,会返回nullptr + * @param index 索引 + * @return 如果超过索引范围,会返回nullptr + * @note 如果此时有上下文标签,上下文的标签也会返回 + * @note 通过索引查找的category必须是visible状态的category,如果通过@ref hideCategory 隐藏的标签,通过索引是找不到的 + * @note 通过@ref categoryByObjectName 可以找到所有加入过的标签, + * 如果想得到ribbonbar管理的所有标签,可以通过函数@ref categoryPages 得到 + * @see categoryIndex categoryByObjectName categoryByName + */ +SARibbonCategory* SARibbonBar::categoryByIndex(int index) const +{ + QVariant var = d_ptr->mRibbonTabBar->tabData(index); + + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + return (p.category); + } + return (nullptr); +} + +/** + * @brief 隐藏category,并不会删除或者取走,只是隐藏 + * @param category + */ +void SARibbonBar::hideCategory(SARibbonCategory* category) +{ + int tabcount = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = d_ptr->mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category == category) { + d_ptr->mHidedCategory.append(p); + d_ptr->mRibbonTabBar->removeTab(i); // 仅仅把tab移除 + // 注意Category隐藏后,contex的位置就会发生变化,需要更新 + d_ptr->updateTabData(); + return; + } + } + } +} + +/** + * @brief 显示被隐藏的category + * @param category + */ +void SARibbonBar::showCategory(SARibbonCategory* category) +{ + for (auto i = d_ptr->mHidedCategory.begin(); i != d_ptr->mHidedCategory.end(); ++i) { + if (i->category == category) { + // 说明要显示 + int index = d_ptr->mRibbonTabBar->insertTab(i->index, i->category->categoryName()); + i->index = index; + d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(*i)); + d_ptr->mHidedCategory.erase(i); // 移除 + // 更新index信息 + d_ptr->updateTabData(); + raiseCategory(category); + return; + } + } + raiseCategory(category); +} + +/** + * @brief 判断这个category是否在显示状态,也就是tabbar有这个category + * @param category + * @return + */ +bool SARibbonBar::isCategoryVisible(const SARibbonCategory* c) const +{ + int tabindex = categoryIndex(c); + + return (tabindex >= 0); +} + +/** + * @brief 获取category的索引 + * @param c + * @return 如果找不到,返回-1 + */ +int SARibbonBar::categoryIndex(const SARibbonCategory* c) const +{ + // category的顺序不能以stackedwidget为准,因为存在contextcategory,contextcategory正常是不显示的 + int tabcount = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = d_ptr->mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category == c) { + return (i); + } + } + } + return (-1); +} + +/** + * @brief 移动一个Category从from index到to index + * @param from + * @param to + */ +void SARibbonBar::moveCategory(int from, int to) +{ + d_ptr->mRibbonTabBar->moveTab(from, to); + // 这时要刷新所有tabdata的index信息 + d_ptr->updateTabData(); + // 这里会触发tabMoved信号,在tabMoved信号中调整stacked里窗口的位置 +} + +/** + * @brief 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 + * + * @return + */ +QList< SARibbonCategory* > SARibbonBar::categoryPages(bool getAll) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + QList< SARibbonCategory* > res; + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (!getAll && w->isContextCategory()) { + // 不是getall且是上下文时跳过 + continue; + } + res.append(w); + } + } + return (res); +} + +/** + * @brief 移除SARibbonCategory + * + * SARibbonBar不会delete SARibbonCategory*,但这个SARibbonCategory会脱离SARibbonBar的管理 + * 表现在tabbar会移除,面板会移除,使用此函数后可以对SARibbonCategory进行delete + * @param category + */ +void SARibbonBar::removeCategory(SARibbonCategory* category) +{ + int index = tabIndex(category); + bool isupdate = false; + if (index >= 0) { + d_ptr->mRibbonTabBar->removeTab(index); + isupdate = true; + } + d_ptr->mStackedContainerWidget->removeWidget(category); + // 同时验证这个category是否是contexcategory里的 + + for (SARibbonContextCategory* c : qAsConst(d_ptr->mContextCategoryList)) { + c->takeCategory(category); + } + // 这时要刷新所有tabdata的index信息 + if (isupdate) { + d_ptr->updateTabData(); + } + // 移除完后需要重绘 + repaint(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 添加上下文标签 + * + * 上下文标签是特殊时候触发的标签,需要用户手动触发 + * + * 调用@ref SARibbonContextCategory::addCategoryPage 可在上下文标签中添加SARibbonCategory, + * 在上下文标签添加的SARibbonCategory,只有在上下文标签显示的时候才会显示 + * @param title 上下文标签的标题,在Office模式下会显示,在wps模式下不显示。默认情况下SARibbonContextCategory的object name也被设置为title + * @param color 上下文标签的颜色,如果指定为空QColor(),将会使用SARibbonBar的默认色系 + * @param id 上下文标签的id,以便进行查找 + * @return 返回上下文标签指针 + * @note SARibbonBar拥有SARibbonContextCategory的管理权,用户避免在外部直接delete,如果要删除,调用@ref destroyContextCategory 函数 + */ +SARibbonContextCategory* SARibbonBar::addContextCategory(const QString& title, const QColor& color, const QVariant& id) +{ + SARibbonContextCategory* context = RibbonSubElementFactory->createRibbonContextCategory(this); + + context->setObjectName(title); + context->setContextTitle(title); + context->setId(id); + context->setContextColor(color.isValid() ? color : d_ptr->getContextCategoryColor()); + addContextCategory(context); + return (context); +} + +/** + * @brief 添加上下文标签 + * @param context + */ +void SARibbonBar::addContextCategory(SARibbonContextCategory* context) +{ + if (nullptr == context) { + return; + } + connect(context, &SARibbonContextCategory::categoryPageAdded, this, &SARibbonBar::onContextsCategoryPageAdded); + connect(context, &SARibbonContextCategory::categoryTitleChanged, this, &SARibbonBar::onContextsCategoryCategoryNameChanged); + // remove并没有绑定,主要是remove后在stacked里也不会显示,remove且delete的话,stacked里也会删除 + d_ptr->mContextCategoryList.append(context); +} + +/** + * @brief 显示上下文标签 + * @param context 上下文标签指针 + */ +void SARibbonBar::showContextCategory(SARibbonContextCategory* context) +{ + if (isContextCategoryVisible(context)) { + return; + } + _SAContextCategoryManagerData contextCategoryData; + + contextCategoryData.contextCategory = context; + for (int i = 0; i < context->categoryCount(); ++i) { + SARibbonCategory* category = context->categoryPage(i); + // 此句如果模式重复设置不会进行多余操作 + category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); + // 切换模式后会改变高度,上下文标签显示时要保证显示出来 + int index = d_ptr->mRibbonTabBar->addTab(category->categoryName()); + contextCategoryData.tabPageIndex.append(index); + + _SARibbonTabData tabdata; + tabdata.category = category; + tabdata.index = index; + d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(tabdata)); + } + d_ptr->mCurrentShowingContextCategory.append(contextCategoryData); + // 由于上下文都是在最后追加,不需要调用updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 隐藏上下文标签 + * @param context 上下文标签指针 + */ +void SARibbonBar::hideContextCategory(SARibbonContextCategory* context) +{ + bool needResize = false; + + for (int i = 0; i < d_ptr->mCurrentShowingContextCategory.size(); ++i) { + if (d_ptr->mCurrentShowingContextCategory[ i ].contextCategory == context) { + const QList< int >& indexs = d_ptr->mCurrentShowingContextCategory[ i ].tabPageIndex; + for (int j = indexs.size() - 1; j >= 0; --j) { + d_ptr->mRibbonTabBar->removeTab(indexs[ j ]); + } + // 注意,再删除ContextCategory后,tab的序号就会改变,这时,这个tab后面的都要调整它的序号 + needResize = true; + d_ptr->mCurrentShowingContextCategory.removeAt(i); + // 移除了ContextCategory后需要break + break; + } + } + if (needResize) { + d_ptr->updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); + } +} + +/** + * @brief 判断上下文是否在显示状态 + * @param context + * @return 在显示状态返回true + * @sa setContextCategoryVisible + */ +bool SARibbonBar::isContextCategoryVisible(SARibbonContextCategory* context) +{ + return (d_ptr->isContainContextCategoryInList(context)); +} + +/** + * @brief 设置上下文标签的显示状态 + * + * 上下文标签的当前显示状态可通过 @ref isContextCategoryVisible 进行判断 + * @param context 上下文标签 + * @param visible 显示状态,true为显示 + */ +void SARibbonBar::setContextCategoryVisible(SARibbonContextCategory* context, bool visible) +{ + if (nullptr == context) { + return; + } + if (visible) { + showContextCategory(context); + } else { + hideContextCategory(context); + } +} + +/** + * @brief 获取所有的上下文标签 + * @return 返回上下文标签列表 + */ +QList< SARibbonContextCategory* > SARibbonBar::contextCategoryList() const +{ + return (d_ptr->mContextCategoryList); +} + +/** + * @brief 销毁上下文标签,上下文标签的SARibbonCategory也会随之销毁 + * @param context 需要销毁的上下文标签指针 + */ +void SARibbonBar::destroyContextCategory(SARibbonContextCategory* context) +{ + if (nullptr == context) { + return; + } + //! 1、如果上下文标签显示中,先隐藏 + if (isContextCategoryVisible(context)) { + hideContextCategory(context); + } + //! 2、删除上下文标签的相关内容 + d_ptr->mContextCategoryList.removeAll(context); + //! + QList< SARibbonCategory* > res = context->categoryList(); + + for (SARibbonCategory* c : qAsConst(res)) { + c->hide(); + c->deleteLater(); + } + context->deleteLater(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 设置为最小/正常模式 + * + * 隐藏模式下,只会显示tabbar,不会显示内容,默认状态是显示模式 + * + * 默认下双击tabbar会切换隐藏显示模式,如果想禁用此功能,可以重载 @ref onCurrentRibbonTabDoubleClicked + * 函数,不对函数进行任何处理即可 + * + * @param isMinimum 参数为true时,切换为Minimum模式 + * @see 此函数会改变@ref RibbonState 状态,通过@ref currentRibbonState 函数可以查看当前状态 + */ +void SARibbonBar::setMinimumMode(bool isMinimum) +{ +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + qDebug() << "SARibbonBar::setHideMode " << isMinimum; +#endif + if (isMinimum) { + d_ptr->setMinimumMode(); + } else { + d_ptr->setNormalMode(); + } + QResizeEvent resizeEvent(size(), size()); + QApplication::sendEvent(this, &resizeEvent); + // 发射信号 + Q_EMIT ribbonModeChanged(isMinimum ? MinimumRibbonMode : NormalRibbonMode); +} + +/// +/// \brief 当前ribbon是否是隐藏模式 +/// \return +/// +bool SARibbonBar::isMinimumMode() const +{ + return (d_ptr->mStackedContainerWidget->isPopupMode()); +} + +/// +/// \brief 设置显示隐藏ribbon按钮 +/// +void SARibbonBar::showMinimumModeButton(bool isShow) +{ + if (isShow) { + activeRightButtonGroup(); + + d_ptr->mMinimumCategoryButtonAction = new QAction(this); + d_ptr->mMinimumCategoryButtonAction->setIcon(style()->standardIcon( + isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); + connect(d_ptr->mMinimumCategoryButtonAction, &QAction::triggered, this, [ this ]() { + this->setMinimumMode(!isMinimumMode()); + this->d_ptr->mMinimumCategoryButtonAction->setIcon(style()->standardIcon( + isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); + }); + d_ptr->mRightButtonGroup->addAction(d_ptr->mMinimumCategoryButtonAction); + + } else { + if (nullptr != d_ptr->mMinimumCategoryButtonAction) { + d_ptr->mMinimumCategoryButtonAction->deleteLater(); + d_ptr->mMinimumCategoryButtonAction = nullptr; + } + } + QResizeEvent resizeEvent(size(), size()); + + QApplication::sendEvent(this, &resizeEvent); +} + +/** + * @brief 是否显示隐藏ribbon按钮 + * @return + */ +bool SARibbonBar::haveShowMinimumModeButton() const +{ + return (nullptr != d_ptr->mMinimumCategoryButtonAction); +} + +/** + @brief 隐藏ribbon对应的action + @return + */ +QAction* SARibbonBar::minimumModeAction() const +{ + return d_ptr->mMinimumCategoryButtonAction; +} + +/** + * @brief 当前ribbon的状态(正常|最小化) + * @return + */ +SARibbonBar::RibbonMode SARibbonBar::currentRibbonState() const +{ + return (d_ptr->mCurrentRibbonMode); +} + +/** + @brief tabBar的高度 + @return + */ +int SARibbonBar::tabBarHeight() const +{ + return d_ptr->tabBarHeigth(); + // return d_ptr->mRibbonTabBar->height(); +} + +/** + * @brief 设置tabbar的高度 + * + * 用户调用setTabBarHeight后,将使用用户设定的高度,而不使用自动计算的高度,这时tabbar高度不会跟随字体等信息重新计算 + * + * @note 注意,在RibbonStyleCompact**模式下,tabbar高度要保证小于等于titlebar高度,否则会显示异常 + * @note 此函数不会自动刷新,如果需要刷新调用此函数后需要调用@ref updateRibbonGeometry + * @param h + */ +void SARibbonBar::setTabBarHeight(int h, bool resizeByNow) +{ + if (nullptr == d_ptr->mUserDefTabBarHeight) { + d_ptr->mUserDefTabBarHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefTabBarHeight) = h; + } + if (resizeByNow) { + updateRibbonGeometry(); + } +} + +/** + @brief 返回标题栏高度 + @sa setTitleBarHeight + @return + */ +int SARibbonBar::titleBarHeight() const +{ + return d_ptr->titleBarHeight(); +} + +/** + @brief 设置标题栏的高度 + @sa titleBarHeight + @note 此操作会发射@ref titleBarHeightChanged 信号 + @param h + */ +void SARibbonBar::setTitleBarHeight(int h, bool resizeByNow) +{ + int oldHeight = d_ptr->mTitleBarHeight; + if (nullptr == d_ptr->mUserDefTitleBarHeight) { + d_ptr->mUserDefTitleBarHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefTitleBarHeight) = h; + // + oldHeight = *(d_ptr->mUserDefTitleBarHeight); + } + + if (resizeByNow) { + updateRibbonGeometry(); + } + Q_EMIT titleBarHeightChanged(oldHeight, h); +} + +/** + * @brief category的高度 + * @return + */ +int SARibbonBar::categoryHeight() const +{ + return d_ptr->mStackedContainerWidget->height(); +} + +/** + * @brief 设置category的高度 + * @param h + * @param resizeByNow + */ +void SARibbonBar::setCategoryHeight(int h, bool resizeByNow) +{ + if (nullptr == d_ptr->mUserDefCategoryHeight) { + d_ptr->mUserDefCategoryHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefCategoryHeight) = h; + } + resizeStackedContainerWidget(); + if (resizeByNow) { + updateRibbonGeometry(); + } +} + +void SARibbonBar::onWindowTitleChanged(const QString& title) +{ + Q_UNUSED(title); + update(); +} + +void SARibbonBar::onWindowIconChanged(const QIcon& i) +{ + if (quickAccessBar()) { + quickAccessBar()->setIcon(i); + } +} + +/** + * @brief category的名字发生改变触发 + * @param title + */ +void SARibbonBar::onCategoryWindowTitleChanged(const QString& title) +{ + // 全部更新一遍 + Q_UNUSED(title); + updateCategoryTitleToTabName(); +} + +/// +/// \brief ribbon的显示界面隐藏 +/// +void SARibbonBar::onStackWidgetHided() +{ +} + +/** + * @brief 标签切换触发槽函数 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabChanged(int index) +{ + QVariant var = d_ptr->mRibbonTabBar->tabData(index); + SARibbonCategory* category = nullptr; + + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + category = p.category; + } + if (!category) { + return; + } + if (d_ptr->mStackedContainerWidget->currentWidget() != category) { + d_ptr->mStackedContainerWidget->setCurrentWidget(category); + } + Q_EMIT currentRibbonTabChanged(index); + if (isMinimumMode()) { + d_ptr->mRibbonTabBar->clearFocus(); + if (!d_ptr->mStackedContainerWidget->isVisible()) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 + QHoverEvent ehl(QEvent::HoverLeave, + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); + QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); + resizeStackedContainerWidget(); + d_ptr->mStackedContainerWidget->setFocus(); + // exec之前先发射信息号,否则会被exec阻塞 + d_ptr->mStackedContainerWidget->show(); + } + } + } +} + +/** + * @brief ribbon tab bar单击 + * + * 此实现必须在eventfilter中传递stackedwidget的QEvent::MouseButtonDblClick事件到tabbar中,否则会导致双击变两次单击 + * + * 单击事件仅用于响应点击同一个tab时 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabClicked(int index) +{ + if (index != d_ptr->mRibbonTabBar->currentIndex()) { + // 点击的标签不一致通过changed槽去处理 + return; + } + if (this->isMinimumMode()) { + if (!this->d_ptr->mStackedContainerWidget->isVisible()) { + if (this->d_ptr->mStackedContainerWidget->isPopupMode()) { + // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 + QHoverEvent ehl(QEvent::HoverLeave, + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); + QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); + // 弹出前都调整一下位置,避免移动后位置异常 + resizeStackedContainerWidget(); + this->d_ptr->mStackedContainerWidget->setFocus(); + this->d_ptr->mStackedContainerWidget->exec(); + } + } + } +} + +/** + * @brief ribbon tab bar双击 + * + * 默认情况下双击会切换最小和正常模式 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabDoubleClicked(int index) +{ + Q_UNUSED(index); + setMinimumMode(!isMinimumMode()); +} + +void SARibbonBar::onContextsCategoryPageAdded(SARibbonCategory* category) +{ + Q_ASSERT_X(category != nullptr, "onContextsCategoryPageAdded", "add nullptr page"); + d_ptr->mStackedContainerWidget->addWidget(category); // 这里stackedWidget用append,其他地方都应该使用insert +} + +/** + * @brief 上下文标签管理的标签的名字发生变换 + * @param category + * @param title + */ +void SARibbonBar::onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title) +{ + Q_UNUSED(category); + Q_UNUSED(title); + updateCategoryTitleToTabName(); +} + +/** + * @brief 标签移动的信号 + * @param from + * @param to + */ +void SARibbonBar::onTabMoved(int from, int to) +{ + const QSignalBlocker blocker(d_ptr->mStackedContainerWidget); + // 调整stacked widget的顺序,调整顺序是为了调用categoryPages函数返回的QList顺序和tabbar一致 + d_ptr->mStackedContainerWidget->moveWidget(from, to); +} + +/** + * @brief 根据SARibbonCategory*指针查找tabbar的index + * + * @param c SARibbonCategory对应的QObject指针 + * @return 如果没有找到,返回-1 + * @note 此函数不会调用SARibbonCategory*的任何方法,因此可以在SARibbonCategory的destroyed槽中调用 + */ +int SARibbonBar::tabIndex(SARibbonCategory* obj) +{ + const int size = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < size; ++i) { + QVariant v = d_ptr->mRibbonTabBar->tabData(i); + if (v.isValid()) { + _SARibbonTabData innervalue = v.value< _SARibbonTabData >(); + if (innervalue.category == obj) { + return (i); + } + } + } + // 如果找不到就从stackedwidget中找 + + return (-1); +} + +void SARibbonBar::resizeAll() +{ + if (isLooseStyle()) { + resizeInLooseStyle(); + } else { + resizeInCompactStyle(); + } + update(); +} + +/** + * @brief 把ribbonbar的内容,同步进各个category中 + * @param autoUpdate + */ +void SARibbonBar::synchronousCategoryData(bool autoUpdate) +{ + iterate([ this ](SARibbonCategory* c) -> bool { + c->setEnableShowPannelTitle(this->isEnableShowPannelTitle()); + c->setPannelTitleHeight(this->pannelTitleHeight()); + c->setCategoryAlignment(this->ribbonAlignment()); + c->setPannelLayoutMode(this->pannelLayoutMode()); + return true; + }); + + //! 直接给一个resizeevent,让所有刷新 + if (autoUpdate) { + QResizeEvent* e = new QResizeEvent(size(), QSize()); + QApplication::postEvent(this, e); + } +} + +/** + @brief 激活tabbar右边的按钮群 + @return 返回右边的按钮群指针 + */ +SARibbonButtonGroupWidget* SARibbonBar::activeRightButtonGroup() +{ + if (nullptr == d_ptr->mRightButtonGroup) { + d_ptr->mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(this); + } + d_ptr->mRightButtonGroup->show(); + return d_ptr->mRightButtonGroup; +} + +/** + @brief 返回右边的按钮群指针 + @return 如果没有创建,返回nullptr + */ +SARibbonButtonGroupWidget* SARibbonBar::rightButtonGroup() +{ + return d_ptr->mRightButtonGroup; +} + +/** + @brief 激活QuickAccessBar + @return + */ +SARibbonQuickAccessBar* SARibbonBar::activeQuickAccessBar() +{ + if (nullptr == d_ptr->mQuickAccessBar) { + d_ptr->mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(this); + d_ptr->mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); + d_ptr->mQuickAccessBar->setIcon(windowIcon()); + } + d_ptr->mQuickAccessBar->show(); + return d_ptr->mQuickAccessBar; +} + +SARibbonQuickAccessBar* SARibbonBar::quickAccessBar() +{ + return (d_ptr->mQuickAccessBar); +} + +/** + * @brief 设置ribbonbar的风格,此函数会重新设置所有元素,包括button的布局方式, + * 尤其是从三行变到两行的过程,重设的内容较多 + * @note 此函数会自动触发ResizeEvent,不需要手动调用 + * @note 默认情况下2行模式不换行,如果想在两行模式换行,在调用SARibbonBar::setRibbonStyle后,再SARibbonToolButton::setEnableWordWrap(true) + * ,同时再调用updateRibbonElementGeometry()刷新布局 + * @param v 样式,见@ref SARibbonBar::RibbonStyle + */ +void SARibbonBar::setRibbonStyle(SARibbonBar::RibbonStyles v) +{ + // 先幅值给变量 + d_ptr->mRibbonStyle = v; +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "setRibbonStyle(" << v << ")" // + << "\n isThreeRowStyle=" << isThreeRowStyle() // + << "\n isTwoRowStyle=" << isTwoRowStyle() // + << "\n isLooseStyle=" << isLooseStyle() // + << "\n isCompactStyle=" << isCompactStyle() // + ; +#endif + // 执行判断 + setEnableWordWrap(isThreeRowStyle(v)); + setTabOnTitle(isCompactStyle()); + d_ptr->mQuickAccessBar->setEnableShowIcon(isLooseStyle()); + setEnableShowPannelTitle(isThreeRowStyle(v)); + setPannelLayoutMode(isThreeRowStyle(v) ? SARibbonPannel::ThreeRowMode : SARibbonPannel::TwoRowMode); + + // 此函数会调用setFixedHeight + synchronousCategoryData(false); // 这里不急着刷新,下面会继续刷新 + d_ptr->resetSize(); + + Q_EMIT ribbonStyleChanged(d_ptr->mRibbonStyle); +} + +/** + @brief 返回当前ribbon的风格 + @return 返回当前ribbon的风格 + */ +SARibbonBar::RibbonStyles SARibbonBar::currentRibbonStyle() const +{ + return (d_ptr->mRibbonStyle); +} + +/** + @brief 切换到对应标签 + @param index 标签索引 + */ +void SARibbonBar::setCurrentIndex(int index) +{ + d_ptr->mRibbonTabBar->setCurrentIndex(index); + // onCurrentRibbonTabChanged(index); +} + +/** + * @brief 返回当前的tab索引 + * @return 当前的索引 + */ +int SARibbonBar::currentIndex() +{ + return (d_ptr->mRibbonTabBar->currentIndex()); +} + +/** + * @brief 确保标签显示出来,tab并切换到对应页 + * @param category 标签指针 + */ +void SARibbonBar::raiseCategory(SARibbonCategory* category) +{ + int index = d_ptr->mStackedContainerWidget->indexOf(category); + + if (index >= 0) { + setCurrentIndex(index); + } +} + +/** + * @brief 判断当前的样式是否为2行 + * @return + */ +bool SARibbonBar::isTwoRowStyle() const +{ + return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::TwoRowMode); +} + +/** + * @brief 判断当前的样式是否为3行 + * @return + */ +bool SARibbonBar::isThreeRowStyle() const +{ + return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::ThreeRowMode); +} + +/** + * @brief 判断当前的样式是否为宽松样式 + * @return + */ +bool SARibbonBar::isLooseStyle() const +{ + return (SARibbonBar::isLooseStyle(currentRibbonStyle())); +} + +/** + * @brief 判断当前的样式是否为紧凑样式 + * @return + */ +bool SARibbonBar::isCompactStyle() const +{ + return (SARibbonBar::isCompactStyle(currentRibbonStyle())); +} + +/** + * @brief 设置标题的文字颜色 + * + * 标题时mainwindow的windowTitle,如果要设置标题,直接调用SARibbonMainWindow::setWindowTitle 进行设置 + * + * 如果不设置标题颜色,默认是SARibbonBar的qss的color属性 + * @param clr + * @note 此函数不会刷新,刷新请自行调用repaint + */ +void SARibbonBar::setWindowTitleTextColor(const QColor& clr) +{ + d_ptr->mTitleTextColor = clr; +} + +/** + * @brief 获取标题的文字颜色 + * @return 如果返回的是无效颜色,!QColor::isValid(),说明没有手动设置颜色,颜色将跟随SARibbonBar的qss的文字颜色 + */ +QColor SARibbonBar::windowTitleTextColor() const +{ + return d_ptr->mTitleTextColor; +} + +/** + * @brief tabbar 底部会绘制一条线条,此接口定义线条颜色 + * @param clr + */ +void SARibbonBar::setTabBarBaseLineColor(const QColor& clr) +{ + d_ptr->mTabBarBaseLineColor = clr; +} + +/** + * @brief 获取tabbar底部基线颜色 + * @return + */ +QColor SARibbonBar::tabBarBaseLineColor() const +{ + return d_ptr->mTabBarBaseLineColor; +} + +/** + * @brief 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局 + * + * @note 此函数调用较慢,避免在高速要求场合调用 + */ +void SARibbonBar::updateRibbonGeometry() +{ + d_ptr->resetSize(); + iterate([](SARibbonCategory* c) -> bool { + c->updateItemGeometry(); + return true; + }); + // !主题变更后,tabbar的长度需要进行刷新 + + //! 直接给一个resizeevent,让所有刷新 + QResizeEvent* e = new QResizeEvent(size(), QSize()); + QApplication::postEvent(this, e); +} + +/** + * @brief SARibbonPannel的布局模式 + * @return + */ +SARibbonPannel::PannelLayoutMode SARibbonBar::pannelLayoutMode() const +{ + return d_ptr->mDefaulePannelLayoutMode; +} + +/** + * @brief SARibbonPannel的布局模式设置 + * @param m + */ +void SARibbonBar::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) +{ + d_ptr->mDefaulePannelLayoutMode = m; + iterate([ m ](SARibbonCategory* c) -> bool { + c->setPannelLayoutMode(m); + return true; + }); +} + +/** + * @brief 设置tab在title上面,这样可以省略title区域 + * @param on + */ +void SARibbonBar::setTabOnTitle(bool on) +{ + if (d_ptr->mIsTabOnTitle != on) { + d_ptr->mIsTabOnTitle = on; + d_ptr->resetSize(); + } +} + +/** + * @brief tab是否在title上面 + * @return + */ +bool SARibbonBar::isTabOnTitle() const +{ + return d_ptr->mIsTabOnTitle; +} + +/** + * @brief 设置标题的对齐方式 + * @param al + */ +void SARibbonBar::setWindowTitleAligment(Qt::Alignment al) +{ + d_ptr->mTitleAligment = al; +} + +/** + * @brief 获取标题的对齐方式 + * @return + */ +Qt::Alignment SARibbonBar::windowTitleAligment() const +{ + return d_ptr->mTitleAligment; +} + +/** + * @brief 设置ribbonbar的按钮文字允许换行 + * @param on + */ +void SARibbonBar::setEnableWordWrap(bool on) +{ + SARibbonToolButton::setEnableWordWrap(on); + updateRibbonGeometry(); +} + +/** + * @brief 判断是否允许换行 + * @return + */ +bool SARibbonBar::isEnableWordWrap() const +{ + return SARibbonToolButton::isEnableWordWrap(); +} + +/** + * @brief pannel标题栏的高度 + * @return + */ +int SARibbonBar::pannelTitleHeight() const +{ + return d_ptr->mPannelTitleHeight; +} + +/** + * @brief 设置pannel的高度 + * @param h + */ +void SARibbonBar::setPannelTitleHeight(int h) +{ + d_ptr->mPannelTitleHeight = h; + iterate([ h ](SARibbonCategory* c) -> bool { + c->setPannelTitleHeight(h); + return true; + }); +} + +/** + * @brief 是否pannel显示标题栏 + * @return + */ +bool SARibbonBar::isEnableShowPannelTitle() const +{ + return d_ptr->mEnableShowPannelTitle; +} + +/** + * @brief 设置显示pannel标题 + * @param on + */ +void SARibbonBar::setEnableShowPannelTitle(bool on) +{ + d_ptr->mEnableShowPannelTitle = on; + iterate([ on ](SARibbonCategory* c) -> bool { + c->setEnableShowPannelTitle(on); + return true; + }); +} + +/** + * @brief 设置pannel的spacing + * @param n + */ +void SARibbonBar::setPannelSpacing(int n) +{ + d_ptr->mPannelSpacing = n; + // 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing + iterate([ n ](SARibbonCategory* category) -> bool { + if (category) { + category->setPannelSpacing(n); + } + return true; + }); +} + +/** + * @brief pannel的spacing + * @return + */ +int SARibbonBar::pannelSpacing() const +{ + return d_ptr->mPannelSpacing; +} + +/** + * @brief 设置pannel按钮的icon尺寸,large action不受此尺寸影响 + * @param s + */ +void SARibbonBar::setPannelToolButtonIconSize(const QSize& s) +{ + d_ptr->mPannelToolButtonSize = s; + // 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing + iterate([ s ](SARibbonCategory* category) -> bool { + if (category) { + category->setPannelToolButtonIconSize(s); + } + return true; + }); +} + +/** + * @brief pannel按钮的icon尺寸,large action不受此尺寸影响 + * @return + */ +QSize SARibbonBar::pannelToolButtonIconSize() const +{ + return d_ptr->mPannelToolButtonSize; +} + +/** + * @brief ribbonbar内部的StackedWidget + * 所有的category都放置在StackedWidget中 + * @return + */ +SARibbonStackedWidget* SARibbonBar::ribbonStackedWidget() +{ + return d_ptr->mStackedContainerWidget; +} + +/** + * @brief 设置是否显示标题 + * @param on + */ +void SARibbonBar::setTitleVisible(bool on) +{ + d_ptr->mIsTitleVisible = on; +} + +/** + * @brief 判断标题是否显示 + * @return + */ +bool SARibbonBar::isTitleVisible() const +{ + return d_ptr->mIsTitleVisible; +} + +/** + @brief 设置上下文标签的颜色列表 + + 上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 + @param cls + */ +void SARibbonBar::setContextCategoryColorList(const QList< QColor >& cls) +{ + d_ptr->mContextCategoryColorList = cls; + if (d_ptr->mContextCategoryColorList.isEmpty()) { + d_ptr->mContextCategoryColorList = defaultContextCategoryColorList(); + } + d_ptr->mContextCategoryColorListIndex = 0; + // 这时需要对已经显示的contextCategoryData的颜色进行重新设置 + for (SARibbonContextCategory* c : d_ptr->mContextCategoryList) { + c->setContextColor(d_ptr->getContextCategoryColor()); + } +} + +/** + @brief 获取上下文标签的颜色列表 + @return + */ +QList< QColor > SARibbonBar::contextCategoryColorList() const +{ + return d_ptr->mContextCategoryColorList; +} + +/** + * @brief 设置contextCategory 标题的颜色 + * @param clr + */ +void SARibbonBar::setContextCategoryTitleTextColor(const QColor& clr) +{ + d_ptr->mContextCategoryTitleTextColor = clr; +} + +/** + * @brief contextCategory 标题的颜色 + * @return + */ +QColor SARibbonBar::contextCategoryTitleTextColor() const +{ + return d_ptr->mContextCategoryTitleTextColor; +} + +/** + @brief 设置ribbon的对齐方式 + @param al + */ +void SARibbonBar::setRibbonAlignment(SARibbonAlignment al) +{ + d_ptr->mRibbonAlignment = al; + synchronousCategoryData(); +} + +/** + @brief ribbon的对齐方式 + @return + */ +SARibbonAlignment SARibbonBar::ribbonAlignment() const +{ + return d_ptr->mRibbonAlignment; +} + +/** + * @brief 此函数会遍历SARibbonBar下的所有Category,执行函数指针bool(SARibbonCategory*) + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonBar::iterate(FpCategoryIterate fp) +{ + const QList< SARibbonCategory* > cs = categoryPages(true); + for (SARibbonCategory* c : cs) { + if (!fp(c)) { + return false; + } + } + return true; +} + +/** + * @brief 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针bool(SARibbonPannel*) + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonBar::iterate(FpPannelIterate fp) +{ + const QList< SARibbonCategory* > cs = categoryPages(true); + for (SARibbonCategory* c : cs) { + if (!c->iterate(fp)) { + return false; + } + } + return true; +} + +/** + * @brief 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget + * @param on + * @param c + */ +void SARibbonBar::setCornerWidgetVisible(bool on, Qt::Corner c) +{ + if (QWidget* w = cornerWidget(c)) { + w->setVisible(on); + } +} + +/** + * @brief SARibbonBar::eventFilter + * @param obj + * @param e + * @return + */ +bool SARibbonBar::eventFilter(QObject* obj, QEvent* e) +{ + if (obj) { + // 调整多文档时在窗口模式下的按钮更新 + if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) { + if ((QEvent::UpdateLater == e->type()) || (QEvent::MouseButtonRelease == e->type()) + || (QEvent::WindowActivate == e->type())) { + QApplication::postEvent(this, new QResizeEvent(size(), size())); + } + } else if (obj == d_ptr->mStackedContainerWidget) { + // 在stack 是popup模式时,点击的是stackedContainerWidget区域外的时候,如果是在ribbonTabBar上点击 + // 那么忽略掉这次点击,把点击下发到ribbonTabBar,这样可以避免stackedContainerWidget在点击ribbonTabBar后 + // 隐藏又显示,出现闪烁 + if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + QMouseEvent* mouseEvent = static_cast< QMouseEvent* >(e); + if (!d_ptr->mStackedContainerWidget->rect().contains(mouseEvent->pos())) { + QWidget* clickedWidget = QApplication::widgetAt(mouseEvent->globalPos()); + if (clickedWidget == d_ptr->mRibbonTabBar) { + const QPoint targetPoint = clickedWidget->mapFromGlobal(mouseEvent->globalPos()); + QMouseEvent* evPress = new QMouseEvent(mouseEvent->type(), + targetPoint, + mouseEvent->globalPos(), + mouseEvent->button(), + mouseEvent->buttons(), + mouseEvent->modifiers()); + QApplication::postEvent(clickedWidget, evPress); + return (true); + } + } + } + } + } + } + return (QMenuBar::eventFilter(obj, e)); +} + +/** + * @brief 根据情况重置tabbar的宽度,主要针对wps模式 + */ +int SARibbonBar::calcMinTabBarWidth() const +{ + // 20200831 + // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 + // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 + // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow + // 计算tab所占用的宽度 + const QMargins& mg = d_ptr->mRibbonTabBar->tabMargin(); + return d_ptr->mRibbonTabBar->sizeHint().width() + (mg.left() + mg.right()); +} + +/** + * @brief 正常模式下的高度 + * + * 有可能SARibbonBar::height和mainBarHeight不相等,这种情况发生在RibbonState::MinimumRibbonMode状态下 + * @return 高度 + */ +int SARibbonBar::normalModeMainBarHeight() const +{ + return d_ptr->getCurrentNormalModeMainBarHeight(); +} + +/** + @brief 最小模式下的高度 + @return + */ +int SARibbonBar::minimumModeMainBarHeight() const +{ + return d_ptr->getCurrentMinimumModeMainBarHeight(); +} + +/** + * @brief 更新所有的category title对应的tab名 + * + * 此函数会对所有的category的名字和tab进行匹配,如果匹配不上会重新设置tab名 + */ +void SARibbonBar::updateCategoryTitleToTabName() +{ + SARibbonTabBar* tab = d_ptr->mRibbonTabBar; + for (int i = 0; i < tab->count(); ++i) { + // 鉴于tab不会很多,不考虑效率问题 + QVariant var = tab->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category && p.category->categoryName() != tab->tabText(i)) { + tab->setTabText(i, p.category->categoryName()); + } + } + } + repaint(); +} + +/** + * @brief 告知WindowButtonGroup的尺寸 + * @param s + */ +void SARibbonBar::setWindowButtonGroupSize(const QSize& s) +{ + d_ptr->mWindowButtonSize = s; +} + +void SARibbonBar::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + if (isLooseStyle()) { + paintInLooseStyle(); + } else { + paintInCompactStyle(); + } +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + QPainter p(this); + HELP_DRAW_RECT(p, m_d->quickAccessBar->geometry()); + HELP_DRAW_RECT(p, m_d->ribbonTabBar->geometry()); + HELP_DRAW_RECT(p, m_d->stackedContainerWidget->geometry()); +#endif +} + +void SARibbonBar::paintInLooseStyle() +{ + QPainter p(this); + + //!绘制tabbar下的基线,这个函数仅仅对office2013主题有用,大部分主题都不绘制基线 + paintTabbarBaseLine(p); + + //! 显示上下文标签 + p.save(); + QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; + // bool isCurrentSelectContextCategoryPage = false; + + QPoint contextCategoryRegion(width(), -1); + QMargins border = contentsMargins(); + + for (int i = 0; i < contextCategoryDataList.size(); ++i) { + QRect contextTitleRect; + QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; + QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); + if (!indexs.isEmpty()) { + contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); + QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); + contextTitleRect.setRight(endRect.right()); + contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); + contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); // 减1像素,避免tabbar基线覆盖 + contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); + // 把区域顶部扩展到窗口顶部 + contextTitleRect.setTop(border.top()); + // 绘制 + paintContextCategoryTab(p, contextCategoryDataList[ i ].contextCategory->contextTitle(), contextTitleRect, clr); + // 更新上下文标签的范围,用于控制标题栏的显示 + if (contextTitleRect.left() < contextCategoryRegion.x()) { + contextCategoryRegion.setX(contextTitleRect.left()); + } + if (contextTitleRect.right() > contextCategoryRegion.y()) { + contextCategoryRegion.setY(contextTitleRect.right()); + } + } + // isCurrentSelectContextCategoryPage = indexs.contains(d_ptr->mRibbonTabBar->currentIndex()); + // if (isCurrentSelectContextCategoryPage) { + // QPen pen; + // pen.setColor(clr); + // pen.setWidth(1); + // p.setPen(pen); + // p.setBrush(Qt::NoBrush); + // p.drawRect(d_ptr->mStackedContainerWidget->geometry()); + // isCurrentSelectContextCategoryPage = false; + // } + } + p.restore(); + //! 显示标题等 + QWidget* parWindow = parentWidget(); + + if (parWindow) { + QRect titleRegion; + if (contextCategoryRegion.y() < 0) { + titleRegion.setRect(d_ptr->mQuickAccessBar->geometry().right() + 1, + border.top(), + width() - d_ptr->mIconRightBorderPosition - border.right() + - d_ptr->mWindowButtonSize.width() - d_ptr->mQuickAccessBar->geometry().right() - 1, + titleBarHeight()); + } else { + int leftwidth = contextCategoryRegion.x() - d_ptr->mQuickAccessBar->geometry().right() + - d_ptr->mIconRightBorderPosition; + int rightwidth = width() - contextCategoryRegion.y() - d_ptr->mWindowButtonSize.width(); + // if (width() - contextCategoryRegion.y() > contextCategoryRegion.x()-x) { + if (rightwidth > leftwidth) { + // 说明右边的区域大一点,标题显示在右,显示在右边需要减去windowbutton宽度 + titleRegion.setRect(contextCategoryRegion.y(), border.top(), rightwidth, titleBarHeight()); + } else { + // 说明左边的大一点 + titleRegion.setRect(d_ptr->mIconRightBorderPosition + d_ptr->mQuickAccessBar->geometry().right(), + border.top(), + leftwidth, + titleBarHeight()); + } + } +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + p.save(); + p.setBrush(QColor(255, 0, 0, 120)); + p.drawRect(titleRegion); + p.restore(); +#endif + paintWindowTitle(p, parWindow->windowTitle(), titleRegion); + } +} + +void SARibbonBar::paintInCompactStyle() +{ + QPainter p(this); + //! + paintTabbarBaseLine(p); + //! 显示上下文标签 + p.save(); + QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; + QMargins border = contentsMargins(); + for (int i = 0; i < contextCategoryDataList.size(); ++i) { + QRect contextTitleRect; + QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; + QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); + if (!indexs.isEmpty()) { + contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); + QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); + contextTitleRect.setRight(endRect.right()); + contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); + contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); + contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); + // 把区域顶部扩展到窗口顶部 + contextTitleRect.setTop(border.top()); + // 绘制 + paintContextCategoryTab(p, QString(), contextTitleRect, clr); + } + } + p.restore(); + //! 显示标题等 + + QWidget* parWindow = parentWidget(); + + if (parWindow) { + int start = d_ptr->mRibbonTabBar->x() + d_ptr->mRibbonTabBar->width(); + int width = d_ptr->mQuickAccessBar->x() - start; + if (width > 20) { + QRect titleRegion(start, border.top(), width, titleBarHeight()); +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + p.save(); + p.setBrush(QColor(255, 0, 0, 120)); + p.drawRect(titleRegion); + p.restore(); +#endif + paintWindowTitle(p, parWindow->windowTitle(), titleRegion); + } + } +} + +void SARibbonBar::resizeStackedContainerWidget() +{ + QMargins border = contentsMargins(); + const QRect& ribbonTabBarGeometry = d_ptr->mRibbonTabBar->geometry(); + + int x = border.left(); + int y = ribbonTabBarGeometry.bottom() + 1; + int w = width() - border.left() - border.right(); + int h = d_ptr->categoryHeight(); + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 弹出模式时,位置为全局位置 + QPoint absPosition = mapToGlobal(QPoint(x, y)); + x = absPosition.x(); + y = absPosition.y(); + } + d_ptr->mStackedContainerWidget->setFixedSize(QSize(w, h)); + d_ptr->mStackedContainerWidget->setGeometry(x, y, w, h); +} + +/** + * @brief 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 + */ +void SARibbonBar::updateContextCategoryManagerData() +{ + const int c = d_ptr->mRibbonTabBar->count(); + + for (_SAContextCategoryManagerData& cd : d_ptr->mCurrentShowingContextCategory) { + cd.tabPageIndex.clear(); + for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { + SARibbonCategory* category = cd.contextCategory->categoryPage(i); + for (int t = 0; t < c; ++t) { + QVariant v = d_ptr->mRibbonTabBar->tabData(t); + if (v.isValid()) { + _SARibbonTabData d = v.value< _SARibbonTabData >(); + if (d.category == category) { + cd.tabPageIndex.append(t); + } + } else { + cd.tabPageIndex.append(-1); + } + } + } + } +} + +/** + * @brief 绘制上下文标签的背景 + * @param painter 绘图QPainter + * @param title 上下文标签的title + * @param contextRect 上下文标签的绘制区域 + * @param color 上下文标签赋予的颜色 + */ +void SARibbonBar::paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color) +{ + // 绘制上下文标签 + // 首先有5像素的实体粗线位于顶部 + QMargins border = contentsMargins(); + painter.save(); + painter.setPen(Qt::NoPen); + painter.setBrush(color); + painter.drawRect(contextRect); + const int contextLineWidth = 5; + // 绘制很线 + QColor gColor = color.darker(); + painter.fillRect(QRect(contextRect.x(), contextRect.y(), contextRect.width(), contextLineWidth), gColor); + + // 只有在office模式下才需要绘制标题 + if (isLooseStyle()) { + if (!title.isEmpty()) { + QRect textRect = QRect(contextRect.x(), + contextRect.y() + contextLineWidth, + contextRect.width(), + contextRect.height() - contextLineWidth - d_ptr->mRibbonTabBar->height()); + painter.setPen(contextCategoryTitleTextColor()); + painter.drawText(textRect, Qt::AlignCenter, title); + } + } + painter.restore(); +} + +void SARibbonBar::resizeEvent(QResizeEvent* e) +{ + Q_UNUSED(e); + if (isLooseStyle()) { + resizeInLooseStyle(); + } else { + resizeInCompactStyle(); + } + update(); +} + +/** + * @brief 重写moveevent是为了在移动时调整isPopupMode状态下的stackedContainerWidget位置 + * @param event + */ +void SARibbonBar::moveEvent(QMoveEvent* e) +{ + if (d_ptr->mStackedContainerWidget) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 弹出模式时,窗口发生了移动,同步调整StackedContainerWidget的位置 + resizeStackedContainerWidget(); + } + } + QMenuBar::moveEvent(e); +} + +/** + * @brief 跟踪字体改变 + * @param event + */ +void SARibbonBar::changeEvent(QEvent* e) +{ + if (nullptr == e) { + return; + } + switch (e->type()) { + case QEvent::FontChange: { + QFont f = font(); + QList< QWidget* > listWidgets = findChildren< QWidget* >(); + for (QWidget* w : listWidgets) { + w->setFont(f); + } + updateRibbonGeometry(); + } break; + case QEvent::StyleChange: { + updateRibbonGeometry(); + } break; + default: + break; + } + QMenuBar::changeEvent(e); +} + +bool SARibbonBar::event(QEvent* e) +{ + switch (e->type()) { + case QEvent::Show: + // 第一次显示刷新 + updateRibbonGeometry(); + break; + default: + break; + } + return QMenuBar::event(e); +} + +void SARibbonBar::resizeInLooseStyle() +{ + synchronousCategoryData(false); + + QMargins border = contentsMargins(); + int x = border.left(); + int y = border.top(); + + // cornerWidget - TopLeftCorner + const int validTitleBarHeight = d_ptr->titleBarHeight(); + const int tabH = d_ptr->tabBarHeigth(); + // tabbar的标签不会因为tabbar的高度而铺满,tabbar的标签是固定高度的,tabbar拉高只会把底部露出来,因此,systab是理论合理的高度 + // 布局corner widget + x += d_ptr->mIconRightBorderPosition + 5; + if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { + if (connerL->isVisibleTo(this)) { + QSize connerSize = connerL->sizeHint(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerL->setGeometry(x, y + detal, connerSize.width(), connerSize.height()); + } else { + connerL->setGeometry(x, y, connerSize.width(), validTitleBarHeight); + } + x = connerL->geometry().right() + 5; + } + } + // quick access bar定位 + if (d_ptr->mQuickAccessBar) { + if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { + if (d_ptr->mQuickAccessBar->height() != validTitleBarHeight) { + d_ptr->mQuickAccessBar->setFixedHeight(validTitleBarHeight); + } + QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); + // 上下留1px的边线 + d_ptr->mQuickAccessBar->setGeometry(x, y + 1, quickAccessBarSize.width(), validTitleBarHeight); + } + } + // 第二行,开始布局applicationButton,tabbar,tabBarRightSizeButtonGroupWidget,TopRightCorner + x = border.left(); + y += validTitleBarHeight; // 此时,y值在titlebar下面 + // applicationButton 定位 + if (d_ptr->mApplicationButton) { + if (d_ptr->mApplicationButton->isVisibleTo(this)) { + // 保证 + d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); + x = d_ptr->mApplicationButton->geometry().right(); + } + } + // top right是一定要配置的,对于多文档窗口,子窗口的缩放等按钮就是通过这个窗口实现, + // 由于这个窗口一定要在最右,因此先对这个窗口进行布局 + // cornerWidget - TopRightCorner + // 获取最右边的位置 + int endX = width() - border.right(); + + if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { + if (connerW->isVisibleTo(this)) { + QSize connerSize = connerW->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < tabH) { + int detal = (tabH - connerSize.height()) / 2; + connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerW->setGeometry(endX, y, connerSize.width(), tabH); + } + } + } + // applicationButton和TopRightCorner完成定位,才可以定位tab bar + // tab bar 定位 + + // tabBar 右边的附加按钮组,这里一般会附加一些类似登录等按钮组 + // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() + if (d_ptr->mRightButtonGroup) { + QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); + endX -= wSize.width(); + // 上下留1px的边线 + d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), tabH - 2); + } + // 最后确定tabbar宽度 + int tabBarAllowedWidth = endX - x; + if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 居中对齐的情况下,Tab要居中显示 + // 得到tab的推荐尺寸 + int mintabBarWidth = calcMinTabBarWidth(); + if (mintabBarWidth >= tabBarAllowedWidth) { + // 这时tabbar没有居中对齐的必要性,空间位置不够了 + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 说明tabbar的宽度有居中的可能性 + int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; + d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); + } + } + resizeStackedContainerWidget(); +} + +void SARibbonBar::resizeInCompactStyle() +{ + synchronousCategoryData(false); + QMargins border = contentsMargins(); + int x = border.left(); + int y = border.top(); + + const int validTitleBarHeight = titleBarHeight(); + + // 先布局右边内容 + // cornerWidget - TopRightCorner + int endX = width() - border.right() - d_ptr->mWindowButtonSize.width(); + + if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { + if (connerW->isVisibleTo(this)) { + QSize connerSize = connerW->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerW->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); + } + } + } + + // tabBar 右边的附加按钮组 + // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() + if (d_ptr->mRightButtonGroup) { + QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); + endX -= wSize.width(); + // 上下留1px的边线 + d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), validTitleBarHeight - 2); + } + // quick access bar定位 + if (d_ptr->mQuickAccessBar) { + if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { + QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); + endX -= quickAccessBarSize.width(); + // 上下留1px的边线 + d_ptr->mQuickAccessBar->setGeometry(endX, y + 1, quickAccessBarSize.width(), validTitleBarHeight - 2); + } + } + // cornerWidget - TopLeftCorner + if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { + if (connerL->isVisibleTo(this)) { + QSize connerSize = connerL->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerL->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerL->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); + } + } + } + + // tab 的y值需要重新计算 + // 紧凑模式下,tabbarHeight不生效 + int tabH = tabBarHeight(); + if (tabH > validTitleBarHeight) { + // 这种直接把tabH设置为validTitleBarHeight + tabH = validTitleBarHeight; + } + + y = y + validTitleBarHeight - tabH; // 如果tabH较小,则下以,让tab底部和title的底部对齐 + + // applicationButton 定位,与TabBar同高 + if (d_ptr->mApplicationButton) { + if (d_ptr->mApplicationButton->isVisibleTo(this)) { + d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); + x = d_ptr->mApplicationButton->geometry().right() + 2; + } + } + // tab bar 定位 wps模式下applicationButton的右边就是tab bar + int tabBarAllowedWidth = endX - x; + // 20200831 + // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 + // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 + // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow + // 计算tab所占用的宽度 + int mintabBarWidth = calcMinTabBarWidth(); + + if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { + if (mintabBarWidth < tabBarAllowedWidth) { + tabBarAllowedWidth = mintabBarWidth; + } + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 居中对齐 + if (mintabBarWidth >= tabBarAllowedWidth) { + // 这时tabbar没有居中对齐的必要性,空间位置不够了 + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 说明tabbar的宽度有居中的可能性 + int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; + d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); + } + } + // 调整整个stackedContainer + resizeStackedContainerWidget(); +} + +/** + * @brief 绘制tabbar下的基准线,这个方法仅仅在office2013模式下需要 + * @param painter + */ +void SARibbonBar::paintTabbarBaseLine(QPainter& painter) +{ + if (!d_ptr->mTabBarBaseLineColor.isValid()) { + return; + } + painter.save(); + // 在tabbar下绘制一条线 + const int lineY = d_ptr->mRibbonTabBar->geometry().bottom(); + QPen pen(d_ptr->mTabBarBaseLineColor); + QMargins border = contentsMargins(); + + pen.setWidth(1); + pen.setStyle(Qt::SolidLine); + painter.setPen(pen); + painter.drawLine(QPoint(border.left(), lineY), QPoint(width() - border.right() - 1, lineY)); + painter.restore(); +} + +/// +/// \brief 绘制标题栏 +/// \param painter +/// \param title 标题 +/// \param contextCategoryRegion 当前显示的上下文标签的范围,上下文标签是可以遮挡标题栏的,因此需要知道上下文标签的范围 +/// x表示左边界,y表示右边界 +/// +void SARibbonBar::paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion) +{ + // 如果标题不显示直接跳出 + if (!isTitleVisible()) { + return; + } + painter.save(); + if (d_ptr->mTitleTextColor.isValid()) { + painter.setPen(d_ptr->mTitleTextColor); + } + painter.drawText(titleRegion, d_ptr->mTitleAligment, title); + painter.restore(); +} + +#if SA_DEBUG_PRINT_SARIBBONBAR +QDebug operator<<(QDebug debug, const SARibbonBar& ribbon) +{ + QDebugStateSaver saver(debug); + QFontMetrics fm = ribbon.fontMetrics(); + debug.nospace() << "SARibbonBar(" << ribbon.versionString() << ")" // + << "\nribbon font metrics info:" // + << "\n - lineSpacing:" << fm.lineSpacing() // + << "\n - height:" << fm.height() // + << "\n - em:" << fm.boundingRect("M").width() // + << "\n - ex:" << fm.boundingRect("X").height() // + << "\nribbon info:" // + << "\n -mTitleBarHeight=" << ribbon.d_ptr->mTitleBarHeight // + << "\n -mTabBarHeight=" << ribbon.d_ptr->mTabBarHeight // + << "\n -mPannelTitleHeight=" << ribbon.d_ptr->mPannelTitleHeight // + << "\n -mCategoryHeight=" << ribbon.d_ptr->mCategoryHeight // + << "\n -mIsTabOnTitle=" << ribbon.d_ptr->mIsTabOnTitle // + << "\n -mEnableShowPannelTitle=" << ribbon.d_ptr->mEnableShowPannelTitle // + << "\n -mWindowButtonSize=" << ribbon.d_ptr->mWindowButtonSize // + << "\n -mIconRightBorderPosition=" << ribbon.d_ptr->mIconRightBorderPosition // + ; + + return debug; +} +#endif + +/*** End of inlined file: SARibbonBar.cpp ***/ + +/*** Start of inlined file: SARibbonElementFactory.cpp ***/ +#include +#include + +SARibbonElementFactory::SARibbonElementFactory() +{ +} + +SARibbonElementFactory::~SARibbonElementFactory() +{ +} + +SARibbonBar* SARibbonElementFactory::createRibbonBar(QWidget* parent) +{ + return (new SARibbonBar(parent)); +} + +SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent) +{ + return (new SARibbonTabBar(parent)); +} + +SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent) +{ + return (new SARibbonApplicationButton(parent)); +} + +SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent) +{ + return (new SARibbonCategory(parent)); +} + +SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent) +{ + return (new SARibbonContextCategory(parent)); +} + +SARibbonPannel* SARibbonElementFactory::createRibbonPannel(QWidget* parent) +{ + return (new SARibbonPannel(parent)); +} + +SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent) +{ + return (new SARibbonSeparatorWidget(parent)); +} + +SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent) +{ + return (new SARibbonGallery(parent)); +} + +SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent) +{ + return (new SARibbonGalleryGroup(parent)); +} + +SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent) +{ + return (new SARibbonToolButton(parent)); +} + +SARibbonControlButton* SARibbonElementFactory::createRibbonControlButton(QWidget* parent) +{ + return (new SARibbonControlButton(parent)); +} + +SARibbonControlToolButton* SARibbonElementFactory::createRibbonControlToolButton(QWidget* parent) +{ + return (new SARibbonControlToolButton(parent)); +} + +SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent) +{ + return (new SARibbonStackedWidget(parent)); +} + +SARibbonButtonGroupWidget* SARibbonElementFactory::craeteButtonGroupWidget(QWidget* parent) +{ + return (new SARibbonButtonGroupWidget(parent)); +} + +SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent) +{ + return (new SARibbonQuickAccessBar(parent)); +} + +SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent) +{ + return (new SARibbonSystemButtonBar(parent)); +} + +/** + * @brief 创建SARibbonPannelOptionButton + * @param pannel 附属的pannel + * @return + * @sa SARibbonPannelOptionButton + */ +SARibbonPannelOptionButton* SARibbonElementFactory::createRibbonPannelOptionButton(SARibbonPannel* pannel) +{ + return (new SARibbonPannelOptionButton(pannel)); +} + +/*** End of inlined file: SARibbonElementFactory.cpp ***/ + +/*** Start of inlined file: SARibbonElementManager.cpp ***/ +SARibbonElementManager::SARibbonElementManager() +{ + mFactory.reset(new SARibbonElementFactory()); +} + +SARibbonElementManager::~SARibbonElementManager() +{ +} + +SARibbonElementManager* SARibbonElementManager::instance() +{ + static SARibbonElementManager s_instance; + + return (&(s_instance)); +} + +SARibbonElementFactory* SARibbonElementManager::factory() +{ + return (mFactory.data()); +} + +void SARibbonElementManager::setupFactory(SARibbonElementFactory* fac) +{ + mFactory.reset(fac); +} + +/*** End of inlined file: SARibbonElementManager.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeData.cpp ***/ +#include +#include +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeData +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +SARibbonCustomizeData::SARibbonCustomizeData() + : indexValue(-1) + , actionRowProportionValue(SARibbonPannelItem::Large) + , mType(UnknowActionType) + , mActionsManagerPointer(nullptr) +{ +} + +SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr) + : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), mType(type), mActionsManagerPointer(mgr) +{ +} + +/** + * @brief 获取CustomizeData的action type + * @return + */ +SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const +{ + return (mType); +} + +/** + * @brief 设置CustomizeData的action type + * @param a + */ +void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a) +{ + mType = a; +} + +/** + * @brief 判断是否是一个正常的CustomizeData + * + * 实际逻辑actionType() != UnknowActionType + * @return 有用的CustomizeData返回true + */ +bool SARibbonCustomizeData::isValid() const +{ + return (actionType() != UnknowActionType); +} + +/** + * @brief 应用SARibbonCustomizeData到SARibbonBar + * @param m + * @return 如果应用失败,返回false,如果actionType==UnknowActionType直接返回false + */ +bool SARibbonCustomizeData::apply(SARibbonBar* bar) const +{ + if (nullptr == bar) { + return (false); + } + switch (actionType()) { + case UnknowActionType: + return (false); + + case AddCategoryActionType: { + // 添加标签 + SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue); + if (nullptr == c) { + return (false); + } + c->setObjectName(categoryObjNameValue); + SARibbonCustomizeData::setCanCustomize(c); + return (true); + } + + case AddPannelActionType: { + // 添加pannel + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* p = c->insertPannel(keyValue, indexValue); + p->setObjectName(pannelObjNameValue); + SARibbonCustomizeData::setCanCustomize(p); + return (true); + } + + case AddActionActionType: { + if (nullptr == mActionsManagerPointer) { + return (false); + } + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + SARibbonCustomizeData::setCanCustomize(act); + pannel->addAction(act, actionRowProportionValue); + return (true); + } + + case RemoveCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + bar->removeCategory(c); + return (true); + } + + case RemovePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + c->removePannel(pannel); + return (true); + } + + case RemoveActionActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + pannel->removeAction(act); + return (true); + } + + case ChangeCategoryOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + int currentindex = bar->categoryIndex(c); + if (-1 == currentindex) { + return (false); + } + int toindex = currentindex + indexValue; + bar->moveCategory(currentindex, toindex); + return (true); + } + + case ChangePannelOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + int pannelIndex = c->pannelIndex(pannel); + if (-1 == pannelIndex) { + return (false); + } + c->movePannel(pannelIndex, pannelIndex + indexValue); + return (true); + } + + case ChangeActionOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + int actindex = pannel->actionIndex(act); + if (actindex <= -1) { + return (false); + } + pannel->moveAction(actindex, actindex + indexValue); + return (true); + } + + case RenameCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + c->setCategoryName(keyValue); + return (true); + } + + case RenamePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + pannel->setPannelName(keyValue); + return (true); + } + + case VisibleCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + if (1 == indexValue) { + bar->showCategory(c); + } else { + bar->hideCategory(c); + } + return (true); + } + + default: + break; + } + return (false); +} + +/** + * @brief 获取actionmanager指针 + * @return + */ +SARibbonActionsManager* SARibbonCustomizeData::actionManager() +{ + return (mActionsManagerPointer); +} + +/** + * @brief 设置ActionsManager + * @param mgr + */ +void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr) +{ + mActionsManagerPointer = mgr; +} + +/** + * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData + * @param title category 的标题 + * @param index category要插入的位置 + * @param objName category的object name + * @return 返回AddCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName) +{ + SARibbonCustomizeData d(AddCategoryActionType); + + d.indexValue = index; + d.keyValue = title; + d.categoryObjNameValue = objName; + return (d); +} + +/** + * @brief 创建一个AddPannelActionType的SARibbonCustomizeData + * @param title pannel的标题 + * @param index pannel的index + * @param categoryobjName pannel的category的objectname + * @param objName pannel的objname + * @return 返回AddPannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddPannelCustomizeData(const QString& title, + int index, + const QString& categoryobjName, + const QString& objName) +{ + SARibbonCustomizeData d(AddPannelActionType); + + d.indexValue = index; + d.keyValue = title; + d.pannelObjNameValue = objName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 添加action + * @param key action的索引 + * @param mgr action管理器 + * @param rp 定义action的占位情况 + * @param categoryObjName action添加到的category的objname + * @param pannelObjName action添加到的category下的pannel的objname + * @param index action添加到的pannel的索引 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(AddActionActionType, mgr); + + d.keyValue = key; + d.categoryObjNameValue = categoryObjName; + d.pannelObjNameValue = pannelObjName; + d.actionRowProportionValue = rp; + + return (d); +} + +/** + * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData + * @param newname 新名字 + * @param index category的索引 + * @return 返回RenameCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname, + const QString& categoryobjName) +{ + SARibbonCustomizeData d(RenameCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 创建一个RenamePannelActionType的SARibbonCustomizeData + * @param newname pannel的名字 + * @param indexValue pannel的索引 + * @param categoryobjName pannel对应的category的object name + * @return 返回RenamePannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenamePannelCustomizeData(const QString& newname, + const QString& categoryobjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(RenamePannelActionType); + + if (pannelObjName.isEmpty() || categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.pannelObjNameValue = pannelObjName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应ChangeCategoryOrderActionType + * @param categoryobjName 需要移动的categoryobjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, + int moveindex) +{ + SARibbonCustomizeData d(ChangeCategoryOrderActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change category order," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangePannelOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangePannelOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + int moveindex) +{ + SARibbonCustomizeData d(ChangePannelOrderActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change pannel order," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangeActionOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex) +{ + SARibbonCustomizeData d(ChangeActionOrderActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change action order," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应RemoveCategoryActionType + * @param categoryobjName 需要移除的objname + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName) +{ + SARibbonCustomizeData d(RemoveCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应RemovePannelActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemovePannelCustomizeData(const QString& categoryobjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(RemovePannelActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + return (d); +} + +/** + * @brief 对应RemoveActionActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr) +{ + SARibbonCustomizeData d(RemoveActionActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove action," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + return (d); +} + +/** + * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData + * @param categoryobjName + * @param isShow + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow) +{ + SARibbonCustomizeData d(VisibleCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize visible category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = isShow ? 1 : 0; + return (d); +} + +/** + * @brief 判断外置属性,是否允许自定义 + * @param obj + * @return + */ +bool SARibbonCustomizeData::isCanCustomize(QObject* obj) +{ + QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 设置外置属性允许自定义 + * @param obj + * @param canbe + */ +void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe) +{ + obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe); +} + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex); + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex) +{ + QList< SARibbonCustomizeData > res; + + for (int i = 0; i < csd.size(); ++i) { + if (!willremoveIndex.contains(i)) { + res << csd[ i ]; + } + } + return (res); +} + +/** + * @brief 对QList进行简化操作 + * + * 此函数会执行如下操作: + * 1、针对同一个category/pannel连续出现的添加和删除操作进行移除(前一步添加,后一步删除) + * + * 2、针对VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + * + * 3、针对RenameCategoryActionType和RenamePannelActionType操作,只保留最后一个 + * + * 4、针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并为一个动作, + * 如果合并后原地不动,则删除 + * + * @param csd + * @return 返回简化的QList + */ +QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd) +{ + int size = csd.size(); + + if (size <= 1) { + return (csd); + } + QList< SARibbonCustomizeData > res; + QList< int > willremoveIndex; // 记录要删除的index + + //! 首先针对连续出现的添加和删除操作进行优化 + for (int i = 1; i < size; ++i) { + if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) { + if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddPannelActionType) && (csd[ i ].actionType() == RemovePannelActionType)) { + if ((csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) { + if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) + && (csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } + } + res = remove_indexs(csd, willremoveIndex); + willremoveIndex.clear(); + + //! 筛选VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == VisibleCategoryActionType) + && (res[ i ].actionType() == VisibleCategoryActionType)) { + if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) { + // 要保证操作的是同一个内容 + willremoveIndex << i - 1; // 删除前一个只保留最后一个 + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对RenameCategoryActionType和RenamePannelActionType操作,只需保留最后一个 + size = res.size(); + for (int i = 0; i < size; ++i) { + if (res[ i ].actionType() == RenameCategoryActionType) { + // 向后查询,如果查询到有同一个Category改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenameCategoryActionType) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } else if (res[ i ].actionType() == RenamePannelActionType) { + // 向后查询,如果查询到有同一个pannel改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenamePannelActionType) + && (res[ i ].pannelObjNameValue == res[ j ].pannelObjNameValue) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType) + && (res[ i ].actionType() == ChangeCategoryOrderActionType) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangePannelOrderActionType) + && (res[ i ].actionType() == ChangePannelOrderActionType) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType) + && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 上一步操作可能会产生indexvalue为0的情况,此操作把indexvalue为0的删除 + size = res.size(); + for (int i = 0; i < size; ++i) { + if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePannelOrderActionType) + || (res[ i ].actionType() == ChangeActionOrderActionType)) { + if (0 == res[ i ].indexValue) { + willremoveIndex << i; + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + return (res); +} + +/*** End of inlined file: SARibbonCustomizeData.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeWidget.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeWidget +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds) +{ + if (cds.size() <= 0) { + return (false); + } + + xml->writeStartElement("sa-ribbon-customize"); + for (const SARibbonCustomizeData& d : cds) { + xml->writeStartElement("customize-data"); + xml->writeAttribute("type", QString::number(d.actionType())); + xml->writeAttribute("index", QString::number(d.indexValue)); + xml->writeAttribute("key", d.keyValue); + xml->writeAttribute("category", d.categoryObjNameValue); + xml->writeAttribute("pannel", d.pannelObjNameValue); + xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue)); + + xml->writeEndElement(); + } + xml->writeEndElement(); + if (xml->hasError()) { + qWarning() << "write has error"; + } + return (true); +} + +QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr) +{ + // 先找到"sa-ribbon-customize" + while (!xml->atEnd()) { + + if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) { + break; + } + xml->readNext(); + } + QList< SARibbonCustomizeData > res; + + // 开始遍历"customize-data" + while (!xml->atEnd()) { + if (xml->isStartElement() && (xml->name().toString() == "customize-data")) { + // 首先读取属性type + SARibbonCustomizeData d; + QXmlStreamAttributes attrs = xml->attributes(); + if (!attrs.hasAttribute("type")) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + bool isOk = false; + int v = xml->attributes().value("type").toInt(&isOk); + if (!isOk) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v)); + // 开始读取子对象 + if (attrs.hasAttribute("index")) { + v = xml->attributes().value("index").toInt(&isOk); + if (isOk) { + d.indexValue = v; + } + } + if (attrs.hasAttribute("key")) { + d.keyValue = attrs.value("key").toString(); + } + if (attrs.hasAttribute("category")) { + d.categoryObjNameValue = attrs.value("category").toString(); + } + if (attrs.hasAttribute("pannel")) { + d.pannelObjNameValue = attrs.value("pannel").toString(); + } + if (attrs.hasAttribute("row-prop")) { + v = xml->attributes().value("row-prop").toInt(&isOk); + if (isOk) { + d.actionRowProportionValue = static_cast< SARibbonPannelItem::RowProportion >(v); + } + } + d.setActionsManager(mgr); + res.append(d); + } + xml->readNext(); + } + if (xml->hasError()) { + qWarning() << xml->errorString(); + } + return (res); +} + +int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + + for (const SARibbonCustomizeData& d : cds) { + if (d.apply(bar)) { + ++c; + } + } + return (c); +} + +int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + // todo 支持反向操作 + return (c); +} + +bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + QFile f(filePath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return (false); + } + f.seek(0); + QXmlStreamReader xml(&f); + + return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr)); +} + +/** + * @brief 构建SARibbonCustomizeWidget的Ui + */ +class SARibbonCustomizeWidgetUi +{ +public: + QHBoxLayout* horizontalLayoutMain; + QVBoxLayout* verticalLayoutSelect; + QLabel* labelSelectAction; + QHBoxLayout* horizontalLayoutSearch; + QComboBox* comboBoxActionIndex; + QLineEdit* lineEditSearchAction; + QListView* listViewSelect; + QVBoxLayout* verticalLayoutMidButtons; + QSpacerItem* verticalSpacerUp; + QPushButton* pushButtonAdd; + QPushButton* pushButtonDelete; + QPushButton* pushButtonReset; + QSpacerItem* verticalSpacerDown; + QLabel* labelProportion; + QComboBox* comboBoxActionProportion; + QVBoxLayout* verticalLayoutResult; + QLabel* labelCustomize; + QHBoxLayout* horizontalLayoutCategorySelect; + QRadioButton* radioButtonMainCategory; + QRadioButton* radioButtonAllCategory; + QButtonGroup* radioButtonGroup; + QTreeView* treeViewResult; + QHBoxLayout* horizontalLayoutActionOptBtns; + QPushButton* pushButtonNewCategory; + QPushButton* pushButtonNewPannel; + QPushButton* pushButtonRename; + QVBoxLayout* verticalLayoutRightButtons; + QSpacerItem* verticalSpacerUp2; + QToolButton* toolButtonUp; + QToolButton* toolButtonDown; + QSpacerItem* verticalSpacerDown2; + + void setupUi(QWidget* customizeWidget) + { + if (customizeWidget->objectName().isEmpty()) { + customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget")); + } + customizeWidget->resize(800, 600); + horizontalLayoutMain = new QHBoxLayout(customizeWidget); + horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain")); + verticalLayoutSelect = new QVBoxLayout(); + verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect")); + labelSelectAction = new QLabel(customizeWidget); + labelSelectAction->setObjectName(QStringLiteral("labelSelectAction")); + + verticalLayoutSelect->addWidget(labelSelectAction); + + horizontalLayoutSearch = new QHBoxLayout(); + horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch")); + comboBoxActionIndex = new QComboBox(customizeWidget); + comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex")); + comboBoxActionIndex->setEditable(false); + + horizontalLayoutSearch->addWidget(comboBoxActionIndex); + + lineEditSearchAction = new QLineEdit(customizeWidget); + lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction")); + + horizontalLayoutSearch->addWidget(lineEditSearchAction); + + verticalLayoutSelect->addLayout(horizontalLayoutSearch); + + listViewSelect = new QListView(customizeWidget); + listViewSelect->setObjectName(QStringLiteral("listViewSelect")); + + verticalLayoutSelect->addWidget(listViewSelect); + + horizontalLayoutMain->addLayout(verticalLayoutSelect); + + verticalLayoutMidButtons = new QVBoxLayout(); + verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons")); + verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerUp); + + pushButtonAdd = new QPushButton(customizeWidget); + pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd")); + pushButtonAdd->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonAdd); + + pushButtonDelete = new QPushButton(customizeWidget); + pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete")); + pushButtonDelete->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonDelete); + + verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerDown); + + labelProportion = new QLabel(customizeWidget); + labelProportion->setObjectName(QStringLiteral("labelProportion")); + verticalLayoutMidButtons->addWidget(labelProportion); + + comboBoxActionProportion = new QComboBox(customizeWidget); + comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion")); + comboBoxActionProportion->setEditable(false); + verticalLayoutMidButtons->addWidget(comboBoxActionProportion); + horizontalLayoutMain->addLayout(verticalLayoutMidButtons); + + verticalLayoutResult = new QVBoxLayout(); + verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult")); + labelCustomize = new QLabel(customizeWidget); + labelCustomize->setObjectName(QStringLiteral("labelCustomize")); + + verticalLayoutResult->addWidget(labelCustomize); + + horizontalLayoutCategorySelect = new QHBoxLayout(); + horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect")); + radioButtonMainCategory = new QRadioButton(customizeWidget); + radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory")); + radioButtonMainCategory->setChecked(false); + + horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory); + + radioButtonAllCategory = new QRadioButton(customizeWidget); + radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory")); + radioButtonAllCategory->setChecked(true); + + horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory); + + radioButtonGroup = new QButtonGroup(customizeWidget); + radioButtonGroup->addButton(radioButtonMainCategory); + radioButtonGroup->addButton(radioButtonAllCategory); + + verticalLayoutResult->addLayout(horizontalLayoutCategorySelect); + + treeViewResult = new QTreeView(customizeWidget); + treeViewResult->setObjectName(QStringLiteral("treeViewResult")); + treeViewResult->setHeaderHidden(true); + treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection); + treeViewResult->setAnimated(true); // 支持动画 + treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不允许直接在item上重命名 + + verticalLayoutResult->addWidget(treeViewResult); + + horizontalLayoutActionOptBtns = new QHBoxLayout(); + horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns")); + pushButtonNewCategory = new QPushButton(customizeWidget); + pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory); + + pushButtonNewPannel = new QPushButton(customizeWidget); + pushButtonNewPannel->setObjectName(QStringLiteral("pushButtonNewPannel")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewPannel); + + pushButtonRename = new QPushButton(customizeWidget); + pushButtonRename->setObjectName(QStringLiteral("pushButtonRename")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonRename); + + pushButtonReset = new QPushButton(customizeWidget); + pushButtonReset->setObjectName(QStringLiteral("pushButtonReset")); + horizontalLayoutActionOptBtns->addWidget(pushButtonReset); + + verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns); + + horizontalLayoutMain->addLayout(verticalLayoutResult); + + verticalLayoutRightButtons = new QVBoxLayout(); + verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons")); + verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerUp2); + + toolButtonUp = new QToolButton(customizeWidget); + toolButtonUp->setObjectName(QStringLiteral("pushButtonUp")); + toolButtonUp->setArrowType(Qt::UpArrow); + toolButtonUp->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonUp); + + toolButtonDown = new QToolButton(customizeWidget); + toolButtonDown->setObjectName(QStringLiteral("pushButtonDown")); + toolButtonDown->setArrowType(Qt::DownArrow); + toolButtonDown->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonDown); + + verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerDown2); + + horizontalLayoutMain->addLayout(verticalLayoutRightButtons); + + retranslateUi(customizeWidget); + } // setupUi + + void retranslateUi(QWidget* customizeWidget) + { + customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR)); + labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR)); // cn:请选择 + lineEditSearchAction->setInputMask(QString()); + lineEditSearchAction->setText(QString()); + lineEditSearchAction->setPlaceholderText( + QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR)); // cn:查找命令 + pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR)); // cn:添加 >> + pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR)); // cn:<< 移除 + labelCustomize->setText( + QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR)); // cn:自定义功能区 + radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR)); // cn:主选项卡 + radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR)); // cn:所有选项卡 + pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR)); // cn:新建选项卡 + pushButtonNewPannel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR)); // cn:新建组 + pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR)); // cn:重命名 + pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR)); // cn:重置 + labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR)); // cn:比例 + } // retranslateUi +}; + +/** + * @brief 管理SARibbonCustomizeWidget的业务逻辑 + */ +class SARibbonCustomizeWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget) +public: + SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory }; ///< 显示类型 + SARibbonBar* mRibbonBar { nullptr }; ///< 保存SARibbonMainWindow的指针 + SARibbonActionsManager* mActionMgr { nullptr }; ///< action管理器 + SARibbonActionsManagerModel* mAcionModel { nullptr }; ///< action管理器对应的model + QStandardItemModel* mRibbonModel { nullptr }; ///< 用于很成ribbon的树 + int mCustomizeCategoryCount { 0 }; ///< 记录自定义Category的个数 + int mCustomizePannelCount { 0 }; ///< 记录自定义Pannel的个数 +public: + PrivateData(SARibbonCustomizeWidget* p); + void updateModel(); + + QList< SARibbonCustomizeData > mCustomizeDatasCache; ///< 缓存记录所有的自定义动作 + QList< SARibbonCustomizeData > mCustomizeDatasApplied; ///< 应用后的所有的自定义动作 + QList< SARibbonCustomizeData > mOldCustomizeDatas; ///< 记录旧的自定义动作,本地文件缓存 + // 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch_suf + static QString makeRandomObjName(const QString& pre); + + int itemLevel(QStandardItem* item) const; + + // + bool isCustomizeItem(QStandardItem* item) const; + + // 把item转换为category + SARibbonCategory* itemToCategory(QStandardItem* item) const; + + // 把item转换为SARibbonPannel + SARibbonPannel* itemToPannel(QStandardItem* item) const; + + // 获取item对应的object name + QString itemObjectName(QStandardItem* item) const; + + // 判断是否可以自定义 + bool isItemCanCustomize(QStandardItem* item) const; + + // 从item转为action + QAction* itemToAction(QStandardItem* item) const; +}; + +SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p) + : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p)) +{ +} + +void SARibbonCustomizeWidget::PrivateData::updateModel() +{ + if (mRibbonBar == nullptr) { + return; + } + mRibbonModel->clear(); + SARibbonBar* ribbonbar = mRibbonBar; + QList< SARibbonCategory* > categorys = ribbonbar->categoryPages(); + + for (const SARibbonCategory* c : qAsConst(categorys)) { + if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) { + // 如果是只显示主内容,如果是上下文标签就忽略 + continue; + } + QStandardItem* ci = new QStandardItem(); + if (c->isContextCategory()) { + ci->setText(QString("[%1]").arg(c->categoryName())); + } else { + ci->setText(c->categoryName()); + } + if (c->isCanCustomize() && !c->isContextCategory()) { + // 上下文标签不做显示隐藏处理 + ci->setCheckable(true); + ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked); + ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->setData(0, SARibbonCustomizeWidget::LevelRole); + ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole); + QList< SARibbonPannel* > pannels = c->pannelList(); + for (const SARibbonPannel* p : qAsConst(pannels)) { + QStandardItem* pi = new QStandardItem(p->pannelName()); + pi->setData(1, SARibbonCustomizeWidget::LevelRole); + pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole); + if (p->isCanCustomize()) { + pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->appendRow(pi); + const QList< SARibbonPannelItem* >& items = p->ribbonPannelItem(); + for (SARibbonPannelItem* i : qAsConst(items)) { + if (i->action->isSeparator()) { + continue; + } + QStandardItem* ii = new QStandardItem(); + // if (i->customWidget) { + // //如果是自定义窗口 + // if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) { + // delete ii; + // continue; //如果窗口啥也没有,就跳过 + // } + // ii->setText(i->widget()->windowTitle()); + // ii->setIcon(i->widget()->windowIcon()); + // if (SARibbonCustomizeData::isCanCustomize(i->widget())) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } else { + // //不是自定义,说明是action + // ii->setText(i->action->text()); + // ii->setIcon(i->action->icon()); + // if (SARibbonCustomizeData::isCanCustomize(i->action)) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } + ii->setText(i->action->text()); + ii->setIcon(i->action->icon()); + if (SARibbonCustomizeData::isCanCustomize(i->action)) { + ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ii->setData(2, SARibbonCustomizeWidget::LevelRole); + ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole); + pi->appendRow(ii); + } + } + mRibbonModel->appendRow(ci); + } +} + +/** + * @brief 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch + * @param pre 前缀 + * @return + */ +QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre) +{ + return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch())); +} + +/** + * @brief 获取item的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const +{ + return (item->data(SARibbonCustomizeWidget::LevelRole).toInt()); +} + +/** + * @brief 判断itemn为自定义的item,自定义的item都带有CustomizeRole角色 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid()); +} + +/** + * @brief 把item转换为category + * @param item + * @return无法转换返回nullptr + */ +SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 0) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonCategory* >(p)); +} + +/** + * @brief 把item转换为pannel + * @param item + * @return 无法转换返回nullptr + */ +SARibbonPannel* SARibbonCustomizeWidget::PrivateData::itemToPannel(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 1) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonPannel* >(p)); +} + +/** + * @brief 获取item对应的objectname + * @param item + * @return 如果无法获取,返回一个空的QString + */ +QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const +{ + QString objName; + + if (isCustomizeItem(item)) { + // 说明是自定义的 + objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString(); + } else { + // 说明这个是非自定义的 + int level = itemLevel(item); + if (0 == level) { + SARibbonCategory* category = itemToCategory(item); + if (category) { + objName = category->objectName(); + } + } else if (1 == level) { + SARibbonPannel* pannel = itemToPannel(item); + if (pannel) { + objName = pannel->objectName(); + } + } + } + return (objName); +} + +/** + * @brief 判断item是否可自定义 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 从item 转为action + * @param item + * @return + */ +QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const +{ + if (2 != itemLevel(item)) { + return (nullptr); + } + // 这里要非常注意,SARibbonCustomizeWidget::CustomizeRole为true时,说明这个是自定义的内容, + // 这时PointerRole里存放的是action指针,不是SARibbonPannelItem + QAction* act = nullptr; + + if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) { + act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + } else { + SARibbonPannelItem* pi = + reinterpret_cast< SARibbonPannelItem* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + act = (pi->action); + } + return (act); +} + +//=================================================== +// SARibbonCustomizeWidget +//=================================================== +/** + * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget + * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针 + * @param parent 用于界面生成的parent,可以和SARibbonMainWindow一样 + * @param f 同QWidget::QWidget的第二个参数 + */ +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonWindow->ribbonBar()); +} + +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonbar); +} + +void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar) +{ + d_ptr->mRibbonBar = ribbonbar; + ui->setupUi(this); + ui->listViewSelect->setModel(d_ptr->mAcionModel); + ui->treeViewResult->setModel(d_ptr->mRibbonModel); + initConnection(); + updateModel(); +} + +SARibbonCustomizeWidget::~SARibbonCustomizeWidget() +{ + delete ui; +} + +void SARibbonCustomizeWidget::initConnection() +{ + // 这个需要qt5.8以上支持 + // connect(ui->comboBoxActionIndex, QOverload::of(&QComboBox::currentIndexChanged) + // , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + connect(ui->comboBoxActionIndex, + static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged), + this, + &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + // 这个需要qt5.8以上支持 + // connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked) + // , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->radioButtonGroup, + static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked), + this, + &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked); + connect(ui->pushButtonNewPannel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPannelClicked); + connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked); + connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked); + connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked); + connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked); + connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked); + connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked); + connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked); + connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged); + connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited); + connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked); +} + +/** + * @brief 设置action管理器 + * @param mgr + */ +void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr) +{ + d_ptr->mActionMgr = mgr; + if (d_ptr->mActionMgr) { + d_ptr->mAcionModel->uninstallActionsManager(); + } + d_ptr->mAcionModel->setupActionsManager(mgr); + // 更新左边复选框 + QList< int > tags = mgr->actionTags(); + + ui->comboBoxActionIndex->clear(); + for (int tag : qAsConst(tags)) { + if (mgr->tagName(tag).isEmpty()) + continue; + ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag); + } +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeWidget::isApplied() const +{ + return (d_ptr->mCustomizeDatasApplied.size() > 0); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeWidget::isCached() const +{ + return (d_ptr->mCustomizeDatasCache.size() > 0); +} + +/** + * @brief 获取model + * @return + */ +const QStandardItemModel* SARibbonCustomizeWidget::model() const +{ + return (d_ptr->mRibbonModel); +} + +/** + * @brief 根据当前的radiobutton选项来更新model + */ +void SARibbonCustomizeWidget::updateModel() +{ + updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory); + if (d_ptr->mRibbonBar) { + SARibbonBar* bar = d_ptr->mRibbonBar; + if (bar) { + ui->comboBoxActionProportion->clear(); + if (bar->isTwoRowStyle()) { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } else { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPannelItem::Medium); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } + } + } +} + +/** + * @brief 更新model + */ +void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type) +{ + d_ptr->mShowType = type; + d_ptr->updateModel(); +} + +/** + * @brief 应用所有设定 + * @return 应用成功返回true + * @note 所有设定有一个应用成功都会返回true + */ +bool SARibbonCustomizeWidget::applys() +{ + simplify(); + if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) { + // 将临时操作存入已应用操作,并清空临时操作 + makeActionsApplied(); + clearCache(); + return true; + } else { + return false; + } +} + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @code + * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口 + * dlg.setupActionsManager(m_actMgr); + * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { + * dlg.applys(); + * QByteArray str; + * QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造,遇到中文会异常 + * xml.setAutoFormatting(true); + * xml.setAutoFormattingIndent(2); + * xml.setCodec("utf-8");//在writeStartDocument之前指定编码 + * xml.writeStartDocument(); + * bool isok = dlg.toXml(&xml); + * xml.writeEndDocument(); + * if (isok) { + * QFile f("customize.xml"); + * if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) { + * QTextStream s(&f); + * s.setCodec("utf-8");//指定编码输出 + * s << str; + * s.flush(); + * } + * m_edit->append("write xml:");//m_edit的定义为:QTextEdit *m_edit; + * m_edit->append(str); + * } + * } + * @endcode + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + * @see sa_customize_datas_to_xml + */ +bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const +{ + QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas; + + if (isApplied()) + res << d_ptr->mCustomizeDatasApplied; + if (isCached()) + res << d_ptr->mCustomizeDatasCache; + + res = SARibbonCustomizeData::simplify(res); + return (sa_customize_datas_to_xml(xml, res)); +} + +/** + * @brief 把配置写入文件中 + * @param xmlpath + * @return + */ +bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { + return (false); + } + QXmlStreamWriter xml(&f); + + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(2); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. + xml.setCodec("utf-8"); // 在writeStartDocument之前指定编码 +#endif + xml.writeStartDocument(); + bool isOK = toXml(&xml); + + xml.writeEndDocument(); + f.close(); + return (isOK); +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xml + * @return + * @note 此函数要在@ref setupActionsManager 函数之后调用 + */ +void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml) +{ + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr); + + d_ptr->mOldCustomizeDatas = cds; +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xmlpath + * @note 此函数要在@ref setupActionsManager 函数之后调用 + * @note 如果程序启动后加载了自定义配置,再调用此窗口时需要调用此函数,把原来的配置加载进来, + * 在生成新动作时会把旧动作保存,但在调用applys时不会调用此加载的动作 + */ +void SARibbonCustomizeWidget::fromXml(const QString& xmlpath) +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + f.seek(0); + QXmlStreamReader xml(&f); + + fromXml(&xml); +} + +/** + * @brief 应用xml配置 + * + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * //只能调用一次 + * static bool has_call = false; + * if (!has_call) { + * QFile f("customize.xml"); + * if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) { + * return; + * } + * f.seek(0); + * QXmlStreamReader xml(&f); + * has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr); + * } + * @endcode + * @param xml + * @param bar SARibbonBar + * @return 所有设定有一个应用成功都会返回true + * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file + */ +bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + // 先找到sa-ribbon-customize标签 + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr); + + return (sa_customize_datas_apply(cds, bar) > 0); +} + +/** + * @brief 清除已应用的动作 + */ +void SARibbonCustomizeWidget::makeActionsApplied() +{ + d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache; +} + +/** + * @brief 清除applied的动作,cancel操作后需要清空已应用的动作 + */ +void SARibbonCustomizeWidget::clearApplied() +{ + d_ptr->mCustomizeDatasApplied.clear(); +} + +/** + * @brief 清除缓存动作 + * + * 在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + */ +void SARibbonCustomizeWidget::clearCache() +{ + d_ptr->mCustomizeDatasCache.clear(); +} + +/** + * @brief 清除所有动作,不包含本地读取的数据 + */ +void SARibbonCustomizeWidget::clear() +{ + clearApplied(); + clearCache(); +} + +/** + * @brief 精简 + */ +void SARibbonCustomizeWidget::simplify() +{ + d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache); +} + +/** + * @brief 获取当前界面选中的行属性 + * @return + */ +SARibbonPannelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const +{ + return (static_cast< SARibbonPannelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt())); +} + +/** + * @brief 获取listview中选中的action + * @return 如果没有选中action,返回nullptr + * @note 如果要获取treeview选中的action,使用@ref itemToAction 函数 + */ +QAction* SARibbonCustomizeWidget::selectedAction() const +{ + QItemSelectionModel* m = ui->listViewSelect->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mAcionModel->indexToAction(i)); +} + +/** + * @brief 把item转换为action + * @param item + * @return 如果没有action可转换,返回nullptr + */ +QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const +{ + return (d_ptr->itemToAction(item)); +} + +/** + * @brief 获取ribbon tree选中的item + * @return + */ +QStandardItem* SARibbonCustomizeWidget::selectedItem() const +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mRibbonModel->itemFromIndex(i)); +} + +/** + * @brief 获取选中的ribbon tree 的level + * @return -1为选中异常,0代表选中了category 1代表选中了pannel 2代表选中了action + */ +int SARibbonCustomizeWidget::selectedRibbonLevel() const +{ + QStandardItem* item = selectedItem(); + + if (item) { + return (itemLevel(item)); + } + return (-1); +} + +/** + * @brief 获取StandardItem 的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const +{ + return (d_ptr->itemLevel(item)); +} + +/** + * @brief 设置某个item被选中 + * @param item + */ +void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible) +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (nullptr == m) { + return; + } + if (m) { + m->clearSelection(); + m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent); + } + if (ensureVisible) { + ui->treeViewResult->scrollTo(item->index()); + } +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const +{ + return (d_ptr->isItemCanCustomize(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const +{ + return (isItemCanCustomize(selectedItem())); +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const +{ + return (d_ptr->isCustomizeItem(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const +{ + return (isCustomizeItem(selectedItem())); +} + +void SARibbonCustomizeWidget::removeItem(QStandardItem* item) +{ + if (item->parent()) { + item->parent()->removeRow(item->row()); + } else { + d_ptr->mRibbonModel->removeRow(item->row()); + } +} + +void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index) +{ + int tag = ui->comboBoxActionIndex->itemData(index).toInt(); + + d_ptr->mAcionModel->setFilter(tag); +} + +void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b) +{ + updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory); +} + +void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked() +{ + int row = d_ptr->mRibbonModel->rowCount(); + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (m && m->hasSelection()) { + QModelIndex i = m->currentIndex(); + while (i.parent().isValid()) { + i = i.parent(); + } + // 获取选中的最顶层item + row = i.row() + 1; + } + QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount))); + + ni->setData(0, SARibbonCustomizeWidget::LevelRole); + d_ptr->mRibbonModel->insertRow(row, ni); + // 设置新增的为选中 + setSelectItem(ni); + // 把动作插入动作列表中 + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData( + ni->text(), ni->row(), SARibbonCustomizeWidget::PrivateData::makeRandomObjName("category")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); +} + +void SARibbonCustomizeWidget::onPushButtonNewPannelClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = selectedRibbonLevel(); + + QStandardItem* ni = new QStandardItem(tr("new pannel[customize]%1").arg(++(d_ptr->mCustomizePannelCount))); + + ni->setData(1, SARibbonCustomizeWidget::LevelRole); + + if (0 == level) { + // 说明是category,插入到最后 + item->appendRow(ni); + } else if (1 == level) { + // 说明选择的是pannel,插入到此pannel之后 + QStandardItem* categoryItem = item->parent(); + if (nullptr == categoryItem) { + return; + } + categoryItem->insertRow(item->row() + 1, ni); + } else { + // 不符合就删除退出 + delete ni; + ni = nullptr; + return; + } + // 查找category的object name + QStandardItem* categoryItem = ni->parent(); + QString categoryObjName = ""; + + categoryObjName = d_ptr->itemObjectName(categoryItem); + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPannelCustomizeData( + ni->text(), ni->row(), categoryObjName, SARibbonCustomizeWidget::PrivateData::makeRandomObjName("pannel")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.pannelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); + setSelectItem(ni); +} + +void SARibbonCustomizeWidget::onPushButtonRenameClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + bool ok; + QString text = ""; + + text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok); + + if (!ok || text.isEmpty()) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 改Category名 + QString cateObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + QString cateObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePannelCustomizeData(text, cateObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else { + // action 不允许改名 + return; + } + item->setText(text); +} + +void SARibbonCustomizeWidget::onPushButtonAddClicked() +{ + QAction* act = selectedAction(); + QStandardItem* item = selectedItem(); + + if ((nullptr == act) || (nullptr == item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 选中category不进行操作 + return; + } else if (2 == level) { + // 选中action,添加到这个action之后,把item设置为pannel + item = item->parent(); + } + QString pannelObjName = d_ptr->itemObjectName(item); + QString categoryObjName = d_ptr->itemObjectName(item->parent()); + QString key = d_ptr->mActionMgr->key(act); + + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData( + key, d_ptr->mActionMgr, selectedRowProportion(), categoryObjName, pannelObjName); + + d_ptr->mCustomizeDatasCache.append(d); + + QStandardItem* actItem = new QStandardItem(act->icon(), act->text()); + + actItem->setData(2, SARibbonCustomizeWidget::LevelRole); + actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole); + actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole); + actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole); // 把action指针传入 + item->appendRow(actItem); +} + +void SARibbonCustomizeWidget::onPushButtonDeleteClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + if (!isItemCanCustomize(item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 删除category + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item)); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + // 删除pannel + QString catObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePannelCustomizeData(catObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (2 == level) { + // 删除Action + QString catObjName = d_ptr->itemObjectName(item->parent()->parent()); + QString pannelObjName = d_ptr->itemObjectName(item->parent()); + QAction* act = itemToAction(item); + QString key = d_ptr->mActionMgr->key(act); + if (key.isEmpty() || catObjName.isEmpty() || pannelObjName.isEmpty()) { + return; + } + + SARibbonCustomizeData d = + SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, pannelObjName, key, d_ptr->mActionMgr); + d_ptr->mCustomizeDatasCache.append(d); + } + // 执行删除操作 + removeItem(item); + // 删除后重新识别 + ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize()); +} + +void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index) +{ + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + // 点击了listview,判断treeview的状态 + Q_UNUSED(index); + ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize()); +} + +void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index) +{ + Q_UNUSED(index); + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = itemLevel(item); + + ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item)); + ui->pushButtonDelete->setEnabled(isItemCanCustomize(item)); // 有CustomizeRole,必有CanCustomizeRole + ui->pushButtonRename->setEnabled( + level != 2 || isItemCanCustomize(item)); // QAction 不能改名 , 有CustomizeRole,必有CanCustomizeRole +} + +void SARibbonCustomizeWidget::onToolButtonUpClicked() +{ + QStandardItem* item = selectedItem(); + + if ((nullptr == item) || (0 == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = + SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(r); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r - 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData( + d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r - 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData( + d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(pannelItem), key, d_ptr->mActionMgr, -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r - 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onToolButtonDownClicked() +{ + QStandardItem* item = selectedItem(); + + if (item == nullptr) { + return; + } + int count = 0; + + if (item->parent()) { + count = item->parent()->rowCount(); + } else { + count = d_ptr->mRibbonModel->rowCount(); + } + if ((nullptr == item) || ((count - 1) == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = + SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(item->row()); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r + 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData( + d_ptr->itemObjectName(paritem), d_ptr->itemObjectName(item), 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r + 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData( + d_ptr->itemObjectName(categoryItem), d_ptr->itemObjectName(pannelItem), key, d_ptr->mActionMgr, -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r + 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item) +{ + if (item == nullptr) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + if (item->isCheckable()) { + QString objname = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = + SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, item->checkState() == Qt::Checked); + d_ptr->mCustomizeDatasCache.append(d); + } + } +} + +void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text) +{ + d_ptr->mAcionModel->search(text); +} + +void SARibbonCustomizeWidget::onPushButtonResetClicked() +{ + int btn = QMessageBox::warning(this, + tr("Warning"), + tr("Are you sure reset all customize setting?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (btn == QMessageBox::Yes) { + clear(); + } +} + +/*** End of inlined file: SARibbonCustomizeWidget.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeDialog.cpp ***/ +#include +#include +#include +#include + +/** + * @brief The SARibbonCustomizeDialogUi class + */ +class SARibbonCustomizeDialogUi +{ +public: + SARibbonCustomizeWidget* customWidget; + QVBoxLayout* verticalLayoutMain; + QHBoxLayout* horizontalLayoutButtonGroup; + QPushButton* pushButtonCancel; + QPushButton* pushButtonOk; + QSpacerItem* spacerItemleft; + void setupUi(SARibbonMainWindow* ribbonWindow, QWidget* customizeDialog) + { + if (customizeDialog->objectName().isEmpty()) { + customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog")); + } + customizeDialog->resize(800, 600); + verticalLayoutMain = new QVBoxLayout(customizeDialog); + verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain")); + + customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog); + customWidget->setObjectName(QStringLiteral("customWidget")); + verticalLayoutMain->addWidget(customWidget); + + horizontalLayoutButtonGroup = new QHBoxLayout(); + horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup")); + + spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayoutButtonGroup->addItem(spacerItemleft); + + pushButtonCancel = new QPushButton(customizeDialog); + pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonCancel); + + pushButtonOk = new QPushButton(customizeDialog); + pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonOk); + verticalLayoutMain->addItem(horizontalLayoutButtonGroup); + retranslateUi(customizeDialog); + } + + void retranslateUi(QWidget* customizeDialog) + { + customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR)); + pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR)); + pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR)); + } +}; + +//////////////////////////////////////////////////////////////////// + +SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, QWidget* p, Qt::WindowFlags f) + : QDialog(p, f), ui(new SARibbonCustomizeDialogUi) +{ + ui->setupUi(ribbonWindow, this); + initConnection(); +} + +/** + * @brief 设置action管理器 + * + * 等同@ref SARibbonCustomizeWidget::setupActionsManager + * @param mgr + */ +SARibbonCustomizeDialog::~SARibbonCustomizeDialog() +{ +} +void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager* mgr) +{ + ui->customWidget->setupActionsManager(mgr); +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeDialog::isApplied() const +{ + return ui->customWidget->isApplied(); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeDialog::isCached() const +{ + return ui->customWidget->isCached(); +} + +void SARibbonCustomizeDialog::initConnection() +{ + connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject); +} + +/** + * @brief 等同SARibbonCustomizeWidget::applys + * + * @ref SARibbonCustomizeWidget::applys + * @return + */ +bool SARibbonCustomizeDialog::applys() +{ + return ui->customWidget->applys(); +} + +/** + * @brief 清除所有动作 + * + * @ref SARibbonCustomizeWidget::clear + */ +void SARibbonCustomizeDialog::clear() +{ + ui->customWidget->clear(); +} + +/** + * @brief 转换为xml + * + * @ref SARibbonCustomizeWidget::toXml + * @param xml + * @return + */ +bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter* xml) const +{ + return (ui->customWidget->toXml(xml)); +} + +/** + * @brief 等同SARibbonCustomizeWidget::toXml + * @ref SARibbonCustomizeWidget::toXml + * @param xmlpath + * @return + */ +bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const +{ + return (ui->customWidget->toXml(xmlpath)); +} + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xml + */ +void SARibbonCustomizeDialog::fromXml(QXmlStreamReader* xml) +{ + ui->customWidget->fromXml(xml); +} + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xmlpath + */ +void SARibbonCustomizeDialog::fromXml(const QString& xmlpath) +{ + ui->customWidget->fromXml(xmlpath); +} + +/** + * @brief 返回SARibbonCustomizeWidget窗口指针 + * + * 通过SARibbonCustomizeWidget窗口可以操作更多的内容 + * + * @return SARibbonCustomizeWidget指针,参考@ref SARibbonCustomizeWidget + */ +SARibbonCustomizeWidget* SARibbonCustomizeDialog::customizeWidget() const +{ + return (ui->customWidget); +} + +/*** End of inlined file: SARibbonCustomizeDialog.cpp ***/ + +/*** Start of inlined file: SARibbonMainWindow.cpp ***/ +#include +#include +#include +#include +#include +#include + +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#include + +#else + +#endif + +/** + * @brief The SARibbonMainWindowPrivate class + */ +class SARibbonMainWindow::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonMainWindow) +public: + PrivateData(SARibbonMainWindow* p); + void installFrameless(SARibbonMainWindow* p); + +public: + SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; + SARibbonSystemButtonBar* mWindowButtonGroup { nullptr }; +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + QWK::WidgetWindowAgent* mFramelessHelper { nullptr }; +#else + SAFramelessHelper* mFramelessHelper { nullptr }; +#endif + SARibbonMainWindowEventFilter* mEventFilter { nullptr }; +}; + +SARibbonMainWindow::PrivateData::PrivateData(SARibbonMainWindow* p) : q_ptr(p) +{ +} + +void SARibbonMainWindow::PrivateData::installFrameless(SARibbonMainWindow* p) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + mFramelessHelper = new QWK::WidgetWindowAgent(p); + mFramelessHelper->setup(p); +#else + mFramelessHelper = new SAFramelessHelper(p); +#endif +} + +//=================================================== +// SARibbonMainWindow +//=================================================== +SARibbonMainWindow::SARibbonMainWindow(QWidget* parent, bool useRibbon, const Qt::WindowFlags flags) + : QMainWindow(parent, flags), d_ptr(new SARibbonMainWindow::PrivateData(this)) +{ + connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonMainWindow::onPrimaryScreenChanged); + if (useRibbon) { + d_ptr->installFrameless(this); + setRibbonBar(createRibbonBar()); + setRibbonTheme(ribbonTheme()); + } +} + +SARibbonMainWindow::~SARibbonMainWindow() +{ +} + +/** + * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr + * @return + */ +SARibbonBar* SARibbonMainWindow::ribbonBar() const +{ + return qobject_cast< SARibbonBar* >(menuWidget()); +} + +/** + * @brief 设置ribbonbar + * @param bar + */ +void SARibbonMainWindow::setRibbonBar(SARibbonBar* ribbon) +{ + QWidget* old = QMainWindow::menuWidget(); + if (old) { + // 如果之前已经设置了menubar,要把之前的删除 + old->deleteLater(); + } + QMainWindow::setMenuWidget(ribbon); + const int th = ribbon->titleBarHeight(); + // 设置window按钮 + if (nullptr == d_ptr->mWindowButtonGroup) { + d_ptr->mWindowButtonGroup = RibbonSubElementFactory->createWindowButtonGroup(this); + // SARibbonSystemButtonBar的eventfilter捕获mainwindow的事件 + // 通过eventerfilter来处理mainwindow的事件,避免用户错误的继承resizeEvent导致systembar的位置异常 + installEventFilter(d_ptr->mWindowButtonGroup); + } + SARibbonSystemButtonBar* sysBar = d_ptr->mWindowButtonGroup; + sysBar->setWindowStates(windowState()); + sysBar->setWindowTitleHeight(th); + sysBar->raise(); // 确保sysbar在最顶层,避免第二次设置ribbonbar的时候,被ribbonbar覆盖了sysbar + sysBar->show(); +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + auto helper = d_ptr->mFramelessHelper; + helper->setTitleBar(ribbon); + // 以下这些窗口,需要允许点击 + helper->setHitTestVisible(sysBar); // IMPORTANT! + helper->setHitTestVisible(ribbon->ribbonTabBar()); // IMPORTANT! + helper->setHitTestVisible(ribbon->rightButtonGroup()); // IMPORTANT! + helper->setHitTestVisible(ribbon->applicationButton()); // IMPORTANT! + helper->setHitTestVisible(ribbon->quickAccessBar()); // IMPORTANT! + helper->setHitTestVisible(ribbon->ribbonStackedWidget()); // IMPORTANT! +#if SARIBBON_ENABLE_SNAP_LAYOUT + if (sysBar->closeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Close, sysBar->closeButton()); + } + if (sysBar->minimizeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Minimize, sysBar->minimizeButton()); + } + if (sysBar->maximizeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Maximize, sysBar->maximizeButton()); + } +#endif +#else + // 捕获ribbonbar的事件 + ribbon->installEventFilter(this); + // 设置窗体的标题栏高度 + d_ptr->mFramelessHelper->setTitleHeight(th); + d_ptr->mFramelessHelper->setRubberBandOnResize(false); +#endif + if (!d_ptr->mEventFilter) { + d_ptr->mEventFilter = new SARibbonMainWindowEventFilter(this); + installEventFilter(d_ptr->mEventFilter); + } + // 最后要提升,否则新加入的会被覆盖 + if (d_ptr->mWindowButtonGroup) { + d_ptr->mWindowButtonGroup->raise(); + } +} + +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + +/** + * @brief 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 + * @param w + * @param visible + */ +void SARibbonMainWindow::setFramelessHitTestVisible(QWidget* w, bool visible) +{ + auto helper = d_ptr->mFramelessHelper; + helper->setHitTestVisible(const_cast< QWidget* >(w), visible); +} +#else + +SAFramelessHelper* SARibbonMainWindow::framelessHelper() +{ + return (d_ptr->mFramelessHelper); +} + +#endif + +bool SARibbonMainWindow::eventFilter(QObject* obj, QEvent* e) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#else + // 这个过滤是为了把ribbonBar上的动作传递到mainwindow,再传递到frameless, + // 由于ribbonbar会遮挡掉frameless的区域,导致frameless无法捕获这些消息 + // 因此必须ribbonBar()->installEventFilter(this); + // 20240101发现installEventFilter后,SARibbonMainWindow没有执行这个回调 + if (obj == ribbonBar()) { + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::Leave: + case QEvent::HoverMove: + case QEvent::MouseButtonDblClick: + QApplication::sendEvent(this, e); + + default: + break; + } + } +#endif + return (QMainWindow::eventFilter(obj, e)); +} + +/** + * @brief 获取系统按钮组,可以在此基础上添加其他按钮 + * @return + */ +SARibbonSystemButtonBar* SARibbonMainWindow::windowButtonBar() const +{ + return d_ptr->mWindowButtonGroup; +} + +/** + * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 + */ +void SARibbonMainWindow::updateWindowFlag(Qt::WindowFlags flags) +{ + if (SARibbonSystemButtonBar* g = d_ptr->mWindowButtonGroup) { + g->updateWindowFlag(flags); + } +} + +/** + * @brief SARibbonMainWindow::setRibbonTheme + * + * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + * @code + * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + * @endcode + * @param theme + */ +void SARibbonMainWindow::setRibbonTheme(SARibbonTheme theme) +{ + sa_set_ribbon_theme(this, theme); + d_ptr->mCurrentRibbonTheme = theme; + if (SARibbonBar* bar = ribbonBar()) { + auto theme = ribbonTheme(); + // 尺寸修正 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeOffice2016Blue: + case SARibbonTheme::RibbonThemeDark: + case SARibbonTheme::RibbonThemeDark2: { + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2013.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 0px; + //! margin-left: 5px; + //! margin-bottom: 0px; + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + tab->setTabMargin(QMargins(5, 0, 0, 0)); + } break; + case SARibbonTheme::RibbonThemeOffice2021Blue: { + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2021.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 5px; + //! margin-left: 5px; + //! margin-bottom: 0px; + tab->setTabMargin(QMargins(5, 0, 5, 0)); + } + default: + break; + } + // 上下文标签颜色设置,以及基线颜色设置 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeDark: + bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2016Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2021Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 + break; + default: + break; + } + // 基线颜色设置 + if (SARibbonTheme::RibbonThemeOffice2013 == theme) { + bar->setTabBarBaseLineColor(QColor(186, 201, 219)); + } else { + bar->setTabBarBaseLineColor(QColor()); + } + } +} + +SARibbonTheme SARibbonMainWindow::ribbonTheme() const +{ + return (d_ptr->mCurrentRibbonTheme); +} + +bool SARibbonMainWindow::isUseRibbon() const +{ + return (nullptr != ribbonBar()); +} + +/** + * @brief 创建ribbonbar的工厂函数 + * + * 用户如果重写了SARibbonBar,可以通过重新此虚函数返回自己的Ribbon实例 + * @return + */ +SARibbonBar* SARibbonMainWindow::createRibbonBar() +{ + SARibbonBar* bar = RibbonSubElementFactory->createRibbonBar(this); + bar->setContentsMargins(3, 0, 3, 0); + return bar; +} + +/** + * @brief 主屏幕切换触发的信号 + * @param screen + */ +void SARibbonMainWindow::onPrimaryScreenChanged(QScreen* screen) +{ + Q_UNUSED(screen); + // 主屏幕切换后,从新计算所有尺寸 + if (SARibbonBar* bar = ribbonBar()) { + qDebug() << "Primary Screen Changed"; + bar->updateRibbonGeometry(); + } +} + +void sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme) +{ + QFile file; + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + file.setFileName(":/theme/resource/theme-win7.qss"); + break; + case SARibbonTheme::RibbonThemeOffice2013: + file.setFileName(":/theme/resource/theme-office2013.qss"); + break; + case SARibbonTheme::RibbonThemeOffice2016Blue: + file.setFileName(":/theme/resource/theme-office2016-blue.qss"); + break; + case SARibbonTheme::RibbonThemeOffice2021Blue: + file.setFileName(":/theme/resource/theme-office2021-blue.qss"); + break; + case SARibbonTheme::RibbonThemeDark: + file.setFileName(":/theme/resource/theme-dark.qss"); + break; + case SARibbonTheme::RibbonThemeDark2: + file.setFileName(":/theme/resource/theme-dark2.qss"); + break; + default: + file.setFileName(":/theme/resource/theme-office2013.qss"); + break; + } + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + // 有反馈用qstring接住文件内容,再设置进去才能生效(qt5.7版本) + QString qss = QString::fromUtf8(file.readAll()); + w->setStyleSheet(qss); +} + +//---------------------------------------------------- +// SARibbonMainWindowEventFilter +//---------------------------------------------------- +SARibbonMainWindowEventFilter::SARibbonMainWindowEventFilter(QObject* par) : QObject(par) +{ +} + +SARibbonMainWindowEventFilter::~SARibbonMainWindowEventFilter() +{ +} + +bool SARibbonMainWindowEventFilter::eventFilter(QObject* obj, QEvent* e) +{ + if (e) { + if (e->type() == QEvent::Resize) { + if (SARibbonMainWindow* m = qobject_cast< SARibbonMainWindow* >(obj)) { + if (SARibbonBar* ribbon = m->ribbonBar()) { + ribbon->setFixedWidth(m->size().width()); + } + } + } + } + return QObject::eventFilter(obj, e); +} + +/*** End of inlined file: SARibbonMainWindow.cpp ***/ + +/*** Start of inlined file: SARibbonWidget.cpp ***/ +#include +#include +#include +#include + +/** + * @brief The SARibbonWidget::PrivateData class + */ +class SARibbonWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonWidget) +public: + PrivateData(SARibbonWidget* p); + void installFrameless(SARibbonWidget* p); + +public: + SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; +}; + +SARibbonWidget::PrivateData::PrivateData(SARibbonWidget* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonWidget +//=================================================== +SARibbonWidget::SARibbonWidget(QWidget* parent) : QWidget(parent), d_ptr(new SARibbonWidget::PrivateData(this)) +{ + // 直接创建SARibbonBar + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + verticalLayout->setSpacing(1); + verticalLayout->setObjectName("verticalLayout"); + verticalLayout->setContentsMargins(0, 0, 0, 0); + SARibbonBar* ribbon = new SARibbonBar(this); + setRibbonBar(ribbon); + connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonWidget::onPrimaryScreenChanged); +} + +SARibbonWidget::~SARibbonWidget() +{ +} + +/** + * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr + * @return + */ +SARibbonBar* SARibbonWidget::ribbonBar() const +{ + QLayout* lay = layout(); + if (lay) { + return qobject_cast< SARibbonBar* >(lay->menuBar()); + } + return nullptr; +} + +/** + * @brief 设置ribbonbar + * @param bar + */ +void SARibbonWidget::setRibbonBar(SARibbonBar* bar) +{ + QLayout* lay = layout(); + if (lay) { + lay->setMenuBar(bar); + } +} + +/** + * @brief SARibbonMainWindow::setRibbonTheme + * + * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + * @code + * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + * @endcode + * @param theme + */ +void SARibbonWidget::setRibbonTheme(SARibbonTheme theme) +{ + sa_set_ribbon_theme(this, theme); + d_ptr->mCurrentRibbonTheme = theme; + if (SARibbonBar* bar = ribbonBar()) { + auto theme = ribbonTheme(); + // 尺寸修正 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeOffice2016Blue: + case SARibbonTheme::RibbonThemeDark: + case SARibbonTheme::RibbonThemeDark2: { + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2013.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 0px; + //! margin-left: 5px; + //! margin-bottom: 0px; + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + tab->setTabMargin(QMargins(5, 0, 0, 0)); + } break; + case SARibbonTheme::RibbonThemeOffice2021Blue: { + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2021.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 5px; + //! margin-left: 5px; + //! margin-bottom: 0px; + tab->setTabMargin(QMargins(5, 0, 5, 0)); + } + default: + break; + } + // 上下文标签颜色设置,以及基线颜色设置 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeDark: + bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2016Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2021Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 + break; + default: + break; + } + // 基线颜色设置 + if (SARibbonTheme::RibbonThemeOffice2013 == theme) { + bar->setTabBarBaseLineColor(QColor(186, 201, 219)); + } else { + bar->setTabBarBaseLineColor(QColor()); + } + } +} + +SARibbonTheme SARibbonWidget::ribbonTheme() const +{ + return (d_ptr->mCurrentRibbonTheme); +} + +bool SARibbonWidget::isUseRibbon() const +{ + return (nullptr != ribbonBar()); +} + +/** + @brief 设置窗口 + + @param 窗口指针 + @note 窗口的所有权归SARibbonWidget管理 + @sa widget + @note 原来设置的窗口会被delete + */ +void SARibbonWidget::setWidget(QWidget* w) +{ + QWidget* oldwidget = takeWidget(); + if (oldwidget) { + oldwidget->hide(); + oldwidget->deleteLater(); + } + if (QLayout* lay = layout()) { + lay->addWidget(w); + } +} + +/** + @brief 获取设置的窗口 + + @return 如果没有,返回nullptr + */ +QWidget* SARibbonWidget::widget() const +{ + QLayout* lay = layout(); + if (lay) { + if (lay->count() == 0) { + return nullptr; + } + return lay->itemAt(0)->widget(); + } + return nullptr; +} + +QWidget* SARibbonWidget::takeWidget() +{ + QLayout* lay = layout(); + if (nullptr == lay) { + return nullptr; + } + if (lay->count() == 0) { + return nullptr; + } + auto item = lay->itemAt(0); + if (nullptr == item) { + return nullptr; + } + lay->removeItem(item); + auto w = item->widget(); + delete item; + if (!w) { + return nullptr; + } + return w; +} + +/** + * @brief 主屏幕切换触发的信号 + * @param screen + */ +void SARibbonWidget::onPrimaryScreenChanged(QScreen* screen) +{ + Q_UNUSED(screen); + // 主屏幕切换后,从新计算所有尺寸 + if (SARibbonBar* bar = ribbonBar()) { + qDebug() << "Primary Screen Changed"; + bar->updateRibbonGeometry(); + } +} + +/*** End of inlined file: SARibbonWidget.cpp ***/ + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("_CRT_SECURE_NO_WARNINGS") +#endif diff --git a/src/SARibbon.h b/src/SARibbon.h index 9f9774eb..bc1993c1 100644 --- a/src/SARibbon.h +++ b/src/SARibbon.h @@ -1,3778 +1,3778 @@ -#ifndef SA_RIBBON_H -#define SA_RIBBON_H -// 定义此宏,将SA_RIBBON_EXPORT定义为空 -#ifndef SA_RIBBON_BAR_NO_EXPORT -#define SA_RIBBON_BAR_NO_EXPORT -#endif -// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 -#ifndef SA_COLOR_WIDGETS_NO_DLL -#define SA_COLOR_WIDGETS_NO_DLL -#endif - -/*** Start of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ -// Global - -/*** Start of inlined file: SAColorWidgetsGlobal.h ***/ -#ifndef SACOLORWIDGETSGLOBAL_H -#define SACOLORWIDGETSGLOBAL_H -#include - -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_MAJ -#define SA_COLOR_WIDGETS_VERSION_MAJ 0 -#endif -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_MIN -#define SA_COLOR_WIDGETS_VERSION_MIN 1 -#endif -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_PAT -#define SA_COLOR_WIDGETS_VERSION_PAT 0 -#endif - -/** - * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 - */ -#ifndef SA_COLOR_WIDGETS_DECLARE_PRIVATE -#define SA_COLOR_WIDGETS_DECLARE_PRIVATE(classname) \ - class PrivateData; \ - friend class classname::PrivateData; \ - QScopedPointer< PrivateData > d_ptr; -#endif -/** - * @def 模仿Q_DECLARE_PUBLIC - */ -#ifndef SA_COLOR_WIDGETS_DECLARE_PUBLIC -#define SA_COLOR_WIDGETS_DECLARE_PUBLIC(classname) \ - friend class classname; \ - classname* q_ptr { nullptr }; -#endif - -#ifndef SA_COLOR_WIDGETS_NO_DLL -#if defined(SA_COLOR_WIDGETS_MAKE_LIB) // 定义此宏将构建library -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API Q_DECL_EXPORT -#endif -#else -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API Q_DECL_IMPORT -#endif -#endif -#else -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API -#endif -#endif - -#endif // SACOLORWIDGETSGLOBAL_H - -/*** End of inlined file: SAColorWidgetsGlobal.h ***/ - -/*** Start of inlined file: SARibbonBarVersionInfo.h ***/ -#ifndef SARIBBONVERSIONINFO_H -#define SARIBBONVERSIONINFO_H -/** - * @file 此文档由cmake根据SARibbonBarVersionInfo.h.in自动生成,任何在此文件上的改动都将覆写 - */ - -/** - * @def ribbon的数字版本 {MAJ}.MIN.PAT - */ -#ifndef SA_RIBBON_BAR_VERSION_MAJ -#define SA_RIBBON_BAR_VERSION_MAJ 2 -#endif -/** - * @def ribbon的数字版本 MAJ.{MIN}.PAT - */ -#ifndef SA_RIBBON_BAR_VERSION_MIN -#define SA_RIBBON_BAR_VERSION_MIN 2 -#endif -/** - * @def ribbon的数字版本 MAJ.MIN.{PAT} - */ -#ifndef SA_RIBBON_BAR_VERSION_PAT -#define SA_RIBBON_BAR_VERSION_PAT 5 -#endif - -/** - * @def 版本号(字符串) - */ -#ifndef SARIBBON_VERSION -#define SARIBBON_VERSION "2.2.5" -#endif - -#endif // SARIBBONVERSIONINFO_H - -/*** End of inlined file: SARibbonBarVersionInfo.h ***/ - -/*** Start of inlined file: SARibbonGlobal.h ***/ -#ifndef SARIBBONGLOBAL_H -#define SARIBBONGLOBAL_H -#include -#include -#include - -class QWidget; - -#ifndef SA_RIBBON_BAR_NO_EXPORT -#if defined(SA_RIBBON_BAR_MAKE_LIB) // 定义此宏将构建library -#define SA_RIBBON_EXPORT Q_DECL_EXPORT -#else -#define SA_RIBBON_EXPORT Q_DECL_IMPORT -#endif -#endif -#ifndef SA_RIBBON_EXPORT -#define SA_RIBBON_EXPORT -#endif - -/** - * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 - */ -#ifndef SA_RIBBON_DECLARE_PRIVATE -#define SA_RIBBON_DECLARE_PRIVATE(classname) \ - class PrivateData; \ - friend class classname::PrivateData; \ - std::unique_ptr< PrivateData > d_ptr; -#endif -/** - * @def 模仿Q_DECLARE_PUBLIC - */ -#ifndef SA_RIBBON_DECLARE_PUBLIC -#define SA_RIBBON_DECLARE_PUBLIC(classname) \ - friend class classname; \ - classname* q_ptr { nullptr }; \ - PrivateData(const PrivateData&) = delete; \ - PrivateData& operator=(const PrivateData&) = delete; -#endif - -/** - * @brief 定义Ribbon的对其方式,目前支持左对齐和居中对其 - * - * @note 如果你编译器提示:成员声明的限定名称非法,那么留意一下文件换行是否为LF,如果是把文件换行改为CRLF - */ -enum class SARibbonAlignment -{ - AlignLeft, ///< 左对齐,tab栏左对齐,同时category也是左对齐 - AlignCenter ///< 居中对其,tab栏居中对齐,同时category也是居中对齐 -}; - -/** - * @brief ribbon主题 - * - * 注意,由于有些qss的尺寸,在C++代码中无法获取到,因此针对用户自定义的qss主题,有些尺寸是需要手动设置进去的 - * - * 例如ribbon tab的margin信息,在QTabBar是无法获取到,而这个影响了SARibbonContextCategory的绘制, - * 因此,在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - */ -enum class SARibbonTheme -{ - RibbonThemeOffice2013, ///< office2013主题 - RibbonThemeOffice2016Blue, ///< office2016-蓝色主题 - RibbonThemeOffice2021Blue, ///< office2021-蓝色主题 - RibbonThemeWindows7, ///< win7主题 - RibbonThemeDark, ///< 暗色主题 - RibbonThemeDark2 -}; - -/** - * @def 属性,用于标记是否可以进行自定义,用于动态设置到@ref SARibbonCategory 和@ref SARibbonPannel - * 值为bool,在为true时,可以通过@ref SARibbonCustomizeWidget 改变这个SARibbonCategory和SARibbonPannel的布局, - * 默认不会有此属性,仅在有此属性且为true时才会在SARibbonCustomizeWidget中能显示为可设置 - */ -#ifndef SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE -#define SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE "_sa_isCanCustomize" -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) -#ifndef SA_FONTMETRICS_WIDTH -#define SA_FONTMETRICS_WIDTH(fm, str) fm.horizontalAdvance(str) -#endif -#else -#ifndef SA_FONTMETRICS_WIDTH -#define SA_FONTMETRICS_WIDTH(fm, str) fm.width(str) -#endif -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT -#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPosition().toPoint() -#endif -#else -#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT -#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPos() -#endif -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#ifndef SA_HOVEREVENT_POS_POINT -#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->position().toPoint() -#endif -#else -#ifndef SA_HOVEREVENT_POS_POINT -#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->pos() -#endif -#endif - -/** - @def 定义此宏,将打印和尺寸刷新相关的信息 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_SIZE_HINT -#define SA_DEBUG_PRINT_SIZE_HINT 0 -#endif - -/** - @def 定义此宏,将打印事件 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_EVENT -#define SA_DEBUG_PRINT_EVENT 0 -#endif - -/** - @def 定义此宏,qDebug将支持SARibbonBar的属性打印 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_SARIBBONBAR -#define SA_DEBUG_PRINT_SARIBBONBAR 0 -#endif - -/** - * @brief 全局的设置ribbon theme函数 - * - * 之所以提供此函数,是因为在某些情况下,SARibbonBar不用在SARibbonMainWindow情况下的时候,也需要设置主题, - * 但主题设置是在SARibbonMainWindow下的为了能在非SARibbonMainWindow下也能设置主题,这里提供@sa sa_set_ribbon_theme函数, - * 可以这样使用: - * @code - * @endcode - * - * @param w - * @param theme - */ -void SA_RIBBON_EXPORT sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme); - -#endif // SARIBBONGLOBAL_H - -/*** End of inlined file: SARibbonGlobal.h ***/ - -// color widget - -/*** Start of inlined file: SAColorMenu.h ***/ -#ifndef SACOLORMENU_H -#define SACOLORMENU_H -#include - -class QWidgetAction; -class SAColorPaletteGridWidget; -class SAColorGridWidget; -class SAColorToolButton; -/** - * @brief 标准颜色菜单 - */ -class SA_COLOR_WIDGETS_API SAColorMenu : public QMenu -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorMenu) -public: - explicit SAColorMenu(QWidget* parent = nullptr); - explicit SAColorMenu(const QString& title, QWidget* parent = nullptr); - ~SAColorMenu(); - // 快速绑定colorbtn - void bindToColorToolButton(SAColorToolButton* btn); - // ThemeColorsPalette对应的action - QWidgetAction* themeColorsPaletteAction() const; - // CustomColorsWidget对应的action - QWidgetAction* getCustomColorsWidgetAction() const; - // 自定义颜色action - QAction* customColorAction() const; - // 获取ThemeColorsPalette - SAColorPaletteGridWidget* colorPaletteGridWidget() const; - // 获取自定义颜色grid - SAColorGridWidget* customColorsWidget() const; - // 构建无颜色action,默认无颜色action是没有的 - void enableNoneColorAction(bool on = true); - // 获取None Color Action,注意,enableNoneColorAction(true),之后才不是nullptr - QAction* noneColorAction() const; -public Q_SLOTS: - // 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 - void emitSelectedColor(const QColor& c); -Q_SIGNALS: - /** - * @brief 选择了颜色触发的信号 - * @param c - */ - void selectedColor(const QColor& c); -private Q_SLOTS: - void onCustomColorActionTriggered(bool on); - void onNoneColorActionTriggered(bool on); - -private: - void init(const QList< QColor >& themeCls); -}; - -#endif // SACOLORMENU_H - -/*** End of inlined file: SAColorMenu.h ***/ - -/*** Start of inlined file: SAColorGridWidget.h ***/ -#ifndef SACOLORGRIDWIDGET_H -#define SACOLORGRIDWIDGET_H -#include -#include - -class QAbstractButton; -class SAColorToolButton; -/** - * @brief 一个grid的Color布局 - * - * 一个形如下面的颜色grid: - * - * □□□□□□□□□ - * - * □□□□□□□□□ - * - * □□□□□□□□□ - */ -class SA_COLOR_WIDGETS_API SAColorGridWidget : public QWidget -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorGridWidget) - Q_PROPERTY(int spacing READ spacing WRITE setSpacing) -public: - using FunColorBtn = std::function< void(SAColorToolButton*) >; - -public: - SAColorGridWidget(QWidget* par = nullptr); - ~SAColorGridWidget(); - // 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - void setColumnCount(int c); - int columnCount() const; - // 设置当前的颜色列表 - void setColorList(const QList< QColor >& cls); - QList< QColor > getColorList() const; - // 间隔 - int spacing() const; - void setSpacing(int v); - // 获取颜色的数量 - int colorCount() const; - // 图标的尺寸 - void setColorIconSize(const QSize& s); - QSize colorIconSize() const; - // 设置颜色是否是checkable - void setColorCheckable(bool on = true); - bool isColorCheckable() const; - // 获取当前选中的颜色 - QColor currentCheckedColor() const; - // 获取index对于的colorbutton - SAColorToolButton* colorButton(int index) const; - // 垂直间距 - void setVerticalSpacing(int v); - int verticalSpacing() const; - // 水平间距 - void setHorizontalSpacing(int v); - int horizontalSpacing() const; - // 清除当前选中状态,这时没有颜色是选中的 - void clearCheckedState(); - // 对所有的colorbtn就行遍历处理,可以通过此函数进行tooltip设置等操作 - void iterationColorBtns(FunColorBtn fn); - // 设置行最小高度 - void setRowMinimumHeight(int row, int minSize); - // 让颜色块左对齐 - void setHorizontalSpacerToRight(bool on = true); -private Q_SLOTS: - void onButtonClicked(QAbstractButton* btn); - void onButtonPressed(QAbstractButton* btn); - void onButtonReleased(QAbstractButton* btn); - void onButtonToggled(QAbstractButton* btn, bool on); - -Q_SIGNALS: - /** - * @brief 对于check模式,check的颜色触发的信号 - * @param c - * @param on - */ - void colorClicked(const QColor& c); - void colorPressed(const QColor& c); - void colorReleased(const QColor& c); - void colorToggled(const QColor& c, bool on); - -public: - virtual QSize sizeHint() const Q_DECL_OVERRIDE; -}; -namespace SA -{ -/** - * @brief 获取标准色列表(一共10种颜色) - * @return - */ -SA_COLOR_WIDGETS_API QList< QColor > getStandardColorList(); -} -#endif // SACOLORGRIDWIDGET_H - -/*** End of inlined file: SAColorGridWidget.h ***/ - -/*** Start of inlined file: SAColorPaletteGridWidget.h ***/ -#ifndef SACOLORPALETTEGRIDWIDGET_H -#define SACOLORPALETTEGRIDWIDGET_H -#include - -class QMenu; -class SAColorToolButton; -/** - * @brief 类似office的颜色选择窗口,有一排标准色,下面有一个颜色板,有3行浅色,有2行深色 - */ -class SA_COLOR_WIDGETS_API SAColorPaletteGridWidget : public QWidget -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorPaletteGridWidget) -public: - SAColorPaletteGridWidget(QWidget* par = nullptr); - SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par = nullptr); - ~SAColorPaletteGridWidget(); - // 设置窗口维护的colorList - void setColorList(const QList< QColor >& cls); - QList< QColor > colorList() const; - // 设置颜色深浅比例factor,默认为{ 180, 160, 140, 75, 50 } - void setFactor(const QList< int >& factor); - QList< int > factor() const; - // 设置iconsize - void setColorIconSize(const QSize& s); - QSize colorIconSize() const; - // 设置颜色是否是checkable - void setColorCheckable(bool on = true); - bool isColorCheckable() const; -private Q_SLOTS: - void onMainColorClicked(const QColor& c); - void onPaletteColorClicked(const QColor& c); - -private: - void init(); -Q_SIGNALS: - /** - * @brief 对于check模式,check的颜色触发的信号 - * @param c - * @param on - */ - void colorClicked(const QColor& c); -}; - -#endif // SACOLORPALETTEGRIDWIDGET_H - -/*** End of inlined file: SAColorPaletteGridWidget.h ***/ - -/*** Start of inlined file: SAColorToolButton.h ***/ -#ifndef SACOLORTOOLBUTTON_H -#define SACOLORTOOLBUTTON_H -#include - -class QPaintEvent; -class QResizeEvent; -class QPainter; -class QStylePainter; -class SAColorMenu; -/** - * @brief 这是一个只显示颜色的toolbutton - * - * - * 在ToolButtonIconOnly模式下,如果没有setIcon,则颜色占用所有区域,如下图所示 - * - * ┌─────┐ - * │color│ - * └─────┘ - * - * 如果在ToolButtonIconOnly模式下有图标,图标在上面显示,下面显示颜色,如下图所示 - * - * ┌─────┐ - * │icon │ - * │color│ - * └─────┘ - * - * 在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下,setIconSize 可以指定颜色的大小, - * 但只在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下有效 - * - * 如果没有设置图标,也就是setIcon(QIcon()),iconSize作为颜色块的大小 - * - * ┌─────────┐ - * │┌─┐ │ - * │└─┘ │ - * └─────────┘ - * - * 如果有图标,颜色条会在图标下方,为图标高度的1/4 为图标宽度一致,如若超过控件的大小,会自动缩小体积 - * - */ -class SA_COLOR_WIDGETS_API SAColorToolButton : public QToolButton -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorToolButton) -public: - /** - * @brief 颜色按钮的内置样式 - * @param parent - * @return - */ - enum ColorToolButtonStyle - { - WithColorMenu, ///< 默认会构建一个SAColorMenu - NoColorMenu ///< 没有ColorMenu - }; - -public: - explicit SAColorToolButton(QWidget* parent = nullptr); - explicit SAColorToolButton(ColorToolButtonStyle style, QWidget* parent = nullptr); - ~SAColorToolButton(); - // 获取颜色 - QColor color() const; - // 设置Margins - void setMargins(const QMargins& mg); - QMargins margins() const; - // 绘制无颜色 - static void paintNoneColor(QPainter* p, const QRect& colorRect); - // 设置颜色按钮的样式 - void setColorToolButtonStyle(ColorToolButtonStyle s); - ColorToolButtonStyle colorToolButtonStyle() const; - // 获取colorMenu,注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr - SAColorMenu* colorMenu() const; - // 建立标准的颜色菜单 - SAColorMenu* createColorMenu(); -public Q_SLOTS: - // 设置颜色,会发射colorChanged信号 - void setColor(const QColor& c); - -protected: - // 获取关键的三个rect位置 - virtual void calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - virtual void paintButton(QStylePainter* p, const QStyleOptionToolButton& opt); - virtual void paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt); - virtual void paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt); - virtual void paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt); - -protected: - virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - virtual QSize sizeHint() const Q_DECL_OVERRIDE; -private Q_SLOTS: - void onButtonClicked(bool checked = false); -Q_SIGNALS: - /** - * @brief 颜色被点击的响应 - * @param color - */ - void colorClicked(const QColor& color, bool checked = false); - /** - * @brief 颜色改变信号 - * @param color - */ - void colorChanged(const QColor& color); -}; - -#endif // SACOLORTOOLBUTTON_H - -/*** End of inlined file: SAColorToolButton.h ***/ - -// sa ribbon - -/*** Start of inlined file: SAFramelessHelper.h ***/ -#ifndef SAFRAMELESSHELPER_H -#define SAFRAMELESSHELPER_H - -#include - -class QWidget; - -class SA_RIBBON_EXPORT SAFramelessHelper : public QObject -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SAFramelessHelper) - friend class SAPrivateFramelessWidgetData; - -public: - explicit SAFramelessHelper(QObject* parent); - ~SAFramelessHelper(); - // 激活窗体 - void activateOn(QWidget* topLevelWidget); - - // 移除窗体 - void removeFrom(QWidget* topLevelWidget); - - // 设置窗体移动 - void setWidgetMovable(bool movable); - - // 设置窗体缩放 - void setWidgetResizable(bool resizable); - - // 设置橡皮筋移动 - void setRubberBandOnMove(bool movable); - - // 设置橡皮筋缩放 - void setRubberBandOnResize(bool resizable); - - // 设置边框的宽度 - void setBorderWidth(int width); - - // 设置标题栏高度 - void setTitleHeight(int height); - bool widgetResizable(); - bool widgetMovable(); - bool rubberBandOnMove(); - bool rubberBandOnResisze(); - uint borderWidth(); - uint titleHeight(); - -protected: - // 事件过滤,进行移动、缩放等 - virtual bool eventFilter(QObject* obj, QEvent* event); -}; - -#endif // FRAMELESSHELPER_H - -/*** End of inlined file: SAFramelessHelper.h ***/ - -/*** Start of inlined file: SARibbonApplicationButton.h ***/ -#ifndef SARIBBONAPPLICATIONBUTTON_H -#define SARIBBONAPPLICATIONBUTTON_H -#include - -/** - * @brief The SARibbonApplicationButton class - * - * 默认的plicationButton,可以通过样式指定不一样的ApplicationButton - */ -class SA_RIBBON_EXPORT SARibbonApplicationButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonApplicationButton(QWidget* parent = nullptr); - explicit SARibbonApplicationButton(const QString& text, QWidget* parent = nullptr); - explicit SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); - ~SARibbonApplicationButton(); -}; - -#endif // SARIBBONAPPLICATIONBUTTON_H - -/*** End of inlined file: SARibbonApplicationButton.h ***/ - -/*** Start of inlined file: SARibbonSystemButtonBar.h ***/ -#ifndef SARIBBONSYSTEMBUTTONBAR_H -#define SARIBBONSYSTEMBUTTONBAR_H - -#include -#include - -/** - * @brief 窗口的最大最小化按钮 - * @note 内部有个SARibbonButtonGroupWidget,其ObjectName = SASystemButtonGroup - * 如果需要qss,可以进行特化处理: - * - * @code - * SARibbonButtonGroupWidget#SASystemButtonGroup - * @endcode - * - */ -class SA_RIBBON_EXPORT SARibbonSystemButtonBar : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonSystemButtonBar) -public: - explicit SARibbonSystemButtonBar(QWidget* parent); - explicit SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags); - ~SARibbonSystemButtonBar(); - void setupMinimizeButton(bool on); - void setupMaximizeButton(bool on); - void setupCloseButton(bool on); - // 以最顶层的widget的WindowFlag作为WindowFlag - void updateWindowFlag(); - void updateWindowFlag(Qt::WindowFlags flags); - // 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 - void setButtonWidthStretch(int close = 4, int max = 3, int min = 3); - // 标题栏高度 - void setWindowTitleHeight(int h); - int windowTitleHeight() const; - // 设置标准系统按钮的宽度 - void setWindowButtonWidth(int w); - int windowButtonWidth() const; - // 设置Qt::WindowStates - void setWindowStates(Qt::WindowStates s); - // 仅获取按钮的状态 - Qt::WindowFlags windowButtonFlags() const; - // 三个标准系统窗口按钮 - QAbstractButton* minimizeButton() const; - QAbstractButton* maximizeButton() const; - QAbstractButton* closeButton() const; - - // 图标尺寸 - void setIconSize(const QSize& ic); - QSize iconSize() const; - // 生成并添加一个action - QAction* addAction(QAction* a, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addAction(const QString& text, - const QIcon& icon, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addMenu(QMenu* menu, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - QAction* addSeparator(); - QAction* addWidget(QWidget* w); - - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - // 捕获mainwindow的事件 - bool eventFilter(QObject* obj, QEvent* event) override; - -protected: - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - -protected Q_SLOTS: - Q_SLOT void closeWindow(); - Q_SLOT void minimizeWindow(); - Q_SLOT void maximizeWindow(); -}; - -/** - * @brief The SARibbonSystemToolButton class - */ -class SARibbonSystemToolButton : public QToolButton -{ - Q_OBJECT -public: - SARibbonSystemToolButton(QWidget* p = nullptr); -}; - -#endif // SARIBBONSYSTEMBUTTONBAR_H - -/*** End of inlined file: SARibbonSystemButtonBar.h ***/ - -/*** Start of inlined file: SARibbonToolButton.h ***/ -#ifndef SARIBBONTOOLBUTTON_H -#define SARIBBONTOOLBUTTON_H - -#include -#include -/** - * @brief Ribbon界面适用的toolButton - * - * 相对于普通toolbutton,主要多了两个类型设置,@ref setButtonType 和 @ref setLargeButtonType - * - * @note @sa setIconSize 函数不在起作用,iconsize是根据当前尺寸动态调整的 - */ -class SA_RIBBON_EXPORT SARibbonToolButton : public QToolButton -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonToolButton) -public: - /** - * @brief 按钮样式 - */ - enum RibbonButtonType - { - LargeButton, - SmallButton - }; - Q_ENUM(RibbonButtonType) - -public: - explicit SARibbonToolButton(QWidget* parent = nullptr); - explicit SARibbonToolButton(QAction* defaultAction, QWidget* parent = nullptr); - ~SARibbonToolButton(); - // 标记按钮的样式,按钮的样式有不同的渲染方式 - RibbonButtonType buttonType() const; - void setButtonType(const RibbonButtonType& buttonType); - // 是否是小按钮 - bool isSmallRibbonButton() const; - // 是否是大按钮 - bool isLargeRibbonButton() const; - // 最小尺寸提示 - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - - // 获取间距 - int spacing() const; - // 更新尺寸 - void updateRect(); - - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - -public: - // 在lite模式下是否允许文字换行 - static void setEnableWordWrap(bool on); - static bool isEnableWordWrap(); - -protected: - virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - virtual void mouseMoveEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void mousePressEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void focusOutEvent(QFocusEvent* e) Q_DECL_OVERRIDE; - virtual void leaveEvent(QEvent* e) Q_DECL_OVERRIDE; - virtual bool hitButton(const QPoint& pos) const Q_DECL_OVERRIDE; - virtual bool event(QEvent* e) Q_DECL_OVERRIDE; - // 事件改变 - 主要为了捕获字体的改变 - virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; - virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; - -protected: - // 绘制按钮 - virtual void paintButton(QPainter& p, const QStyleOptionToolButton& opt); - // 绘制图标 - virtual void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); - // 绘制文本 - virtual void paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect); - // 绘制Indicator - virtual void paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect); - -private: - static void drawArrow(const QStyle* style, - const QStyleOptionToolButton* toolbutton, - const QRect& rect, - QPainter* painter, - const QWidget* widget = 0); - -protected: -}; - -namespace SA -{ -QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt); -} -#endif // SARIBBONTOOLBUTTON_H - -/*** End of inlined file: SARibbonToolButton.h ***/ - -/*** Start of inlined file: SARibbonColorToolButton.h ***/ -#ifndef SARIBBONCOLORTOOLBUTTON_H -#define SARIBBONCOLORTOOLBUTTON_H - -class SAColorMenu; -/** - * @brief Refer to the color setting button in the office, which can display the color below the icon(参考office的颜色设置按钮,可以显示颜色在图标下方) - */ -class SA_RIBBON_EXPORT SARibbonColorToolButton : public SARibbonToolButton -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonColorToolButton) -public: - /** - * @brief 颜色样式 - */ - enum ColorStyle - { - ColorUnderIcon, ///< 颜色在icon下方,这个要求必须设置icon - ColorFillToIcon ///< 颜色作为icon,这个模式下在setColor会自动生成一个颜色icon替换掉原来的icon,因此setIcon函数没有作用 - }; - -public: - explicit SARibbonColorToolButton(QWidget* parent = nullptr); - explicit SARibbonColorToolButton(QAction* defaultAction, QWidget* parent = nullptr); - ~SARibbonColorToolButton(); - // 获取颜色 - QColor color() const; - // 设置颜色显示方案 - void setColorStyle(ColorStyle s); - ColorStyle colorStyle() const; - // 建立标准的颜色菜单 - SAColorMenu* setupStandardColorMenu(); -public Q_SLOTS: - // 设置颜色,会发射colorChanged信号 - void setColor(const QColor& c); -private Q_SLOTS: - void onButtonClicked(bool checked = false); -Q_SIGNALS: - /** - * @brief 颜色被点击的响应 - * @param color - */ - void colorClicked(const QColor& color, bool checked = false); - /** - * @brief 颜色改变信号 - * @param color - */ - void colorChanged(const QColor& color); - -protected: - void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); -}; - -#endif // SARIBBONCOLORTOOLBUTTON_H - -/*** End of inlined file: SARibbonColorToolButton.h ***/ - -/*** Start of inlined file: SARibbonLineWidgetContainer.h ***/ -#ifndef SARIBBONLINEWIDGETCONTAINER_H -#define SARIBBONLINEWIDGETCONTAINER_H - -#include -#include -#include - -/** - * @brief 一个窗口容器,把窗口放置中间,前面后面都可以设置文本,主要用于放置在pannel上的小窗口 - * - * 实现如下效果: - * - * PrefixLabel|_Widget_|SuffixLabel - * - */ -class SA_RIBBON_EXPORT SARibbonLineWidgetContainer : public QWidget -{ -public: - explicit SARibbonLineWidgetContainer(QWidget* par = nullptr); - ~SARibbonLineWidgetContainer(); - // 设置widget,不允许设置一个nullptr - void setWidget(QWidget* innerWidget); - - // 设置前缀 - void setPrefix(const QString& str); - - // 设置后缀 - void setSuffix(const QString& str); - - // 前缀文本框 - QLabel* labelPrefix() const; - - // 后缀文本框 - QLabel* labelSuffix() const; - -private: - // 两个文本 - QLabel* m_labelPrefix; - QLabel* m_labelSuffix; - QWidget* m_innerWidget; -}; - -#endif // SARIBBONWIDGETCONTAINER_H - -/*** End of inlined file: SARibbonLineWidgetContainer.h ***/ - -/*** Start of inlined file: SARibbonActionsManager.h ***/ -#ifndef SARIBBONACTIONSMANAGER_H -#define SARIBBONACTIONSMANAGER_H - -#include -#include -#include -#include -#include -#include -class SARibbonBar; -class SARibbonCategory; - -/** - * @brief 用于管理SARibbon的所有Action - * - * SARibbonActionsManager维护着两个表,一个是tag(标签)对应的Action list, - * 一个是所有接受SARibbonActionsManager管理的action list。 - * - * SARibbonActionsManager的标签对应一组actions,每个标签对应的action可以重复出现, - * 但SARibbonActionsManager维护的action list里只有一份action,不会重复出现。 - * - * tag用于对action list分组,每个tag的实体名字通过@ref setTagName 进行设置,在语言变化时需要及时调用 - * setTagName设置新的标签对应的文本。 - * - * SARibbonActionsManager默认预设了6个常用标签见@ref SARibbonActionsManager::ActionTag ,用户自定义标签需要在 - * SARibbonActionsManager::UserDefineActionTag值的基础上进行累加。 - * - * @ref filter (等同@ref actions )函数用于提取标签管理的action list,@ref allActions 函数返回SARibbonActionsManager - * 管理的所有标签。 - * - * 通过@ref autoRegisteActions 函数可以快速的建立action的管理,此函数会遍历@ref SARibbonBar下所有@ref SARibbonPannel - * 添加的action,并给予Category建立tag,正常使用用户仅需关注此autoRegisteActions函数即可 - * - * - */ -class SA_RIBBON_EXPORT SARibbonActionsManager : public QObject -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManager) - friend class SARibbonActionsManagerModel; - -public: - /** - * @brief 定义action的标签 - */ - enum ActionTag - { - UnknowActionTag = 0, ///< 未知的tag - CommonlyUsedActionTag = 0x01, ///< 预设tag-常用命令 - NotInFunctionalAreaActionTag = 0x02, ///< 预设tag-不在功能区命令 - AutoCategoryDistinguishBeginTag = 0x1000, ///< 自动按Category划分的标签起始,在@ref autoRegisteActions 函数会用到 - AutoCategoryDistinguishEndTag = 0x2000, ///< 自动按Category划分的标签结束,在@ref autoRegisteActions 函数会用到 - NotInRibbonCategoryTag = 0x2001, ///< 不在功能区的标签@ref autoRegisteActions 函数会遍历所有category的action - UserDefineActionTag = 0x8000 ///< 自定义标签,所有用户自定义tag要大于此tag - }; - explicit SARibbonActionsManager(SARibbonBar* bar); - ~SARibbonActionsManager(); - // 设置tag对应的名字 - void setTagName(int tag, const QString& name); - - // 获取tag对应的名字 - QString tagName(int tag) const; - - // 移除tag,注意,这个函数非常耗时 - void removeTag(int tag); - - // 注册action - bool registeAction(QAction* act, int tag, const QString& key = QString(), bool enableEmit = true); - - // 取消action的注册 - void unregisteAction(QAction* act, bool enableEmit = true); - - // 过滤得到actions对应的引用,实际是一个迭代器 - QList< QAction* >& filter(int tag); - - // 通过tag筛选出系列action - QList< QAction* >& actions(int tag); - const QList< QAction* > actions(int tag) const; - - // 获取所有的标签 - QList< int > actionTags() const; - - // 通过key获取action - QAction* action(const QString& key) const; - - // 通过action找到key - QString key(QAction* act) const; - - // 返回所有管理的action数 - int count() const; - - // 返回所有管理的actions - QList< QAction* > allActions() const; - - // 自动加载action,返回tag对应的Category指针 - QMap< int, SARibbonCategory* > autoRegisteActions(SARibbonBar* bar); - - // 自动加载widget下的actions函数返回的action,返回加载的数量,这些 - QSet< QAction* > autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit = false); - - // 根据标题查找action - QList< QAction* > search(const QString& text); - - // 清除 - void clear(); - -Q_SIGNALS: - - /** - * @brief 标签变化触发的信号,变化包括新增和删除 - */ - void actionTagChanged(int tag, bool isdelete); - -private Q_SLOTS: - void onActionDestroyed(QObject* o); - void onCategoryTitleChanged(const QString& title); - -private: - void removeAction(QAction* act, bool enableEmit = true); -}; - -/** - * @brief SARibbonActionsManager 对应的model - */ -class SA_RIBBON_EXPORT SARibbonActionsManagerModel : public QAbstractListModel -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManagerModel) -public: - explicit SARibbonActionsManagerModel(QObject* p = nullptr); - explicit SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p = nullptr); - ~SARibbonActionsManagerModel(); - virtual int rowCount(const QModelIndex& parent) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - virtual QVariant data(const QModelIndex& index, int role) const override; - void setFilter(int tag); - void update(); - void setupActionsManager(SARibbonActionsManager* m); - void uninstallActionsManager(); - QAction* indexToAction(QModelIndex index) const; - void search(const QString& text); - -private Q_SLOTS: - void onActionTagChanged(int tag, bool isdelete); -}; - -#endif // SARIBBONACTIONSMANAGER_H - -/*** End of inlined file: SARibbonActionsManager.h ***/ - -/*** Start of inlined file: SARibbonLineEdit.h ***/ -#ifndef SARIBBONLINEEDIT_H -#define SARIBBONLINEEDIT_H - -#include - -/** - * @brief The SARibbonLineEdit class - */ -class SA_RIBBON_EXPORT SARibbonLineEdit : public QLineEdit -{ - Q_OBJECT -public: - explicit SARibbonLineEdit(QWidget* parent = nullptr); - explicit SARibbonLineEdit(const QString& text, QWidget* parent = nullptr); - ~SARibbonLineEdit(); -}; - -#endif // SARIBBONLINEEDIT_H - -/*** End of inlined file: SARibbonLineEdit.h ***/ - -/*** Start of inlined file: SARibbonCheckBox.h ***/ -#ifndef SARIBBONCHECKBOX_H -#define SARIBBONCHECKBOX_H - -#include - -/** - * @brief The SARibbonCheckBox class - */ -class SA_RIBBON_EXPORT SARibbonCheckBox : public QCheckBox -{ - Q_OBJECT -public: - explicit SARibbonCheckBox(QWidget* parent = nullptr); - explicit SARibbonCheckBox(const QString& text, QWidget* parent = nullptr); - ~SARibbonCheckBox(); -}; - -#endif // SARIBBONCHECKBOX_H - -/*** End of inlined file: SARibbonCheckBox.h ***/ - -/*** Start of inlined file: SARibbonComboBox.h ***/ -#ifndef SARIBBONCOMBOBOX_H -#define SARIBBONCOMBOBOX_H - -#include - -/** - * @brief QComboBox的Ribbon显示,可以显示QIcon和windowTitle在左侧 - */ -class SA_RIBBON_EXPORT SARibbonComboBox : public QComboBox -{ - Q_OBJECT -public: - explicit SARibbonComboBox(QWidget* parent = nullptr); - ~SARibbonComboBox(); -}; - -#endif // SARIBBONCOMBOBOX_H - -/*** End of inlined file: SARibbonComboBox.h ***/ - -/*** Start of inlined file: SARibbonButtonGroupWidget.h ***/ -#ifndef SARIBBONBUTTONGROUPWIDGET_H -#define SARIBBONBUTTONGROUPWIDGET_H - -#include -#include -#include -class SARibbonControlButton; -/** - * @brief 用于管理一组Action,类似于QToolBar - */ -class SA_RIBBON_EXPORT SARibbonButtonGroupWidget : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonButtonGroupWidget) -public: - using FpButtonIterate = std::function< bool(SARibbonControlButton*) >; - -public: - explicit SARibbonButtonGroupWidget(QWidget* parent = nullptr); - ~SARibbonButtonGroupWidget(); - - // 图标尺寸 - void setIconSize(const QSize& ic); - QSize iconSize() const; - // 生成并添加一个action - QAction* addAction(QAction* a, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addAction(const QString& text, - const QIcon& icon, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addMenu(QMenu* menu, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - QAction* addSeparator(); - QAction* addWidget(QWidget* w); - // 从ButtonGroupWidget中把action对应的button提取出来,如果action没有对应的button,就返回nullptr - SARibbonControlButton* actionToRibbonControlToolButton(QAction* action); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -public: - bool iterate(FpButtonIterate fp); -Q_SIGNALS: - - /** - * @brief 参考QToolBar::actionTriggered的信号 - * @param action - */ - void actionTriggered(QAction* action); - -protected: - virtual void actionEvent(QActionEvent* e) override; -}; - -#endif // SARIBBONBUTTONGROUPWIDGET_H - -/*** End of inlined file: SARibbonButtonGroupWidget.h ***/ - -/*** Start of inlined file: SARibbonStackedWidget.h ***/ -#ifndef SARIBBONSTACKEDWIDGET_H -#define SARIBBONSTACKEDWIDGET_H -#include - -class QHideEvent; -class QResizeEvent; - -/** - * @brief 有qdialog功能的stackwidget,用于在最小化时stack能像dialog那样弹出来 - */ -class SA_RIBBON_EXPORT SARibbonStackedWidget : public QStackedWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonStackedWidget) -public: - explicit SARibbonStackedWidget(QWidget* parent); - ~SARibbonStackedWidget(); - void setPopupMode(); - bool isPopupMode() const; - void setNormalMode(); - bool isNormalMode() const; - void exec(); - - /** - * @brief 类似tabbar的moveTab函数,交换两个窗口的index - * @param from - * @param to - * @note 此操作会触发widgetRemoved(int index)信号 - */ - void moveWidget(int from, int to); - -protected: - // void mouseReleaseEvent(QMouseEvent *e); - void hideEvent(QHideEvent* e) Q_DECL_OVERRIDE; - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; -Q_SIGNALS: - /** - * @brief hidWindow - */ - void hidWindow(); -}; - -#endif // SARIBBONSTACKEDWIDGET_H - -/*** End of inlined file: SARibbonStackedWidget.h ***/ - -/*** Start of inlined file: SARibbonSeparatorWidget.h ***/ -#ifndef SARIBBONSEPARATORWIDGET_H -#define SARIBBONSEPARATORWIDGET_H - -#include -#include - -/// -/// \brief 用于显示分割线 -/// -class SA_RIBBON_EXPORT SARibbonSeparatorWidget : public QFrame -{ - Q_OBJECT -public: - explicit SARibbonSeparatorWidget(QWidget* parent = nullptr); - ~SARibbonSeparatorWidget(); -}; - -#endif // SARIBBONSEPARATORWIDGET_H - -/*** End of inlined file: SARibbonSeparatorWidget.h ***/ - -/*** Start of inlined file: SARibbonCtrlContainer.h ***/ -#ifndef SARIBBONCTROLCONTAINER_H -#define SARIBBONCTROLCONTAINER_H - -#include -#include -class QStyleOption; - -/** - * @brief 用于装载一个窗体的容器,这个窗体会布满整个SARibbonCtrlContainer,但会预留空间显示icon或者title - * - * ---------------------- - * |icon|text| widget | - * ---------------------- - */ -class SA_RIBBON_EXPORT SARibbonCtrlContainer : public QWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCtrlContainer) -public: - explicit SARibbonCtrlContainer(QWidget* parent = nullptr); - ~SARibbonCtrlContainer(); - - void setEnableShowIcon(bool b); - void setEnableShowTitle(bool b); - // 判断是否存在容器窗口 - bool hasContainerWidget() const; - // 图标 - void setIcon(const QIcon& i); - void setIcon(const QPixmap& pixmap); - QIcon icon() const; - // 图标 - void setText(const QString& t); - QString text() const; - // 设置窗口 - void setContainerWidget(QWidget* w); - QWidget* containerWidget(); - const QWidget* containerWidget() const; - // 获取线索icon的窗口 - QWidget* iconWidget() const; -}; - -#endif // SARIBBONCTROLCONTAINER_H - -/*** End of inlined file: SARibbonCtrlContainer.h ***/ - -/*** Start of inlined file: SARibbonQuickAccessBar.h ***/ -#ifndef SARIBBONQUICKACCESSBAR_H -#define SARIBBONQUICKACCESSBAR_H - -#include -#include -class SARibbonToolButton; -class SARibbonButtonGroupWidget; -/// -/// \brief ribbon左上顶部的快速响应栏 -/// -class SA_RIBBON_EXPORT SARibbonQuickAccessBar : public SARibbonCtrlContainer -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonQuickAccessBar) -public: - explicit SARibbonQuickAccessBar(QWidget* parent = nullptr); - ~SARibbonQuickAccessBar(); - void addSeparator(); - void addAction(QAction* act, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - void addWidget(QWidget* w); - void addMenu(QMenu* m, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - // 获取内部管理的ButtonGroupWidget - SARibbonButtonGroupWidget* buttonGroupWidget(); - const SARibbonButtonGroupWidget* buttonGroupWidget() const; - // 设置图标尺寸 - void setIconSize(const QSize& s); - QSize iconSize() const; -}; - -#endif // SARIBBONQUICKACCESSBAR_H - -/*** End of inlined file: SARibbonQuickAccessBar.h ***/ - -/*** Start of inlined file: SARibbonTabBar.h ***/ -#ifndef SARIBBONTABBAR_H -#define SARIBBONTABBAR_H - -#include -#include - -/** - * @brief The SARibbonTabBar class - */ -class SA_RIBBON_EXPORT SARibbonTabBar : public QTabBar -{ - Q_OBJECT -public: - explicit SARibbonTabBar(QWidget* parent = nullptr); - ~SARibbonTabBar(); - const QMargins& tabMargin() const; - void setTabMargin(const QMargins& tabMargin); - -protected: - QSize tabSizeHint(int index) const; - -private: - QMargins m_tabMargin; -}; - -#endif // SARIBBONTABBAR_H - -/*** End of inlined file: SARibbonTabBar.h ***/ - -/*** Start of inlined file: SARibbonControlButton.h ***/ -#ifndef SARIBBONCONTROLBUTTON_H -#define SARIBBONCONTROLBUTTON_H -#include - -/** - * @brief 用于SARibbonGallery的control button - * - * 为了防止外部qss的影响,单独继承一个类 - */ -class SA_RIBBON_EXPORT SARibbonControlButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonControlButton(QWidget* parent = nullptr); - ~SARibbonControlButton(); -}; - -/** - * @brief 用于SARibbonButtonGroupWidget的control button - * - * 为了防止SARibbonToolButton qss的影响,单独继承一个类 - */ -class SA_RIBBON_EXPORT SARibbonControlToolButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonControlToolButton(QWidget* parent = nullptr); - ~SARibbonControlToolButton(); -}; - -#endif // SARIBBONPANNELTOOLBUTTON_H - -/*** End of inlined file: SARibbonControlButton.h ***/ - -/*** Start of inlined file: SARibbonMenu.h ***/ -#ifndef SARIBBONMENU_H -#define SARIBBONMENU_H - -#include - -/// -/// \brief 用在ribbon的menu -/// 可以通过StyleSheet设置样式而不影响QMenu -/// -class SA_RIBBON_EXPORT SARibbonMenu : public QMenu -{ - Q_OBJECT -public: - explicit SARibbonMenu(QWidget* parent = nullptr); - explicit SARibbonMenu(const QString& title, QWidget* parent = nullptr); - ~SARibbonMenu(); - QAction* addRibbonMenu(SARibbonMenu* menu); - SARibbonMenu* addRibbonMenu(const QString& title); - SARibbonMenu* addRibbonMenu(const QIcon& icon, const QString& title); - QAction* addWidget(QWidget* w); -}; - -#endif // SARIBBONMENU_H - -/*** End of inlined file: SARibbonMenu.h ***/ - -/*** Start of inlined file: SARibbonPannelOptionButton.h ***/ -#ifndef SARIBBONPANNELOPTIONBUTTON_H -#define SARIBBONPANNELOPTIONBUTTON_H -#include - -class QAction; - -/** - * @brief Pannel右下角的操作按钮 - * - * 此按钮和一个action关联,使用@ref SARibbonPannel::addOptionAction 函数用于生成此按钮,正常来说 - * 用户并不需要直接操作此类,仅仅用于样式设计 - * 如果一定要重载此按钮,可以通过重载@ref SARibbonElementFactory - * 的 @ref SARibbonElementFactory::createRibbonPannelOptionButton来实现新的OptionButton - * - */ -class SA_RIBBON_EXPORT SARibbonPannelOptionButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonPannelOptionButton(QWidget* parent = nullptr); - ~SARibbonPannelOptionButton(); -}; - -#endif // SAROBBONPANNELOPTIONBUTTON_H - -/*** End of inlined file: SARibbonPannelOptionButton.h ***/ - -/*** Start of inlined file: SARibbonPannelItem.h ***/ -#ifndef SARIBBONPANNELITEM_H -#define SARIBBONPANNELITEM_H - -#include -#include -class SARibbonToolButton; -/** - * @brief 是对pannel所有子窗口的抽象,参考qt的toolbar - * - * 参考qt的toolbar,pannel所有子窗口内容都通过QAction进行抽象,包括gallery这些窗口,也是通过QAction进行抽象 - * QAction最终会转换为SARibbonPannelItem,每个SARibbonPannelItem都含有一个widget,SARibbonPannel的布局 - * 就基于SARibbonPannelItem - * - * 无窗口的action会在内部生成一个SARibbonToolButton, - */ -class SA_RIBBON_EXPORT SARibbonPannelItem : public QWidgetItem -{ -public: - /** - * @brief 定义了行的占比,ribbon中有large,media和small三种占比 - */ - enum RowProportion - { - None, ///< 为定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small - Large, ///< 大占比,一个widget的高度会充满整个pannel - Medium, ///< 中占比,在@ref SARibbonPannel::pannelLayoutMode 为 @ref SARibbonPannel::ThreeRowMode 时才会起作用,且要同一列里两个都是Medium时,会在三行中占据两行 - Small ///< 小占比,占SARibbonPannel的一行,Medium在不满足条件时也会变为Small,但不会变为Large - }; - explicit SARibbonPannelItem(QWidget* widget); - ~SARibbonPannelItem(); - - bool isEmpty() const Q_DECL_OVERRIDE; - - short rowIndex; ///< 记录当前item属于第几行,hide模式下为-1 - int columnIndex; ///< 记录当前item属于第几列,hide模式下为-1 - QRect itemWillSetGeometry; ///< 在调用SARibbonPannelLayout::updateGeomArray会更新这个此处,实际设置的时候会QWidgetItem::setGeometry设置Geometry - QAction* action; /// < 记录action,参考QToolBarLayoutItem - bool customWidget; ///< 对于没有窗口的action,实际也会有一个SARibbonToolButton,在销毁时要delete掉 - SARibbonPannelItem::RowProportion rowProportion; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion -}; -#ifndef SA_ActionPropertyName_RowProportion -#define SA_ActionPropertyName_RowProportion "_sa_RowProportion" -#endif -#ifndef SA_ActionPropertyName_ToolButtonPopupMode -#define SA_ActionPropertyName_ToolButtonPopupMode "_sa_ToolButtonPopupMode" -#endif -#ifndef SA_ActionPropertyName_ToolButtonStyle -#define SA_ActionPropertyName_ToolButtonStyle "_sa_ToolButtonStyle" -#endif -#endif // SARIBBONPANNELITEM_H - -/*** End of inlined file: SARibbonPannelItem.h ***/ - -/*** Start of inlined file: SARibbonPannelLayout.h ***/ -#ifndef SARIBBONPANNELLAYOUT_H -#define SARIBBONPANNELLAYOUT_H - -#include - -class QToolButton; -class SARibbonPannel; -class SARibbonPannelLabel; -/** - * @brief 针对SARibbonPannel的布局 - * - * SARibbonPannelLayout实际是一个列布局,每一列有2~3行,看窗口定占几行 - * - * 核心函数: @ref SARibbonPannelLayout::createItem - * - * @note QLayout::contentsMargins 函数不会启作用,如果要设置contentsMargins,使用@sa setPannelContentsMargins - */ -class SA_RIBBON_EXPORT SARibbonPannelLayout : public QLayout -{ - Q_OBJECT - friend class SARibbonPannel; - -public: - explicit SARibbonPannelLayout(QWidget* p = nullptr); - ~SARibbonPannelLayout(); - // SARibbonPannelLayout additem 无效 - void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; - // QLayout 所必须的重载函数 - QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; - QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; - int count() const Q_DECL_OVERRIDE; - bool isEmpty() const Q_DECL_OVERRIDE; - void invalidate() Q_DECL_OVERRIDE; - Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; - void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; - QSize minimumSize() const Q_DECL_OVERRIDE; - QSize sizeHint() const Q_DECL_OVERRIDE; - - // 获取ribbonpannel - SARibbonPannel* ribbonPannel() const; - // SARibbonPannel主要通过此函数来添加action - void insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); - // 设置OptionAction,如果要去除,传入nullptr指针即可 - void setOptionAction(QAction* action); - bool isHaveOptionAction() const; - // 通过action获取SARibbonPannelItem - SARibbonPannelItem* pannelItem(QAction* action) const; - // 获取最后一个添加的item - SARibbonPannelItem* lastItem() const; - // 获取最后生成的窗口 - QWidget* lastWidget() const; - // 移动两个item - void move(int from, int to); - // 判断是否需要重新布局 - bool isDirty() const; - // 更新尺寸 - void updateGeomArray(); - // 通过action获取SARibbonPannelItem的索引 - int indexByAction(QAction* action) const; - // 标题高度 - int pannelTitleHeight() const; - void setPannelTitleHeight(int newTitleHeight); - // 判断是否存在标题 - bool isEnableShowPannelTitle() const; - void setEnableShowPannelTitle(bool on); - // 返回大按钮的高度 - int largeButtonHeight() const; - // 标题区域和按钮的间隔 - int pannelTitleSpace() const; - void setPannelTitleSpace(int newTitleSpace); - // pannel 标题的label - SARibbonPannelLabel* pannelTitleLabel() const; - // 设置按钮的icon尺寸,large action不受此尺寸影响 - void setToolButtonIconSize(const QSize& s); - QSize toolButtonIconSize() const; - -protected: - // 获取optionAction 按钮尺寸 - QSize optionActionButtonSize() const; - // 布局action - void doLayout(); - // 把action转换为item,对于纯Action,此函数会创建SARibbonToolButton, - // rp用于告诉Layout生成什么样的窗口,详细见SARibbonPannelItem::RowProportion - SARibbonPannelItem* createItem(QAction* action, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); - void updateGeomArray(const QRect& setrect); - // 重新计算扩展条码,此函数必须在updateGeomArray函数之后调用 - void recalcExpandGeomArray(const QRect& setrect); - // 返回optionActionButton的尺寸 - -private: - // 根据列数,计算窗口的宽度,以及最大宽度 - void columnWidthInfo(int colindex, int& width, int& maximum) const; - // 设置titlelabel - void setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel); - -private: - QList< SARibbonPannelItem* > mItems; - int mColumnCount { 0 }; ///< 记录有多少列 - bool mExpandFlag { false }; ///< 标记是否是会扩展的 - QSize mSizeHint; ///< sizeHint返回的尺寸 - QSize mDefaultToolButtonIconSize { 22, 22 }; ///< 记录默认的按钮图标尺寸 - bool mDirty { true }; ///< 用于标记是否需要刷新元素,参考QToolBarLayout源码 - int mLargeHeight { 0 }; ///< 记录大图标的高度 - int mTitleHeight { 15 }; ///< 标题区域高度 - int mTitleSpace { 2 }; ///< 标题区域和按钮的间隔 - bool mEnableShowTitle { true }; ///< 是否运行显示pannel标题 - SARibbonPannelLabel* mTitleLabel { nullptr }; ///< titlelabel指针 - QRect mTitleLabelGeometry; ///< titlelabel的位置 - QToolButton* mOptionActionBtn { nullptr }; ///< optionAction对应的button - QRect mOptionActionBtnGeometry; ///< optionAction的位置 -}; - -#endif // SARIBBONPANNELLAYOUT_H - -/*** End of inlined file: SARibbonPannelLayout.h ***/ - -/*** Start of inlined file: SARibbonPannel.h ***/ -#ifndef SARIBBONPANNEL_H -#define SARIBBONPANNEL_H - -#include -#include -#include -class SARibbonMenu; -class SARibbonGallery; -class QGridLayout; -class SARibbonPannelOptionButton; -class SARibbonPannelLayout; -class SARibbonCategory; -class SARibbonBar; - -/** - * @brief SARibbonPannel的标题label,此类用于qss - */ -class SA_RIBBON_EXPORT SARibbonPannelLabel : public QLabel -{ - Q_OBJECT -public: - SARibbonPannelLabel(QWidget* parent = nullptr); -}; - -/** - * @brief pannel页窗口,pannel是ribbon的面板用于承放控件 - * - * ribbon的pannel分为两行模式和三行模式,以office为代表的ribbon为3行模式,以WPS为代表的“紧凑派”就是2行模式, - * SARibbon可通过SARibbonBar的@ref SARibbonBar::RibbonStyle 来指定模式(通过函数@ref SARibbonBar::setRibbonStyle ) - * - * 在pannel中,可以通过@ref setExpanding 函数指定pannel水平扩展,如果pannel里面没有能水平扩展的控件,将会留白,因此, - * 建议在pannel里面有水平扩展的控件如(@ref SARibbonGallery )才指定这个函数 - * - * pannel的布局通过@ref SARibbonPannelLayout 来实现,如果有其他布局,可以通过继承@ref - * SARibbonElementCreateDelegate::createRibbonPannel 函数返回带有自己布局的pannel,但你必须继承对应的虚函数 - */ -class SA_RIBBON_EXPORT SARibbonPannel : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonPannel) - friend class SARibbonCategory; - friend class SARibbonCategoryPrivate; - friend class SARibbonCustomizeWidgetPrivate; - friend class SARibbonPannelLayout; - Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) - Q_PROPERTY(bool isExpanding READ isExpanding WRITE setExpanding) - Q_PROPERTY(QString pannelName READ pannelName WRITE setPannelName) -public: - enum PannelLayoutMode - { - ThreeRowMode, ///< 三行布局模式,office就是三行布局模式,pannel能布置3行小toolbutton,默认模式 - TwoRowMode ///< 两行布局模式,wps的后续布局模式就是两行布局模式,pannel能布置2行小toolbutton - }; - Q_ENUM(PannelLayoutMode) -public: - using FpRibbonToolButtonIterate = std::function< bool(SARibbonToolButton*) >; - -public: - explicit SARibbonPannel(QWidget* parent = nullptr); - explicit SARibbonPannel(const QString& name, QWidget* parent = nullptr); - ~SARibbonPannel() Q_DECL_OVERRIDE; - using QWidget::addAction; - - // 把action加入到pannel - void addAction(QAction* action, SARibbonPannelItem::RowProportion rp); - // 生成并添加一个action - void addAction(QAction* act, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); - // 把action加入到pannel,并以大图标显示 - void addLargeAction(QAction* action); - // 把action加入到pannel,在三行模式下会以中图标显示 - void addMediumAction(QAction* action); - // 把action加入到pannel,并以小图标显示 - void addSmallAction(QAction* action); - - // 把action加入到pannel,并以小图标显示 - void addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 把action加入到pannel,并以大图标显示 - void addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 把action加入到pannel,在三行模式下会以中图标显示 - void addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - - QAction* addAction(const QString& text, - const QIcon& icon, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); - - // 添加menu - void addMenu(QMenu* menu, - SARibbonPannelItem::RowProportion rp, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加普通大菜单 - void addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加普通小按钮菜单 - void addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加窗口 - QAction* addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp); - - // 添加窗口,占用ribbon的一行 - QAction* addSmallWidget(QWidget* w); - - // 添加窗口,占用ribbon的一行 - QAction* addMediumWidget(QWidget* w); - - // 添加窗口,占用所有行 - QAction* addLargeWidget(QWidget* w); - - // 添加一个Gallery - SARibbonGallery* addGallery(bool expanding = true); - - // 添加分割线 - QAction* addSeparator(); - - // 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr - SARibbonToolButton* actionToRibbonToolButton(QAction* action); - - // 设置操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - // OptionAction也会触发actionTriggered信号 - void setOptionAction(QAction* action); - - // 判断是否存在OptionAction - bool isHaveOptionAction() const; - - // 获取所有的buttons - QList< SARibbonToolButton* > ribbonToolButtons() const; - - // 获取PannelLayoutMode - PannelLayoutMode pannelLayoutMode() const; - void setPannelLayoutMode(PannelLayoutMode mode); - // 更新按钮的尺寸,这个函数在pannel的布局状态变换后需要调用刷新 - void resetToolButtonSize(); - - // 判断是否为2行模式 - bool isTwoRow() const - { - return (TwoRowMode == pannelLayoutMode()); - } - - // 把pannel设置为扩展模式,此时会撑大水平区域 - void setExpanding(bool isExpanding = true); - - // 是否是扩展模式 - bool isExpanding() const; - - // 标题栏高度 - void setTitleHeight(int h); - int titleHeight() const; - - // 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 - bool isEnableShowTitle() const; - void setEnableShowTitle(bool on); - - // action对应的布局index,此操作一般用于移动moveAction,其他意义不大 - int actionIndex(QAction* act) const; - - // 移动action - void moveAction(int from, int to); - - // 判断是否可以自定义 - bool isCanCustomize() const; - void setCanCustomize(bool b); - - // 标题 - QString pannelName() const; - void setPannelName(const QString& title); - - // 大图标的高度 - int largeButtonHeight() const; - // 获取布局对应的item,此函数目前仅仅在自定义过程中用到 - const QList< SARibbonPannelItem* >& ribbonPannelItem() const; - // 获取pannel layout - SARibbonPannelLayout* pannelLayout() const; - // 更新布局 - void updateItemGeometry(); - // 获取category指针,如果没有parent,或者不在category管理,返回nullptr - SARibbonCategory* category() const; - // 获取ribbonBar指针,如果没有返回nullptr - SARibbonBar* ribbonBar() const; - // 设置按钮之间的间隔 - void setSpacing(int n); - int spacing() const; - // - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - // 设置按钮的icon尺寸,large action不受此尺寸影响 - void setToolButtonIconSize(const QSize& s); - QSize toolButtonIconSize() const; - // 此函数会遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 - bool iterate(FpRibbonToolButtonIterate fp); - - // 获取最近添加的按钮 - SARibbonToolButton* lastAddActionButton(); - -Q_SIGNALS: - - /** - * @brief 等同于QToolBar::actionTriggered - * @param action - */ - void actionTriggered(QAction* action); - - /** - * @brief pannel的标题发生了改变 - * @param n - */ - void pannelNameChanged(const QString& n); - -public: - // pannel高度推荐 - static int pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight); - // 把action的行属性设置进action中,action自身携带了行属性 - static void setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp); - // 获取action的行属性 - static SARibbonPannelItem::RowProportion getActionRowProportionProperty(QAction* action); - // 把action的PopupMode属性设置进action中,action自身携带了PopupMode属性 - static void setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 获取action的PopupMode属性 - static QToolButton::ToolButtonPopupMode getActionToolButtonPopupModeProperty(QAction* action); - // 把action的ToolButtonStyle属性设置进action中,action自身携带了ToolButtonStyle属性 - static void setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle); - // 获取action的ToolButtonStyle属性 - static Qt::ToolButtonStyle getActionToolButtonStyleProperty(QAction* action); - -protected: - virtual bool event(QEvent* e) Q_DECL_OVERRIDE; - virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; - virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; -}; - -#endif // SARIBBONPANNEL_H - -/*** End of inlined file: SARibbonPannel.h ***/ - -/*** Start of inlined file: SARibbonCategory.h ***/ -#ifndef SARIBBONCATEGORY_H -#define SARIBBONCATEGORY_H - -#include - -#include -#include -#include - -class QAction; -class QHBoxLayout; -class QWheelEvent; -class SARibbonBar; -class SARibbonCategoryLayout; -/** - * @brief 一项ribbon tab页 - * @note SARibbonCategory的windowTitle影响了其在SARibbonBar的标签显示, - * 如果要改标签名字,直接调用SARibbonCategory的setWindowTitle函数 - */ -class SA_RIBBON_EXPORT SARibbonCategory : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCategory) - friend class SARibbonBar; - friend class SARibbonContextCategory; - Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) - Q_PROPERTY(QString categoryName READ categoryName WRITE setCategoryName) -public: - using FpPannelIterate = std::function< bool(SARibbonPannel*) >; - -public: - explicit SARibbonCategory(QWidget* p = nullptr); - explicit SARibbonCategory(const QString& name, QWidget* p = nullptr); - ~SARibbonCategory(); - - // category的名字 - QString categoryName() const; - - // 设置category名字,等同setWindowTitle - void setCategoryName(const QString& title); - - // 设置pannel的模式 - SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; - void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); - - // 添加pannel - SARibbonPannel* addPannel(const QString& title); - - // 添加pannel - void addPannel(SARibbonPannel* pannel); - - // qt designer专用 - Q_INVOKABLE void addPannel(QWidget* pannel); - - // 插入pannel - SARibbonPannel* insertPannel(const QString& title, int index); - - // 通过名字查找pannel - SARibbonPannel* pannelByName(const QString& title) const; - - // 通过ObjectName查找pannel - SARibbonPannel* pannelByObjectName(const QString& objname) const; - - // 通过索引找到pannel,如果超过索引范围,会返回nullptr - SARibbonPannel* pannelByIndex(int index) const; - - // 查找pannel的index - int pannelIndex(SARibbonPannel* p) const; - - // 移动一个Pannel从from index到to index - void movePannel(int from, int to); - - // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 - bool takePannel(SARibbonPannel* pannel); - - // 移除Pannel,Category会直接回收SARibbonPannel内存 - bool removePannel(SARibbonPannel* pannel); - bool removePannel(int index); - - // 返回所有的Pannel - QList< SARibbonPannel* > pannelList() const; - - // - QSize sizeHint() const Q_DECL_OVERRIDE; - - // 如果是ContextCategory,此函数返回true - bool isContextCategory() const; - - // pannel的个数 - int pannelCount() const; - - // 判断是否可以自定义 - bool isCanCustomize() const; - void setCanCustomize(bool b); - - // 设置pannel的标题栏高度 - int pannelTitleHeight() const; - void setPannelTitleHeight(int h); - - // 设置pannel是否显示标题栏 - bool isEnableShowPannelTitle() const; - void setEnableShowPannelTitle(bool on); - - // 设置Category的对齐方式 - void setCategoryAlignment(SARibbonAlignment al); - SARibbonAlignment categoryAlignment() const; - - // 设置pannel的spacing - void setPannelSpacing(int n); - int pannelSpacing() const; - - // 设置pannel按钮的icon尺寸,large action不受此尺寸影响 - void setPannelToolButtonIconSize(const QSize& s); - QSize pannelToolButtonIconSize() const; - - // 获取对应的ribbonbar,如果没有加入ribbonbar的管理,此值为null - SARibbonBar* ribbonBar() const; - - // 刷新category的尺寸布局 - void updateItemGeometry(); - - // 此函数会遍历Category下的所有pannel,执行函数指针,函数指针返回false则停止迭代 - bool iterate(FpPannelIterate fp); -Q_SIGNALS: - /** - * @brief category标题发生了改变信号 - * @param n - */ - void categoryNameChanged(const QString& n); - - /** - * @brief 参考QToolBar::actionTriggered的信号 - * @param action - */ - void actionTriggered(QAction* action); - -protected: - virtual bool event(QEvent* e) override; - // 处理滚轮事件 - void wheelEvent(QWheelEvent* event) override; - // - void changeEvent(QEvent* event) override; - - // 标记这个是上下文标签 - void markIsContextCategory(bool isContextCategory = true); - - // 获取SARibbonCategoryLayoutlayout - SARibbonCategoryLayout* categoryLayout() const; -}; - -/** - * @brief SARibbonCategory无法完全显示时,显示的调整按钮 - * - * 重新定义是为了防止被外部的样式影响,同时可以使用SARibbonCategoryScrollButton的样式定义 - */ -class SA_RIBBON_EXPORT SARibbonCategoryScrollButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p = nullptr); - ~SARibbonCategoryScrollButton(); -}; - -#endif // SARIBBONCATEGORY_H - -/*** End of inlined file: SARibbonCategory.h ***/ - -/*** Start of inlined file: SARibbonCategoryLayout.h ***/ -#ifndef SARIBBONCATEGORYLAYOUT_H -#define SARIBBONCATEGORYLAYOUT_H - -#include -#include -#include - -class SARibbonPannel; -class SARibbonCategoryLayoutItem; -class SARibbonSeparatorWidget; - -/** - * @brief The SARibbonCategoryLayout class - */ -class SA_RIBBON_EXPORT SARibbonCategoryLayout : public QLayout -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCategoryLayout) -public: - explicit SARibbonCategoryLayout(SARibbonCategory* parent); - ~SARibbonCategoryLayout(); - - SARibbonCategory* ribbonCategory() const; - - virtual void addItem(QLayoutItem* item) override; - virtual QLayoutItem* itemAt(int index) const override; - virtual QLayoutItem* takeAt(int index) override; - SARibbonCategoryLayoutItem* takePannelItem(int index); - SARibbonCategoryLayoutItem* takePannelItem(SARibbonPannel* pannel); - bool takePannel(SARibbonPannel* pannel); - virtual int count() const override; - void setGeometry(const QRect& rect) override; - QSize sizeHint() const override; - QSize minimumSize() const override; - Qt::Orientations expandingDirections() const override; - void invalidate() override; - // 追加一个pannel - void addPannel(SARibbonPannel* pannel); - // 插入一个pannel - void insertPannel(int index, SARibbonPannel* pannel); - // - QSize categoryContentSize() const; - // 更新尺寸 - void updateGeometryArr(); - - // 执行位置调整 - void doLayout(); - - // 返回所有pannels - QList< SARibbonPannel* > pannels() const; - - // 通过obj name获取pannel - SARibbonPannel* pannelByObjectName(const QString& objname) const; - // 通过pannel name获取pannel - SARibbonPannel* pannelByName(const QString& pannelname) const; - // 通过索引找到pannel,如果超过索引范围,会返回nullptr - SARibbonPannel* pannelByIndex(int i) const; - // 移动pannel - void movePannel(int from, int to); - // pannel的数量 - int pannelCount() const; - // pannel的索引 - int pannelIndex(SARibbonPannel* p) const; - // 获取所有的pannel - QList< SARibbonPannel* > pannelList() const; - // 执行滚轮事件 - void scroll(int px); - // 判断是否有滚动过 - bool isScrolled() const; - // category的总宽度 - int categoryTotalWidth() const; - // 设置Category的对齐方式 - void setCategoryAlignment(SARibbonAlignment al); - SARibbonAlignment categoryAlignment() const; -private Q_SLOTS: - void onLeftScrollButtonClicked(); - void onRightScrollButtonClicked(); -}; - -/** - * @brief SARibbonCategoryLayoutItem,用于标识SARibbonCategoryLayout的item - */ -class SA_RIBBON_EXPORT SARibbonCategoryLayoutItem : public QWidgetItem -{ -public: - explicit SARibbonCategoryLayoutItem(SARibbonPannel* w); - ~SARibbonCategoryLayoutItem(); - SARibbonSeparatorWidget* separatorWidget; - // 把内部的widget转换为pannel - SARibbonPannel* toPannelWidget(); - QRect mWillSetGeometry; ///< pannel将要设置的Geometry - QRect mWillSetSeparatorGeometry; ///< pannel将要设置的Separator的Geometry -}; -#endif // SARIBBONCATEGORYLAYOUT_H - -/*** End of inlined file: SARibbonCategoryLayout.h ***/ - -/*** Start of inlined file: SARibbonContextCategory.h ***/ -#ifndef SARIBBONCONTEXTCATEGORY_H -#define SARIBBONCONTEXTCATEGORY_H - -#include - -/** - * @brief 管理上下文标签的类 - */ -class SA_RIBBON_EXPORT SARibbonContextCategory : public QObject -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonContextCategory) -public: - explicit SARibbonContextCategory(QWidget* parent = nullptr); - ~SARibbonContextCategory(); - // 上下文目录添加下属目录 - SARibbonCategory* addCategoryPage(const QString& title); - void addCategoryPage(SARibbonCategory* category); - // 获取上下文标签下管理的标签个数 - int categoryCount() const; - - // 设置id - void setId(const QVariant& id); - QVariant id() const; - - // 设置上下文颜色 - void setContextColor(const QColor color); - QColor contextColor() const; - - // 上下文标签的内容 - QString contextTitle() const; - void setContextTitle(const QString& contextTitle); - - // 获取对应的tab页 - SARibbonCategory* categoryPage(int index); - - // 获取所有的SARibbonCategory* - QList< SARibbonCategory* > categoryList() const; - - // 移除category - bool takeCategory(SARibbonCategory* category); - - // 判断上下文是否维护了此SARibbonCategory - bool isHaveCategory(SARibbonCategory* category) const; -Q_SIGNALS: - /** - * @brief 标签加入上下文 - * @param category - */ - void categoryPageAdded(SARibbonCategory* category); - - /** - * @brief 标签从上下文移除 - * @param category - */ - void categoryPageRemoved(SARibbonCategory* category); - - /** - * @brief 上下文的标题发生改变 - * @param title - */ - void contextTitleChanged(const QString& title); - - /** - * @brief 上下文标签维护的标签页名字发生了改变 - * @param category 发生改变的上下文标签页 - * @param title 新名字 - */ - void categoryTitleChanged(SARibbonCategory* category, const QString& title); -private Q_SLOTS: - void onCategoryTitleChanged(const QString& title); - -protected: - // 获取父级窗口 - QWidget* parentWidget() const; - virtual bool eventFilter(QObject* watched, QEvent* e) override; -}; - -#endif // SARIBBONCONTEXTCATEGORY_H - -/*** End of inlined file: SARibbonContextCategory.h ***/ - -/*** Start of inlined file: SARibbonGalleryItem.h ***/ -#ifndef SARIBBONGALLERYITEM_H -#define SARIBBONGALLERYITEM_H - -#include -#include -#include -#include -class SARibbonGalleryGroup; - -/// -/// \brief 类似QStandardItem的GalleryItem -/// -class SA_RIBBON_EXPORT SARibbonGalleryItem -{ - friend class SARibbonGalleryGroupModel; - -public: - explicit SARibbonGalleryItem(); - explicit SARibbonGalleryItem(const QString& text, const QIcon& icon); - explicit SARibbonGalleryItem(QAction* act); - virtual ~SARibbonGalleryItem(); - // 设置角色 - void setData(int role, const QVariant& data); - virtual QVariant data(int role) const; - - // 设置文字描述 - void setText(const QString& text); - QString text() const; - - // 设置tooltip - void setToolTip(const QString& text); - QString toolTip() const; - - // 设置图标 - void setIcon(const QIcon& ico); - QIcon icon() const; - - // 设置是否可见 - bool isSelectable() const; - void setSelectable(bool isSelectable); - - // 设置是否可选 - bool isEnable() const; - void setEnable(bool isEnable); - - // 设置item的flag - void setFlags(Qt::ItemFlags flag); - virtual Qt::ItemFlags flags() const; - - // 设置action - void setAction(QAction* act); - QAction* action(); - - // 文字对齐方式 - void setTextAlignment(Qt::Alignment a); - Qt::Alignment textAlignment() const; - -private: - QMap< int, QVariant > mDatas; - Qt::ItemFlags mFlags; - QAction* mAction; -}; - -#endif // SARIBBONGALLERYITEM_H - -/*** End of inlined file: SARibbonGalleryItem.h ***/ - -/*** Start of inlined file: SARibbonGalleryGroup.h ***/ -#ifndef SARIBBONGALLERYGROUP_H -#define SARIBBONGALLERYGROUP_H - -#include -#include -#include - -/// -/// \brief SARibbonGalleryGroup对应的显示代理 -/// -class SA_RIBBON_EXPORT SARibbonGalleryGroupItemDelegate : public QStyledItemDelegate -{ -public: - explicit SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent = nullptr); - ~SARibbonGalleryGroupItemDelegate(); - virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - - virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; - virtual void paintIconOnly(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual void paintIconWithText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual void - paintIconWithTextWordWrap(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - -private: - SARibbonGalleryGroup* mGroup; -}; - -/// -/// \brief SARibbonGalleryGroup对应的model -/// -class SA_RIBBON_EXPORT SARibbonGalleryGroupModel : public QAbstractListModel -{ - Q_OBJECT -public: - SARibbonGalleryGroupModel(QObject* parent = Q_NULLPTR); - ~SARibbonGalleryGroupModel(); - virtual int rowCount(const QModelIndex& parent) const Q_DECL_OVERRIDE; - virtual Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; - virtual QVariant data(const QModelIndex& index, int role) const Q_DECL_OVERRIDE; - virtual QModelIndex index(int row, int column, const QModelIndex& parent) const Q_DECL_OVERRIDE; - virtual bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; - void clear(); - SARibbonGalleryItem* at(int row) const; - void insert(int row, SARibbonGalleryItem* item); - SARibbonGalleryItem* take(int row); - void append(SARibbonGalleryItem* item); - -private: - QList< SARibbonGalleryItem* > mItems; -}; - -/** - * @brief Gallery的组 - * - * 组负责显示管理Gallery Item - */ -class SA_RIBBON_EXPORT SARibbonGalleryGroup : public QListView -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonGalleryGroup) -public: - /** - * @brief GalleryGroup显示的样式 - */ - enum GalleryGroupStyle - { - IconWithText, ///< 图标带文字 - IconWithWordWrapText, ///< 图标带文字,文字会换行显示,此模式只会对DisplayOneRow生效,如果不是DisplayOneRow,等同IconWithText - IconOnly ///< 只有图标 - }; - - /** - * @brief 定义Gallery在一个pannel下面显示的图标行数 - */ - enum DisplayRow - { - DisplayOneRow = 1, ///< 显示1行,默认 - DisplayTwoRow = 2, - DisplayThreeRow = 3 - }; - - SARibbonGalleryGroup(QWidget* w = 0); - - virtual ~SARibbonGalleryGroup(); - // 重新计算grid尺寸 - void setRecalcGridSizeBlock(bool on = true); - bool isRecalcGridSizeBlock() const; - void recalcGridSize(); - void recalcGridSize(int galleryHeight); - // 设置显示的样式 - void setGalleryGroupStyle(GalleryGroupStyle style); - GalleryGroupStyle galleryGroupStyle() const; - // 添加一个item - void addItem(const QString& text, const QIcon& icon); - void addItem(SARibbonGalleryItem* item); - // 以一个aciton作为item添加 - void addActionItem(QAction* act); - void addActionItemList(const QList< QAction* >& acts); - - // 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 - void setupGroupModel(); - SARibbonGalleryGroupModel* groupModel(); - // 标题 - void setGroupTitle(const QString& title); - QString groupTitle() const; - void selectByIndex(int i); - // 设置显示的行数 - void setDisplayRow(DisplayRow r); - DisplayRow displayRow() const; - // 设置grid最小的宽度,默认为0(不限制),可以限定grid的宽度 - void setGridMinimumWidth(int w); - int gridMinimumWidth() const; - // 设置grid最大的宽度,默认为0(不限制),可以限定grid的宽度 - void setGridMaximumWidth(int w); - int gridMaximumWidth() const; - // 获取SARibbonGalleryGroup管理的actiongroup - QActionGroup* actionGroup() const; -private Q_SLOTS: - void onItemClicked(const QModelIndex& index); - void onItemEntered(const QModelIndex& index); -Q_SIGNALS: - void groupTitleChanged(const QString& title); - /** - * @brief 等同QActionGroup的triggered - * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup - * @param action - */ - void triggered(QAction* action); - /** - * @brief 等同QActionGroup的triggered - * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup - * @note 此属性需要通过QAbstractItemView::entered(const QModelIndex &index)激活,因此要保证设置了setMouseTracking(true) - * @param action - */ - void hovered(QAction* action); -}; - -#endif // SARIBBONGALLERYGROUP_H - -/*** End of inlined file: SARibbonGalleryGroup.h ***/ - -/*** Start of inlined file: SARibbonGallery.h ***/ -#ifndef SARIBBONGALLERY_H -#define SARIBBONGALLERY_H - -#include -#include - -#include -class QLabel; -class QVBoxLayout; -class SARibbonGalleryViewport; - -/** - * @brief 针对SARibbonGallery控件的按钮 - */ -class SA_RIBBON_EXPORT SARibbonGalleryButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonGalleryButton(QWidget* parent = nullptr); - ~SARibbonGalleryButton(); -}; - -/** - * @brief Gallery控件 - * - * Gallery控件是由一个当前激活的@sa SARibbonGalleryGroup 和弹出的 @sa SARibbonGalleryViewport 组成 - * - * @sa SARibbonGalleryGroup 是继承@sa QListView actions通过icon展示出来,相关的属性可以按照QListView设置 - * - * @sa SARibbonGalleryViewport 是一个内部有垂直布局的窗体,在弹出激活时,把管理的SARibbonGalleryGroup都展示出来 - * - * 示例如下: - * @code - * SARibbonGallery* gallery = pannel1->addGallery(); - * QList< QAction* > galleryActions; - * ...create many actions ... - * SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); - * galleryActions.clear(); - * ...create many actions ... - * gallery->addCategoryActions(tr("Apps"), galleryActions); - * gallery->setCurrentViewGroup(group1); - * @endcode - */ -class SA_RIBBON_EXPORT SARibbonGallery : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonGallery) -public: - explicit SARibbonGallery(QWidget* parent = nullptr); - virtual ~SARibbonGallery(); - virtual QSize sizeHint() const override; - // 添加一个GalleryGroup - SARibbonGalleryGroup* addGalleryGroup(); - // 添加一个GalleryGroup - void addGalleryGroup(SARibbonGalleryGroup* group); - // 快速添加一组actions - SARibbonGalleryGroup* addCategoryActions(const QString& title, QList< QAction* > actions); - // 设置当前显示的SARibbonGalleryGroup - void setCurrentViewGroup(SARibbonGalleryGroup* group); - // 获取当前显示的SARibbonGalleryGroup - SARibbonGalleryGroup* currentViewGroup() const; - // 获取弹出窗口指针 - SARibbonGalleryViewport* getPopupViewPort() const; - -public: - // 设置最右边三个控制按钮的最大宽度(默认15) - static void setGalleryButtonMaximumWidth(int w); -Q_SIGNALS: - /** - * @brief 转发管理的SARibbonGalleryGroup::triggered - * 所有加入SARibbonGallery的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup - * @param action - */ - void triggered(QAction* action); - /** - * @brief 转发管理的SARibbonGalleryGroup::hovered - * @note 此属性需要确保SARibbonGalleryGroup::setMouseTracking(true) - * @param action - */ - void hovered(QAction* action); - -public Q_SLOTS: - // 上翻页 - virtual void pageUp(); - // 下翻页 - virtual void pageDown(); - // 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 - virtual void showMoreDetail(); -protected Q_SLOTS: - void onItemClicked(const QModelIndex& index); - virtual void onTriggered(QAction* action); - -private: - SARibbonGalleryViewport* ensureGetPopupViewPort(); - -protected: - void resizeEvent(QResizeEvent* event) override; - void paintEvent(QPaintEvent* event) override; -}; - -/// -/// \brief SARibbonGallery的Viewport class -/// -class SARibbonGalleryViewport : public QWidget -{ - Q_OBJECT -public: - SARibbonGalleryViewport(QWidget* parent); - // 添加窗口不带标题 - void addWidget(QWidget* w); - // 添加窗口,带标题 - void addWidget(QWidget* w, const QString& title); - // 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 - QLabel* titleLabel(QWidget* w); - // 移除窗口 - void removeWidget(QWidget* w); -public Q_SLOTS: - void onTitleChanged(QWidget* w, const QString& title); - -private: - QVBoxLayout* m_layout; - QMap< QWidget*, QLabel* > _widgetToTitleLable; ///< QWidget和lable的对应 -}; - -#endif // SARIBBONGALLERY_H - -/*** End of inlined file: SARibbonGallery.h ***/ - -/*** Start of inlined file: SARibbonBar.h ***/ -#ifndef SARIBBONBAR_H -#define SARIBBONBAR_H - -#include -#include -#include - -class QAction; -class QAbstractButton; -class SARibbonElementFactory; -class SARibbonTabBar; -class SARibbonButtonGroupWidget; -class SARibbonQuickAccessBar; -class SARibbonStackedWidget; - -/** - @brief SARibbonBar继承于QMenuBar,在SARibbonMainWindow中直接替换了原来的QMenuBar - - 通过setRibbonStyle函数设置ribbon的风格: - - @code - void setRibbonStyle(RibbonStyles v); - @endcode - - SARibbonBar参考office和wps,提供了四种风格的Ribbon模式,@ref SARibbonBar::RibbonStyles - - 如果想ribbon占用的空间足够小,WpsLiteStyleTwoRow模式能比OfficeStyle节省35%的高度空间 - - 如何生成ribbon?先看看一个传统的Menu/ToolBar是如何生成的: - - @code - void MainWindow::MainWindow() - { - QMenu *fileMenu = menuBar()->addMenu(tr("&File")); - QToolBar *fileToolBar = addToolBar(tr("File")); - //生成action - QAction *newAct = new QAction(newIcon, tr("&New"), this); - fileMenu->addAction(newAct); - fileToolBar->addAction(newAct); - - QAction *openAct = new QAction(openIcon, tr("&Open..."), this); - fileMenu->addAction(openAct); - fileToolBar->addAction(openAct); - } - @endcode - - 传统的Menu/ToolBar主要通过QMenu的addMenu添加菜单,通过QMainWindow::addToolBar生成QToolBar, - 再把QAction设置进QMenu和QToolBar中 - - SARibbonBar和传统方法相似,不过相对于传统的Menu/ToolBar QMenu和QToolBar是平级的, - Ribbon是有明显的层级关系,SARibbonBar下面是 @ref SARibbonCategory, - SARibbonCategory下面是@ref SARibbonPannel ,SARibbonPannel下面是@ref SARibbonToolButton , - SARibbonToolButton管理着QAction - - 因此,生成一个ribbon只需以下几个函数: - @code - SARibbonCategory * SARibbonBar::addCategoryPage(const QString& title); - SARibbonPannel * SARibbonCategory::addPannel(const QString& title); - SARibbonToolButton * SARibbonPannel::addLargeAction(QAction *action); - SARibbonToolButton * SARibbonPannel::addSmallAction(QAction *action); - @endcode - - 因此生成步骤如下: - - @code - //成员变量 - SARibbonCategory* categoryMain; - SARibbonPannel* FilePannel; - - //建立ui - void setupRibbonUi() - { - ...... - //ribbonwindow为SARibbonMainWindow - SARibbonBar* ribbon = ribbonwindow->ribbonBar(); - ribbon->setRibbonStyle(SARibbonBar::WpsLiteStyle); - //添加一个Main标签 - categoryMain = ribbon->addCategoryPage(QStringLiteral("Main")); - //Main标签下添加一个File Pannel - FilePannel = categoryMain->addPannel(QStringLiteral("FilePannel")); - //开始为File Pannel添加action - FilePannel->addLargeAction(actionNew); - FilePannel->addLargeAction(actionOpen); - FilePannel->addLargeAction(actionSave); - FilePannel->addSmallAction(actionImportMesh); - FilePannel->addSmallAction(actionImportGeometry); - } - @endcode - */ -class SA_RIBBON_EXPORT SARibbonBar : public QMenuBar -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonBar) - friend class SARibbonMainWindow; - friend class SARibbonSystemButtonBar; - Q_PROPERTY(RibbonStyles ribbonStyle READ currentRibbonStyle WRITE setRibbonStyle) - Q_PROPERTY(bool minimumMode READ isMinimumMode WRITE setMinimumMode) - Q_PROPERTY(bool minimumModeButton READ haveShowMinimumModeButton WRITE showMinimumModeButton) - Q_PROPERTY(QColor windowTitleTextColor READ windowTitleTextColor WRITE setWindowTitleTextColor) - Q_PROPERTY(QColor tabBarBaseLineColor READ tabBarBaseLineColor WRITE setTabBarBaseLineColor) - Q_PROPERTY(Qt::Alignment windowTitleAligment READ windowTitleAligment WRITE setWindowTitleAligment) - Q_PROPERTY(bool enableWordWrap READ isEnableWordWrap WRITE setEnableWordWrap) - Q_PROPERTY(bool enableShowPannelTitle READ isEnableShowPannelTitle WRITE setEnableShowPannelTitle) - Q_PROPERTY(bool tabOnTitle READ isTabOnTitle WRITE setTabOnTitle) - Q_PROPERTY(SARibbonPannel::PannelLayoutMode pannelLayoutMode READ pannelLayoutMode WRITE setPannelLayoutMode) - -public: - enum RibbonStyleFlag - { - RibbonStyleLoose = 0x0001, // bit:0000 0001 - RibbonStyleCompact = 0x0002, // bit:0000 0010 - RibbonStyleThreeRow = 0x0010, // bit:0001 0000 - RibbonStyleTwoRow = 0x0020, // bit:0010 0000 - - RibbonStyleLooseThreeRow = RibbonStyleLoose | RibbonStyleThreeRow, ///< 宽松结构,3行模式 - RibbonStyleCompactThreeRow = RibbonStyleCompact | RibbonStyleThreeRow, ///< 紧凑结构,3行模式 - RibbonStyleLooseTwoRow = RibbonStyleLoose | RibbonStyleTwoRow, ///< 宽松结构,2行模式 - RibbonStyleCompactTwoRow = RibbonStyleCompact | RibbonStyleTwoRow ///< 紧凑结构,2行模式 - }; - Q_ENUM(RibbonStyleFlag) - Q_DECLARE_FLAGS(RibbonStyles, RibbonStyleFlag) - Q_FLAG(RibbonStyles) - - /** - * @brief 定义当前ribbon 的状态 - */ - enum RibbonMode - { - MinimumRibbonMode, ///< 缩小模式 - NormalRibbonMode ///< 正常模式 - }; - Q_ENUM(RibbonMode) - - using FpCategoryIterate = std::function< bool(SARibbonCategory*) >; - using FpPannelIterate = SARibbonCategory::FpPannelIterate; - -public: - // 判断RibbonStyle是否为2行模式 - static bool isTwoRowStyle(RibbonStyles s); - static bool isThreeRowStyle(RibbonStyles s); - // 判断是否是office样式 - static bool isLooseStyle(RibbonStyles s); - static bool isCompactStyle(RibbonStyles s); - // 获取版本信息 - static QString versionString(); - - // 获取默认的上下文标签颜色列表 - static QList< QColor > defaultContextCategoryColorList(); - - // - static void initHighDpi(); - -public: - // 构造函数 - explicit SARibbonBar(QWidget* parent = nullptr); - ~SARibbonBar(); - // 获取applicationButton - QAbstractButton* applicationButton(); - - // 设置applicationButton - void setApplicationButton(QAbstractButton* btn); - - // 获取tabbar - SARibbonTabBar* ribbonTabBar(); - - // 添加一个标签 - SARibbonCategory* addCategoryPage(const QString& title); - void addCategoryPage(SARibbonCategory* category); - - // 为了支持Qt designer,添加的一个重载函数 - Q_INVOKABLE void addCategoryPage(QWidget* category); - - // 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 - SARibbonCategory* insertCategoryPage(const QString& title, int index); - void insertCategoryPage(SARibbonCategory* category, int index); - - // 通过名字查找Category - SARibbonCategory* categoryByName(const QString& title) const; - - // 通过ObjectName查找Category - SARibbonCategory* categoryByObjectName(const QString& objname) const; - - // 通过索引找到category,如果超过索引范围,会返回nullptr - SARibbonCategory* categoryByIndex(int index) const; - - // 隐藏category,并不会删除或者取走,只是隐藏 - void hideCategory(SARibbonCategory* category); - - // 显示被隐藏的category - void showCategory(SARibbonCategory* category); - - // 判断这个category是否在显示状态,也就是tabbar有这个category - bool isCategoryVisible(const SARibbonCategory* c) const; - - // 获取category的索引 - int categoryIndex(const SARibbonCategory* c) const; - - // 移动一个Category从from index到to index - void moveCategory(int from, int to); - - // 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 - QList< SARibbonCategory* > categoryPages(bool getAll = true) const; - - // 移除SARibbonCategory - void removeCategory(SARibbonCategory* category); - - // 添加一个上下文标签 - SARibbonContextCategory* - addContextCategory(const QString& title, const QColor& color = QColor(), const QVariant& id = QVariant()); - void addContextCategory(SARibbonContextCategory* context); - - // 显示一个上下文标签 - void showContextCategory(SARibbonContextCategory* context); - - // 隐藏一个上下文标签 - void hideContextCategory(SARibbonContextCategory* context); - - // 判断上下文是否是在显示状态 - bool isContextCategoryVisible(SARibbonContextCategory* context); - - // 设置上下文标签的显示或隐藏 - void setContextCategoryVisible(SARibbonContextCategory* context, bool visible); - - // 获取所有的上下文标签 - QList< SARibbonContextCategory* > contextCategoryList() const; - - // 移除ContextCategory - void destroyContextCategory(SARibbonContextCategory* context); - - // 设置为隐藏模式 - void setMinimumMode(bool isHide); - - // 当前Ribbon是否是隐藏模式 - bool isMinimumMode() const; - - // 设置显示隐藏ribbon按钮 - void showMinimumModeButton(bool isShow = true); - - // 是否显示隐藏ribbon按钮 - bool haveShowMinimumModeButton() const; - - // 隐藏ribbon对应的action - QAction* minimumModeAction() const; - - // 当前的模式 - RibbonMode currentRibbonState() const; - - // ribbon tab的高度 - int tabBarHeight() const; - void setTabBarHeight(int h, bool resizeByNow = true); - - // 标题栏的高度 - int titleBarHeight() const; - void setTitleBarHeight(int h, bool resizeByNow = true); - - // category的高度 - int categoryHeight() const; - void setCategoryHeight(int h, bool resizeByNow = true); - - // 获取正常模式下的mainBar的高度 - int normalModeMainBarHeight() const; - - // 最小模式下的MainBar高度 - int minimumModeMainBarHeight() const; - - // 激活tabbar右边的按钮群 - SARibbonButtonGroupWidget* activeRightButtonGroup(); - - // 右侧按钮群 - SARibbonButtonGroupWidget* rightButtonGroup(); - - // 激活QuickAccessBar - SARibbonQuickAccessBar* activeQuickAccessBar(); - - // 快速响应栏 - SARibbonQuickAccessBar* quickAccessBar(); - - // 设置ribbon的风格 - void setRibbonStyle(RibbonStyles v); - RibbonStyles currentRibbonStyle() const; - - // 设置当前ribbon的index - void setCurrentIndex(int index); - - // 返回当前的tab索引 - int currentIndex(); - - // 确保标签显示出来 - void raiseCategory(SARibbonCategory* category); - - // 判断当前的行数 - bool isTwoRowStyle() const; - bool isThreeRowStyle() const; - - // 判断当前的样式 - bool isLooseStyle() const; - bool isCompactStyle() const; - - // 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局,在发现刷新问题时,可以调用此函数 - void updateRibbonGeometry(); - - // 设置pannel的模式 - SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; - void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); - - // 设置tab在title上面,这样可以省略title区域 - void setTabOnTitle(bool on); - bool isTabOnTitle() const; - - // tabbar 底部会绘制一条线条,此接口定义线条颜色 - void setTabBarBaseLineColor(const QColor& clr); - QColor tabBarBaseLineColor() const; - - // 设置标题颜色,如果不设置标题颜色,默认是SARibbonBar的qss的color属性 - void setWindowTitleTextColor(const QColor& clr); - QColor windowTitleTextColor() const; - - // 设置标题的对齐方式 - void setWindowTitleAligment(Qt::Alignment al); - Qt::Alignment windowTitleAligment() const; - - // 设置按钮允许换行,注意图标大小是由文字决定的,两行文字会让图标变小,如果想图标变大,文字不换行是最好的 - void setEnableWordWrap(bool on); - bool isEnableWordWrap() const; - - // 设置pannel的标题栏高度 - int pannelTitleHeight() const; - void setPannelTitleHeight(int h); - - // 设置pannel是否显示标题栏 - bool isEnableShowPannelTitle() const; - void setEnableShowPannelTitle(bool on); - - // 设置pannel的spacing - void setPannelSpacing(int n); - int pannelSpacing() const; - - // 设置pannel按钮的icon尺寸,large action不受此尺寸影响 - void setPannelToolButtonIconSize(const QSize& s); - QSize pannelToolButtonIconSize() const; - - // 获取SARibbonStackedWidget,谨慎使用此函数 - SARibbonStackedWidget* ribbonStackedWidget(); - - // 设置是否显示标题 - void setTitleVisible(bool on = false); - bool isTitleVisible() const; - - // 上下文标签的颜色列表,上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 - void setContextCategoryColorList(const QList< QColor >& cls); - QList< QColor > contextCategoryColorList() const; - - // 设置context category 标题的文字颜色 - void setContextCategoryTitleTextColor(const QColor& clr); - QColor contextCategoryTitleTextColor() const; - - // 设置ribbon的对齐方式 - void setRibbonAlignment(SARibbonAlignment al); - SARibbonAlignment ribbonAlignment() const; - - // 此函数会遍历SARibbonBar下的所有Category,执行函数指针(bool(SARibbonCategory*)),函数指针返回false则停止迭代 - bool iterate(FpCategoryIterate fp); - // 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针(bool(SARibbonPannel*)),函数指针返回false则停止迭代 - bool iterate(FpPannelIterate fp); - - // 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget - void setCornerWidgetVisible(bool on, Qt::Corner c = Qt::TopLeftCorner); -Q_SIGNALS: - - /** - @brief 应用按钮点击响应 - 左上角的按钮,通过关联此信号触发应用按钮点击的效果 - - 例如想点击按钮后弹出一个全屏的窗口(如office这些) - */ - void applicationButtonClicked(); - - /** - @brief 标签页变化触发的信号 - @param index - */ - void currentRibbonTabChanged(int index); - - /** - @brief ribbon的状态发生了变化后触发此信号 - @param nowState 变更之后的ribbon状态 - */ - void ribbonModeChanged(SARibbonBar::RibbonMode nowState); - - /** - @brief ribbon的状态发生了变化后触发此信号 - @param nowStyle 变更之后的ribbon样式 - */ - void ribbonStyleChanged(SARibbonBar::RibbonStyles nowStyle); - - /** - @brief 标题栏高度发生了变化的信号 - @param oldHeight - @param newHeight - */ - void titleBarHeightChanged(int oldHeight, int newHeight); - - /** - * @brief 参考QToolBar::actionTriggered的信号 - * @param action - */ - void actionTriggered(QAction* action); - -protected: - bool eventFilter(QObject* obj, QEvent* e) override; - // 根据情况重置tabbar的宽度,主要针对wps模式 - int calcMinTabBarWidth() const; - // 更新 - void updateCategoryTitleToTabName(); - // 告知WindowButtonGroup的尺寸 - void setWindowButtonGroupSize(const QSize& s); -protected Q_SLOTS: - void onWindowTitleChanged(const QString& title); - void onWindowIconChanged(const QIcon& i); - void onCategoryWindowTitleChanged(const QString& title); - void onStackWidgetHided(); - virtual void onCurrentRibbonTabChanged(int index); - virtual void onCurrentRibbonTabClicked(int index); - virtual void onCurrentRibbonTabDoubleClicked(int index); - void onContextsCategoryPageAdded(SARibbonCategory* category); - void onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title); - void onTabMoved(int from, int to); - -private: - int tabIndex(SARibbonCategory* obj); - void resizeAll(); - void resizeInLooseStyle(); - void resizeInCompactStyle(); - void paintInLooseStyle(); - void paintInCompactStyle(); - void resizeStackedContainerWidget(); - - // 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 - void updateContextCategoryManagerData(); - void synchronousCategoryData(bool autoUpdate = true); - -protected: - virtual void paintEvent(QPaintEvent* e) override; - virtual void resizeEvent(QResizeEvent* e) override; - virtual void moveEvent(QMoveEvent* e) override; - virtual void changeEvent(QEvent* e) override; - virtual bool event(QEvent* e) override; - virtual void paintTabbarBaseLine(QPainter& painter); - virtual void paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion); - virtual void - paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color); -#if SA_DEBUG_PRINT_SARIBBONBAR - SA_RIBBON_EXPORT friend QDebug operator<<(QDebug debug, const SARibbonBar& ribbon); -#endif -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(SARibbonBar::RibbonStyles) - -#endif // SARIBBONBAR_H - -/*** End of inlined file: SARibbonBar.h ***/ - -/*** Start of inlined file: SARibbonElementFactory.h ***/ -#ifndef SARIBBONELEMENTFACTORY_H -#define SARIBBONELEMENTFACTORY_H - -#include -#include -#include -#include - -class QWidget; -class SARibbonBar; -class SARibbonTabBar; -class SARibbonApplicationButton; -class SARibbonCategory; -class SARibbonContextCategory; -class SARibbonPannel; -class SARibbonSeparatorWidget; -class SARibbonGallery; -class SARibbonGalleryGroup; -class SARibbonToolButton; -class SARibbonControlButton; -class SARibbonControlToolButton; -class SARibbonButtonGroupWidget; -class SARibbonStackedWidget; -class SARibbonQuickAccessBar; -class SARibbonPannelOptionButton; -class SARibbonSystemButtonBar; - -/// -/// \brief SARibbon的子元素创建的工厂,SARibbon内部创建子元素都通过SARibbonElementFactory来创建 -/// -/// 由于SARibbonBar是一个复合控件,很多子窗口组合而成,有些部件的创建如果想继承,那么就需要这个工厂类来处理 -/// 如SARibbonCategory,可以重载此类的createRibbonCategory,返回重载的类的实例 -/// -class SA_RIBBON_EXPORT SARibbonElementFactory -{ -public: - SARibbonElementFactory(); - virtual ~SARibbonElementFactory(); - virtual SARibbonBar* createRibbonBar(QWidget* parent); - virtual SARibbonTabBar* createRibbonTabBar(QWidget* parent); - virtual SARibbonApplicationButton* createRibbonApplicationButton(QWidget* parent); - virtual SARibbonCategory* createRibbonCategory(QWidget* parent); - virtual SARibbonContextCategory* createRibbonContextCategory(QWidget* parent); - virtual SARibbonPannel* createRibbonPannel(QWidget* parent); - virtual SARibbonSeparatorWidget* createRibbonSeparatorWidget(QWidget* parent); - virtual SARibbonGallery* createRibbonGallery(QWidget* parent); - virtual SARibbonGalleryGroup* createRibbonGalleryGroup(QWidget* parent); - virtual SARibbonToolButton* createRibbonToolButton(QWidget* parent); - virtual SARibbonControlButton* createRibbonControlButton(QWidget* parent); - virtual SARibbonControlToolButton* createRibbonControlToolButton(QWidget* parent); - virtual SARibbonStackedWidget* createRibbonStackedWidget(SARibbonBar* parent); - virtual SARibbonButtonGroupWidget* craeteButtonGroupWidget(QWidget* parent); - virtual SARibbonQuickAccessBar* createQuickAccessBar(QWidget* parent); - virtual SARibbonSystemButtonBar* createWindowButtonGroup(QWidget* parent); - virtual SARibbonPannelOptionButton* createRibbonPannelOptionButton(SARibbonPannel* pannel); -}; - -#endif // SARIBBONELEMENTFACTORY_H - -/*** End of inlined file: SARibbonElementFactory.h ***/ - -/*** Start of inlined file: SARibbonElementManager.h ***/ -#ifndef SARIBBONELEMENTMANAGER_H -#define SARIBBONELEMENTMANAGER_H - -/** - @brief 此类是一个全局单例,用于管理SARibbonElementFactory - - 如果你有自己的某个部件要重写,首先你需要有自己的ElementFactory - @code - class MyRibbonElementFactory : public SARibbonElementFactory{ - public: - ... - virtual SARibbonPannel* createRibbonPannel(QWidget* parent){ - return new MyRibbonPannel(parent); - } - }; - @endcode - - 然后,你需要在ribbonbar创建之前把自己的ElementFactory设置进去 - - 这个一般会在main函数中进行 - - @code - SARibbonElementManager::instance()->setupFactory(new MyRibbonElementFactory); - @endcode - - 这样,SARibbon创建的pannel就是你自己重写的MyRibbonPannel - - */ -class SA_RIBBON_EXPORT SARibbonElementManager -{ -protected: - SARibbonElementManager(); - -public: - virtual ~SARibbonElementManager(); - static SARibbonElementManager* instance(); - SARibbonElementFactory* factory(); - void setupFactory(SARibbonElementFactory* fac); - -private: - QScopedPointer< SARibbonElementFactory > mFactory; -}; -#ifndef RibbonSubElementMgr -#define RibbonSubElementMgr SARibbonElementManager::instance() -#endif -#ifndef RibbonSubElementFactory -#define RibbonSubElementFactory SARibbonElementManager::instance()->factory() -#endif - -#endif // SARIBBONELEMENTMANAGER_H - -/*** End of inlined file: SARibbonElementManager.h ***/ - -/*** Start of inlined file: SARibbonCustomizeData.h ***/ -#ifndef SARIBBONCUSTOMIZEDATA_H -#define SARIBBONCUSTOMIZEDATA_H - -#include -class SARibbonBar; -class SARibbonMainWindow; - -/** - * @brief 记录所有自定义操作的数据类 - * @note 此数据依赖于@ref SARibbonActionsManager 要在SARibbonActionsManager之后使用此类 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeData -{ -public: - enum ActionType - { - UnknowActionType = 0, ///< 未知操作 - AddCategoryActionType, ///< 添加category操作(1) - AddPannelActionType, ///< 添加pannel操作(2) - AddActionActionType, ///< 添加action操作(3) - RemoveCategoryActionType, ///< 删除category操作(4) - RemovePannelActionType, ///< 删除pannel操作(5) - RemoveActionActionType, ///< 删除action操作(6) - ChangeCategoryOrderActionType, ///< 改变category顺序的操作(7) - ChangePannelOrderActionType, ///< 改变pannel顺序的操作(8) - ChangeActionOrderActionType, ///< 改变action顺序的操作(9) - RenameCategoryActionType, ///< 对category更名操作(10) - RenamePannelActionType, ///< 对Pannel更名操作(11) - VisibleCategoryActionType ///< 对category执行隐藏/显示操作(12) - }; - SARibbonCustomizeData(); - SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr = nullptr); - // 获取CustomizeData的action type - ActionType actionType() const; - - // 设置CustomizeData的action type - void setActionType(ActionType a); - - // 判断是否是一个正常的CustomizeData - bool isValid() const; - - // 应用SARibbonCustomizeData - bool apply(SARibbonBar* bar) const; - - // 获取actionmanager指针 - SARibbonActionsManager* actionManager(); - - // 设置ActionsManager - void setActionsManager(SARibbonActionsManager* mgr); - - // 对应AddCategoryActionType - static SARibbonCustomizeData makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName); - - // 对应AddPannelActionType - static SARibbonCustomizeData - makeAddPannelCustomizeData(const QString& title, int index, const QString& categoryobjName, const QString& objName); - - // 对应AddActionActionType - static SARibbonCustomizeData makeAddActionCustomizeData(const QString& key, - SARibbonActionsManager* mgr, - SARibbonPannelItem::RowProportion rp, - const QString& categoryObjName, - const QString& pannelObjName); - - // 对应RenameCategoryActionType - static SARibbonCustomizeData makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName); - - // 对应RenamePannelActionType - static SARibbonCustomizeData - makeRenamePannelCustomizeData(const QString& newname, const QString& categoryobjName, const QString& pannelObjName); - - // 对应RemoveCategoryActionType - static SARibbonCustomizeData makeRemoveCategoryCustomizeData(const QString& categoryobjName); - - // 对应ChangeCategoryOrderActionType - static SARibbonCustomizeData makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex); - - // 对应ChangePannelOrderActionType - static SARibbonCustomizeData - makeChangePannelOrderCustomizeData(const QString& categoryobjName, const QString& pannelObjName, int moveindex); - - // 对应ChangeActionOrderActionType - static SARibbonCustomizeData makeChangeActionOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr, - int moveindex); - - // 对应RemovePannelActionType - static SARibbonCustomizeData makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName); - - // 对应RemoveActionActionType - static SARibbonCustomizeData makeRemoveActionCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr); - - // 对应VisibleCategoryActionType - static SARibbonCustomizeData makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow); - - // 判断是否可以自定义,如果某个action不想被编辑,可以通过此函数设置 - static bool isCanCustomize(QObject* obj); - static void setCanCustomize(QObject* obj, bool canbe = true); - - // 对QList进行简化 - static QList< SARibbonCustomizeData > simplify(const QList< SARibbonCustomizeData >& csd); - -public: - /** - * @brief 记录顺序的参数 - * - * 在actionType==AddCategoryActionType时,此参数记录Category的insert位置, - * 在actionType==AddPannelActionType时,此参数记录pannel的insert位置, - * 在actionType==AddActionActionType时,此参数记录pannel的insert位置 - */ - int indexValue; - - /** - * @brief 记录标题、索引等参数 - * - * 在actionType==AddCategoryActionType时,key为category标题, - * 在actionType==AddPannelActionType时,key为pannel标题, - * 在actionType==AddActionActionType时,key为action的查询依据,基于SARibbonActionsManager::action查询 - */ - QString keyValue; - - /** - * @brief 记录categoryObjName,用于定位Category - */ - QString categoryObjNameValue; - - /** - * @brief 记录pannelObjName,saribbon的Customize索引大部分基于objname - */ - QString pannelObjNameValue; - - SARibbonPannelItem::RowProportion actionRowProportionValue; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion -private: - ActionType mType; ///< 标记这个data是category还是pannel亦或是action - SARibbonActionsManager* mActionsManagerPointer; -}; -Q_DECLARE_METATYPE(SARibbonCustomizeData) - -typedef QList< SARibbonCustomizeData > SARibbonCustomizeDataList; - -#endif // SARIBBONCUSTOMIZEDATA_H - -/*** End of inlined file: SARibbonCustomizeData.h ***/ - -/*** Start of inlined file: SARibbonCustomizeWidget.h ***/ -#ifndef SARIBBONCUSTOMIZEWIDGET_H -#define SARIBBONCUSTOMIZEWIDGET_H - -#include - -// SARibbonCustomizeWidget 特有 -class SARibbonCustomizeWidgetUi; -class SARibbonMainWindow; -class SARibbonBar; -// -class QStandardItemModel; -class QStandardItem; -class QAbstractButton; -// -class QXmlStreamWriter; -class QXmlStreamReader; - -/** - * @brief 自定义界面窗口 - * - * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 - * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeWidget::fromXml 同步配置文件,这样再次修改后的配置文件就一致 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeWidget : public QWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCustomizeWidget) -public: - // 保留接口 - explicit SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, - QWidget* parent = nullptr, - Qt::WindowFlags f = Qt::WindowFlags()); - // 对于不使用SARibbonMainWindow的情况,使用此构造函数 - explicit SARibbonCustomizeWidget(SARibbonBar* ribbonbar, - QWidget* parent = nullptr, - Qt::WindowFlags f = Qt::WindowFlags()); - ~SARibbonCustomizeWidget(); - - /** - * @brief 定义ribbon树的显示类型 - */ - enum RibbonTreeShowType - { - ShowAllCategory, ///< 显示所有Category,包括contextcategory - ShowMainCategory ///< 显示主要的category,不包含上下文 - }; - - /** - * @brief QStandardItem对应的role - */ - enum ItemRole - { - LevelRole = Qt::UserRole + 1, ///< 代表这是层级,有0:category 1:pannel 2:item - PointerRole = Qt::UserRole + 2, ///< 代表这是存放指针。根据LevelRole来进行转 - CanCustomizeRole = Qt::UserRole + 3, ///< 代表个item是可以自定义的.bool - CustomizeRole = Qt::UserRole + 4, ///< 代表这个是自定义的item,bool,主要用于那些自己添加的标签和pannel,有此角色必有CanCustomizeRole - CustomizeObjNameRole = Qt::UserRole + 5 ///< 记录了临时的自定义内容的obj名 QString - }; - - // 设置action管理器 - void setupActionsManager(SARibbonActionsManager* mgr); - - // 判断用户是否有要存储的内容,对应save动作 - bool isApplied() const; - - // 判断用户是否有改动内容,对应apply动作 - bool isCached() const; - - // 获取model - const QStandardItemModel* model() const; - - // 根据当前的radiobutton选项来更新model - void updateModel(); - - // 更新model - void updateModel(RibbonTreeShowType type); - - // 应用所有的设定 - bool applys(); - - // 转换为xml - bool toXml(QXmlStreamWriter* xml) const; - bool toXml(const QString& xmlpath) const; - - // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - void fromXml(QXmlStreamReader* xml); - void fromXml(const QString& xmlpath); - - // 应用xml配置,可以结合customize_datas_from_xml和customize_datas_apply函数 - static bool fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr); - - // 缓存应用的动作,这些动作不会被clear清除,用于本地存储 - void makeActionsApplied(); - - // 清除applied的动作,cancel操作后需要清空已应用的动作 - void clearApplied(); - // 清除缓存动作,在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 - void clearCache(); - // 清除所有动作,不包含本地读取的数据 - void clear(); - -protected: - // 把QList进行裁剪,把一些动作合并 - void simplify(); - - SARibbonPannelItem::RowProportion selectedRowProportion() const; - - QAction* selectedAction() const; - QAction* itemToAction(QStandardItem* item) const; - - QStandardItem* selectedItem() const; - - // 获取选中的ribbon tree 的level - int selectedRibbonLevel() const; - - // 根据选中的item判断 - int itemLevel(QStandardItem* item) const; - - // 设置某个item被选中 - void setSelectItem(QStandardItem* item, bool ensureVisible = true); - - // 判断itemn能否改动,可以改动返回true - bool isItemCanCustomize(QStandardItem* item) const; - bool isSelectedItemCanCustomize() const; - - // 判断item是否是自定义的item - bool isCustomizeItem(QStandardItem* item) const; - bool isSelectedItemIsCustomize() const; - - // 删除一个item - void removeItem(QStandardItem* item); - -private Q_SLOTS: - void onComboBoxActionIndexCurrentIndexChanged(int index); - void onRadioButtonGroupButtonClicked(QAbstractButton* b); - void onPushButtonNewCategoryClicked(); - void onPushButtonNewPannelClicked(); - void onPushButtonRenameClicked(); - void onPushButtonAddClicked(); - void onPushButtonDeleteClicked(); - void onListViewSelectClicked(const QModelIndex& index); - void onTreeViewResultClicked(const QModelIndex& index); - void onToolButtonUpClicked(); - void onToolButtonDownClicked(); - void onItemChanged(QStandardItem* item); - void onLineEditSearchActionTextEdited(const QString& text); - void onPushButtonResetClicked(); - -private: - void init(SARibbonBar* ribbonbar); - void initConnection(); - -private: - SARibbonCustomizeWidgetUi* ui; -}; - -/** - * @brief 转换为xml - * - * 此函数仅会写element,不会写document相关内容,因此如果需要写document, - * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() - * @param xml QXmlStreamWriter指针 - * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); - * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, - * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: - * @param cds 基于QList生成的步骤 - * @return 如果出现异常,返回false,如果没有自定义数据也会返回false - */ -bool SA_RIBBON_EXPORT sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds); - -/** - * @brief 通过xml获取QList - * @param xml - * @return QList - */ -QList< SARibbonCustomizeData > SA_RIBBON_EXPORT sa_customize_datas_from_xml(QXmlStreamReader* xml, - SARibbonActionsManager* mgr); - -/** - * @brief 应用QList - * @param cds - * @param w SARibbonBar指针 - * @return 成功应用的个数 - */ -int SA_RIBBON_EXPORT sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); - -/** - * @brief 反向取消应用 - * @param cds - * @param w SARibbonBar指针 - * @return 成功应用的个数 - */ -int SA_RIBBON_EXPORT sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); - -/** - * @brief 直接加载xml自定义ribbon配置文件用于ribbon的自定义显示 - * @param filePath xml配置文件 - * @param w 主窗体 - * @param mgr action管理器 - * @return 成功返回true - * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: - * @code - * static bool has_call = false; - * if (!has_call) { - * has_call = sa_apply_customize_from_xml_file("customize.xml", this, m_actMgr); - * } - * @endcode - */ -bool SA_RIBBON_EXPORT sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr); - -#endif // SARIBBONCUSTOMIZEWIDGET_H - -/*** End of inlined file: SARibbonCustomizeWidget.h ***/ - -/*** Start of inlined file: SARibbonCustomizeDialog.h ***/ -#ifndef SARIBBONCUSTOMIZEDIALOG_H -#define SARIBBONCUSTOMIZEDIALOG_H - -#include - -class SARibbonActionsManager; -class SARibbonCustomizeDialogUi; -class QXmlStreamWriter; - -/** - * @brief SARibbonCustomizeWidget的对话框封装 - * - * 此功能依赖于@ref SARibbonActionsManager ,SARibbonActionsManager建议作为mianwindow的成员变量, - * SARibbonActionsManager可以快速绑定所有QAction,详细见SARibbonActionsManager的说明 - * - * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 - * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeDialog::fromXml 同步配置文件,这样再次修改后的配置文件就一致 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeDialog : public QDialog -{ - Q_OBJECT -public: - explicit SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, - QWidget* p = nullptr, - Qt::WindowFlags f = Qt::WindowFlags()); - ~SARibbonCustomizeDialog(); - // 设置action管理器 - void setupActionsManager(SARibbonActionsManager* mgr); - - // 判断用户是否有要存储的内容,对应save动作 - bool isApplied() const; - - // 判断用户是否有改动内容,对应apply动作 - bool isCached() const; - - // 应用所有的设定 - bool applys(); - - // 清除所有动作 - void clear(); - - // 转换为xml - bool toXml(QXmlStreamWriter* xml) const; - bool toXml(const QString& xmlpath) const; - - // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - void fromXml(QXmlStreamReader* xml); - void fromXml(const QString& xmlpath); - - // 返回SARibbonCustomizeWidget窗口指针 - SARibbonCustomizeWidget* customizeWidget() const; - -private: - void initConnection(); - - SARibbonCustomizeDialogUi* ui; -}; - -#endif // SARIBBONCUSTOMIZEDIALOG_H - -/*** End of inlined file: SARibbonCustomizeDialog.h ***/ - -/*** Start of inlined file: SARibbonMainWindow.h ***/ -#ifndef SARIBBONMAINWINDOW_H -#define SARIBBONMAINWINDOW_H - -#include - -#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER -class SAFramelessHelper; -#endif -class QAction; -class SARibbonBar; -class SARibbonSystemButtonBar; -class QScreen; -/** - * @brief 如果要使用SARibbonBar,必须使用此类代替QMainWindow - * - * 由于ribbon的风格和传统的Toolbar风格差异较大, - * SARibbonBar使用需要把原有的QMainWindow替换为SARibbonMainWindow, - * SARibbonMainWindow是个无边框窗体,继承自QMainWindow(目前使用第三方的无边框方案https://github.com/wangwenx190/framelesshelper), - * 其构造函数的参数useRibbon用于指定是否使用ribbon风格,默认为true - * - * @code - * SARibbonMainWindow(QWidget* parent = nullptr,bool useRibbon = true); - * @endcode - * - * 如果想换回非ribbon风格,只需要把useRibbon设置为false即可, - * 成员函数isUseRibbon用于判断当前是否为ribbon模式,这个函数在兼容传统Toolbar风格和ribbon风格时非常有用 - * - * 但这个不支持动态切换,因此,实际工程中,你若要进行切换,需要写配置文件,程序在构造时传入模式, - * 并且,你的程序要做好判断,因为非Ribbon模式下,所有Ribbon相关的接口都会返回空指针 - * - * @code - * bool isUseRibbon() const; - * @endcode - * - * @ref SARibbonMainWindow 提供了几种常用的ribbon样式 - * 通过@ref setRibbonTheme 可改变ribbon的样式,用户也可通过qss自己定义自己的样式 - * - * 如果你自己继承了SARibbonBar,你可以通过@ref setRibbonBar 设置自己的ribbonbar进去 - * - * 另外有个一个更加高效的方法,来实现自定义的Ribbon,就是继承一个@ref SARibbonElementFactory - * - * @code - * class MyRibbonFactory:public SARibbonElementFactory{ - * ... - * virtual SARibbonBar* createRibbonBar(QWidget* parent){ - * return new MyRibbonBar(parent); - * } - * }; - * @endcode - * - * SARibbonMainWindow生成之前(一般在main函数),设置元件工厂: - * - * @code - * SARibbonElementManager::instance()->setupFactory(new MyRibbonFactory()); - * @endcode - * - * 此时,Ribbon的元素会通过MyRibbonFactory的接口来生成 - */ -class SA_RIBBON_EXPORT SARibbonMainWindow : public QMainWindow -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonMainWindow) - friend class SARibbonBar; - Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) - -public: - explicit SARibbonMainWindow(QWidget* parent = nullptr, bool useRibbon = true, const Qt::WindowFlags flags = {}); - ~SARibbonMainWindow() override; - // 返回SARibbonBar - SARibbonBar* ribbonBar() const; - // 设置ribbonbar - void setRibbonBar(SARibbonBar* ribbon); -#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER - // 返回SAFramelessHelper - SAFramelessHelper* framelessHelper(); -#else - // 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 - void setFramelessHitTestVisible(QWidget* w, bool visible = true); -#endif - // 此函数仅用于控制最小最大化和关闭按钮的显示 - void updateWindowFlag(Qt::WindowFlags flags); - - // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); - void setRibbonTheme(SARibbonTheme theme); - SARibbonTheme ribbonTheme() const; - // 判断当前是否使用ribbon模式 - bool isUseRibbon() const; - // 把ribbonbar的事件传递到frameless - virtual bool eventFilter(QObject* obj, QEvent* e) Q_DECL_OVERRIDE; - // 获取最大化,最小化,关闭按钮所在的bar。可以通过此函数在最大最小化按钮旁边设置内容 - SARibbonSystemButtonBar* windowButtonBar() const; - -protected: - // 创建ribbonbar的工厂函数 - SARibbonBar* createRibbonBar(); -private Q_SLOTS: - void onPrimaryScreenChanged(QScreen* screen); -}; - -/** - * @brief 针对SARibbonMainWindow的事件处理器,主要处理systembar的位置调整 - */ -class SA_RIBBON_EXPORT SARibbonMainWindowEventFilter : public QObject -{ - Q_OBJECT -public: - explicit SARibbonMainWindowEventFilter(QObject* par); - ~SARibbonMainWindowEventFilter(); - virtual bool eventFilter(QObject* obj, QEvent* e) override; -}; - -#endif // SARIBBONMAINWINDOW_H - -/*** End of inlined file: SARibbonMainWindow.h ***/ - -/*** Start of inlined file: SARibbonWidget.h ***/ -#ifndef SARIBBONWIDGET_H -#define SARIBBONWIDGET_H - -#include - -class SARibbonBar; -class QScreen; -/** - * @brief 带SARibbonBar的Widget - * - * @note 注意,SARibbonWidget利用了布局来把ribbonbar放到最顶部,不要给此widget添加布局,否则会导致布局异常, - * 正确的做法是把布局的窗口通过setWidget设置进SARibbonWidget - * - */ -class SA_RIBBON_EXPORT SARibbonWidget : public QWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonWidget) - friend class SARibbonBar; - Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) - -public: - explicit SARibbonWidget(QWidget* parent = nullptr); - ~SARibbonWidget(); - // 返回SARibbonBar - SARibbonBar* ribbonBar() const; - // 设置ribbonbar - void setRibbonBar(SARibbonBar* bar); - - // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonTheme::RibbonThemeDark); }); - void setRibbonTheme(SARibbonTheme theme); - SARibbonTheme ribbonTheme() const; - // 判断当前是否使用ribbon模式 - bool isUseRibbon() const; - // 设置窗口 - void setWidget(QWidget* w); - QWidget* widget() const; - // - QWidget* takeWidget(); -private Q_SLOTS: - void onPrimaryScreenChanged(QScreen* screen); -}; - -#endif // SARIBBONWIDGET_H - -/*** End of inlined file: SARibbonWidget.h ***/ - -/*** End of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ - -#endif +#ifndef SA_RIBBON_H +#define SA_RIBBON_H +// 定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif +// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +/*** Start of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ +// Global + +/*** Start of inlined file: SAColorWidgetsGlobal.h ***/ +#ifndef SACOLORWIDGETSGLOBAL_H +#define SACOLORWIDGETSGLOBAL_H +#include + +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MAJ +#define SA_COLOR_WIDGETS_VERSION_MAJ 0 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MIN +#define SA_COLOR_WIDGETS_VERSION_MIN 1 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_PAT +#define SA_COLOR_WIDGETS_VERSION_PAT 0 +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PRIVATE +#define SA_COLOR_WIDGETS_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + QScopedPointer< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PUBLIC +#define SA_COLOR_WIDGETS_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; +#endif + +#ifndef SA_COLOR_WIDGETS_NO_DLL +#if defined(SA_COLOR_WIDGETS_MAKE_LIB) // 定义此宏将构建library +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_EXPORT +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_IMPORT +#endif +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API +#endif +#endif + +#endif // SACOLORWIDGETSGLOBAL_H + +/*** End of inlined file: SAColorWidgetsGlobal.h ***/ + +/*** Start of inlined file: SARibbonBarVersionInfo.h ***/ +#ifndef SARIBBONVERSIONINFO_H +#define SARIBBONVERSIONINFO_H +/** + * @file 此文档由cmake根据SARibbonBarVersionInfo.h.in自动生成,任何在此文件上的改动都将覆写 + */ + +/** + * @def ribbon的数字版本 {MAJ}.MIN.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MAJ +#define SA_RIBBON_BAR_VERSION_MAJ 2 +#endif +/** + * @def ribbon的数字版本 MAJ.{MIN}.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MIN +#define SA_RIBBON_BAR_VERSION_MIN 2 +#endif +/** + * @def ribbon的数字版本 MAJ.MIN.{PAT} + */ +#ifndef SA_RIBBON_BAR_VERSION_PAT +#define SA_RIBBON_BAR_VERSION_PAT 5 +#endif + +/** + * @def 版本号(字符串) + */ +#ifndef SARIBBON_VERSION +#define SARIBBON_VERSION "2.2.5" +#endif + +#endif // SARIBBONVERSIONINFO_H + +/*** End of inlined file: SARibbonBarVersionInfo.h ***/ + +/*** Start of inlined file: SARibbonGlobal.h ***/ +#ifndef SARIBBONGLOBAL_H +#define SARIBBONGLOBAL_H +#include +#include +#include + +class QWidget; + +#ifndef SA_RIBBON_BAR_NO_EXPORT +#if defined(SA_RIBBON_BAR_MAKE_LIB) // 定义此宏将构建library +#define SA_RIBBON_EXPORT Q_DECL_EXPORT +#else +#define SA_RIBBON_EXPORT Q_DECL_IMPORT +#endif +#endif +#ifndef SA_RIBBON_EXPORT +#define SA_RIBBON_EXPORT +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_RIBBON_DECLARE_PRIVATE +#define SA_RIBBON_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + std::unique_ptr< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_RIBBON_DECLARE_PUBLIC +#define SA_RIBBON_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; \ + PrivateData(const PrivateData&) = delete; \ + PrivateData& operator=(const PrivateData&) = delete; +#endif + +/** + * @brief 定义Ribbon的对其方式,目前支持左对齐和居中对其 + * + * @note 如果你编译器提示:成员声明的限定名称非法,那么留意一下文件换行是否为LF,如果是把文件换行改为CRLF + */ +enum class SARibbonAlignment +{ + AlignLeft, ///< 左对齐,tab栏左对齐,同时category也是左对齐 + AlignCenter ///< 居中对其,tab栏居中对齐,同时category也是居中对齐 +}; + +/** + * @brief ribbon主题 + * + * 注意,由于有些qss的尺寸,在C++代码中无法获取到,因此针对用户自定义的qss主题,有些尺寸是需要手动设置进去的 + * + * 例如ribbon tab的margin信息,在QTabBar是无法获取到,而这个影响了SARibbonContextCategory的绘制, + * 因此,在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + */ +enum class SARibbonTheme +{ + RibbonThemeOffice2013, ///< office2013主题 + RibbonThemeOffice2016Blue, ///< office2016-蓝色主题 + RibbonThemeOffice2021Blue, ///< office2021-蓝色主题 + RibbonThemeWindows7, ///< win7主题 + RibbonThemeDark, ///< 暗色主题 + RibbonThemeDark2 +}; + +/** + * @def 属性,用于标记是否可以进行自定义,用于动态设置到@ref SARibbonCategory 和@ref SARibbonPannel + * 值为bool,在为true时,可以通过@ref SARibbonCustomizeWidget 改变这个SARibbonCategory和SARibbonPannel的布局, + * 默认不会有此属性,仅在有此属性且为true时才会在SARibbonCustomizeWidget中能显示为可设置 + */ +#ifndef SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE +#define SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE "_sa_isCanCustomize" +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.horizontalAdvance(str) +#endif +#else +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.width(str) +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPosition().toPoint() +#endif +#else +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPos() +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->position().toPoint() +#endif +#else +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->pos() +#endif +#endif + +/** + @def 定义此宏,将打印和尺寸刷新相关的信息 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SIZE_HINT +#define SA_DEBUG_PRINT_SIZE_HINT 0 +#endif + +/** + @def 定义此宏,将打印事件 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_EVENT +#define SA_DEBUG_PRINT_EVENT 0 +#endif + +/** + @def 定义此宏,qDebug将支持SARibbonBar的属性打印 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SARIBBONBAR +#define SA_DEBUG_PRINT_SARIBBONBAR 0 +#endif + +/** + * @brief 全局的设置ribbon theme函数 + * + * 之所以提供此函数,是因为在某些情况下,SARibbonBar不用在SARibbonMainWindow情况下的时候,也需要设置主题, + * 但主题设置是在SARibbonMainWindow下的为了能在非SARibbonMainWindow下也能设置主题,这里提供@sa sa_set_ribbon_theme函数, + * 可以这样使用: + * @code + * @endcode + * + * @param w + * @param theme + */ +void SA_RIBBON_EXPORT sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme); + +#endif // SARIBBONGLOBAL_H + +/*** End of inlined file: SARibbonGlobal.h ***/ + +// color widget + +/*** Start of inlined file: SAColorMenu.h ***/ +#ifndef SACOLORMENU_H +#define SACOLORMENU_H +#include + +class QWidgetAction; +class SAColorPaletteGridWidget; +class SAColorGridWidget; +class SAColorToolButton; +/** + * @brief 标准颜色菜单 + */ +class SA_COLOR_WIDGETS_API SAColorMenu : public QMenu +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorMenu) +public: + explicit SAColorMenu(QWidget* parent = nullptr); + explicit SAColorMenu(const QString& title, QWidget* parent = nullptr); + ~SAColorMenu(); + // 快速绑定colorbtn + void bindToColorToolButton(SAColorToolButton* btn); + // ThemeColorsPalette对应的action + QWidgetAction* themeColorsPaletteAction() const; + // CustomColorsWidget对应的action + QWidgetAction* getCustomColorsWidgetAction() const; + // 自定义颜色action + QAction* customColorAction() const; + // 获取ThemeColorsPalette + SAColorPaletteGridWidget* colorPaletteGridWidget() const; + // 获取自定义颜色grid + SAColorGridWidget* customColorsWidget() const; + // 构建无颜色action,默认无颜色action是没有的 + void enableNoneColorAction(bool on = true); + // 获取None Color Action,注意,enableNoneColorAction(true),之后才不是nullptr + QAction* noneColorAction() const; +public Q_SLOTS: + // 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + void emitSelectedColor(const QColor& c); +Q_SIGNALS: + /** + * @brief 选择了颜色触发的信号 + * @param c + */ + void selectedColor(const QColor& c); +private Q_SLOTS: + void onCustomColorActionTriggered(bool on); + void onNoneColorActionTriggered(bool on); + +private: + void init(const QList< QColor >& themeCls); +}; + +#endif // SACOLORMENU_H + +/*** End of inlined file: SAColorMenu.h ***/ + +/*** Start of inlined file: SAColorGridWidget.h ***/ +#ifndef SACOLORGRIDWIDGET_H +#define SACOLORGRIDWIDGET_H +#include +#include + +class QAbstractButton; +class SAColorToolButton; +/** + * @brief 一个grid的Color布局 + * + * 一个形如下面的颜色grid: + * + * □□□□□□□□□ + * + * □□□□□□□□□ + * + * □□□□□□□□□ + */ +class SA_COLOR_WIDGETS_API SAColorGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorGridWidget) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) +public: + using FunColorBtn = std::function< void(SAColorToolButton*) >; + +public: + SAColorGridWidget(QWidget* par = nullptr); + ~SAColorGridWidget(); + // 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + void setColumnCount(int c); + int columnCount() const; + // 设置当前的颜色列表 + void setColorList(const QList< QColor >& cls); + QList< QColor > getColorList() const; + // 间隔 + int spacing() const; + void setSpacing(int v); + // 获取颜色的数量 + int colorCount() const; + // 图标的尺寸 + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; + // 获取当前选中的颜色 + QColor currentCheckedColor() const; + // 获取index对于的colorbutton + SAColorToolButton* colorButton(int index) const; + // 垂直间距 + void setVerticalSpacing(int v); + int verticalSpacing() const; + // 水平间距 + void setHorizontalSpacing(int v); + int horizontalSpacing() const; + // 清除当前选中状态,这时没有颜色是选中的 + void clearCheckedState(); + // 对所有的colorbtn就行遍历处理,可以通过此函数进行tooltip设置等操作 + void iterationColorBtns(FunColorBtn fn); + // 设置行最小高度 + void setRowMinimumHeight(int row, int minSize); + // 让颜色块左对齐 + void setHorizontalSpacerToRight(bool on = true); +private Q_SLOTS: + void onButtonClicked(QAbstractButton* btn); + void onButtonPressed(QAbstractButton* btn); + void onButtonReleased(QAbstractButton* btn); + void onButtonToggled(QAbstractButton* btn, bool on); + +Q_SIGNALS: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); + void colorPressed(const QColor& c); + void colorReleased(const QColor& c); + void colorToggled(const QColor& c, bool on); + +public: + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +}; +namespace SA +{ +/** + * @brief 获取标准色列表(一共10种颜色) + * @return + */ +SA_COLOR_WIDGETS_API QList< QColor > getStandardColorList(); +} +#endif // SACOLORGRIDWIDGET_H + +/*** End of inlined file: SAColorGridWidget.h ***/ + +/*** Start of inlined file: SAColorPaletteGridWidget.h ***/ +#ifndef SACOLORPALETTEGRIDWIDGET_H +#define SACOLORPALETTEGRIDWIDGET_H +#include + +class QMenu; +class SAColorToolButton; +/** + * @brief 类似office的颜色选择窗口,有一排标准色,下面有一个颜色板,有3行浅色,有2行深色 + */ +class SA_COLOR_WIDGETS_API SAColorPaletteGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorPaletteGridWidget) +public: + SAColorPaletteGridWidget(QWidget* par = nullptr); + SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par = nullptr); + ~SAColorPaletteGridWidget(); + // 设置窗口维护的colorList + void setColorList(const QList< QColor >& cls); + QList< QColor > colorList() const; + // 设置颜色深浅比例factor,默认为{ 180, 160, 140, 75, 50 } + void setFactor(const QList< int >& factor); + QList< int > factor() const; + // 设置iconsize + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; +private Q_SLOTS: + void onMainColorClicked(const QColor& c); + void onPaletteColorClicked(const QColor& c); + +private: + void init(); +Q_SIGNALS: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); +}; + +#endif // SACOLORPALETTEGRIDWIDGET_H + +/*** End of inlined file: SAColorPaletteGridWidget.h ***/ + +/*** Start of inlined file: SAColorToolButton.h ***/ +#ifndef SACOLORTOOLBUTTON_H +#define SACOLORTOOLBUTTON_H +#include + +class QPaintEvent; +class QResizeEvent; +class QPainter; +class QStylePainter; +class SAColorMenu; +/** + * @brief 这是一个只显示颜色的toolbutton + * + * + * 在ToolButtonIconOnly模式下,如果没有setIcon,则颜色占用所有区域,如下图所示 + * + * ┌─────┐ + * │color│ + * └─────┘ + * + * 如果在ToolButtonIconOnly模式下有图标,图标在上面显示,下面显示颜色,如下图所示 + * + * ┌─────┐ + * │icon │ + * │color│ + * └─────┘ + * + * 在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下,setIconSize 可以指定颜色的大小, + * 但只在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下有效 + * + * 如果没有设置图标,也就是setIcon(QIcon()),iconSize作为颜色块的大小 + * + * ┌─────────┐ + * │┌─┐ │ + * │└─┘ │ + * └─────────┘ + * + * 如果有图标,颜色条会在图标下方,为图标高度的1/4 为图标宽度一致,如若超过控件的大小,会自动缩小体积 + * + */ +class SA_COLOR_WIDGETS_API SAColorToolButton : public QToolButton +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorToolButton) +public: + /** + * @brief 颜色按钮的内置样式 + * @param parent + * @return + */ + enum ColorToolButtonStyle + { + WithColorMenu, ///< 默认会构建一个SAColorMenu + NoColorMenu ///< 没有ColorMenu + }; + +public: + explicit SAColorToolButton(QWidget* parent = nullptr); + explicit SAColorToolButton(ColorToolButtonStyle style, QWidget* parent = nullptr); + ~SAColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置Margins + void setMargins(const QMargins& mg); + QMargins margins() const; + // 绘制无颜色 + static void paintNoneColor(QPainter* p, const QRect& colorRect); + // 设置颜色按钮的样式 + void setColorToolButtonStyle(ColorToolButtonStyle s); + ColorToolButtonStyle colorToolButtonStyle() const; + // 获取colorMenu,注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr + SAColorMenu* colorMenu() const; + // 建立标准的颜色菜单 + SAColorMenu* createColorMenu(); +public Q_SLOTS: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); + +protected: + // 获取关键的三个rect位置 + virtual void calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + virtual void paintButton(QStylePainter* p, const QStyleOptionToolButton& opt); + virtual void paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt); + virtual void paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt); + virtual void paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +private Q_SLOTS: + void onButtonClicked(bool checked = false); +Q_SIGNALS: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); +}; + +#endif // SACOLORTOOLBUTTON_H + +/*** End of inlined file: SAColorToolButton.h ***/ + +// sa ribbon + +/*** Start of inlined file: SAFramelessHelper.h ***/ +#ifndef SAFRAMELESSHELPER_H +#define SAFRAMELESSHELPER_H + +#include + +class QWidget; + +class SA_RIBBON_EXPORT SAFramelessHelper : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SAFramelessHelper) + friend class SAPrivateFramelessWidgetData; + +public: + explicit SAFramelessHelper(QObject* parent); + ~SAFramelessHelper(); + // 激活窗体 + void activateOn(QWidget* topLevelWidget); + + // 移除窗体 + void removeFrom(QWidget* topLevelWidget); + + // 设置窗体移动 + void setWidgetMovable(bool movable); + + // 设置窗体缩放 + void setWidgetResizable(bool resizable); + + // 设置橡皮筋移动 + void setRubberBandOnMove(bool movable); + + // 设置橡皮筋缩放 + void setRubberBandOnResize(bool resizable); + + // 设置边框的宽度 + void setBorderWidth(int width); + + // 设置标题栏高度 + void setTitleHeight(int height); + bool widgetResizable(); + bool widgetMovable(); + bool rubberBandOnMove(); + bool rubberBandOnResisze(); + uint borderWidth(); + uint titleHeight(); + +protected: + // 事件过滤,进行移动、缩放等 + virtual bool eventFilter(QObject* obj, QEvent* event); +}; + +#endif // FRAMELESSHELPER_H + +/*** End of inlined file: SAFramelessHelper.h ***/ + +/*** Start of inlined file: SARibbonApplicationButton.h ***/ +#ifndef SARIBBONAPPLICATIONBUTTON_H +#define SARIBBONAPPLICATIONBUTTON_H +#include + +/** + * @brief The SARibbonApplicationButton class + * + * 默认的plicationButton,可以通过样式指定不一样的ApplicationButton + */ +class SA_RIBBON_EXPORT SARibbonApplicationButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonApplicationButton(QWidget* parent = nullptr); + explicit SARibbonApplicationButton(const QString& text, QWidget* parent = nullptr); + explicit SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); + ~SARibbonApplicationButton(); +}; + +#endif // SARIBBONAPPLICATIONBUTTON_H + +/*** End of inlined file: SARibbonApplicationButton.h ***/ + +/*** Start of inlined file: SARibbonSystemButtonBar.h ***/ +#ifndef SARIBBONSYSTEMBUTTONBAR_H +#define SARIBBONSYSTEMBUTTONBAR_H + +#include +#include + +/** + * @brief 窗口的最大最小化按钮 + * @note 内部有个SARibbonButtonGroupWidget,其ObjectName = SASystemButtonGroup + * 如果需要qss,可以进行特化处理: + * + * @code + * SARibbonButtonGroupWidget#SASystemButtonGroup + * @endcode + * + */ +class SA_RIBBON_EXPORT SARibbonSystemButtonBar : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonSystemButtonBar) +public: + explicit SARibbonSystemButtonBar(QWidget* parent); + explicit SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags); + ~SARibbonSystemButtonBar(); + void setupMinimizeButton(bool on); + void setupMaximizeButton(bool on); + void setupCloseButton(bool on); + // 以最顶层的widget的WindowFlag作为WindowFlag + void updateWindowFlag(); + void updateWindowFlag(Qt::WindowFlags flags); + // 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 + void setButtonWidthStretch(int close = 4, int max = 3, int min = 3); + // 标题栏高度 + void setWindowTitleHeight(int h); + int windowTitleHeight() const; + // 设置标准系统按钮的宽度 + void setWindowButtonWidth(int w); + int windowButtonWidth() const; + // 设置Qt::WindowStates + void setWindowStates(Qt::WindowStates s); + // 仅获取按钮的状态 + Qt::WindowFlags windowButtonFlags() const; + // 三个标准系统窗口按钮 + QAbstractButton* minimizeButton() const; + QAbstractButton* maximizeButton() const; + QAbstractButton* closeButton() const; + + // 图标尺寸 + void setIconSize(const QSize& ic); + QSize iconSize() const; + // 生成并添加一个action + QAction* addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addMenu(QMenu* menu, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + QAction* addSeparator(); + QAction* addWidget(QWidget* w); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + // 捕获mainwindow的事件 + bool eventFilter(QObject* obj, QEvent* event) override; + +protected: + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + +protected Q_SLOTS: + Q_SLOT void closeWindow(); + Q_SLOT void minimizeWindow(); + Q_SLOT void maximizeWindow(); +}; + +/** + * @brief The SARibbonSystemToolButton class + */ +class SARibbonSystemToolButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonSystemToolButton(QWidget* p = nullptr); +}; + +#endif // SARIBBONSYSTEMBUTTONBAR_H + +/*** End of inlined file: SARibbonSystemButtonBar.h ***/ + +/*** Start of inlined file: SARibbonToolButton.h ***/ +#ifndef SARIBBONTOOLBUTTON_H +#define SARIBBONTOOLBUTTON_H + +#include +#include +/** + * @brief Ribbon界面适用的toolButton + * + * 相对于普通toolbutton,主要多了两个类型设置,@ref setButtonType 和 @ref setLargeButtonType + * + * @note @sa setIconSize 函数不在起作用,iconsize是根据当前尺寸动态调整的 + */ +class SA_RIBBON_EXPORT SARibbonToolButton : public QToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonToolButton) +public: + /** + * @brief 按钮样式 + */ + enum RibbonButtonType + { + LargeButton, + SmallButton + }; + Q_ENUM(RibbonButtonType) + +public: + explicit SARibbonToolButton(QWidget* parent = nullptr); + explicit SARibbonToolButton(QAction* defaultAction, QWidget* parent = nullptr); + ~SARibbonToolButton(); + // 标记按钮的样式,按钮的样式有不同的渲染方式 + RibbonButtonType buttonType() const; + void setButtonType(const RibbonButtonType& buttonType); + // 是否是小按钮 + bool isSmallRibbonButton() const; + // 是否是大按钮 + bool isLargeRibbonButton() const; + // 最小尺寸提示 + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + + // 获取间距 + int spacing() const; + // 更新尺寸 + void updateRect(); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + +public: + // 在lite模式下是否允许文字换行 + static void setEnableWordWrap(bool on); + static bool isEnableWordWrap(); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mousePressEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void focusOutEvent(QFocusEvent* e) Q_DECL_OVERRIDE; + virtual void leaveEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual bool hitButton(const QPoint& pos) const Q_DECL_OVERRIDE; + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + // 事件改变 - 主要为了捕获字体的改变 + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + +protected: + // 绘制按钮 + virtual void paintButton(QPainter& p, const QStyleOptionToolButton& opt); + // 绘制图标 + virtual void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); + // 绘制文本 + virtual void paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect); + // 绘制Indicator + virtual void paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect); + +private: + static void drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget = 0); + +protected: +}; + +namespace SA +{ +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt); +} +#endif // SARIBBONTOOLBUTTON_H + +/*** End of inlined file: SARibbonToolButton.h ***/ + +/*** Start of inlined file: SARibbonColorToolButton.h ***/ +#ifndef SARIBBONCOLORTOOLBUTTON_H +#define SARIBBONCOLORTOOLBUTTON_H + +class SAColorMenu; +/** + * @brief Refer to the color setting button in the office, which can display the color below the icon(参考office的颜色设置按钮,可以显示颜色在图标下方) + */ +class SA_RIBBON_EXPORT SARibbonColorToolButton : public SARibbonToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonColorToolButton) +public: + /** + * @brief 颜色样式 + */ + enum ColorStyle + { + ColorUnderIcon, ///< 颜色在icon下方,这个要求必须设置icon + ColorFillToIcon ///< 颜色作为icon,这个模式下在setColor会自动生成一个颜色icon替换掉原来的icon,因此setIcon函数没有作用 + }; + +public: + explicit SARibbonColorToolButton(QWidget* parent = nullptr); + explicit SARibbonColorToolButton(QAction* defaultAction, QWidget* parent = nullptr); + ~SARibbonColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置颜色显示方案 + void setColorStyle(ColorStyle s); + ColorStyle colorStyle() const; + // 建立标准的颜色菜单 + SAColorMenu* setupStandardColorMenu(); +public Q_SLOTS: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); +private Q_SLOTS: + void onButtonClicked(bool checked = false); +Q_SIGNALS: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); + +protected: + void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); +}; + +#endif // SARIBBONCOLORTOOLBUTTON_H + +/*** End of inlined file: SARibbonColorToolButton.h ***/ + +/*** Start of inlined file: SARibbonLineWidgetContainer.h ***/ +#ifndef SARIBBONLINEWIDGETCONTAINER_H +#define SARIBBONLINEWIDGETCONTAINER_H + +#include +#include +#include + +/** + * @brief 一个窗口容器,把窗口放置中间,前面后面都可以设置文本,主要用于放置在pannel上的小窗口 + * + * 实现如下效果: + * + * PrefixLabel|_Widget_|SuffixLabel + * + */ +class SA_RIBBON_EXPORT SARibbonLineWidgetContainer : public QWidget +{ +public: + explicit SARibbonLineWidgetContainer(QWidget* par = nullptr); + ~SARibbonLineWidgetContainer(); + // 设置widget,不允许设置一个nullptr + void setWidget(QWidget* innerWidget); + + // 设置前缀 + void setPrefix(const QString& str); + + // 设置后缀 + void setSuffix(const QString& str); + + // 前缀文本框 + QLabel* labelPrefix() const; + + // 后缀文本框 + QLabel* labelSuffix() const; + +private: + // 两个文本 + QLabel* m_labelPrefix; + QLabel* m_labelSuffix; + QWidget* m_innerWidget; +}; + +#endif // SARIBBONWIDGETCONTAINER_H + +/*** End of inlined file: SARibbonLineWidgetContainer.h ***/ + +/*** Start of inlined file: SARibbonActionsManager.h ***/ +#ifndef SARIBBONACTIONSMANAGER_H +#define SARIBBONACTIONSMANAGER_H + +#include +#include +#include +#include +#include +#include +class SARibbonBar; +class SARibbonCategory; + +/** + * @brief 用于管理SARibbon的所有Action + * + * SARibbonActionsManager维护着两个表,一个是tag(标签)对应的Action list, + * 一个是所有接受SARibbonActionsManager管理的action list。 + * + * SARibbonActionsManager的标签对应一组actions,每个标签对应的action可以重复出现, + * 但SARibbonActionsManager维护的action list里只有一份action,不会重复出现。 + * + * tag用于对action list分组,每个tag的实体名字通过@ref setTagName 进行设置,在语言变化时需要及时调用 + * setTagName设置新的标签对应的文本。 + * + * SARibbonActionsManager默认预设了6个常用标签见@ref SARibbonActionsManager::ActionTag ,用户自定义标签需要在 + * SARibbonActionsManager::UserDefineActionTag值的基础上进行累加。 + * + * @ref filter (等同@ref actions )函数用于提取标签管理的action list,@ref allActions 函数返回SARibbonActionsManager + * 管理的所有标签。 + * + * 通过@ref autoRegisteActions 函数可以快速的建立action的管理,此函数会遍历@ref SARibbonBar下所有@ref SARibbonPannel + * 添加的action,并给予Category建立tag,正常使用用户仅需关注此autoRegisteActions函数即可 + * + * + */ +class SA_RIBBON_EXPORT SARibbonActionsManager : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManager) + friend class SARibbonActionsManagerModel; + +public: + /** + * @brief 定义action的标签 + */ + enum ActionTag + { + UnknowActionTag = 0, ///< 未知的tag + CommonlyUsedActionTag = 0x01, ///< 预设tag-常用命令 + NotInFunctionalAreaActionTag = 0x02, ///< 预设tag-不在功能区命令 + AutoCategoryDistinguishBeginTag = 0x1000, ///< 自动按Category划分的标签起始,在@ref autoRegisteActions 函数会用到 + AutoCategoryDistinguishEndTag = 0x2000, ///< 自动按Category划分的标签结束,在@ref autoRegisteActions 函数会用到 + NotInRibbonCategoryTag = 0x2001, ///< 不在功能区的标签@ref autoRegisteActions 函数会遍历所有category的action + UserDefineActionTag = 0x8000 ///< 自定义标签,所有用户自定义tag要大于此tag + }; + explicit SARibbonActionsManager(SARibbonBar* bar); + ~SARibbonActionsManager(); + // 设置tag对应的名字 + void setTagName(int tag, const QString& name); + + // 获取tag对应的名字 + QString tagName(int tag) const; + + // 移除tag,注意,这个函数非常耗时 + void removeTag(int tag); + + // 注册action + bool registeAction(QAction* act, int tag, const QString& key = QString(), bool enableEmit = true); + + // 取消action的注册 + void unregisteAction(QAction* act, bool enableEmit = true); + + // 过滤得到actions对应的引用,实际是一个迭代器 + QList< QAction* >& filter(int tag); + + // 通过tag筛选出系列action + QList< QAction* >& actions(int tag); + const QList< QAction* > actions(int tag) const; + + // 获取所有的标签 + QList< int > actionTags() const; + + // 通过key获取action + QAction* action(const QString& key) const; + + // 通过action找到key + QString key(QAction* act) const; + + // 返回所有管理的action数 + int count() const; + + // 返回所有管理的actions + QList< QAction* > allActions() const; + + // 自动加载action,返回tag对应的Category指针 + QMap< int, SARibbonCategory* > autoRegisteActions(SARibbonBar* bar); + + // 自动加载widget下的actions函数返回的action,返回加载的数量,这些 + QSet< QAction* > autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit = false); + + // 根据标题查找action + QList< QAction* > search(const QString& text); + + // 清除 + void clear(); + +Q_SIGNALS: + + /** + * @brief 标签变化触发的信号,变化包括新增和删除 + */ + void actionTagChanged(int tag, bool isdelete); + +private Q_SLOTS: + void onActionDestroyed(QObject* o); + void onCategoryTitleChanged(const QString& title); + +private: + void removeAction(QAction* act, bool enableEmit = true); +}; + +/** + * @brief SARibbonActionsManager 对应的model + */ +class SA_RIBBON_EXPORT SARibbonActionsManagerModel : public QAbstractListModel +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManagerModel) +public: + explicit SARibbonActionsManagerModel(QObject* p = nullptr); + explicit SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p = nullptr); + ~SARibbonActionsManagerModel(); + virtual int rowCount(const QModelIndex& parent) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual QVariant data(const QModelIndex& index, int role) const override; + void setFilter(int tag); + void update(); + void setupActionsManager(SARibbonActionsManager* m); + void uninstallActionsManager(); + QAction* indexToAction(QModelIndex index) const; + void search(const QString& text); + +private Q_SLOTS: + void onActionTagChanged(int tag, bool isdelete); +}; + +#endif // SARIBBONACTIONSMANAGER_H + +/*** End of inlined file: SARibbonActionsManager.h ***/ + +/*** Start of inlined file: SARibbonLineEdit.h ***/ +#ifndef SARIBBONLINEEDIT_H +#define SARIBBONLINEEDIT_H + +#include + +/** + * @brief The SARibbonLineEdit class + */ +class SA_RIBBON_EXPORT SARibbonLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit SARibbonLineEdit(QWidget* parent = nullptr); + explicit SARibbonLineEdit(const QString& text, QWidget* parent = nullptr); + ~SARibbonLineEdit(); +}; + +#endif // SARIBBONLINEEDIT_H + +/*** End of inlined file: SARibbonLineEdit.h ***/ + +/*** Start of inlined file: SARibbonCheckBox.h ***/ +#ifndef SARIBBONCHECKBOX_H +#define SARIBBONCHECKBOX_H + +#include + +/** + * @brief The SARibbonCheckBox class + */ +class SA_RIBBON_EXPORT SARibbonCheckBox : public QCheckBox +{ + Q_OBJECT +public: + explicit SARibbonCheckBox(QWidget* parent = nullptr); + explicit SARibbonCheckBox(const QString& text, QWidget* parent = nullptr); + ~SARibbonCheckBox(); +}; + +#endif // SARIBBONCHECKBOX_H + +/*** End of inlined file: SARibbonCheckBox.h ***/ + +/*** Start of inlined file: SARibbonComboBox.h ***/ +#ifndef SARIBBONCOMBOBOX_H +#define SARIBBONCOMBOBOX_H + +#include + +/** + * @brief QComboBox的Ribbon显示,可以显示QIcon和windowTitle在左侧 + */ +class SA_RIBBON_EXPORT SARibbonComboBox : public QComboBox +{ + Q_OBJECT +public: + explicit SARibbonComboBox(QWidget* parent = nullptr); + ~SARibbonComboBox(); +}; + +#endif // SARIBBONCOMBOBOX_H + +/*** End of inlined file: SARibbonComboBox.h ***/ + +/*** Start of inlined file: SARibbonButtonGroupWidget.h ***/ +#ifndef SARIBBONBUTTONGROUPWIDGET_H +#define SARIBBONBUTTONGROUPWIDGET_H + +#include +#include +#include +class SARibbonControlButton; +/** + * @brief 用于管理一组Action,类似于QToolBar + */ +class SA_RIBBON_EXPORT SARibbonButtonGroupWidget : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonButtonGroupWidget) +public: + using FpButtonIterate = std::function< bool(SARibbonControlButton*) >; + +public: + explicit SARibbonButtonGroupWidget(QWidget* parent = nullptr); + ~SARibbonButtonGroupWidget(); + + // 图标尺寸 + void setIconSize(const QSize& ic); + QSize iconSize() const; + // 生成并添加一个action + QAction* addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addMenu(QMenu* menu, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + QAction* addSeparator(); + QAction* addWidget(QWidget* w); + // 从ButtonGroupWidget中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + SARibbonControlButton* actionToRibbonControlToolButton(QAction* action); + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + +public: + bool iterate(FpButtonIterate fp); +Q_SIGNALS: + + /** + * @brief 参考QToolBar::actionTriggered的信号 + * @param action + */ + void actionTriggered(QAction* action); + +protected: + virtual void actionEvent(QActionEvent* e) override; +}; + +#endif // SARIBBONBUTTONGROUPWIDGET_H + +/*** End of inlined file: SARibbonButtonGroupWidget.h ***/ + +/*** Start of inlined file: SARibbonStackedWidget.h ***/ +#ifndef SARIBBONSTACKEDWIDGET_H +#define SARIBBONSTACKEDWIDGET_H +#include + +class QHideEvent; +class QResizeEvent; + +/** + * @brief 有qdialog功能的stackwidget,用于在最小化时stack能像dialog那样弹出来 + */ +class SA_RIBBON_EXPORT SARibbonStackedWidget : public QStackedWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonStackedWidget) +public: + explicit SARibbonStackedWidget(QWidget* parent); + ~SARibbonStackedWidget(); + void setPopupMode(); + bool isPopupMode() const; + void setNormalMode(); + bool isNormalMode() const; + void exec(); + + /** + * @brief 类似tabbar的moveTab函数,交换两个窗口的index + * @param from + * @param to + * @note 此操作会触发widgetRemoved(int index)信号 + */ + void moveWidget(int from, int to); + +protected: + // void mouseReleaseEvent(QMouseEvent *e); + void hideEvent(QHideEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; +Q_SIGNALS: + /** + * @brief hidWindow + */ + void hidWindow(); +}; + +#endif // SARIBBONSTACKEDWIDGET_H + +/*** End of inlined file: SARibbonStackedWidget.h ***/ + +/*** Start of inlined file: SARibbonSeparatorWidget.h ***/ +#ifndef SARIBBONSEPARATORWIDGET_H +#define SARIBBONSEPARATORWIDGET_H + +#include +#include + +/// +/// \brief 用于显示分割线 +/// +class SA_RIBBON_EXPORT SARibbonSeparatorWidget : public QFrame +{ + Q_OBJECT +public: + explicit SARibbonSeparatorWidget(QWidget* parent = nullptr); + ~SARibbonSeparatorWidget(); +}; + +#endif // SARIBBONSEPARATORWIDGET_H + +/*** End of inlined file: SARibbonSeparatorWidget.h ***/ + +/*** Start of inlined file: SARibbonCtrlContainer.h ***/ +#ifndef SARIBBONCTROLCONTAINER_H +#define SARIBBONCTROLCONTAINER_H + +#include +#include +class QStyleOption; + +/** + * @brief 用于装载一个窗体的容器,这个窗体会布满整个SARibbonCtrlContainer,但会预留空间显示icon或者title + * + * ---------------------- + * |icon|text| widget | + * ---------------------- + */ +class SA_RIBBON_EXPORT SARibbonCtrlContainer : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCtrlContainer) +public: + explicit SARibbonCtrlContainer(QWidget* parent = nullptr); + ~SARibbonCtrlContainer(); + + void setEnableShowIcon(bool b); + void setEnableShowTitle(bool b); + // 判断是否存在容器窗口 + bool hasContainerWidget() const; + // 图标 + void setIcon(const QIcon& i); + void setIcon(const QPixmap& pixmap); + QIcon icon() const; + // 图标 + void setText(const QString& t); + QString text() const; + // 设置窗口 + void setContainerWidget(QWidget* w); + QWidget* containerWidget(); + const QWidget* containerWidget() const; + // 获取线索icon的窗口 + QWidget* iconWidget() const; +}; + +#endif // SARIBBONCTROLCONTAINER_H + +/*** End of inlined file: SARibbonCtrlContainer.h ***/ + +/*** Start of inlined file: SARibbonQuickAccessBar.h ***/ +#ifndef SARIBBONQUICKACCESSBAR_H +#define SARIBBONQUICKACCESSBAR_H + +#include +#include +class SARibbonToolButton; +class SARibbonButtonGroupWidget; +/// +/// \brief ribbon左上顶部的快速响应栏 +/// +class SA_RIBBON_EXPORT SARibbonQuickAccessBar : public SARibbonCtrlContainer +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonQuickAccessBar) +public: + explicit SARibbonQuickAccessBar(QWidget* parent = nullptr); + ~SARibbonQuickAccessBar(); + void addSeparator(); + void addAction(QAction* act, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + void addWidget(QWidget* w); + void addMenu(QMenu* m, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + // 获取内部管理的ButtonGroupWidget + SARibbonButtonGroupWidget* buttonGroupWidget(); + const SARibbonButtonGroupWidget* buttonGroupWidget() const; + // 设置图标尺寸 + void setIconSize(const QSize& s); + QSize iconSize() const; +}; + +#endif // SARIBBONQUICKACCESSBAR_H + +/*** End of inlined file: SARibbonQuickAccessBar.h ***/ + +/*** Start of inlined file: SARibbonTabBar.h ***/ +#ifndef SARIBBONTABBAR_H +#define SARIBBONTABBAR_H + +#include +#include + +/** + * @brief The SARibbonTabBar class + */ +class SA_RIBBON_EXPORT SARibbonTabBar : public QTabBar +{ + Q_OBJECT +public: + explicit SARibbonTabBar(QWidget* parent = nullptr); + ~SARibbonTabBar(); + const QMargins& tabMargin() const; + void setTabMargin(const QMargins& tabMargin); + +protected: + QSize tabSizeHint(int index) const; + +private: + QMargins m_tabMargin; +}; + +#endif // SARIBBONTABBAR_H + +/*** End of inlined file: SARibbonTabBar.h ***/ + +/*** Start of inlined file: SARibbonControlButton.h ***/ +#ifndef SARIBBONCONTROLBUTTON_H +#define SARIBBONCONTROLBUTTON_H +#include + +/** + * @brief 用于SARibbonGallery的control button + * + * 为了防止外部qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonControlButton(QWidget* parent = nullptr); + ~SARibbonControlButton(); +}; + +/** + * @brief 用于SARibbonButtonGroupWidget的control button + * + * 为了防止SARibbonToolButton qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlToolButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonControlToolButton(QWidget* parent = nullptr); + ~SARibbonControlToolButton(); +}; + +#endif // SARIBBONPANNELTOOLBUTTON_H + +/*** End of inlined file: SARibbonControlButton.h ***/ + +/*** Start of inlined file: SARibbonMenu.h ***/ +#ifndef SARIBBONMENU_H +#define SARIBBONMENU_H + +#include + +/// +/// \brief 用在ribbon的menu +/// 可以通过StyleSheet设置样式而不影响QMenu +/// +class SA_RIBBON_EXPORT SARibbonMenu : public QMenu +{ + Q_OBJECT +public: + explicit SARibbonMenu(QWidget* parent = nullptr); + explicit SARibbonMenu(const QString& title, QWidget* parent = nullptr); + ~SARibbonMenu(); + QAction* addRibbonMenu(SARibbonMenu* menu); + SARibbonMenu* addRibbonMenu(const QString& title); + SARibbonMenu* addRibbonMenu(const QIcon& icon, const QString& title); + QAction* addWidget(QWidget* w); +}; + +#endif // SARIBBONMENU_H + +/*** End of inlined file: SARibbonMenu.h ***/ + +/*** Start of inlined file: SARibbonPannelOptionButton.h ***/ +#ifndef SARIBBONPANNELOPTIONBUTTON_H +#define SARIBBONPANNELOPTIONBUTTON_H +#include + +class QAction; + +/** + * @brief Pannel右下角的操作按钮 + * + * 此按钮和一个action关联,使用@ref SARibbonPannel::addOptionAction 函数用于生成此按钮,正常来说 + * 用户并不需要直接操作此类,仅仅用于样式设计 + * 如果一定要重载此按钮,可以通过重载@ref SARibbonElementFactory + * 的 @ref SARibbonElementFactory::createRibbonPannelOptionButton来实现新的OptionButton + * + */ +class SA_RIBBON_EXPORT SARibbonPannelOptionButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonPannelOptionButton(QWidget* parent = nullptr); + ~SARibbonPannelOptionButton(); +}; + +#endif // SAROBBONPANNELOPTIONBUTTON_H + +/*** End of inlined file: SARibbonPannelOptionButton.h ***/ + +/*** Start of inlined file: SARibbonPannelItem.h ***/ +#ifndef SARIBBONPANNELITEM_H +#define SARIBBONPANNELITEM_H + +#include +#include +class SARibbonToolButton; +/** + * @brief 是对pannel所有子窗口的抽象,参考qt的toolbar + * + * 参考qt的toolbar,pannel所有子窗口内容都通过QAction进行抽象,包括gallery这些窗口,也是通过QAction进行抽象 + * QAction最终会转换为SARibbonPannelItem,每个SARibbonPannelItem都含有一个widget,SARibbonPannel的布局 + * 就基于SARibbonPannelItem + * + * 无窗口的action会在内部生成一个SARibbonToolButton, + */ +class SA_RIBBON_EXPORT SARibbonPannelItem : public QWidgetItem +{ +public: + /** + * @brief 定义了行的占比,ribbon中有large,media和small三种占比 + */ + enum RowProportion + { + None, ///< 为定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small + Large, ///< 大占比,一个widget的高度会充满整个pannel + Medium, ///< 中占比,在@ref SARibbonPannel::pannelLayoutMode 为 @ref SARibbonPannel::ThreeRowMode 时才会起作用,且要同一列里两个都是Medium时,会在三行中占据两行 + Small ///< 小占比,占SARibbonPannel的一行,Medium在不满足条件时也会变为Small,但不会变为Large + }; + explicit SARibbonPannelItem(QWidget* widget); + ~SARibbonPannelItem(); + + bool isEmpty() const Q_DECL_OVERRIDE; + + short rowIndex; ///< 记录当前item属于第几行,hide模式下为-1 + int columnIndex; ///< 记录当前item属于第几列,hide模式下为-1 + QRect itemWillSetGeometry; ///< 在调用SARibbonPannelLayout::updateGeomArray会更新这个此处,实际设置的时候会QWidgetItem::setGeometry设置Geometry + QAction* action; /// < 记录action,参考QToolBarLayoutItem + bool customWidget; ///< 对于没有窗口的action,实际也会有一个SARibbonToolButton,在销毁时要delete掉 + SARibbonPannelItem::RowProportion rowProportion; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +}; +#ifndef SA_ActionPropertyName_RowProportion +#define SA_ActionPropertyName_RowProportion "_sa_RowProportion" +#endif +#ifndef SA_ActionPropertyName_ToolButtonPopupMode +#define SA_ActionPropertyName_ToolButtonPopupMode "_sa_ToolButtonPopupMode" +#endif +#ifndef SA_ActionPropertyName_ToolButtonStyle +#define SA_ActionPropertyName_ToolButtonStyle "_sa_ToolButtonStyle" +#endif +#endif // SARIBBONPANNELITEM_H + +/*** End of inlined file: SARibbonPannelItem.h ***/ + +/*** Start of inlined file: SARibbonPannelLayout.h ***/ +#ifndef SARIBBONPANNELLAYOUT_H +#define SARIBBONPANNELLAYOUT_H + +#include + +class QToolButton; +class SARibbonPannel; +class SARibbonPannelLabel; +/** + * @brief 针对SARibbonPannel的布局 + * + * SARibbonPannelLayout实际是一个列布局,每一列有2~3行,看窗口定占几行 + * + * 核心函数: @ref SARibbonPannelLayout::createItem + * + * @note QLayout::contentsMargins 函数不会启作用,如果要设置contentsMargins,使用@sa setPannelContentsMargins + */ +class SA_RIBBON_EXPORT SARibbonPannelLayout : public QLayout +{ + Q_OBJECT + friend class SARibbonPannel; + +public: + explicit SARibbonPannelLayout(QWidget* p = nullptr); + ~SARibbonPannelLayout(); + // SARibbonPannelLayout additem 无效 + void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; + // QLayout 所必须的重载函数 + QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; + QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; + int count() const Q_DECL_OVERRIDE; + bool isEmpty() const Q_DECL_OVERRIDE; + void invalidate() Q_DECL_OVERRIDE; + Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; + void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; + QSize minimumSize() const Q_DECL_OVERRIDE; + QSize sizeHint() const Q_DECL_OVERRIDE; + + // 获取ribbonpannel + SARibbonPannel* ribbonPannel() const; + // SARibbonPannel主要通过此函数来添加action + void insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + // 设置OptionAction,如果要去除,传入nullptr指针即可 + void setOptionAction(QAction* action); + bool isHaveOptionAction() const; + // 通过action获取SARibbonPannelItem + SARibbonPannelItem* pannelItem(QAction* action) const; + // 获取最后一个添加的item + SARibbonPannelItem* lastItem() const; + // 获取最后生成的窗口 + QWidget* lastWidget() const; + // 移动两个item + void move(int from, int to); + // 判断是否需要重新布局 + bool isDirty() const; + // 更新尺寸 + void updateGeomArray(); + // 通过action获取SARibbonPannelItem的索引 + int indexByAction(QAction* action) const; + // 标题高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int newTitleHeight); + // 判断是否存在标题 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + // 返回大按钮的高度 + int largeButtonHeight() const; + // 标题区域和按钮的间隔 + int pannelTitleSpace() const; + void setPannelTitleSpace(int newTitleSpace); + // pannel 标题的label + SARibbonPannelLabel* pannelTitleLabel() const; + // 设置按钮的icon尺寸,large action不受此尺寸影响 + void setToolButtonIconSize(const QSize& s); + QSize toolButtonIconSize() const; + +protected: + // 获取optionAction 按钮尺寸 + QSize optionActionButtonSize() const; + // 布局action + void doLayout(); + // 把action转换为item,对于纯Action,此函数会创建SARibbonToolButton, + // rp用于告诉Layout生成什么样的窗口,详细见SARibbonPannelItem::RowProportion + SARibbonPannelItem* createItem(QAction* action, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + void updateGeomArray(const QRect& setrect); + // 重新计算扩展条码,此函数必须在updateGeomArray函数之后调用 + void recalcExpandGeomArray(const QRect& setrect); + // 返回optionActionButton的尺寸 + +private: + // 根据列数,计算窗口的宽度,以及最大宽度 + void columnWidthInfo(int colindex, int& width, int& maximum) const; + // 设置titlelabel + void setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel); + +private: + QList< SARibbonPannelItem* > mItems; + int mColumnCount { 0 }; ///< 记录有多少列 + bool mExpandFlag { false }; ///< 标记是否是会扩展的 + QSize mSizeHint; ///< sizeHint返回的尺寸 + QSize mDefaultToolButtonIconSize { 22, 22 }; ///< 记录默认的按钮图标尺寸 + bool mDirty { true }; ///< 用于标记是否需要刷新元素,参考QToolBarLayout源码 + int mLargeHeight { 0 }; ///< 记录大图标的高度 + int mTitleHeight { 15 }; ///< 标题区域高度 + int mTitleSpace { 2 }; ///< 标题区域和按钮的间隔 + bool mEnableShowTitle { true }; ///< 是否运行显示pannel标题 + SARibbonPannelLabel* mTitleLabel { nullptr }; ///< titlelabel指针 + QRect mTitleLabelGeometry; ///< titlelabel的位置 + QToolButton* mOptionActionBtn { nullptr }; ///< optionAction对应的button + QRect mOptionActionBtnGeometry; ///< optionAction的位置 +}; + +#endif // SARIBBONPANNELLAYOUT_H + +/*** End of inlined file: SARibbonPannelLayout.h ***/ + +/*** Start of inlined file: SARibbonPannel.h ***/ +#ifndef SARIBBONPANNEL_H +#define SARIBBONPANNEL_H + +#include +#include +#include +class SARibbonMenu; +class SARibbonGallery; +class QGridLayout; +class SARibbonPannelOptionButton; +class SARibbonPannelLayout; +class SARibbonCategory; +class SARibbonBar; + +/** + * @brief SARibbonPannel的标题label,此类用于qss + */ +class SA_RIBBON_EXPORT SARibbonPannelLabel : public QLabel +{ + Q_OBJECT +public: + SARibbonPannelLabel(QWidget* parent = nullptr); +}; + +/** + * @brief pannel页窗口,pannel是ribbon的面板用于承放控件 + * + * ribbon的pannel分为两行模式和三行模式,以office为代表的ribbon为3行模式,以WPS为代表的“紧凑派”就是2行模式, + * SARibbon可通过SARibbonBar的@ref SARibbonBar::RibbonStyle 来指定模式(通过函数@ref SARibbonBar::setRibbonStyle ) + * + * 在pannel中,可以通过@ref setExpanding 函数指定pannel水平扩展,如果pannel里面没有能水平扩展的控件,将会留白,因此, + * 建议在pannel里面有水平扩展的控件如(@ref SARibbonGallery )才指定这个函数 + * + * pannel的布局通过@ref SARibbonPannelLayout 来实现,如果有其他布局,可以通过继承@ref + * SARibbonElementCreateDelegate::createRibbonPannel 函数返回带有自己布局的pannel,但你必须继承对应的虚函数 + */ +class SA_RIBBON_EXPORT SARibbonPannel : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonPannel) + friend class SARibbonCategory; + friend class SARibbonCategoryPrivate; + friend class SARibbonCustomizeWidgetPrivate; + friend class SARibbonPannelLayout; + Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) + Q_PROPERTY(bool isExpanding READ isExpanding WRITE setExpanding) + Q_PROPERTY(QString pannelName READ pannelName WRITE setPannelName) +public: + enum PannelLayoutMode + { + ThreeRowMode, ///< 三行布局模式,office就是三行布局模式,pannel能布置3行小toolbutton,默认模式 + TwoRowMode ///< 两行布局模式,wps的后续布局模式就是两行布局模式,pannel能布置2行小toolbutton + }; + Q_ENUM(PannelLayoutMode) +public: + using FpRibbonToolButtonIterate = std::function< bool(SARibbonToolButton*) >; + +public: + explicit SARibbonPannel(QWidget* parent = nullptr); + explicit SARibbonPannel(const QString& name, QWidget* parent = nullptr); + ~SARibbonPannel() Q_DECL_OVERRIDE; + using QWidget::addAction; + + // 把action加入到pannel + void addAction(QAction* action, SARibbonPannelItem::RowProportion rp); + // 生成并添加一个action + void addAction(QAction* act, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action); + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action); + + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + + QAction* addAction(const QString& text, + const QIcon& icon, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + + // 添加menu + void addMenu(QMenu* menu, + SARibbonPannelItem::RowProportion rp, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通大菜单 + void addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通小按钮菜单 + void addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加窗口 + QAction* addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp); + + // 添加窗口,占用ribbon的一行 + QAction* addSmallWidget(QWidget* w); + + // 添加窗口,占用ribbon的一行 + QAction* addMediumWidget(QWidget* w); + + // 添加窗口,占用所有行 + QAction* addLargeWidget(QWidget* w); + + // 添加一个Gallery + SARibbonGallery* addGallery(bool expanding = true); + + // 添加分割线 + QAction* addSeparator(); + + // 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + SARibbonToolButton* actionToRibbonToolButton(QAction* action); + + // 设置操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + // OptionAction也会触发actionTriggered信号 + void setOptionAction(QAction* action); + + // 判断是否存在OptionAction + bool isHaveOptionAction() const; + + // 获取所有的buttons + QList< SARibbonToolButton* > ribbonToolButtons() const; + + // 获取PannelLayoutMode + PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(PannelLayoutMode mode); + // 更新按钮的尺寸,这个函数在pannel的布局状态变换后需要调用刷新 + void resetToolButtonSize(); + + // 判断是否为2行模式 + bool isTwoRow() const + { + return (TwoRowMode == pannelLayoutMode()); + } + + // 把pannel设置为扩展模式,此时会撑大水平区域 + void setExpanding(bool isExpanding = true); + + // 是否是扩展模式 + bool isExpanding() const; + + // 标题栏高度 + void setTitleHeight(int h); + int titleHeight() const; + + // 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + bool isEnableShowTitle() const; + void setEnableShowTitle(bool on); + + // action对应的布局index,此操作一般用于移动moveAction,其他意义不大 + int actionIndex(QAction* act) const; + + // 移动action + void moveAction(int from, int to); + + // 判断是否可以自定义 + bool isCanCustomize() const; + void setCanCustomize(bool b); + + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + + // 大图标的高度 + int largeButtonHeight() const; + // 获取布局对应的item,此函数目前仅仅在自定义过程中用到 + const QList< SARibbonPannelItem* >& ribbonPannelItem() const; + // 获取pannel layout + SARibbonPannelLayout* pannelLayout() const; + // 更新布局 + void updateItemGeometry(); + // 获取category指针,如果没有parent,或者不在category管理,返回nullptr + SARibbonCategory* category() const; + // 获取ribbonBar指针,如果没有返回nullptr + SARibbonBar* ribbonBar() const; + // 设置按钮之间的间隔 + void setSpacing(int n); + int spacing() const; + // + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + // 设置按钮的icon尺寸,large action不受此尺寸影响 + void setToolButtonIconSize(const QSize& s); + QSize toolButtonIconSize() const; + // 此函数会遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 + bool iterate(FpRibbonToolButtonIterate fp); + + // 获取最近添加的按钮 + SARibbonToolButton* lastAddActionButton(); + +Q_SIGNALS: + + /** + * @brief 等同于QToolBar::actionTriggered + * @param action + */ + void actionTriggered(QAction* action); + + /** + * @brief pannel的标题发生了改变 + * @param n + */ + void pannelNameChanged(const QString& n); + +public: + // pannel高度推荐 + static int pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight); + // 把action的行属性设置进action中,action自身携带了行属性 + static void setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp); + // 获取action的行属性 + static SARibbonPannelItem::RowProportion getActionRowProportionProperty(QAction* action); + // 把action的PopupMode属性设置进action中,action自身携带了PopupMode属性 + static void setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 获取action的PopupMode属性 + static QToolButton::ToolButtonPopupMode getActionToolButtonPopupModeProperty(QAction* action); + // 把action的ToolButtonStyle属性设置进action中,action自身携带了ToolButtonStyle属性 + static void setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle); + // 获取action的ToolButtonStyle属性 + static Qt::ToolButtonStyle getActionToolButtonStyleProperty(QAction* action); + +protected: + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; +}; + +#endif // SARIBBONPANNEL_H + +/*** End of inlined file: SARibbonPannel.h ***/ + +/*** Start of inlined file: SARibbonCategory.h ***/ +#ifndef SARIBBONCATEGORY_H +#define SARIBBONCATEGORY_H + +#include + +#include +#include +#include + +class QAction; +class QHBoxLayout; +class QWheelEvent; +class SARibbonBar; +class SARibbonCategoryLayout; +/** + * @brief 一项ribbon tab页 + * @note SARibbonCategory的windowTitle影响了其在SARibbonBar的标签显示, + * 如果要改标签名字,直接调用SARibbonCategory的setWindowTitle函数 + */ +class SA_RIBBON_EXPORT SARibbonCategory : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCategory) + friend class SARibbonBar; + friend class SARibbonContextCategory; + Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) + Q_PROPERTY(QString categoryName READ categoryName WRITE setCategoryName) +public: + using FpPannelIterate = std::function< bool(SARibbonPannel*) >; + +public: + explicit SARibbonCategory(QWidget* p = nullptr); + explicit SARibbonCategory(const QString& name, QWidget* p = nullptr); + ~SARibbonCategory(); + + // category的名字 + QString categoryName() const; + + // 设置category名字,等同setWindowTitle + void setCategoryName(const QString& title); + + // 设置pannel的模式 + SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); + + // 添加pannel + SARibbonPannel* addPannel(const QString& title); + + // 添加pannel + void addPannel(SARibbonPannel* pannel); + + // qt designer专用 + Q_INVOKABLE void addPannel(QWidget* pannel); + + // 插入pannel + SARibbonPannel* insertPannel(const QString& title, int index); + + // 通过名字查找pannel + SARibbonPannel* pannelByName(const QString& title) const; + + // 通过ObjectName查找pannel + SARibbonPannel* pannelByObjectName(const QString& objname) const; + + // 通过索引找到pannel,如果超过索引范围,会返回nullptr + SARibbonPannel* pannelByIndex(int index) const; + + // 查找pannel的index + int pannelIndex(SARibbonPannel* p) const; + + // 移动一个Pannel从from index到to index + void movePannel(int from, int to); + + // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 + bool takePannel(SARibbonPannel* pannel); + + // 移除Pannel,Category会直接回收SARibbonPannel内存 + bool removePannel(SARibbonPannel* pannel); + bool removePannel(int index); + + // 返回所有的Pannel + QList< SARibbonPannel* > pannelList() const; + + // + QSize sizeHint() const Q_DECL_OVERRIDE; + + // 如果是ContextCategory,此函数返回true + bool isContextCategory() const; + + // pannel的个数 + int pannelCount() const; + + // 判断是否可以自定义 + bool isCanCustomize() const; + void setCanCustomize(bool b); + + // 设置pannel的标题栏高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int h); + + // 设置pannel是否显示标题栏 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + + // 设置Category的对齐方式 + void setCategoryAlignment(SARibbonAlignment al); + SARibbonAlignment categoryAlignment() const; + + // 设置pannel的spacing + void setPannelSpacing(int n); + int pannelSpacing() const; + + // 设置pannel按钮的icon尺寸,large action不受此尺寸影响 + void setPannelToolButtonIconSize(const QSize& s); + QSize pannelToolButtonIconSize() const; + + // 获取对应的ribbonbar,如果没有加入ribbonbar的管理,此值为null + SARibbonBar* ribbonBar() const; + + // 刷新category的尺寸布局 + void updateItemGeometry(); + + // 此函数会遍历Category下的所有pannel,执行函数指针,函数指针返回false则停止迭代 + bool iterate(FpPannelIterate fp); +Q_SIGNALS: + /** + * @brief category标题发生了改变信号 + * @param n + */ + void categoryNameChanged(const QString& n); + + /** + * @brief 参考QToolBar::actionTriggered的信号 + * @param action + */ + void actionTriggered(QAction* action); + +protected: + virtual bool event(QEvent* e) override; + // 处理滚轮事件 + void wheelEvent(QWheelEvent* event) override; + // + void changeEvent(QEvent* event) override; + + // 标记这个是上下文标签 + void markIsContextCategory(bool isContextCategory = true); + + // 获取SARibbonCategoryLayoutlayout + SARibbonCategoryLayout* categoryLayout() const; +}; + +/** + * @brief SARibbonCategory无法完全显示时,显示的调整按钮 + * + * 重新定义是为了防止被外部的样式影响,同时可以使用SARibbonCategoryScrollButton的样式定义 + */ +class SA_RIBBON_EXPORT SARibbonCategoryScrollButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p = nullptr); + ~SARibbonCategoryScrollButton(); +}; + +#endif // SARIBBONCATEGORY_H + +/*** End of inlined file: SARibbonCategory.h ***/ + +/*** Start of inlined file: SARibbonCategoryLayout.h ***/ +#ifndef SARIBBONCATEGORYLAYOUT_H +#define SARIBBONCATEGORYLAYOUT_H + +#include +#include +#include + +class SARibbonPannel; +class SARibbonCategoryLayoutItem; +class SARibbonSeparatorWidget; + +/** + * @brief The SARibbonCategoryLayout class + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayout : public QLayout +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCategoryLayout) +public: + explicit SARibbonCategoryLayout(SARibbonCategory* parent); + ~SARibbonCategoryLayout(); + + SARibbonCategory* ribbonCategory() const; + + virtual void addItem(QLayoutItem* item) override; + virtual QLayoutItem* itemAt(int index) const override; + virtual QLayoutItem* takeAt(int index) override; + SARibbonCategoryLayoutItem* takePannelItem(int index); + SARibbonCategoryLayoutItem* takePannelItem(SARibbonPannel* pannel); + bool takePannel(SARibbonPannel* pannel); + virtual int count() const override; + void setGeometry(const QRect& rect) override; + QSize sizeHint() const override; + QSize minimumSize() const override; + Qt::Orientations expandingDirections() const override; + void invalidate() override; + // 追加一个pannel + void addPannel(SARibbonPannel* pannel); + // 插入一个pannel + void insertPannel(int index, SARibbonPannel* pannel); + // + QSize categoryContentSize() const; + // 更新尺寸 + void updateGeometryArr(); + + // 执行位置调整 + void doLayout(); + + // 返回所有pannels + QList< SARibbonPannel* > pannels() const; + + // 通过obj name获取pannel + SARibbonPannel* pannelByObjectName(const QString& objname) const; + // 通过pannel name获取pannel + SARibbonPannel* pannelByName(const QString& pannelname) const; + // 通过索引找到pannel,如果超过索引范围,会返回nullptr + SARibbonPannel* pannelByIndex(int i) const; + // 移动pannel + void movePannel(int from, int to); + // pannel的数量 + int pannelCount() const; + // pannel的索引 + int pannelIndex(SARibbonPannel* p) const; + // 获取所有的pannel + QList< SARibbonPannel* > pannelList() const; + // 执行滚轮事件 + void scroll(int px); + // 判断是否有滚动过 + bool isScrolled() const; + // category的总宽度 + int categoryTotalWidth() const; + // 设置Category的对齐方式 + void setCategoryAlignment(SARibbonAlignment al); + SARibbonAlignment categoryAlignment() const; +private Q_SLOTS: + void onLeftScrollButtonClicked(); + void onRightScrollButtonClicked(); +}; + +/** + * @brief SARibbonCategoryLayoutItem,用于标识SARibbonCategoryLayout的item + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayoutItem : public QWidgetItem +{ +public: + explicit SARibbonCategoryLayoutItem(SARibbonPannel* w); + ~SARibbonCategoryLayoutItem(); + SARibbonSeparatorWidget* separatorWidget; + // 把内部的widget转换为pannel + SARibbonPannel* toPannelWidget(); + QRect mWillSetGeometry; ///< pannel将要设置的Geometry + QRect mWillSetSeparatorGeometry; ///< pannel将要设置的Separator的Geometry +}; +#endif // SARIBBONCATEGORYLAYOUT_H + +/*** End of inlined file: SARibbonCategoryLayout.h ***/ + +/*** Start of inlined file: SARibbonContextCategory.h ***/ +#ifndef SARIBBONCONTEXTCATEGORY_H +#define SARIBBONCONTEXTCATEGORY_H + +#include + +/** + * @brief 管理上下文标签的类 + */ +class SA_RIBBON_EXPORT SARibbonContextCategory : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonContextCategory) +public: + explicit SARibbonContextCategory(QWidget* parent = nullptr); + ~SARibbonContextCategory(); + // 上下文目录添加下属目录 + SARibbonCategory* addCategoryPage(const QString& title); + void addCategoryPage(SARibbonCategory* category); + // 获取上下文标签下管理的标签个数 + int categoryCount() const; + + // 设置id + void setId(const QVariant& id); + QVariant id() const; + + // 设置上下文颜色 + void setContextColor(const QColor color); + QColor contextColor() const; + + // 上下文标签的内容 + QString contextTitle() const; + void setContextTitle(const QString& contextTitle); + + // 获取对应的tab页 + SARibbonCategory* categoryPage(int index); + + // 获取所有的SARibbonCategory* + QList< SARibbonCategory* > categoryList() const; + + // 移除category + bool takeCategory(SARibbonCategory* category); + + // 判断上下文是否维护了此SARibbonCategory + bool isHaveCategory(SARibbonCategory* category) const; +Q_SIGNALS: + /** + * @brief 标签加入上下文 + * @param category + */ + void categoryPageAdded(SARibbonCategory* category); + + /** + * @brief 标签从上下文移除 + * @param category + */ + void categoryPageRemoved(SARibbonCategory* category); + + /** + * @brief 上下文的标题发生改变 + * @param title + */ + void contextTitleChanged(const QString& title); + + /** + * @brief 上下文标签维护的标签页名字发生了改变 + * @param category 发生改变的上下文标签页 + * @param title 新名字 + */ + void categoryTitleChanged(SARibbonCategory* category, const QString& title); +private Q_SLOTS: + void onCategoryTitleChanged(const QString& title); + +protected: + // 获取父级窗口 + QWidget* parentWidget() const; + virtual bool eventFilter(QObject* watched, QEvent* e) override; +}; + +#endif // SARIBBONCONTEXTCATEGORY_H + +/*** End of inlined file: SARibbonContextCategory.h ***/ + +/*** Start of inlined file: SARibbonGalleryItem.h ***/ +#ifndef SARIBBONGALLERYITEM_H +#define SARIBBONGALLERYITEM_H + +#include +#include +#include +#include +class SARibbonGalleryGroup; + +/// +/// \brief 类似QStandardItem的GalleryItem +/// +class SA_RIBBON_EXPORT SARibbonGalleryItem +{ + friend class SARibbonGalleryGroupModel; + +public: + explicit SARibbonGalleryItem(); + explicit SARibbonGalleryItem(const QString& text, const QIcon& icon); + explicit SARibbonGalleryItem(QAction* act); + virtual ~SARibbonGalleryItem(); + // 设置角色 + void setData(int role, const QVariant& data); + virtual QVariant data(int role) const; + + // 设置文字描述 + void setText(const QString& text); + QString text() const; + + // 设置tooltip + void setToolTip(const QString& text); + QString toolTip() const; + + // 设置图标 + void setIcon(const QIcon& ico); + QIcon icon() const; + + // 设置是否可见 + bool isSelectable() const; + void setSelectable(bool isSelectable); + + // 设置是否可选 + bool isEnable() const; + void setEnable(bool isEnable); + + // 设置item的flag + void setFlags(Qt::ItemFlags flag); + virtual Qt::ItemFlags flags() const; + + // 设置action + void setAction(QAction* act); + QAction* action(); + + // 文字对齐方式 + void setTextAlignment(Qt::Alignment a); + Qt::Alignment textAlignment() const; + +private: + QMap< int, QVariant > mDatas; + Qt::ItemFlags mFlags; + QAction* mAction; +}; + +#endif // SARIBBONGALLERYITEM_H + +/*** End of inlined file: SARibbonGalleryItem.h ***/ + +/*** Start of inlined file: SARibbonGalleryGroup.h ***/ +#ifndef SARIBBONGALLERYGROUP_H +#define SARIBBONGALLERYGROUP_H + +#include +#include +#include + +/// +/// \brief SARibbonGalleryGroup对应的显示代理 +/// +class SA_RIBBON_EXPORT SARibbonGalleryGroupItemDelegate : public QStyledItemDelegate +{ +public: + explicit SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent = nullptr); + ~SARibbonGalleryGroupItemDelegate(); + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + virtual void paintIconOnly(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void paintIconWithText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void + paintIconWithTextWordWrap(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + +private: + SARibbonGalleryGroup* mGroup; +}; + +/// +/// \brief SARibbonGalleryGroup对应的model +/// +class SA_RIBBON_EXPORT SARibbonGalleryGroupModel : public QAbstractListModel +{ + Q_OBJECT +public: + SARibbonGalleryGroupModel(QObject* parent = Q_NULLPTR); + ~SARibbonGalleryGroupModel(); + virtual int rowCount(const QModelIndex& parent) const Q_DECL_OVERRIDE; + virtual Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; + virtual QVariant data(const QModelIndex& index, int role) const Q_DECL_OVERRIDE; + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const Q_DECL_OVERRIDE; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; + void clear(); + SARibbonGalleryItem* at(int row) const; + void insert(int row, SARibbonGalleryItem* item); + SARibbonGalleryItem* take(int row); + void append(SARibbonGalleryItem* item); + +private: + QList< SARibbonGalleryItem* > mItems; +}; + +/** + * @brief Gallery的组 + * + * 组负责显示管理Gallery Item + */ +class SA_RIBBON_EXPORT SARibbonGalleryGroup : public QListView +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonGalleryGroup) +public: + /** + * @brief GalleryGroup显示的样式 + */ + enum GalleryGroupStyle + { + IconWithText, ///< 图标带文字 + IconWithWordWrapText, ///< 图标带文字,文字会换行显示,此模式只会对DisplayOneRow生效,如果不是DisplayOneRow,等同IconWithText + IconOnly ///< 只有图标 + }; + + /** + * @brief 定义Gallery在一个pannel下面显示的图标行数 + */ + enum DisplayRow + { + DisplayOneRow = 1, ///< 显示1行,默认 + DisplayTwoRow = 2, + DisplayThreeRow = 3 + }; + + SARibbonGalleryGroup(QWidget* w = 0); + + virtual ~SARibbonGalleryGroup(); + // 重新计算grid尺寸 + void setRecalcGridSizeBlock(bool on = true); + bool isRecalcGridSizeBlock() const; + void recalcGridSize(); + void recalcGridSize(int galleryHeight); + // 设置显示的样式 + void setGalleryGroupStyle(GalleryGroupStyle style); + GalleryGroupStyle galleryGroupStyle() const; + // 添加一个item + void addItem(const QString& text, const QIcon& icon); + void addItem(SARibbonGalleryItem* item); + // 以一个aciton作为item添加 + void addActionItem(QAction* act); + void addActionItemList(const QList< QAction* >& acts); + + // 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 + void setupGroupModel(); + SARibbonGalleryGroupModel* groupModel(); + // 标题 + void setGroupTitle(const QString& title); + QString groupTitle() const; + void selectByIndex(int i); + // 设置显示的行数 + void setDisplayRow(DisplayRow r); + DisplayRow displayRow() const; + // 设置grid最小的宽度,默认为0(不限制),可以限定grid的宽度 + void setGridMinimumWidth(int w); + int gridMinimumWidth() const; + // 设置grid最大的宽度,默认为0(不限制),可以限定grid的宽度 + void setGridMaximumWidth(int w); + int gridMaximumWidth() const; + // 获取SARibbonGalleryGroup管理的actiongroup + QActionGroup* actionGroup() const; +private Q_SLOTS: + void onItemClicked(const QModelIndex& index); + void onItemEntered(const QModelIndex& index); +Q_SIGNALS: + void groupTitleChanged(const QString& title); + /** + * @brief 等同QActionGroup的triggered + * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @param action + */ + void triggered(QAction* action); + /** + * @brief 等同QActionGroup的triggered + * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @note 此属性需要通过QAbstractItemView::entered(const QModelIndex &index)激活,因此要保证设置了setMouseTracking(true) + * @param action + */ + void hovered(QAction* action); +}; + +#endif // SARIBBONGALLERYGROUP_H + +/*** End of inlined file: SARibbonGalleryGroup.h ***/ + +/*** Start of inlined file: SARibbonGallery.h ***/ +#ifndef SARIBBONGALLERY_H +#define SARIBBONGALLERY_H + +#include +#include + +#include +class QLabel; +class QVBoxLayout; +class SARibbonGalleryViewport; + +/** + * @brief 针对SARibbonGallery控件的按钮 + */ +class SA_RIBBON_EXPORT SARibbonGalleryButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonGalleryButton(QWidget* parent = nullptr); + ~SARibbonGalleryButton(); +}; + +/** + * @brief Gallery控件 + * + * Gallery控件是由一个当前激活的@sa SARibbonGalleryGroup 和弹出的 @sa SARibbonGalleryViewport 组成 + * + * @sa SARibbonGalleryGroup 是继承@sa QListView actions通过icon展示出来,相关的属性可以按照QListView设置 + * + * @sa SARibbonGalleryViewport 是一个内部有垂直布局的窗体,在弹出激活时,把管理的SARibbonGalleryGroup都展示出来 + * + * 示例如下: + * @code + * SARibbonGallery* gallery = pannel1->addGallery(); + * QList< QAction* > galleryActions; + * ...create many actions ... + * SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); + * galleryActions.clear(); + * ...create many actions ... + * gallery->addCategoryActions(tr("Apps"), galleryActions); + * gallery->setCurrentViewGroup(group1); + * @endcode + */ +class SA_RIBBON_EXPORT SARibbonGallery : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonGallery) +public: + explicit SARibbonGallery(QWidget* parent = nullptr); + virtual ~SARibbonGallery(); + virtual QSize sizeHint() const override; + // 添加一个GalleryGroup + SARibbonGalleryGroup* addGalleryGroup(); + // 添加一个GalleryGroup + void addGalleryGroup(SARibbonGalleryGroup* group); + // 快速添加一组actions + SARibbonGalleryGroup* addCategoryActions(const QString& title, QList< QAction* > actions); + // 设置当前显示的SARibbonGalleryGroup + void setCurrentViewGroup(SARibbonGalleryGroup* group); + // 获取当前显示的SARibbonGalleryGroup + SARibbonGalleryGroup* currentViewGroup() const; + // 获取弹出窗口指针 + SARibbonGalleryViewport* getPopupViewPort() const; + +public: + // 设置最右边三个控制按钮的最大宽度(默认15) + static void setGalleryButtonMaximumWidth(int w); +Q_SIGNALS: + /** + * @brief 转发管理的SARibbonGalleryGroup::triggered + * 所有加入SARibbonGallery的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @param action + */ + void triggered(QAction* action); + /** + * @brief 转发管理的SARibbonGalleryGroup::hovered + * @note 此属性需要确保SARibbonGalleryGroup::setMouseTracking(true) + * @param action + */ + void hovered(QAction* action); + +public Q_SLOTS: + // 上翻页 + virtual void pageUp(); + // 下翻页 + virtual void pageDown(); + // 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + virtual void showMoreDetail(); +protected Q_SLOTS: + void onItemClicked(const QModelIndex& index); + virtual void onTriggered(QAction* action); + +private: + SARibbonGalleryViewport* ensureGetPopupViewPort(); + +protected: + void resizeEvent(QResizeEvent* event) override; + void paintEvent(QPaintEvent* event) override; +}; + +/// +/// \brief SARibbonGallery的Viewport class +/// +class SARibbonGalleryViewport : public QWidget +{ + Q_OBJECT +public: + SARibbonGalleryViewport(QWidget* parent); + // 添加窗口不带标题 + void addWidget(QWidget* w); + // 添加窗口,带标题 + void addWidget(QWidget* w, const QString& title); + // 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + QLabel* titleLabel(QWidget* w); + // 移除窗口 + void removeWidget(QWidget* w); +public Q_SLOTS: + void onTitleChanged(QWidget* w, const QString& title); + +private: + QVBoxLayout* m_layout; + QMap< QWidget*, QLabel* > _widgetToTitleLable; ///< QWidget和lable的对应 +}; + +#endif // SARIBBONGALLERY_H + +/*** End of inlined file: SARibbonGallery.h ***/ + +/*** Start of inlined file: SARibbonBar.h ***/ +#ifndef SARIBBONBAR_H +#define SARIBBONBAR_H + +#include +#include +#include + +class QAction; +class QAbstractButton; +class SARibbonElementFactory; +class SARibbonTabBar; +class SARibbonButtonGroupWidget; +class SARibbonQuickAccessBar; +class SARibbonStackedWidget; + +/** + @brief SARibbonBar继承于QMenuBar,在SARibbonMainWindow中直接替换了原来的QMenuBar + + 通过setRibbonStyle函数设置ribbon的风格: + + @code + void setRibbonStyle(RibbonStyles v); + @endcode + + SARibbonBar参考office和wps,提供了四种风格的Ribbon模式,@ref SARibbonBar::RibbonStyles + + 如果想ribbon占用的空间足够小,WpsLiteStyleTwoRow模式能比OfficeStyle节省35%的高度空间 + + 如何生成ribbon?先看看一个传统的Menu/ToolBar是如何生成的: + + @code + void MainWindow::MainWindow() + { + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QToolBar *fileToolBar = addToolBar(tr("File")); + //生成action + QAction *newAct = new QAction(newIcon, tr("&New"), this); + fileMenu->addAction(newAct); + fileToolBar->addAction(newAct); + + QAction *openAct = new QAction(openIcon, tr("&Open..."), this); + fileMenu->addAction(openAct); + fileToolBar->addAction(openAct); + } + @endcode + + 传统的Menu/ToolBar主要通过QMenu的addMenu添加菜单,通过QMainWindow::addToolBar生成QToolBar, + 再把QAction设置进QMenu和QToolBar中 + + SARibbonBar和传统方法相似,不过相对于传统的Menu/ToolBar QMenu和QToolBar是平级的, + Ribbon是有明显的层级关系,SARibbonBar下面是 @ref SARibbonCategory, + SARibbonCategory下面是@ref SARibbonPannel ,SARibbonPannel下面是@ref SARibbonToolButton , + SARibbonToolButton管理着QAction + + 因此,生成一个ribbon只需以下几个函数: + @code + SARibbonCategory * SARibbonBar::addCategoryPage(const QString& title); + SARibbonPannel * SARibbonCategory::addPannel(const QString& title); + SARibbonToolButton * SARibbonPannel::addLargeAction(QAction *action); + SARibbonToolButton * SARibbonPannel::addSmallAction(QAction *action); + @endcode + + 因此生成步骤如下: + + @code + //成员变量 + SARibbonCategory* categoryMain; + SARibbonPannel* FilePannel; + + //建立ui + void setupRibbonUi() + { + ...... + //ribbonwindow为SARibbonMainWindow + SARibbonBar* ribbon = ribbonwindow->ribbonBar(); + ribbon->setRibbonStyle(SARibbonBar::WpsLiteStyle); + //添加一个Main标签 + categoryMain = ribbon->addCategoryPage(QStringLiteral("Main")); + //Main标签下添加一个File Pannel + FilePannel = categoryMain->addPannel(QStringLiteral("FilePannel")); + //开始为File Pannel添加action + FilePannel->addLargeAction(actionNew); + FilePannel->addLargeAction(actionOpen); + FilePannel->addLargeAction(actionSave); + FilePannel->addSmallAction(actionImportMesh); + FilePannel->addSmallAction(actionImportGeometry); + } + @endcode + */ +class SA_RIBBON_EXPORT SARibbonBar : public QMenuBar +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonBar) + friend class SARibbonMainWindow; + friend class SARibbonSystemButtonBar; + Q_PROPERTY(RibbonStyles ribbonStyle READ currentRibbonStyle WRITE setRibbonStyle) + Q_PROPERTY(bool minimumMode READ isMinimumMode WRITE setMinimumMode) + Q_PROPERTY(bool minimumModeButton READ haveShowMinimumModeButton WRITE showMinimumModeButton) + Q_PROPERTY(QColor windowTitleTextColor READ windowTitleTextColor WRITE setWindowTitleTextColor) + Q_PROPERTY(QColor tabBarBaseLineColor READ tabBarBaseLineColor WRITE setTabBarBaseLineColor) + Q_PROPERTY(Qt::Alignment windowTitleAligment READ windowTitleAligment WRITE setWindowTitleAligment) + Q_PROPERTY(bool enableWordWrap READ isEnableWordWrap WRITE setEnableWordWrap) + Q_PROPERTY(bool enableShowPannelTitle READ isEnableShowPannelTitle WRITE setEnableShowPannelTitle) + Q_PROPERTY(bool tabOnTitle READ isTabOnTitle WRITE setTabOnTitle) + Q_PROPERTY(SARibbonPannel::PannelLayoutMode pannelLayoutMode READ pannelLayoutMode WRITE setPannelLayoutMode) + +public: + enum RibbonStyleFlag + { + RibbonStyleLoose = 0x0001, // bit:0000 0001 + RibbonStyleCompact = 0x0002, // bit:0000 0010 + RibbonStyleThreeRow = 0x0010, // bit:0001 0000 + RibbonStyleTwoRow = 0x0020, // bit:0010 0000 + + RibbonStyleLooseThreeRow = RibbonStyleLoose | RibbonStyleThreeRow, ///< 宽松结构,3行模式 + RibbonStyleCompactThreeRow = RibbonStyleCompact | RibbonStyleThreeRow, ///< 紧凑结构,3行模式 + RibbonStyleLooseTwoRow = RibbonStyleLoose | RibbonStyleTwoRow, ///< 宽松结构,2行模式 + RibbonStyleCompactTwoRow = RibbonStyleCompact | RibbonStyleTwoRow ///< 紧凑结构,2行模式 + }; + Q_ENUM(RibbonStyleFlag) + Q_DECLARE_FLAGS(RibbonStyles, RibbonStyleFlag) + Q_FLAG(RibbonStyles) + + /** + * @brief 定义当前ribbon 的状态 + */ + enum RibbonMode + { + MinimumRibbonMode, ///< 缩小模式 + NormalRibbonMode ///< 正常模式 + }; + Q_ENUM(RibbonMode) + + using FpCategoryIterate = std::function< bool(SARibbonCategory*) >; + using FpPannelIterate = SARibbonCategory::FpPannelIterate; + +public: + // 判断RibbonStyle是否为2行模式 + static bool isTwoRowStyle(RibbonStyles s); + static bool isThreeRowStyle(RibbonStyles s); + // 判断是否是office样式 + static bool isLooseStyle(RibbonStyles s); + static bool isCompactStyle(RibbonStyles s); + // 获取版本信息 + static QString versionString(); + + // 获取默认的上下文标签颜色列表 + static QList< QColor > defaultContextCategoryColorList(); + + // + static void initHighDpi(); + +public: + // 构造函数 + explicit SARibbonBar(QWidget* parent = nullptr); + ~SARibbonBar(); + // 获取applicationButton + QAbstractButton* applicationButton(); + + // 设置applicationButton + void setApplicationButton(QAbstractButton* btn); + + // 获取tabbar + SARibbonTabBar* ribbonTabBar(); + + // 添加一个标签 + SARibbonCategory* addCategoryPage(const QString& title); + void addCategoryPage(SARibbonCategory* category); + + // 为了支持Qt designer,添加的一个重载函数 + Q_INVOKABLE void addCategoryPage(QWidget* category); + + // 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 + SARibbonCategory* insertCategoryPage(const QString& title, int index); + void insertCategoryPage(SARibbonCategory* category, int index); + + // 通过名字查找Category + SARibbonCategory* categoryByName(const QString& title) const; + + // 通过ObjectName查找Category + SARibbonCategory* categoryByObjectName(const QString& objname) const; + + // 通过索引找到category,如果超过索引范围,会返回nullptr + SARibbonCategory* categoryByIndex(int index) const; + + // 隐藏category,并不会删除或者取走,只是隐藏 + void hideCategory(SARibbonCategory* category); + + // 显示被隐藏的category + void showCategory(SARibbonCategory* category); + + // 判断这个category是否在显示状态,也就是tabbar有这个category + bool isCategoryVisible(const SARibbonCategory* c) const; + + // 获取category的索引 + int categoryIndex(const SARibbonCategory* c) const; + + // 移动一个Category从from index到to index + void moveCategory(int from, int to); + + // 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 + QList< SARibbonCategory* > categoryPages(bool getAll = true) const; + + // 移除SARibbonCategory + void removeCategory(SARibbonCategory* category); + + // 添加一个上下文标签 + SARibbonContextCategory* + addContextCategory(const QString& title, const QColor& color = QColor(), const QVariant& id = QVariant()); + void addContextCategory(SARibbonContextCategory* context); + + // 显示一个上下文标签 + void showContextCategory(SARibbonContextCategory* context); + + // 隐藏一个上下文标签 + void hideContextCategory(SARibbonContextCategory* context); + + // 判断上下文是否是在显示状态 + bool isContextCategoryVisible(SARibbonContextCategory* context); + + // 设置上下文标签的显示或隐藏 + void setContextCategoryVisible(SARibbonContextCategory* context, bool visible); + + // 获取所有的上下文标签 + QList< SARibbonContextCategory* > contextCategoryList() const; + + // 移除ContextCategory + void destroyContextCategory(SARibbonContextCategory* context); + + // 设置为隐藏模式 + void setMinimumMode(bool isHide); + + // 当前Ribbon是否是隐藏模式 + bool isMinimumMode() const; + + // 设置显示隐藏ribbon按钮 + void showMinimumModeButton(bool isShow = true); + + // 是否显示隐藏ribbon按钮 + bool haveShowMinimumModeButton() const; + + // 隐藏ribbon对应的action + QAction* minimumModeAction() const; + + // 当前的模式 + RibbonMode currentRibbonState() const; + + // ribbon tab的高度 + int tabBarHeight() const; + void setTabBarHeight(int h, bool resizeByNow = true); + + // 标题栏的高度 + int titleBarHeight() const; + void setTitleBarHeight(int h, bool resizeByNow = true); + + // category的高度 + int categoryHeight() const; + void setCategoryHeight(int h, bool resizeByNow = true); + + // 获取正常模式下的mainBar的高度 + int normalModeMainBarHeight() const; + + // 最小模式下的MainBar高度 + int minimumModeMainBarHeight() const; + + // 激活tabbar右边的按钮群 + SARibbonButtonGroupWidget* activeRightButtonGroup(); + + // 右侧按钮群 + SARibbonButtonGroupWidget* rightButtonGroup(); + + // 激活QuickAccessBar + SARibbonQuickAccessBar* activeQuickAccessBar(); + + // 快速响应栏 + SARibbonQuickAccessBar* quickAccessBar(); + + // 设置ribbon的风格 + void setRibbonStyle(RibbonStyles v); + RibbonStyles currentRibbonStyle() const; + + // 设置当前ribbon的index + void setCurrentIndex(int index); + + // 返回当前的tab索引 + int currentIndex(); + + // 确保标签显示出来 + void raiseCategory(SARibbonCategory* category); + + // 判断当前的行数 + bool isTwoRowStyle() const; + bool isThreeRowStyle() const; + + // 判断当前的样式 + bool isLooseStyle() const; + bool isCompactStyle() const; + + // 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局,在发现刷新问题时,可以调用此函数 + void updateRibbonGeometry(); + + // 设置pannel的模式 + SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); + + // 设置tab在title上面,这样可以省略title区域 + void setTabOnTitle(bool on); + bool isTabOnTitle() const; + + // tabbar 底部会绘制一条线条,此接口定义线条颜色 + void setTabBarBaseLineColor(const QColor& clr); + QColor tabBarBaseLineColor() const; + + // 设置标题颜色,如果不设置标题颜色,默认是SARibbonBar的qss的color属性 + void setWindowTitleTextColor(const QColor& clr); + QColor windowTitleTextColor() const; + + // 设置标题的对齐方式 + void setWindowTitleAligment(Qt::Alignment al); + Qt::Alignment windowTitleAligment() const; + + // 设置按钮允许换行,注意图标大小是由文字决定的,两行文字会让图标变小,如果想图标变大,文字不换行是最好的 + void setEnableWordWrap(bool on); + bool isEnableWordWrap() const; + + // 设置pannel的标题栏高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int h); + + // 设置pannel是否显示标题栏 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + + // 设置pannel的spacing + void setPannelSpacing(int n); + int pannelSpacing() const; + + // 设置pannel按钮的icon尺寸,large action不受此尺寸影响 + void setPannelToolButtonIconSize(const QSize& s); + QSize pannelToolButtonIconSize() const; + + // 获取SARibbonStackedWidget,谨慎使用此函数 + SARibbonStackedWidget* ribbonStackedWidget(); + + // 设置是否显示标题 + void setTitleVisible(bool on = false); + bool isTitleVisible() const; + + // 上下文标签的颜色列表,上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 + void setContextCategoryColorList(const QList< QColor >& cls); + QList< QColor > contextCategoryColorList() const; + + // 设置context category 标题的文字颜色 + void setContextCategoryTitleTextColor(const QColor& clr); + QColor contextCategoryTitleTextColor() const; + + // 设置ribbon的对齐方式 + void setRibbonAlignment(SARibbonAlignment al); + SARibbonAlignment ribbonAlignment() const; + + // 此函数会遍历SARibbonBar下的所有Category,执行函数指针(bool(SARibbonCategory*)),函数指针返回false则停止迭代 + bool iterate(FpCategoryIterate fp); + // 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针(bool(SARibbonPannel*)),函数指针返回false则停止迭代 + bool iterate(FpPannelIterate fp); + + // 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget + void setCornerWidgetVisible(bool on, Qt::Corner c = Qt::TopLeftCorner); +Q_SIGNALS: + + /** + @brief 应用按钮点击响应 - 左上角的按钮,通过关联此信号触发应用按钮点击的效果 + + 例如想点击按钮后弹出一个全屏的窗口(如office这些) + */ + void applicationButtonClicked(); + + /** + @brief 标签页变化触发的信号 + @param index + */ + void currentRibbonTabChanged(int index); + + /** + @brief ribbon的状态发生了变化后触发此信号 + @param nowState 变更之后的ribbon状态 + */ + void ribbonModeChanged(SARibbonBar::RibbonMode nowState); + + /** + @brief ribbon的状态发生了变化后触发此信号 + @param nowStyle 变更之后的ribbon样式 + */ + void ribbonStyleChanged(SARibbonBar::RibbonStyles nowStyle); + + /** + @brief 标题栏高度发生了变化的信号 + @param oldHeight + @param newHeight + */ + void titleBarHeightChanged(int oldHeight, int newHeight); + + /** + * @brief 参考QToolBar::actionTriggered的信号 + * @param action + */ + void actionTriggered(QAction* action); + +protected: + bool eventFilter(QObject* obj, QEvent* e) override; + // 根据情况重置tabbar的宽度,主要针对wps模式 + int calcMinTabBarWidth() const; + // 更新 + void updateCategoryTitleToTabName(); + // 告知WindowButtonGroup的尺寸 + void setWindowButtonGroupSize(const QSize& s); +protected Q_SLOTS: + void onWindowTitleChanged(const QString& title); + void onWindowIconChanged(const QIcon& i); + void onCategoryWindowTitleChanged(const QString& title); + void onStackWidgetHided(); + virtual void onCurrentRibbonTabChanged(int index); + virtual void onCurrentRibbonTabClicked(int index); + virtual void onCurrentRibbonTabDoubleClicked(int index); + void onContextsCategoryPageAdded(SARibbonCategory* category); + void onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title); + void onTabMoved(int from, int to); + +private: + int tabIndex(SARibbonCategory* obj); + void resizeAll(); + void resizeInLooseStyle(); + void resizeInCompactStyle(); + void paintInLooseStyle(); + void paintInCompactStyle(); + void resizeStackedContainerWidget(); + + // 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 + void updateContextCategoryManagerData(); + void synchronousCategoryData(bool autoUpdate = true); + +protected: + virtual void paintEvent(QPaintEvent* e) override; + virtual void resizeEvent(QResizeEvent* e) override; + virtual void moveEvent(QMoveEvent* e) override; + virtual void changeEvent(QEvent* e) override; + virtual bool event(QEvent* e) override; + virtual void paintTabbarBaseLine(QPainter& painter); + virtual void paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion); + virtual void + paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color); +#if SA_DEBUG_PRINT_SARIBBONBAR + SA_RIBBON_EXPORT friend QDebug operator<<(QDebug debug, const SARibbonBar& ribbon); +#endif +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(SARibbonBar::RibbonStyles) + +#endif // SARIBBONBAR_H + +/*** End of inlined file: SARibbonBar.h ***/ + +/*** Start of inlined file: SARibbonElementFactory.h ***/ +#ifndef SARIBBONELEMENTFACTORY_H +#define SARIBBONELEMENTFACTORY_H + +#include +#include +#include +#include + +class QWidget; +class SARibbonBar; +class SARibbonTabBar; +class SARibbonApplicationButton; +class SARibbonCategory; +class SARibbonContextCategory; +class SARibbonPannel; +class SARibbonSeparatorWidget; +class SARibbonGallery; +class SARibbonGalleryGroup; +class SARibbonToolButton; +class SARibbonControlButton; +class SARibbonControlToolButton; +class SARibbonButtonGroupWidget; +class SARibbonStackedWidget; +class SARibbonQuickAccessBar; +class SARibbonPannelOptionButton; +class SARibbonSystemButtonBar; + +/// +/// \brief SARibbon的子元素创建的工厂,SARibbon内部创建子元素都通过SARibbonElementFactory来创建 +/// +/// 由于SARibbonBar是一个复合控件,很多子窗口组合而成,有些部件的创建如果想继承,那么就需要这个工厂类来处理 +/// 如SARibbonCategory,可以重载此类的createRibbonCategory,返回重载的类的实例 +/// +class SA_RIBBON_EXPORT SARibbonElementFactory +{ +public: + SARibbonElementFactory(); + virtual ~SARibbonElementFactory(); + virtual SARibbonBar* createRibbonBar(QWidget* parent); + virtual SARibbonTabBar* createRibbonTabBar(QWidget* parent); + virtual SARibbonApplicationButton* createRibbonApplicationButton(QWidget* parent); + virtual SARibbonCategory* createRibbonCategory(QWidget* parent); + virtual SARibbonContextCategory* createRibbonContextCategory(QWidget* parent); + virtual SARibbonPannel* createRibbonPannel(QWidget* parent); + virtual SARibbonSeparatorWidget* createRibbonSeparatorWidget(QWidget* parent); + virtual SARibbonGallery* createRibbonGallery(QWidget* parent); + virtual SARibbonGalleryGroup* createRibbonGalleryGroup(QWidget* parent); + virtual SARibbonToolButton* createRibbonToolButton(QWidget* parent); + virtual SARibbonControlButton* createRibbonControlButton(QWidget* parent); + virtual SARibbonControlToolButton* createRibbonControlToolButton(QWidget* parent); + virtual SARibbonStackedWidget* createRibbonStackedWidget(SARibbonBar* parent); + virtual SARibbonButtonGroupWidget* craeteButtonGroupWidget(QWidget* parent); + virtual SARibbonQuickAccessBar* createQuickAccessBar(QWidget* parent); + virtual SARibbonSystemButtonBar* createWindowButtonGroup(QWidget* parent); + virtual SARibbonPannelOptionButton* createRibbonPannelOptionButton(SARibbonPannel* pannel); +}; + +#endif // SARIBBONELEMENTFACTORY_H + +/*** End of inlined file: SARibbonElementFactory.h ***/ + +/*** Start of inlined file: SARibbonElementManager.h ***/ +#ifndef SARIBBONELEMENTMANAGER_H +#define SARIBBONELEMENTMANAGER_H + +/** + @brief 此类是一个全局单例,用于管理SARibbonElementFactory + + 如果你有自己的某个部件要重写,首先你需要有自己的ElementFactory + @code + class MyRibbonElementFactory : public SARibbonElementFactory{ + public: + ... + virtual SARibbonPannel* createRibbonPannel(QWidget* parent){ + return new MyRibbonPannel(parent); + } + }; + @endcode + + 然后,你需要在ribbonbar创建之前把自己的ElementFactory设置进去 + + 这个一般会在main函数中进行 + + @code + SARibbonElementManager::instance()->setupFactory(new MyRibbonElementFactory); + @endcode + + 这样,SARibbon创建的pannel就是你自己重写的MyRibbonPannel + + */ +class SA_RIBBON_EXPORT SARibbonElementManager +{ +protected: + SARibbonElementManager(); + +public: + virtual ~SARibbonElementManager(); + static SARibbonElementManager* instance(); + SARibbonElementFactory* factory(); + void setupFactory(SARibbonElementFactory* fac); + +private: + QScopedPointer< SARibbonElementFactory > mFactory; +}; +#ifndef RibbonSubElementMgr +#define RibbonSubElementMgr SARibbonElementManager::instance() +#endif +#ifndef RibbonSubElementFactory +#define RibbonSubElementFactory SARibbonElementManager::instance()->factory() +#endif + +#endif // SARIBBONELEMENTMANAGER_H + +/*** End of inlined file: SARibbonElementManager.h ***/ + +/*** Start of inlined file: SARibbonCustomizeData.h ***/ +#ifndef SARIBBONCUSTOMIZEDATA_H +#define SARIBBONCUSTOMIZEDATA_H + +#include +class SARibbonBar; +class SARibbonMainWindow; + +/** + * @brief 记录所有自定义操作的数据类 + * @note 此数据依赖于@ref SARibbonActionsManager 要在SARibbonActionsManager之后使用此类 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeData +{ +public: + enum ActionType + { + UnknowActionType = 0, ///< 未知操作 + AddCategoryActionType, ///< 添加category操作(1) + AddPannelActionType, ///< 添加pannel操作(2) + AddActionActionType, ///< 添加action操作(3) + RemoveCategoryActionType, ///< 删除category操作(4) + RemovePannelActionType, ///< 删除pannel操作(5) + RemoveActionActionType, ///< 删除action操作(6) + ChangeCategoryOrderActionType, ///< 改变category顺序的操作(7) + ChangePannelOrderActionType, ///< 改变pannel顺序的操作(8) + ChangeActionOrderActionType, ///< 改变action顺序的操作(9) + RenameCategoryActionType, ///< 对category更名操作(10) + RenamePannelActionType, ///< 对Pannel更名操作(11) + VisibleCategoryActionType ///< 对category执行隐藏/显示操作(12) + }; + SARibbonCustomizeData(); + SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr = nullptr); + // 获取CustomizeData的action type + ActionType actionType() const; + + // 设置CustomizeData的action type + void setActionType(ActionType a); + + // 判断是否是一个正常的CustomizeData + bool isValid() const; + + // 应用SARibbonCustomizeData + bool apply(SARibbonBar* bar) const; + + // 获取actionmanager指针 + SARibbonActionsManager* actionManager(); + + // 设置ActionsManager + void setActionsManager(SARibbonActionsManager* mgr); + + // 对应AddCategoryActionType + static SARibbonCustomizeData makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName); + + // 对应AddPannelActionType + static SARibbonCustomizeData + makeAddPannelCustomizeData(const QString& title, int index, const QString& categoryobjName, const QString& objName); + + // 对应AddActionActionType + static SARibbonCustomizeData makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName); + + // 对应RenameCategoryActionType + static SARibbonCustomizeData makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName); + + // 对应RenamePannelActionType + static SARibbonCustomizeData + makeRenamePannelCustomizeData(const QString& newname, const QString& categoryobjName, const QString& pannelObjName); + + // 对应RemoveCategoryActionType + static SARibbonCustomizeData makeRemoveCategoryCustomizeData(const QString& categoryobjName); + + // 对应ChangeCategoryOrderActionType + static SARibbonCustomizeData makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex); + + // 对应ChangePannelOrderActionType + static SARibbonCustomizeData + makeChangePannelOrderCustomizeData(const QString& categoryobjName, const QString& pannelObjName, int moveindex); + + // 对应ChangeActionOrderActionType + static SARibbonCustomizeData makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex); + + // 对应RemovePannelActionType + static SARibbonCustomizeData makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName); + + // 对应RemoveActionActionType + static SARibbonCustomizeData makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr); + + // 对应VisibleCategoryActionType + static SARibbonCustomizeData makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow); + + // 判断是否可以自定义,如果某个action不想被编辑,可以通过此函数设置 + static bool isCanCustomize(QObject* obj); + static void setCanCustomize(QObject* obj, bool canbe = true); + + // 对QList进行简化 + static QList< SARibbonCustomizeData > simplify(const QList< SARibbonCustomizeData >& csd); + +public: + /** + * @brief 记录顺序的参数 + * + * 在actionType==AddCategoryActionType时,此参数记录Category的insert位置, + * 在actionType==AddPannelActionType时,此参数记录pannel的insert位置, + * 在actionType==AddActionActionType时,此参数记录pannel的insert位置 + */ + int indexValue; + + /** + * @brief 记录标题、索引等参数 + * + * 在actionType==AddCategoryActionType时,key为category标题, + * 在actionType==AddPannelActionType时,key为pannel标题, + * 在actionType==AddActionActionType时,key为action的查询依据,基于SARibbonActionsManager::action查询 + */ + QString keyValue; + + /** + * @brief 记录categoryObjName,用于定位Category + */ + QString categoryObjNameValue; + + /** + * @brief 记录pannelObjName,saribbon的Customize索引大部分基于objname + */ + QString pannelObjNameValue; + + SARibbonPannelItem::RowProportion actionRowProportionValue; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +private: + ActionType mType; ///< 标记这个data是category还是pannel亦或是action + SARibbonActionsManager* mActionsManagerPointer; +}; +Q_DECLARE_METATYPE(SARibbonCustomizeData) + +typedef QList< SARibbonCustomizeData > SARibbonCustomizeDataList; + +#endif // SARIBBONCUSTOMIZEDATA_H + +/*** End of inlined file: SARibbonCustomizeData.h ***/ + +/*** Start of inlined file: SARibbonCustomizeWidget.h ***/ +#ifndef SARIBBONCUSTOMIZEWIDGET_H +#define SARIBBONCUSTOMIZEWIDGET_H + +#include + +// SARibbonCustomizeWidget 特有 +class SARibbonCustomizeWidgetUi; +class SARibbonMainWindow; +class SARibbonBar; +// +class QStandardItemModel; +class QStandardItem; +class QAbstractButton; +// +class QXmlStreamWriter; +class QXmlStreamReader; + +/** + * @brief 自定义界面窗口 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeWidget::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeWidget : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCustomizeWidget) +public: + // 保留接口 + explicit SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, + QWidget* parent = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + // 对于不使用SARibbonMainWindow的情况,使用此构造函数 + explicit SARibbonCustomizeWidget(SARibbonBar* ribbonbar, + QWidget* parent = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + ~SARibbonCustomizeWidget(); + + /** + * @brief 定义ribbon树的显示类型 + */ + enum RibbonTreeShowType + { + ShowAllCategory, ///< 显示所有Category,包括contextcategory + ShowMainCategory ///< 显示主要的category,不包含上下文 + }; + + /** + * @brief QStandardItem对应的role + */ + enum ItemRole + { + LevelRole = Qt::UserRole + 1, ///< 代表这是层级,有0:category 1:pannel 2:item + PointerRole = Qt::UserRole + 2, ///< 代表这是存放指针。根据LevelRole来进行转 + CanCustomizeRole = Qt::UserRole + 3, ///< 代表个item是可以自定义的.bool + CustomizeRole = Qt::UserRole + 4, ///< 代表这个是自定义的item,bool,主要用于那些自己添加的标签和pannel,有此角色必有CanCustomizeRole + CustomizeObjNameRole = Qt::UserRole + 5 ///< 记录了临时的自定义内容的obj名 QString + }; + + // 设置action管理器 + void setupActionsManager(SARibbonActionsManager* mgr); + + // 判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + // 判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + // 获取model + const QStandardItemModel* model() const; + + // 根据当前的radiobutton选项来更新model + void updateModel(); + + // 更新model + void updateModel(RibbonTreeShowType type); + + // 应用所有的设定 + bool applys(); + + // 转换为xml + bool toXml(QXmlStreamWriter* xml) const; + bool toXml(const QString& xmlpath) const; + + // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader* xml); + void fromXml(const QString& xmlpath); + + // 应用xml配置,可以结合customize_datas_from_xml和customize_datas_apply函数 + static bool fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr); + + // 缓存应用的动作,这些动作不会被clear清除,用于本地存储 + void makeActionsApplied(); + + // 清除applied的动作,cancel操作后需要清空已应用的动作 + void clearApplied(); + // 清除缓存动作,在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + void clearCache(); + // 清除所有动作,不包含本地读取的数据 + void clear(); + +protected: + // 把QList进行裁剪,把一些动作合并 + void simplify(); + + SARibbonPannelItem::RowProportion selectedRowProportion() const; + + QAction* selectedAction() const; + QAction* itemToAction(QStandardItem* item) const; + + QStandardItem* selectedItem() const; + + // 获取选中的ribbon tree 的level + int selectedRibbonLevel() const; + + // 根据选中的item判断 + int itemLevel(QStandardItem* item) const; + + // 设置某个item被选中 + void setSelectItem(QStandardItem* item, bool ensureVisible = true); + + // 判断itemn能否改动,可以改动返回true + bool isItemCanCustomize(QStandardItem* item) const; + bool isSelectedItemCanCustomize() const; + + // 判断item是否是自定义的item + bool isCustomizeItem(QStandardItem* item) const; + bool isSelectedItemIsCustomize() const; + + // 删除一个item + void removeItem(QStandardItem* item); + +private Q_SLOTS: + void onComboBoxActionIndexCurrentIndexChanged(int index); + void onRadioButtonGroupButtonClicked(QAbstractButton* b); + void onPushButtonNewCategoryClicked(); + void onPushButtonNewPannelClicked(); + void onPushButtonRenameClicked(); + void onPushButtonAddClicked(); + void onPushButtonDeleteClicked(); + void onListViewSelectClicked(const QModelIndex& index); + void onTreeViewResultClicked(const QModelIndex& index); + void onToolButtonUpClicked(); + void onToolButtonDownClicked(); + void onItemChanged(QStandardItem* item); + void onLineEditSearchActionTextEdited(const QString& text); + void onPushButtonResetClicked(); + +private: + void init(SARibbonBar* ribbonbar); + void initConnection(); + +private: + SARibbonCustomizeWidgetUi* ui; +}; + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * @param xml QXmlStreamWriter指针 + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @param cds 基于QList生成的步骤 + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + */ +bool SA_RIBBON_EXPORT sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds); + +/** + * @brief 通过xml获取QList + * @param xml + * @return QList + */ +QList< SARibbonCustomizeData > SA_RIBBON_EXPORT sa_customize_datas_from_xml(QXmlStreamReader* xml, + SARibbonActionsManager* mgr); + +/** + * @brief 应用QList + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 反向取消应用 + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 直接加载xml自定义ribbon配置文件用于ribbon的自定义显示 + * @param filePath xml配置文件 + * @param w 主窗体 + * @param mgr action管理器 + * @return 成功返回true + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * static bool has_call = false; + * if (!has_call) { + * has_call = sa_apply_customize_from_xml_file("customize.xml", this, m_actMgr); + * } + * @endcode + */ +bool SA_RIBBON_EXPORT sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr); + +#endif // SARIBBONCUSTOMIZEWIDGET_H + +/*** End of inlined file: SARibbonCustomizeWidget.h ***/ + +/*** Start of inlined file: SARibbonCustomizeDialog.h ***/ +#ifndef SARIBBONCUSTOMIZEDIALOG_H +#define SARIBBONCUSTOMIZEDIALOG_H + +#include + +class SARibbonActionsManager; +class SARibbonCustomizeDialogUi; +class QXmlStreamWriter; + +/** + * @brief SARibbonCustomizeWidget的对话框封装 + * + * 此功能依赖于@ref SARibbonActionsManager ,SARibbonActionsManager建议作为mianwindow的成员变量, + * SARibbonActionsManager可以快速绑定所有QAction,详细见SARibbonActionsManager的说明 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeDialog::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeDialog : public QDialog +{ + Q_OBJECT +public: + explicit SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, + QWidget* p = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + ~SARibbonCustomizeDialog(); + // 设置action管理器 + void setupActionsManager(SARibbonActionsManager* mgr); + + // 判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + // 判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + // 应用所有的设定 + bool applys(); + + // 清除所有动作 + void clear(); + + // 转换为xml + bool toXml(QXmlStreamWriter* xml) const; + bool toXml(const QString& xmlpath) const; + + // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader* xml); + void fromXml(const QString& xmlpath); + + // 返回SARibbonCustomizeWidget窗口指针 + SARibbonCustomizeWidget* customizeWidget() const; + +private: + void initConnection(); + + SARibbonCustomizeDialogUi* ui; +}; + +#endif // SARIBBONCUSTOMIZEDIALOG_H + +/*** End of inlined file: SARibbonCustomizeDialog.h ***/ + +/*** Start of inlined file: SARibbonMainWindow.h ***/ +#ifndef SARIBBONMAINWINDOW_H +#define SARIBBONMAINWINDOW_H + +#include + +#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +class SAFramelessHelper; +#endif +class QAction; +class SARibbonBar; +class SARibbonSystemButtonBar; +class QScreen; +/** + * @brief 如果要使用SARibbonBar,必须使用此类代替QMainWindow + * + * 由于ribbon的风格和传统的Toolbar风格差异较大, + * SARibbonBar使用需要把原有的QMainWindow替换为SARibbonMainWindow, + * SARibbonMainWindow是个无边框窗体,继承自QMainWindow(目前使用第三方的无边框方案https://github.com/wangwenx190/framelesshelper), + * 其构造函数的参数useRibbon用于指定是否使用ribbon风格,默认为true + * + * @code + * SARibbonMainWindow(QWidget* parent = nullptr,bool useRibbon = true); + * @endcode + * + * 如果想换回非ribbon风格,只需要把useRibbon设置为false即可, + * 成员函数isUseRibbon用于判断当前是否为ribbon模式,这个函数在兼容传统Toolbar风格和ribbon风格时非常有用 + * + * 但这个不支持动态切换,因此,实际工程中,你若要进行切换,需要写配置文件,程序在构造时传入模式, + * 并且,你的程序要做好判断,因为非Ribbon模式下,所有Ribbon相关的接口都会返回空指针 + * + * @code + * bool isUseRibbon() const; + * @endcode + * + * @ref SARibbonMainWindow 提供了几种常用的ribbon样式 + * 通过@ref setRibbonTheme 可改变ribbon的样式,用户也可通过qss自己定义自己的样式 + * + * 如果你自己继承了SARibbonBar,你可以通过@ref setRibbonBar 设置自己的ribbonbar进去 + * + * 另外有个一个更加高效的方法,来实现自定义的Ribbon,就是继承一个@ref SARibbonElementFactory + * + * @code + * class MyRibbonFactory:public SARibbonElementFactory{ + * ... + * virtual SARibbonBar* createRibbonBar(QWidget* parent){ + * return new MyRibbonBar(parent); + * } + * }; + * @endcode + * + * SARibbonMainWindow生成之前(一般在main函数),设置元件工厂: + * + * @code + * SARibbonElementManager::instance()->setupFactory(new MyRibbonFactory()); + * @endcode + * + * 此时,Ribbon的元素会通过MyRibbonFactory的接口来生成 + */ +class SA_RIBBON_EXPORT SARibbonMainWindow : public QMainWindow +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonMainWindow) + friend class SARibbonBar; + Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) + +public: + explicit SARibbonMainWindow(QWidget* parent = nullptr, bool useRibbon = true, const Qt::WindowFlags flags = {}); + ~SARibbonMainWindow() override; + // 返回SARibbonBar + SARibbonBar* ribbonBar() const; + // 设置ribbonbar + void setRibbonBar(SARibbonBar* ribbon); +#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + // 返回SAFramelessHelper + SAFramelessHelper* framelessHelper(); +#else + // 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 + void setFramelessHitTestVisible(QWidget* w, bool visible = true); +#endif + // 此函数仅用于控制最小最大化和关闭按钮的显示 + void updateWindowFlag(Qt::WindowFlags flags); + + // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + void setRibbonTheme(SARibbonTheme theme); + SARibbonTheme ribbonTheme() const; + // 判断当前是否使用ribbon模式 + bool isUseRibbon() const; + // 把ribbonbar的事件传递到frameless + virtual bool eventFilter(QObject* obj, QEvent* e) Q_DECL_OVERRIDE; + // 获取最大化,最小化,关闭按钮所在的bar。可以通过此函数在最大最小化按钮旁边设置内容 + SARibbonSystemButtonBar* windowButtonBar() const; + +protected: + // 创建ribbonbar的工厂函数 + SARibbonBar* createRibbonBar(); +private Q_SLOTS: + void onPrimaryScreenChanged(QScreen* screen); +}; + +/** + * @brief 针对SARibbonMainWindow的事件处理器,主要处理systembar的位置调整 + */ +class SA_RIBBON_EXPORT SARibbonMainWindowEventFilter : public QObject +{ + Q_OBJECT +public: + explicit SARibbonMainWindowEventFilter(QObject* par); + ~SARibbonMainWindowEventFilter(); + virtual bool eventFilter(QObject* obj, QEvent* e) override; +}; + +#endif // SARIBBONMAINWINDOW_H + +/*** End of inlined file: SARibbonMainWindow.h ***/ + +/*** Start of inlined file: SARibbonWidget.h ***/ +#ifndef SARIBBONWIDGET_H +#define SARIBBONWIDGET_H + +#include + +class SARibbonBar; +class QScreen; +/** + * @brief 带SARibbonBar的Widget + * + * @note 注意,SARibbonWidget利用了布局来把ribbonbar放到最顶部,不要给此widget添加布局,否则会导致布局异常, + * 正确的做法是把布局的窗口通过setWidget设置进SARibbonWidget + * + */ +class SA_RIBBON_EXPORT SARibbonWidget : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonWidget) + friend class SARibbonBar; + Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) + +public: + explicit SARibbonWidget(QWidget* parent = nullptr); + ~SARibbonWidget(); + // 返回SARibbonBar + SARibbonBar* ribbonBar() const; + // 设置ribbonbar + void setRibbonBar(SARibbonBar* bar); + + // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonTheme::RibbonThemeDark); }); + void setRibbonTheme(SARibbonTheme theme); + SARibbonTheme ribbonTheme() const; + // 判断当前是否使用ribbon模式 + bool isUseRibbon() const; + // 设置窗口 + void setWidget(QWidget* w); + QWidget* widget() const; + // + QWidget* takeWidget(); +private Q_SLOTS: + void onPrimaryScreenChanged(QScreen* screen); +}; + +#endif // SARIBBONWIDGET_H + +/*** End of inlined file: SARibbonWidget.h ***/ + +/*** End of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ + +#endif diff --git a/src/SARibbonBar/SARibbonActionsManager.cpp b/src/SARibbonBar/SARibbonActionsManager.cpp index 2d9965a5..8fd800a5 100644 --- a/src/SARibbonBar/SARibbonActionsManager.cpp +++ b/src/SARibbonBar/SARibbonActionsManager.cpp @@ -1,638 +1,638 @@ -#include "SARibbonActionsManager.h" -#include -#include -#include -#include -#include "SARibbonBar.h" - -class SARibbonActionsManager::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager) -public: - PrivateData(SARibbonActionsManager* p); - void clear(); - - QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList - QMap< int, QString > mTagToName; ///< tag对应的名字 - QHash< QString, QAction* > mKeyToAction; ///< key对应action - QMap< QAction*, QString > mActionToKey; ///< action对应key - QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用 - int mSale; ///< 盐用于生成固定的id,在用户不主动设置key时,id基于msale生成,只要SARibbonActionsManager的调用registeAction顺序不变,生成的id都不变,因为它是基于自增实现的 -}; - -SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0) -{ -} - -void SARibbonActionsManager::PrivateData::clear() -{ - mTagToActions.clear(); - mTagToName.clear(); - mKeyToAction.clear(); - mActionToKey.clear(); - mTagToCategory.clear(); - mSale = 0; -} - -SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar) - : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this)) -{ - autoRegisteActions(bar); -} - -SARibbonActionsManager::~SARibbonActionsManager() -{ -} - -/** - * @brief 设置tag对应的名字,通过这个可以得到tag和文本的映射 - * @param tag - * @param name - * @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字 - */ -void SARibbonActionsManager::setTagName(int tag, const QString& name) -{ - d_ptr->mTagToName[ tag ] = name; -} - -/** - * @brief 获取tag对应的中文名字 - * @param tag - * @return - */ -QString SARibbonActionsManager::tagName(int tag) const -{ - return (d_ptr->mTagToName.value(tag, "")); -} - -/** - * @brief 移除tag - * @note 注意,这个函数非常耗时 - * @param tag - */ -void SARibbonActionsManager::removeTag(int tag) -{ - QList< QAction* > oldacts = actions(tag); - - // 开始移除 - d_ptr->mTagToActions.remove(tag); - d_ptr->mTagToName.remove(tag); - // 开始查找需要移出总表的action - QList< QAction* > needRemoveAct; - QList< QAction* > total; - - for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { - total += i.value(); - } - for (QAction* a : qAsConst(oldacts)) { - if (!total.contains(a)) { - needRemoveAct.append(a); - } - } - // 从总表移除action - for (QAction* a : qAsConst(needRemoveAct)) { - auto i = d_ptr->mActionToKey.find(a); - if (i != d_ptr->mActionToKey.end()) { - d_ptr->mKeyToAction.remove(i.value()); - d_ptr->mActionToKey.erase(i); - } - } -} - -/** - * @brief 把action注册到管理器中,实现action的管理 - * @param act - * @param tag tag是可以按照位进行叠加,见 @ref ActionTag 如果 - * 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值, - * registeAction的tag是直接记录进去的,如果要多个标签并存,在registe之前先或好tag - * @param key key是action对应的key,一个key只对应一个action,是查找action的关键 - * ,默认情况为一个QString(),这时key是QAction的objectName - * @param enableEmit 控制是否发射@ref actionTagChanged 信号 - * @note 同一个action多次注册不同的tag可以通过tag索引到action,但通过action只能索引到最后一个注册的tag - * @note tag的新增会触发actionTagChanged信号 - */ -bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit) -{ - if (nullptr == act) { - return (false); - } - QString k = key; - - if (k.isEmpty()) { - k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName()); - } - if (d_ptr->mKeyToAction.contains(k)) { - qWarning() << "key: " - << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction"; - return (false); - } - d_ptr->mKeyToAction[ k ] = act; - d_ptr->mActionToKey[ act ] = k; - // 记录tag 对 action - bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号 - d_ptr->mTagToActions[ tag ].append(act); - // 绑定槽 - connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); - if (isneedemit && enableEmit) { - // 说明新增tag - Q_EMIT actionTagChanged(tag, false); - } - return (true); -} - -/** - * @brief 取消action的注册 - * - * 如果tag对应的最后一个action被撤销,tag也将一块删除 - * @param act - * @note tag的删除会触发actionTagChanged信号 - * @note 如果action关联了多个tag,这些tag里的action都会被删除,对应的key也同理 - */ -void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit) -{ - if (nullptr == act) { - return; - } - // 绑定槽 - disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); - removeAction(act, enableEmit); -} - -/** - * @brief 移除action - * - * 仅移除内存内容 - * @param act - * @param enableEmit - */ -void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit) -{ - QList< int > deletedTags; // 记录删除的tag,用于触发actionTagChanged - QMap< int, QList< QAction* > > tagToActions; ///< tag : QList - - for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { - // 把不是act的内容转移到tagToActions和tagToActionKeys中,之后再和m_d里的替换 - auto tmpi = tagToActions.insert(i.key(), QList< QAction* >()); - int count = 0; - for (int j = 0; j < i.value().size(); ++j) { - if (i.value()[ j ] != act) { - tmpi.value().append(act); - ++count; - } - } - if (0 == count) { - // 说明这个tag没有内容 - tagToActions.erase(tmpi); - deletedTags.append(i.key()); - } - } - // 删除mKeyToAction - QString key = d_ptr->mActionToKey.value(act); - - d_ptr->mActionToKey.remove(act); - d_ptr->mKeyToAction.remove(key); - - // 置换 - d_ptr->mTagToActions.swap(tagToActions); - // 发射信号 - if (enableEmit) { - for (int tagdelete : qAsConst(deletedTags)) { - Q_EMIT actionTagChanged(tagdelete, true); - } - } -} - -/** - * @brief 等同actions - * @param tag - * @return - */ -QList< QAction* >& SARibbonActionsManager::filter(int tag) -{ - return (actions(tag)); -} - -/** - * @brief 根据tag得到actions - * @param tag - * @return - */ -QList< QAction* >& SARibbonActionsManager::actions(int tag) -{ - return (d_ptr->mTagToActions[ tag ]); -} - -const QList< QAction* > SARibbonActionsManager::actions(int tag) const -{ - return (d_ptr->mTagToActions[ tag ]); -} - -/** - * @brief 获取所有的标签 - * @return - */ -QList< int > SARibbonActionsManager::actionTags() const -{ - return (d_ptr->mTagToActions.keys()); -} - -/** - * @brief 通过key获取action - * @param key - * @return 如果没有key,返回nullptr - */ -QAction* SARibbonActionsManager::action(const QString& key) const -{ - return (d_ptr->mKeyToAction.value(key, nullptr)); -} - -/** - * @brief 通过action找到key - * @param act - * @return 如果找不到,返回QString() - */ -QString SARibbonActionsManager::key(QAction* act) const -{ - return (d_ptr->mActionToKey.value(act, QString())); -} - -/** - * @brief 返回所有管理的action数 - * @return - */ -int SARibbonActionsManager::count() const -{ - return (d_ptr->mKeyToAction.size()); -} - -/** - * @brief 返回所有管理的actions - * @return - */ -QList< QAction* > SARibbonActionsManager::allActions() const -{ - return (d_ptr->mKeyToAction.values()); -} - -/** - * @brief 自动加载SARibbonBar的action - * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object,找到action注册, - * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类 - * - * 此函数会把所有category下的action生成tag并注册,返回的QMap是记录了category对应的tag - * - * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action,但不在任何一个category下的作为NotInRibbonCategoryTag标签注册,默认名字会赋予not - * in ribbon, 可以通过@ref setTagName 改变 - * - * @param w - * @return - * @note 此函数的调用最好在category设置了标题后调用,因为会以category的标题作为标签的命名 - */ -QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar) -{ - QMap< int, SARibbonCategory* > res; - // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象,把所有action找到 - QWidget* parWidget = bar->parentWidget(); - QSet< QAction* > mainwindowActions; - if (parWidget) { - for (QObject* o : qAsConst(parWidget->children())) { - if (QAction* a = qobject_cast< QAction* >(o)) { - // 说明是action - if (!a->objectName().isEmpty()) { - mainwindowActions.insert(a); - } - } - } - } - // 开始遍历每个category,加入action - - if (nullptr == bar) { - // 非ribbon模式,直接退出 - return (res); - } - QSet< QAction* > categoryActions; - QList< SARibbonCategory* > categorys = bar->categoryPages(); - int tag = AutoCategoryDistinguishBeginTag; - - for (SARibbonCategory* c : qAsConst(categorys)) { - QList< SARibbonPannel* > pannels = c->pannelList(); - for (SARibbonPannel* p : qAsConst(pannels)) { - categoryActions += autoRegisteWidgetActions(p, tag, false); - } - setTagName(tag, c->categoryName()); - res[ tag ] = c; - ++tag; - } - // 找到不在功能区的actions - QSet< QAction* > notincategory = mainwindowActions - categoryActions; - - for (QAction* a : qAsConst(notincategory)) { - if (!a->objectName().isEmpty()) { - registeAction(a, NotInRibbonCategoryTag, a->objectName(), false); - } - } - if (notincategory.size() > 0) { - setTagName(NotInRibbonCategoryTag, tr("not in ribbon")); - } - for (auto i = res.begin(); i != res.end(); ++i) { - connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged); - } - d_ptr->mTagToCategory = res; - return (res); -} - -/** - * @brief 自动加载action - * @param w - * @param tag - * @param enableEmit - * @return 返回成功加入SARibbonActionsManager管理的action - */ -QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit) -{ - QSet< QAction* > res; - QList< QAction* > was = w->actions(); - - for (QAction* a : qAsConst(was)) { - if (res.contains(a) || a->objectName().isEmpty()) { - // 重复内容不重复加入 - // 没有object name不加入 - continue; - } - if (registeAction(a, tag, a->objectName(), enableEmit)) { - res.insert(a); - } - } - return (res); -} - -/** - * @brief 根据标题查找action - * @param text - * @return - */ -QList< QAction* > SARibbonActionsManager::search(const QString& text) -{ - QList< QAction* > res; - - if (text.isEmpty()) { - return (res); - } - QStringList kws = text.split(" "); - - if (kws.isEmpty()) { - kws.append(text); - } - QList< QAction* > acts = d_ptr->mActionToKey.keys(); - - for (const QString& k : qAsConst(kws)) { - for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) { - if (i.key()->text().contains(k, Qt::CaseInsensitive)) { - res.append(i.key()); - } - } - } - return (res); -} - -void SARibbonActionsManager::clear() -{ - d_ptr->clear(); -} - -/** - * @brief action 被delete时,将触发此槽把管理的action删除 - * @param o - * @note 这个函数不会触发actionTagChanged信号 - */ -void SARibbonActionsManager::onActionDestroyed(QObject* o) -{ - QAction* act = static_cast< QAction* >(o); - - removeAction(act, false); -} - -/** - * @brief autoRegisteActions函数会关联此槽,在标签内容改变时改变tag 对应 文本 - * @param title - */ -void SARibbonActionsManager::onCategoryTitleChanged(const QString& title) -{ - SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender()); - - if (nullptr == c) { - return; - } - int tag = d_ptr->mTagToCategory.key(c, -1); - - if (tag == -1) { - return; - } - setTagName(tag, title); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonActionsModel -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -class SARibbonActionsManagerModel::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel) -public: - PrivateData(SARibbonActionsManagerModel* p); - void updateRef(); - int count() const; - QAction* at(int index); - bool isNull() const; - -public: - SARibbonActionsManager* mMgr { nullptr }; - int mTag { SARibbonActionsManager::CommonlyUsedActionTag }; - QString mSeatchText; - QList< QAction* > mActions; -}; - -SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p) -{ -} - -void SARibbonActionsManagerModel::PrivateData::updateRef() -{ - if (isNull()) { - return; - } - if (!mSeatchText.isEmpty()) { - mActions = mMgr->search(mSeatchText); - } else { - mActions = mMgr->actions(mTag); - } -} - -int SARibbonActionsManagerModel::PrivateData::count() const -{ - if (isNull()) { - return (0); - } - return (mActions.size()); -} - -QAction* SARibbonActionsManagerModel::PrivateData::at(int index) -{ - if (isNull()) { - return (nullptr); - } - if (index >= count()) { - return (nullptr); - } - return (mActions.at(index)); -} - -bool SARibbonActionsManagerModel::PrivateData::isNull() const -{ - return (mMgr == nullptr); -} - -//=================================================== -// SARibbonActionsManagerModel -//=================================================== - -SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p) - : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) -{ -} - -SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p) - : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) -{ - setupActionsManager(m); -} - -SARibbonActionsManagerModel::~SARibbonActionsManagerModel() -{ -} - -int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const -{ - if (parent.isValid()) { // 非顶层 - return (0); - } - // 顶层 - return (d_ptr->count()); -} - -QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - Q_UNUSED(section); - if (role != Qt::DisplayRole) { - return (QVariant()); - } - if (Qt::Horizontal == orientation) { - return (tr("action name")); - } - return (QVariant()); -} - -Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const -{ - if (!index.isValid()) { - return (Qt::NoItemFlags); - } - return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); -} - -QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const -{ - QAction* act = indexToAction(index); - - if (nullptr == act) { - return (QVariant()); - } - - switch (role) { - case Qt::DisplayRole: { - QString str = act->text(); - if (!str.isEmpty()) { - return str; - } - // 如果文本为空,一般是没有设置文本,或者是窗口,如果是窗口,那么用windowtitle作为文本 - if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) { - if (QWidget* w = wa->defaultWidget()) { - str = w->windowTitle(); - } - } - return str; - } - case Qt::DecorationRole: - return (act->icon()); - - default: - break; - } - return (QVariant()); -} - -void SARibbonActionsManagerModel::setFilter(int tag) -{ - d_ptr->mTag = tag; - update(); -} - -void SARibbonActionsManagerModel::update() -{ - beginResetModel(); - d_ptr->updateRef(); - endResetModel(); -} - -void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m) -{ - d_ptr->mMgr = m; - d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; - d_ptr->mActions = m->filter(d_ptr->mTag); - connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); - update(); -} - -void SARibbonActionsManagerModel::uninstallActionsManager() -{ - if (!d_ptr->isNull()) { - disconnect( - d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); - d_ptr->mMgr = nullptr; - d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; - } - update(); -} - -QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const -{ - if (!index.isValid()) { - return (nullptr); - } - if (index.row() >= d_ptr->count()) { - return (nullptr); - } - return (d_ptr->at(index.row())); -} - -/** - * @brief 查找 - * @param text - */ -void SARibbonActionsManagerModel::search(const QString& text) -{ - d_ptr->mSeatchText = text; - update(); -} - -void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete) -{ - if (isdelete && (tag == d_ptr->mTag)) { - d_ptr->mTag = SARibbonActionsManager::UnknowActionTag; - update(); - } else { - if (tag == d_ptr->mTag) { - update(); - } - } -} +#include "SARibbonActionsManager.h" +#include +#include +#include +#include +#include "SARibbonBar.h" + +class SARibbonActionsManager::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager) +public: + PrivateData(SARibbonActionsManager* p); + void clear(); + + QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList + QMap< int, QString > mTagToName; ///< tag对应的名字 + QHash< QString, QAction* > mKeyToAction; ///< key对应action + QMap< QAction*, QString > mActionToKey; ///< action对应key + QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用 + int mSale; ///< 盐用于生成固定的id,在用户不主动设置key时,id基于msale生成,只要SARibbonActionsManager的调用registeAction顺序不变,生成的id都不变,因为它是基于自增实现的 +}; + +SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0) +{ +} + +void SARibbonActionsManager::PrivateData::clear() +{ + mTagToActions.clear(); + mTagToName.clear(); + mKeyToAction.clear(); + mActionToKey.clear(); + mTagToCategory.clear(); + mSale = 0; +} + +SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar) + : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this)) +{ + autoRegisteActions(bar); +} + +SARibbonActionsManager::~SARibbonActionsManager() +{ +} + +/** + * @brief 设置tag对应的名字,通过这个可以得到tag和文本的映射 + * @param tag + * @param name + * @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字 + */ +void SARibbonActionsManager::setTagName(int tag, const QString& name) +{ + d_ptr->mTagToName[ tag ] = name; +} + +/** + * @brief 获取tag对应的中文名字 + * @param tag + * @return + */ +QString SARibbonActionsManager::tagName(int tag) const +{ + return (d_ptr->mTagToName.value(tag, "")); +} + +/** + * @brief 移除tag + * @note 注意,这个函数非常耗时 + * @param tag + */ +void SARibbonActionsManager::removeTag(int tag) +{ + QList< QAction* > oldacts = actions(tag); + + // 开始移除 + d_ptr->mTagToActions.remove(tag); + d_ptr->mTagToName.remove(tag); + // 开始查找需要移出总表的action + QList< QAction* > needRemoveAct; + QList< QAction* > total; + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + total += i.value(); + } + for (QAction* a : qAsConst(oldacts)) { + if (!total.contains(a)) { + needRemoveAct.append(a); + } + } + // 从总表移除action + for (QAction* a : qAsConst(needRemoveAct)) { + auto i = d_ptr->mActionToKey.find(a); + if (i != d_ptr->mActionToKey.end()) { + d_ptr->mKeyToAction.remove(i.value()); + d_ptr->mActionToKey.erase(i); + } + } +} + +/** + * @brief 把action注册到管理器中,实现action的管理 + * @param act + * @param tag tag是可以按照位进行叠加,见 @ref ActionTag 如果 + * 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值, + * registeAction的tag是直接记录进去的,如果要多个标签并存,在registe之前先或好tag + * @param key key是action对应的key,一个key只对应一个action,是查找action的关键 + * ,默认情况为一个QString(),这时key是QAction的objectName + * @param enableEmit 控制是否发射@ref actionTagChanged 信号 + * @note 同一个action多次注册不同的tag可以通过tag索引到action,但通过action只能索引到最后一个注册的tag + * @note tag的新增会触发actionTagChanged信号 + */ +bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit) +{ + if (nullptr == act) { + return (false); + } + QString k = key; + + if (k.isEmpty()) { + k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName()); + } + if (d_ptr->mKeyToAction.contains(k)) { + qWarning() << "key: " + << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction"; + return (false); + } + d_ptr->mKeyToAction[ k ] = act; + d_ptr->mActionToKey[ act ] = k; + // 记录tag 对 action + bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号 + d_ptr->mTagToActions[ tag ].append(act); + // 绑定槽 + connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + if (isneedemit && enableEmit) { + // 说明新增tag + Q_EMIT actionTagChanged(tag, false); + } + return (true); +} + +/** + * @brief 取消action的注册 + * + * 如果tag对应的最后一个action被撤销,tag也将一块删除 + * @param act + * @note tag的删除会触发actionTagChanged信号 + * @note 如果action关联了多个tag,这些tag里的action都会被删除,对应的key也同理 + */ +void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit) +{ + if (nullptr == act) { + return; + } + // 绑定槽 + disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + removeAction(act, enableEmit); +} + +/** + * @brief 移除action + * + * 仅移除内存内容 + * @param act + * @param enableEmit + */ +void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit) +{ + QList< int > deletedTags; // 记录删除的tag,用于触发actionTagChanged + QMap< int, QList< QAction* > > tagToActions; ///< tag : QList + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + // 把不是act的内容转移到tagToActions和tagToActionKeys中,之后再和m_d里的替换 + auto tmpi = tagToActions.insert(i.key(), QList< QAction* >()); + int count = 0; + for (int j = 0; j < i.value().size(); ++j) { + if (i.value()[ j ] != act) { + tmpi.value().append(act); + ++count; + } + } + if (0 == count) { + // 说明这个tag没有内容 + tagToActions.erase(tmpi); + deletedTags.append(i.key()); + } + } + // 删除mKeyToAction + QString key = d_ptr->mActionToKey.value(act); + + d_ptr->mActionToKey.remove(act); + d_ptr->mKeyToAction.remove(key); + + // 置换 + d_ptr->mTagToActions.swap(tagToActions); + // 发射信号 + if (enableEmit) { + for (int tagdelete : qAsConst(deletedTags)) { + Q_EMIT actionTagChanged(tagdelete, true); + } + } +} + +/** + * @brief 等同actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::filter(int tag) +{ + return (actions(tag)); +} + +/** + * @brief 根据tag得到actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::actions(int tag) +{ + return (d_ptr->mTagToActions[ tag ]); +} + +const QList< QAction* > SARibbonActionsManager::actions(int tag) const +{ + return (d_ptr->mTagToActions[ tag ]); +} + +/** + * @brief 获取所有的标签 + * @return + */ +QList< int > SARibbonActionsManager::actionTags() const +{ + return (d_ptr->mTagToActions.keys()); +} + +/** + * @brief 通过key获取action + * @param key + * @return 如果没有key,返回nullptr + */ +QAction* SARibbonActionsManager::action(const QString& key) const +{ + return (d_ptr->mKeyToAction.value(key, nullptr)); +} + +/** + * @brief 通过action找到key + * @param act + * @return 如果找不到,返回QString() + */ +QString SARibbonActionsManager::key(QAction* act) const +{ + return (d_ptr->mActionToKey.value(act, QString())); +} + +/** + * @brief 返回所有管理的action数 + * @return + */ +int SARibbonActionsManager::count() const +{ + return (d_ptr->mKeyToAction.size()); +} + +/** + * @brief 返回所有管理的actions + * @return + */ +QList< QAction* > SARibbonActionsManager::allActions() const +{ + return (d_ptr->mKeyToAction.values()); +} + +/** + * @brief 自动加载SARibbonBar的action + * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object,找到action注册, + * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类 + * + * 此函数会把所有category下的action生成tag并注册,返回的QMap是记录了category对应的tag + * + * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action,但不在任何一个category下的作为NotInRibbonCategoryTag标签注册,默认名字会赋予not + * in ribbon, 可以通过@ref setTagName 改变 + * + * @param w + * @return + * @note 此函数的调用最好在category设置了标题后调用,因为会以category的标题作为标签的命名 + */ +QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar) +{ + QMap< int, SARibbonCategory* > res; + // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象,把所有action找到 + QWidget* parWidget = bar->parentWidget(); + QSet< QAction* > mainwindowActions; + if (parWidget) { + for (QObject* o : qAsConst(parWidget->children())) { + if (QAction* a = qobject_cast< QAction* >(o)) { + // 说明是action + if (!a->objectName().isEmpty()) { + mainwindowActions.insert(a); + } + } + } + } + // 开始遍历每个category,加入action + + if (nullptr == bar) { + // 非ribbon模式,直接退出 + return (res); + } + QSet< QAction* > categoryActions; + QList< SARibbonCategory* > categorys = bar->categoryPages(); + int tag = AutoCategoryDistinguishBeginTag; + + for (SARibbonCategory* c : qAsConst(categorys)) { + QList< SARibbonPannel* > pannels = c->pannelList(); + for (SARibbonPannel* p : qAsConst(pannels)) { + categoryActions += autoRegisteWidgetActions(p, tag, false); + } + setTagName(tag, c->categoryName()); + res[ tag ] = c; + ++tag; + } + // 找到不在功能区的actions + QSet< QAction* > notincategory = mainwindowActions - categoryActions; + + for (QAction* a : qAsConst(notincategory)) { + if (!a->objectName().isEmpty()) { + registeAction(a, NotInRibbonCategoryTag, a->objectName(), false); + } + } + if (notincategory.size() > 0) { + setTagName(NotInRibbonCategoryTag, tr("not in ribbon")); + } + for (auto i = res.begin(); i != res.end(); ++i) { + connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged); + } + d_ptr->mTagToCategory = res; + return (res); +} + +/** + * @brief 自动加载action + * @param w + * @param tag + * @param enableEmit + * @return 返回成功加入SARibbonActionsManager管理的action + */ +QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit) +{ + QSet< QAction* > res; + QList< QAction* > was = w->actions(); + + for (QAction* a : qAsConst(was)) { + if (res.contains(a) || a->objectName().isEmpty()) { + // 重复内容不重复加入 + // 没有object name不加入 + continue; + } + if (registeAction(a, tag, a->objectName(), enableEmit)) { + res.insert(a); + } + } + return (res); +} + +/** + * @brief 根据标题查找action + * @param text + * @return + */ +QList< QAction* > SARibbonActionsManager::search(const QString& text) +{ + QList< QAction* > res; + + if (text.isEmpty()) { + return (res); + } + QStringList kws = text.split(" "); + + if (kws.isEmpty()) { + kws.append(text); + } + QList< QAction* > acts = d_ptr->mActionToKey.keys(); + + for (const QString& k : qAsConst(kws)) { + for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) { + if (i.key()->text().contains(k, Qt::CaseInsensitive)) { + res.append(i.key()); + } + } + } + return (res); +} + +void SARibbonActionsManager::clear() +{ + d_ptr->clear(); +} + +/** + * @brief action 被delete时,将触发此槽把管理的action删除 + * @param o + * @note 这个函数不会触发actionTagChanged信号 + */ +void SARibbonActionsManager::onActionDestroyed(QObject* o) +{ + QAction* act = static_cast< QAction* >(o); + + removeAction(act, false); +} + +/** + * @brief autoRegisteActions函数会关联此槽,在标签内容改变时改变tag 对应 文本 + * @param title + */ +void SARibbonActionsManager::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender()); + + if (nullptr == c) { + return; + } + int tag = d_ptr->mTagToCategory.key(c, -1); + + if (tag == -1) { + return; + } + setTagName(tag, title); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonActionsModel +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +class SARibbonActionsManagerModel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel) +public: + PrivateData(SARibbonActionsManagerModel* p); + void updateRef(); + int count() const; + QAction* at(int index); + bool isNull() const; + +public: + SARibbonActionsManager* mMgr { nullptr }; + int mTag { SARibbonActionsManager::CommonlyUsedActionTag }; + QString mSeatchText; + QList< QAction* > mActions; +}; + +SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p) +{ +} + +void SARibbonActionsManagerModel::PrivateData::updateRef() +{ + if (isNull()) { + return; + } + if (!mSeatchText.isEmpty()) { + mActions = mMgr->search(mSeatchText); + } else { + mActions = mMgr->actions(mTag); + } +} + +int SARibbonActionsManagerModel::PrivateData::count() const +{ + if (isNull()) { + return (0); + } + return (mActions.size()); +} + +QAction* SARibbonActionsManagerModel::PrivateData::at(int index) +{ + if (isNull()) { + return (nullptr); + } + if (index >= count()) { + return (nullptr); + } + return (mActions.at(index)); +} + +bool SARibbonActionsManagerModel::PrivateData::isNull() const +{ + return (mMgr == nullptr); +} + +//=================================================== +// SARibbonActionsManagerModel +//=================================================== + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ +} + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ + setupActionsManager(m); +} + +SARibbonActionsManagerModel::~SARibbonActionsManagerModel() +{ +} + +int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { // 非顶层 + return (0); + } + // 顶层 + return (d_ptr->count()); +} + +QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + if (role != Qt::DisplayRole) { + return (QVariant()); + } + if (Qt::Horizontal == orientation) { + return (tr("action name")); + } + return (QVariant()); +} + +Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) { + return (Qt::NoItemFlags); + } + return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); +} + +QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const +{ + QAction* act = indexToAction(index); + + if (nullptr == act) { + return (QVariant()); + } + + switch (role) { + case Qt::DisplayRole: { + QString str = act->text(); + if (!str.isEmpty()) { + return str; + } + // 如果文本为空,一般是没有设置文本,或者是窗口,如果是窗口,那么用windowtitle作为文本 + if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) { + if (QWidget* w = wa->defaultWidget()) { + str = w->windowTitle(); + } + } + return str; + } + case Qt::DecorationRole: + return (act->icon()); + + default: + break; + } + return (QVariant()); +} + +void SARibbonActionsManagerModel::setFilter(int tag) +{ + d_ptr->mTag = tag; + update(); +} + +void SARibbonActionsManagerModel::update() +{ + beginResetModel(); + d_ptr->updateRef(); + endResetModel(); +} + +void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m) +{ + d_ptr->mMgr = m; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + d_ptr->mActions = m->filter(d_ptr->mTag); + connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + update(); +} + +void SARibbonActionsManagerModel::uninstallActionsManager() +{ + if (!d_ptr->isNull()) { + disconnect( + d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + d_ptr->mMgr = nullptr; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + } + update(); +} + +QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const +{ + if (!index.isValid()) { + return (nullptr); + } + if (index.row() >= d_ptr->count()) { + return (nullptr); + } + return (d_ptr->at(index.row())); +} + +/** + * @brief 查找 + * @param text + */ +void SARibbonActionsManagerModel::search(const QString& text) +{ + d_ptr->mSeatchText = text; + update(); +} + +void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete) +{ + if (isdelete && (tag == d_ptr->mTag)) { + d_ptr->mTag = SARibbonActionsManager::UnknowActionTag; + update(); + } else { + if (tag == d_ptr->mTag) { + update(); + } + } +} diff --git a/src/SARibbonBar/SARibbonActionsManager.h b/src/SARibbonBar/SARibbonActionsManager.h index 5abbd2b4..be4ac02e 100644 --- a/src/SARibbonBar/SARibbonActionsManager.h +++ b/src/SARibbonBar/SARibbonActionsManager.h @@ -1,147 +1,147 @@ -#ifndef SARIBBONACTIONSMANAGER_H -#define SARIBBONACTIONSMANAGER_H -#include "SARibbonGlobal.h" -#include -#include -#include -#include -#include -#include -class SARibbonBar; -class SARibbonCategory; - -/** - * @brief 用于管理SARibbon的所有Action - * - * SARibbonActionsManager维护着两个表,一个是tag(标签)对应的Action list, - * 一个是所有接受SARibbonActionsManager管理的action list。 - * - * SARibbonActionsManager的标签对应一组actions,每个标签对应的action可以重复出现, - * 但SARibbonActionsManager维护的action list里只有一份action,不会重复出现。 - * - * tag用于对action list分组,每个tag的实体名字通过@ref setTagName 进行设置,在语言变化时需要及时调用 - * setTagName设置新的标签对应的文本。 - * - * SARibbonActionsManager默认预设了6个常用标签见@ref SARibbonActionsManager::ActionTag ,用户自定义标签需要在 - * SARibbonActionsManager::UserDefineActionTag值的基础上进行累加。 - * - * @ref filter (等同@ref actions )函数用于提取标签管理的action list,@ref allActions 函数返回SARibbonActionsManager - * 管理的所有标签。 - * - * 通过@ref autoRegisteActions 函数可以快速的建立action的管理,此函数会遍历@ref SARibbonBar下所有@ref SARibbonPannel 添加的action,并给予Category建立tag,正常使用用户仅需关注此autoRegisteActions函数即可 - * - * - */ -class SA_RIBBON_EXPORT SARibbonActionsManager : public QObject -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManager) - friend class SARibbonActionsManagerModel; - -public: - /** - * @brief 定义action的标签 - */ - enum ActionTag - { - UnknowActionTag = 0, ///< 未知的tag - CommonlyUsedActionTag = 0x01, ///< 预设tag-常用命令 - NotInFunctionalAreaActionTag = 0x02, ///< 预设tag-不在功能区命令 - AutoCategoryDistinguishBeginTag = 0x1000, ///< 自动按Category划分的标签起始,在@ref autoRegisteActions 函数会用到 - AutoCategoryDistinguishEndTag = 0x2000, ///< 自动按Category划分的标签结束,在@ref autoRegisteActions 函数会用到 - NotInRibbonCategoryTag = 0x2001, ///< 不在功能区的标签@ref autoRegisteActions 函数会遍历所有category的action - UserDefineActionTag = 0x8000 ///< 自定义标签,所有用户自定义tag要大于此tag - }; - explicit SARibbonActionsManager(SARibbonBar* bar); - ~SARibbonActionsManager(); - //设置tag对应的名字 - void setTagName(int tag, const QString& name); - - //获取tag对应的名字 - QString tagName(int tag) const; - - //移除tag,注意,这个函数非常耗时 - void removeTag(int tag); - - //注册action - bool registeAction(QAction* act, int tag, const QString& key = QString(), bool enableEmit = true); - - //取消action的注册 - void unregisteAction(QAction* act, bool enableEmit = true); - - //过滤得到actions对应的引用,实际是一个迭代器 - QList< QAction* >& filter(int tag); - - //通过tag筛选出系列action - QList< QAction* >& actions(int tag); - const QList< QAction* > actions(int tag) const; - - //获取所有的标签 - QList< int > actionTags() const; - - //通过key获取action - QAction* action(const QString& key) const; - - //通过action找到key - QString key(QAction* act) const; - - //返回所有管理的action数 - int count() const; - - //返回所有管理的actions - QList< QAction* > allActions() const; - - //自动加载action,返回tag对应的Category指针 - QMap< int, SARibbonCategory* > autoRegisteActions(SARibbonBar* bar); - - //自动加载widget下的actions函数返回的action,返回加载的数量,这些 - QSet< QAction* > autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit = false); - - //根据标题查找action - QList< QAction* > search(const QString& text); - - //清除 - void clear(); - -Q_SIGNALS: - - /** - * @brief 标签变化触发的信号,变化包括新增和删除 - */ - void actionTagChanged(int tag, bool isdelete); - -private Q_SLOTS: - void onActionDestroyed(QObject* o); - void onCategoryTitleChanged(const QString& title); - -private: - void removeAction(QAction* act, bool enableEmit = true); -}; - -/** - * @brief SARibbonActionsManager 对应的model - */ -class SA_RIBBON_EXPORT SARibbonActionsManagerModel : public QAbstractListModel -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManagerModel) -public: - explicit SARibbonActionsManagerModel(QObject* p = nullptr); - explicit SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p = nullptr); - ~SARibbonActionsManagerModel(); - virtual int rowCount(const QModelIndex& parent) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - virtual QVariant data(const QModelIndex& index, int role) const override; - void setFilter(int tag); - void update(); - void setupActionsManager(SARibbonActionsManager* m); - void uninstallActionsManager(); - QAction* indexToAction(QModelIndex index) const; - void search(const QString& text); - -private Q_SLOTS: - void onActionTagChanged(int tag, bool isdelete); -}; - -#endif // SARIBBONACTIONSMANAGER_H +#ifndef SARIBBONACTIONSMANAGER_H +#define SARIBBONACTIONSMANAGER_H +#include "SARibbonGlobal.h" +#include +#include +#include +#include +#include +#include +class SARibbonBar; +class SARibbonCategory; + +/** + * @brief 用于管理SARibbon的所有Action + * + * SARibbonActionsManager维护着两个表,一个是tag(标签)对应的Action list, + * 一个是所有接受SARibbonActionsManager管理的action list。 + * + * SARibbonActionsManager的标签对应一组actions,每个标签对应的action可以重复出现, + * 但SARibbonActionsManager维护的action list里只有一份action,不会重复出现。 + * + * tag用于对action list分组,每个tag的实体名字通过@ref setTagName 进行设置,在语言变化时需要及时调用 + * setTagName设置新的标签对应的文本。 + * + * SARibbonActionsManager默认预设了6个常用标签见@ref SARibbonActionsManager::ActionTag ,用户自定义标签需要在 + * SARibbonActionsManager::UserDefineActionTag值的基础上进行累加。 + * + * @ref filter (等同@ref actions )函数用于提取标签管理的action list,@ref allActions 函数返回SARibbonActionsManager + * 管理的所有标签。 + * + * 通过@ref autoRegisteActions 函数可以快速的建立action的管理,此函数会遍历@ref SARibbonBar下所有@ref SARibbonPannel 添加的action,并给予Category建立tag,正常使用用户仅需关注此autoRegisteActions函数即可 + * + * + */ +class SA_RIBBON_EXPORT SARibbonActionsManager : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManager) + friend class SARibbonActionsManagerModel; + +public: + /** + * @brief 定义action的标签 + */ + enum ActionTag + { + UnknowActionTag = 0, ///< 未知的tag + CommonlyUsedActionTag = 0x01, ///< 预设tag-常用命令 + NotInFunctionalAreaActionTag = 0x02, ///< 预设tag-不在功能区命令 + AutoCategoryDistinguishBeginTag = 0x1000, ///< 自动按Category划分的标签起始,在@ref autoRegisteActions 函数会用到 + AutoCategoryDistinguishEndTag = 0x2000, ///< 自动按Category划分的标签结束,在@ref autoRegisteActions 函数会用到 + NotInRibbonCategoryTag = 0x2001, ///< 不在功能区的标签@ref autoRegisteActions 函数会遍历所有category的action + UserDefineActionTag = 0x8000 ///< 自定义标签,所有用户自定义tag要大于此tag + }; + explicit SARibbonActionsManager(SARibbonBar* bar); + ~SARibbonActionsManager(); + //设置tag对应的名字 + void setTagName(int tag, const QString& name); + + //获取tag对应的名字 + QString tagName(int tag) const; + + //移除tag,注意,这个函数非常耗时 + void removeTag(int tag); + + //注册action + bool registeAction(QAction* act, int tag, const QString& key = QString(), bool enableEmit = true); + + //取消action的注册 + void unregisteAction(QAction* act, bool enableEmit = true); + + //过滤得到actions对应的引用,实际是一个迭代器 + QList< QAction* >& filter(int tag); + + //通过tag筛选出系列action + QList< QAction* >& actions(int tag); + const QList< QAction* > actions(int tag) const; + + //获取所有的标签 + QList< int > actionTags() const; + + //通过key获取action + QAction* action(const QString& key) const; + + //通过action找到key + QString key(QAction* act) const; + + //返回所有管理的action数 + int count() const; + + //返回所有管理的actions + QList< QAction* > allActions() const; + + //自动加载action,返回tag对应的Category指针 + QMap< int, SARibbonCategory* > autoRegisteActions(SARibbonBar* bar); + + //自动加载widget下的actions函数返回的action,返回加载的数量,这些 + QSet< QAction* > autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit = false); + + //根据标题查找action + QList< QAction* > search(const QString& text); + + //清除 + void clear(); + +Q_SIGNALS: + + /** + * @brief 标签变化触发的信号,变化包括新增和删除 + */ + void actionTagChanged(int tag, bool isdelete); + +private Q_SLOTS: + void onActionDestroyed(QObject* o); + void onCategoryTitleChanged(const QString& title); + +private: + void removeAction(QAction* act, bool enableEmit = true); +}; + +/** + * @brief SARibbonActionsManager 对应的model + */ +class SA_RIBBON_EXPORT SARibbonActionsManagerModel : public QAbstractListModel +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManagerModel) +public: + explicit SARibbonActionsManagerModel(QObject* p = nullptr); + explicit SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p = nullptr); + ~SARibbonActionsManagerModel(); + virtual int rowCount(const QModelIndex& parent) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual QVariant data(const QModelIndex& index, int role) const override; + void setFilter(int tag); + void update(); + void setupActionsManager(SARibbonActionsManager* m); + void uninstallActionsManager(); + QAction* indexToAction(QModelIndex index) const; + void search(const QString& text); + +private Q_SLOTS: + void onActionTagChanged(int tag, bool isdelete); +}; + +#endif // SARIBBONACTIONSMANAGER_H diff --git a/src/SARibbonBar/SARibbonBarVersionInfo.h b/src/SARibbonBar/SARibbonBarVersionInfo.h index e4dd8507..a2de9b07 100644 --- a/src/SARibbonBar/SARibbonBarVersionInfo.h +++ b/src/SARibbonBar/SARibbonBarVersionInfo.h @@ -1,33 +1,33 @@ -#ifndef SARIBBONVERSIONINFO_H -#define SARIBBONVERSIONINFO_H -/** - * @file 此文档由cmake根据SARibbonBarVersionInfo.h.in自动生成,任何在此文件上的改动都将覆写 - */ - -/** - * @def ribbon的数字版本 {MAJ}.MIN.PAT - */ -#ifndef SA_RIBBON_BAR_VERSION_MAJ -#define SA_RIBBON_BAR_VERSION_MAJ 2 -#endif -/** - * @def ribbon的数字版本 MAJ.{MIN}.PAT - */ -#ifndef SA_RIBBON_BAR_VERSION_MIN -#define SA_RIBBON_BAR_VERSION_MIN 2 -#endif -/** - * @def ribbon的数字版本 MAJ.MIN.{PAT} - */ -#ifndef SA_RIBBON_BAR_VERSION_PAT -#define SA_RIBBON_BAR_VERSION_PAT 5 -#endif - -/** - * @def 版本号(字符串) - */ -#ifndef SARIBBON_VERSION -#define SARIBBON_VERSION "2.2.5" -#endif - -#endif // SARIBBONVERSIONINFO_H +#ifndef SARIBBONVERSIONINFO_H +#define SARIBBONVERSIONINFO_H +/** + * @file 此文档由cmake根据SARibbonBarVersionInfo.h.in自动生成,任何在此文件上的改动都将覆写 + */ + +/** + * @def ribbon的数字版本 {MAJ}.MIN.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MAJ +#define SA_RIBBON_BAR_VERSION_MAJ 2 +#endif +/** + * @def ribbon的数字版本 MAJ.{MIN}.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MIN +#define SA_RIBBON_BAR_VERSION_MIN 2 +#endif +/** + * @def ribbon的数字版本 MAJ.MIN.{PAT} + */ +#ifndef SA_RIBBON_BAR_VERSION_PAT +#define SA_RIBBON_BAR_VERSION_PAT 5 +#endif + +/** + * @def 版本号(字符串) + */ +#ifndef SARIBBON_VERSION +#define SARIBBON_VERSION "2.2.5" +#endif + +#endif // SARIBBONVERSIONINFO_H diff --git a/src/SARibbonBar/SARibbonCategoryLayout.cpp b/src/SARibbonBar/SARibbonCategoryLayout.cpp index 188a1703..caf56fe7 100644 --- a/src/SARibbonBar/SARibbonCategoryLayout.cpp +++ b/src/SARibbonBar/SARibbonCategoryLayout.cpp @@ -1,723 +1,723 @@ -#include "SARibbonCategoryLayout.h" -#include -#include "SARibbonPannel.h" -#include "SARibbonElementManager.h" -#include "SARibbonSeparatorWidget.h" -#include -#include - -#ifndef SARibbonCategoryLayout_DEBUG_PRINT -#define SARibbonCategoryLayout_DEBUG_PRINT 1 -#endif -/** - * @brief The SARibbonCategoryLayoutPrivate class - */ -class SARibbonCategoryLayout::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout) -public: - PrivateData(SARibbonCategoryLayout* p); - // 计算所有元素的sizehint总宽度 - int totalSizeHintWidth() const; - -public: - bool mDirty { true }; - bool mIsRightScrollBtnShow { false }; ///< 标记右滚动按钮是否需要显示 - bool mIsLeftScrollBtnShow { false }; ///< 标记左滚动按钮是否需要显示 - SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 - SARibbonCategoryScrollButton* mRightScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 - int mTotalWidth { 0 }; - int mXBase { 0 }; - QSize mSizeHint; - QSize mMinSizeHint; - QList< SARibbonCategoryLayoutItem* > mItemList; - SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 -}; - -//============================================================= -// SARibbonCategoryLayoutPrivate -//============================================================= - -SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p) -{ -} - -/** - * @brief 计算所有元素的SizeHint宽度总和 - * @return - */ -int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const -{ - int total = 0; - QMargins mag = q_ptr->contentsMargins(); -#if SA_DEBUG_PRINT_SIZE_HINT - int debug_i__ = 0; - QString debug_totalSizeHintWidth__; -#endif - if (!mag.isNull()) { - total += (mag.left() + mag.right()); - } - // 先计算总长 - for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) { - if (item->isEmpty()) { -// 如果是hide就直接跳过 -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - ++debug_i__; - debug_totalSizeHintWidth__ += QString(" [%1](%2)is empty skip\n") - .arg(debug_i__) - .arg(item->toPannelWidget()->pannelName()); -#endif - continue; - } - // 这里要使用widget()->sizeHint(),因为pannel的标题会影总体布局,此处需要修改 - // TODO - QSize pannelSize = item->widget()->sizeHint(); - QSize SeparatorSize(0, 0); - if (item->separatorWidget) { - SeparatorSize = item->separatorWidget->sizeHint(); - } - total += pannelSize.width(); - total += SeparatorSize.width(); -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - ++debug_i__; - debug_totalSizeHintWidth__ += QString("|-[%1]pannelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n") - .arg(debug_i__) - .arg(pannelSize.width()) - .arg(pannelSize.height()) - .arg(SeparatorSize.width()) - .arg(SeparatorSize.height()) - .arg(item->toPannelWidget()->pannelName()); -#endif - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total; - qDebug().noquote() << debug_totalSizeHintWidth__; -#endif - return (total); -} - -//============================================================= -// SARibbonCategoryLayout -//============================================================= - -SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent) - : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this)) -{ - setContentsMargins(1, 1, 1, 1); - d_ptr->mLeftScrollBtn = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent); - d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent); - d_ptr->mLeftScrollBtn->setVisible(false); - d_ptr->mRightScrollBtn->setVisible(false); - connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked); - connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked); -} - -SARibbonCategoryLayout::~SARibbonCategoryLayout() -{ - while (auto item = takePannelItem(0)) { - delete item; - } -} - -SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const -{ - return (qobject_cast< SARibbonCategory* >(parentWidget())); -} - -void SARibbonCategoryLayout::addItem(QLayoutItem* item) -{ - Q_UNUSED(item); - qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPannel instead"); - invalidate(); -} - -/** - * @brief 返回pannel的layout - * @param index 索引 - * @return - * @note 注意,pannel是和分割线一起的,但这个只返回一个pannel对应的layout - */ -QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const -{ - SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr); - - return (item); -} - -/** - * @brief 提取layout - * @param index - * @return - */ -QLayoutItem* SARibbonCategoryLayout::takeAt(int index) -{ - QLayoutItem* r = takePannelItem(index); - invalidate(); - return r; -} - -SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(int index) -{ - if ((index >= 0) && (index < d_ptr->mItemList.size())) { - SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index); - if (item->widget()) { - item->widget()->hide(); - } - if (item->separatorWidget) { - item->separatorWidget->hide(); - } - return (item); - } - return (nullptr); -} - -SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(SARibbonPannel* pannel) -{ - for (int i = 0; i < d_ptr->mItemList.size(); ++i) { - SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ]; - if (item->widget() == pannel) { - return (takePannelItem(i)); - } - } - return (nullptr); -} - -/** - * @brief 移除pannel,对应的分割线也会删除 - * @param pannel - * @return - */ -bool SARibbonCategoryLayout::takePannel(SARibbonPannel* pannel) -{ - SARibbonCategoryLayoutItem* i = takePannelItem(pannel); - if (i) { - SARibbonSeparatorWidget* sp = i->separatorWidget; - if (sp) { - sp->deleteLater(); - } - delete i; - invalidate(); - return true; - } - return false; -} - -int SARibbonCategoryLayout::count() const -{ - return (d_ptr->mItemList.size()); -} - -void SARibbonCategoryLayout::addPannel(SARibbonPannel* pannel) -{ - insertPannel(d_ptr->mItemList.count(), pannel); -} - -/** - * @brief 插入一个pannel - * @param index 索引 - * @param pannel - * @return 返回对应的分割线SARibbonSeparatorWidget - * @note 在SARibbonCategoryLayout的布局中,一个pannel会携带一个分割线 - */ -void SARibbonCategoryLayout::insertPannel(int index, SARibbonPannel* pannel) -{ - index = qMax(0, index); - index = qMin(d_ptr->mItemList.count(), index); - SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(pannel); - - // 分割线 - item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget()); - // 插入list中 - d_ptr->mItemList.insert(index, item); - // 标记需要重新计算尺寸 - invalidate(); -} - -QSize SARibbonCategoryLayout::sizeHint() const -{ - if (d_ptr->mSizeHint.isNull()) { - SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); - that->updateGeometryArr(); - } - return (d_ptr->mSizeHint); -} - -QSize SARibbonCategoryLayout::minimumSize() const -{ - if (d_ptr->mMinSizeHint.isNull()) { - SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); - that->updateGeometryArr(); - } - return (d_ptr->mMinSizeHint); -} - -/** - * @brief SARibbonCategory充满整个stacked widget - * @return - */ -Qt::Orientations SARibbonCategoryLayout::expandingDirections() const -{ - return (Qt::Horizontal | Qt::Vertical); -} - -void SARibbonCategoryLayout::invalidate() -{ - d_ptr->mDirty = true; - QLayout::invalidate(); -} -/** - * @brief category的内容尺寸(把margins减去) - * @return - */ -QSize SARibbonCategoryLayout::categoryContentSize() const -{ - SARibbonCategory* category = ribbonCategory(); - QSize s = category->size(); - QMargins mag = contentsMargins(); - if (!mag.isNull()) { - s.rheight() -= (mag.top() + mag.bottom()); - s.rwidth() -= (mag.right() + mag.left()); - } - return (s); -} - -/** - * @brief 更新尺寸 - */ -void SARibbonCategoryLayout::updateGeometryArr() -{ - SARibbonCategory* category = ribbonCategory(); - if (nullptr == category) { - return; - } - int categoryWidth = category->width(); - QMargins mag = contentsMargins(); - int height = category->height(); - int y = 0; - - if (!mag.isNull()) { - y = mag.top(); - height -= (mag.top() + mag.bottom()); - // categoryWidth不能把mag减去,减去后会导致categoryWidth不是实际的categoryWidth - // categoryWidth -= (mag.right() + mag.left()); - } - // total 是总宽,不是x坐标系,x才是坐标系 - int total = d_ptr->totalSizeHintWidth(); - - // 扩展的宽度 - int expandWidth = 0; - -// 如果total < categoryWidth,m_d->mXBase可以设置为0 -// 判断是否超过总长度 -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout::updateGeometryArr" - << "\n|-category name=" << category->categoryName() // - << "\n|-category height=" << height // - << "\n|-totalSizeHintWidth=" << total // - << "\n|-y=" << y // - << "\n|-expandWidth:" << expandWidth // - << "\n|-mag=" << mag; -#endif - if (total > categoryWidth) { - // 超过总长度,需要显示滚动按钮 - if (0 == d_ptr->mXBase) { - // 已经移动到最左,需要可以向右移动 - d_ptr->mIsRightScrollBtnShow = true; - d_ptr->mIsLeftScrollBtnShow = false; - } else if (d_ptr->mXBase <= (categoryWidth - total)) { - // 已经移动到最右,需要可以向左移动 - d_ptr->mIsRightScrollBtnShow = false; - d_ptr->mIsLeftScrollBtnShow = true; - } else { - // 移动到中间两边都可以动 - d_ptr->mIsRightScrollBtnShow = true; - d_ptr->mIsLeftScrollBtnShow = true; - } - } else { - // 说明total 小于 categoryWidth - // 记录可以扩展的数量 - int canExpandingCount = 0; - d_ptr->mIsRightScrollBtnShow = false; - d_ptr->mIsLeftScrollBtnShow = false; - // 这个是避免一开始totalWidth > categorySize.width(),通过滚动按钮调整了m_d->mBaseX - // 随之调整了窗体尺寸,调整后totalWidth < categorySize.width()导致category在原来位置 - // 无法显示,必须这里把mBaseX设置为0 - - d_ptr->mXBase = 0; - // - - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* p = qobject_cast< SARibbonPannel* >(item->widget())) { - if (p->isExpanding()) { - // pannel可扩展 - ++canExpandingCount; - } - } - } - // 计算可扩展的宽度 - if (canExpandingCount > 0) { - expandWidth = (categoryWidth - total) / canExpandingCount; - } else { - expandWidth = 0; - } - } - int x = d_ptr->mXBase; - if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) { - // 如果是居中对齐,同时没有伸缩的pannel,同时总宽度没有超过category的宽度 - x = (categoryWidth - total) / 2; - } - total = 0; // total重新计算 - // 先按照sizeHint设置所有的尺寸 - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (item->isEmpty()) { - // 如果是hide就直接跳过 - if (item->separatorWidget) { - // pannel hide分割线也要hide - item->separatorWidget->hide(); - } - item->mWillSetGeometry = QRect(0, 0, 0, 0); - item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0); - continue; - } - SARibbonPannel* p = item->toPannelWidget(); - if (nullptr == p) { - qDebug() << "unknow widget in SARibbonCategoryLayout"; - continue; - } - // p->layout()->update(); - QSize pannelSize = p->sizeHint(); - QSize SeparatorSize(0, 0); - if (item->separatorWidget) { - SeparatorSize = item->separatorWidget->sizeHint(); - } - if (p->isExpanding()) { - // 可扩展,就把pannel扩展到最大 - pannelSize.setWidth(pannelSize.width() + expandWidth); - } - int w = pannelSize.width(); - - item->mWillSetGeometry = QRect(x, y, w, height); - x += w; - total += w; - w = SeparatorSize.width(); - item->mWillSetSeparatorGeometry = QRect(x, y, w, height); - x += w; - total += w; - } - d_ptr->mTotalWidth = total; - d_ptr->mSizeHint = QSize(d_ptr->mTotalWidth, height); - d_ptr->mMinSizeHint = QSize(categoryWidth, height); -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint - << ",Category name=" << category->categoryName(); -#endif -} - -/** - * @brief 执行layout调整 - */ -void SARibbonCategoryLayout::doLayout() -{ - if (d_ptr->mDirty) { - updateGeometryArr(); - } - SARibbonCategory* category = ribbonCategory(); - // 两个滚动按钮的位置永远不变 - d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height()); - d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height()); - QList< QWidget* > showWidgets, hideWidgets; -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - int debug_i__(0); - qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName(); -#endif - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (item->isEmpty()) { - hideWidgets << item->widget(); - if (item->separatorWidget) { - hideWidgets << item->separatorWidget; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() << ",will hide"; - ++debug_i__; -#endif - } else { - //! 这里不能用item->setGeometry(item->mWillSetGeometry);这样会得到一个很奇怪的显示效果 - //! 就是窗口的最左边不会移出去,而是把最右边压缩, - item->widget()->setGeometry(item->mWillSetGeometry); - // item->widget()->setFixedSize(item->mWillSetGeometry.size()); - // item->widget()->move(item->mWillSetGeometry.topLeft()); - // item->setGeometry(item->mWillSetGeometry); - showWidgets << item->widget(); - if (item->separatorWidget) { - item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry); - showWidgets << item->separatorWidget; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() - << "),willSetGeometry:" << item->mWillSetGeometry - << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry; - ++debug_i__; -#endif - } - } - - d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow); - d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow); - if (d_ptr->mIsRightScrollBtnShow) { - d_ptr->mRightScrollBtn->raise(); - } - if (d_ptr->mIsLeftScrollBtnShow) { - d_ptr->mLeftScrollBtn->raise(); - } - // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 - for (QWidget* w : qAsConst(showWidgets)) { - if (!w->isVisible()) { - w->show(); - } - } - for (QWidget* w : qAsConst(hideWidgets)) { - if (w->isVisible()) { - w->hide(); - } - } -} - -/** - * @brief 返回所有pannels - * @return - */ -QList< SARibbonPannel* > SARibbonCategoryLayout::pannels() const -{ - QList< SARibbonPannel* > res; - - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - SARibbonPannel* p = item->toPannelWidget(); - res.append(p); - } - return (res); -} - -/** - * @brief 通过ObjectName查找pannel - * @param objname - * @return - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByObjectName(const QString& objname) const -{ - for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) { - if (SARibbonPannel* pannel = item->toPannelWidget()) { - if (pannel->objectName() == objname) { - return pannel; - } - } - } - return nullptr; -} - -/** - * @brief 通过名字查找pannel - * @param title - * @return 如果有重名,只会返回第一个符合条件的 - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByName(const QString& pannelname) const -{ - for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* pannel = item->toPannelWidget()) { - if (pannel->pannelName() == pannelname) { - return (pannel); - } - } - } - return (nullptr); -} - -/** - * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr - * @param i - * @return - */ -SARibbonPannel* SARibbonCategoryLayout::pannelByIndex(int i) const -{ - if (i >= 0 && i < d_ptr->mItemList.size()) { - return d_ptr->mItemList[ i ]->toPannelWidget(); - } - return nullptr; -} - -/** - * @brief 移动pannel - * @param from - * @param to - */ -void SARibbonCategoryLayout::movePannel(int from, int to) -{ - d_ptr->mItemList.move(from, to); - doLayout(); -} - -/** - * @brief 返回pannel的个数 - * @return - */ -int SARibbonCategoryLayout::pannelCount() const -{ - return d_ptr->mItemList.size(); -} - -/** - * @brief 查找pannel对应的索引 - * @param p - * @return 如果找不到,返回-1 - */ -int SARibbonCategoryLayout::pannelIndex(SARibbonPannel* p) const -{ - int c = pannelCount(); - - for (int i = 0; i < c; ++i) { - if (d_ptr->mItemList[ i ]->toPannelWidget() == p) { - return (i); - } - } - return (-1); -} - -/** - * @brief 获取所有的pannel - * @return - */ -QList< SARibbonPannel* > SARibbonCategoryLayout::pannelList() const -{ - QList< SARibbonPannel* > res; - - for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) { - if (SARibbonPannel* p = i->toPannelWidget()) { - res.append(p); - } - } - return (res); -} - -/** - * @brief 执行滚动 - * @param px - */ -void SARibbonCategoryLayout::scroll(int px) -{ - QSize contentSize = categoryContentSize(); - d_ptr->mXBase += px; - if (d_ptr->mXBase > 0) { - d_ptr->mXBase = 0; - } else if ((d_ptr->mXBase + d_ptr->mTotalWidth) < contentSize.width()) { - d_ptr->mXBase = contentSize.width() - d_ptr->mTotalWidth; - } - invalidate(); -} - -/** - * @brief 判断是否滚动过 - * @return - */ -bool SARibbonCategoryLayout::isScrolled() const -{ - return (d_ptr->mXBase != 0); -} - -/** - * @brief 这个宽度是实际内容的宽度,有可能大于size.width,也有可能小于 - * @return - */ -int SARibbonCategoryLayout::categoryTotalWidth() const -{ - return d_ptr->mTotalWidth; -} - -/** - @brief 设置Category的对齐方式 - - 居中对齐会让pannel以居中进行对齐 - @param al - */ -void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al) -{ - d_ptr->mCategoryAlignment = al; -} - -/** - @brief Category的对齐方式 - @return - */ -SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const -{ - return d_ptr->mCategoryAlignment; -} - -void SARibbonCategoryLayout::onLeftScrollButtonClicked() -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); - int width = category->width(); - // 求总宽 - int totalWidth = d_ptr->mTotalWidth; - - if (totalWidth > width) { - int tmp = d_ptr->mXBase + width; - if (tmp > 0) { - tmp = 0; - } - d_ptr->mXBase = tmp; - } else { - d_ptr->mXBase = 0; - } - invalidate(); -} - -void SARibbonCategoryLayout::onRightScrollButtonClicked() -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); - int width = category->width(); - // 求总宽 - int totalWidth = d_ptr->mTotalWidth; - - if (totalWidth > width) { - int tmp = d_ptr->mXBase - width; - if (tmp < (width - totalWidth)) { - tmp = width - totalWidth; - } - d_ptr->mXBase = tmp; - } else { - d_ptr->mXBase = 0; - } - invalidate(); -} - -void SARibbonCategoryLayout::setGeometry(const QRect& rect) -{ - QRect old = geometry(); - if (old == rect) { - return; - } -#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName() - << ")======="; -#endif - QLayout::setGeometry(rect); - d_ptr->mDirty = false; - updateGeometryArr(); - doLayout(); -} -//============================================================= -// SARibbonCategoryLayoutItem -//============================================================= - -SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPannel* w) : QWidgetItem(w) -{ - separatorWidget = nullptr; -} - -SARibbonCategoryLayoutItem::~SARibbonCategoryLayoutItem() -{ -} - -SARibbonPannel* SARibbonCategoryLayoutItem::toPannelWidget() -{ - return qobject_cast< SARibbonPannel* >(widget()); -} +#include "SARibbonCategoryLayout.h" +#include +#include "SARibbonPannel.h" +#include "SARibbonElementManager.h" +#include "SARibbonSeparatorWidget.h" +#include +#include + +#ifndef SARibbonCategoryLayout_DEBUG_PRINT +#define SARibbonCategoryLayout_DEBUG_PRINT 1 +#endif +/** + * @brief The SARibbonCategoryLayoutPrivate class + */ +class SARibbonCategoryLayout::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout) +public: + PrivateData(SARibbonCategoryLayout* p); + // 计算所有元素的sizehint总宽度 + int totalSizeHintWidth() const; + +public: + bool mDirty { true }; + bool mIsRightScrollBtnShow { false }; ///< 标记右滚动按钮是否需要显示 + bool mIsLeftScrollBtnShow { false }; ///< 标记左滚动按钮是否需要显示 + SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + SARibbonCategoryScrollButton* mRightScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + int mTotalWidth { 0 }; + int mXBase { 0 }; + QSize mSizeHint; + QSize mMinSizeHint; + QList< SARibbonCategoryLayoutItem* > mItemList; + SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 +}; + +//============================================================= +// SARibbonCategoryLayoutPrivate +//============================================================= + +SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p) +{ +} + +/** + * @brief 计算所有元素的SizeHint宽度总和 + * @return + */ +int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const +{ + int total = 0; + QMargins mag = q_ptr->contentsMargins(); +#if SA_DEBUG_PRINT_SIZE_HINT + int debug_i__ = 0; + QString debug_totalSizeHintWidth__; +#endif + if (!mag.isNull()) { + total += (mag.left() + mag.right()); + } + // 先计算总长 + for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) { + if (item->isEmpty()) { +// 如果是hide就直接跳过 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += QString(" [%1](%2)is empty skip\n") + .arg(debug_i__) + .arg(item->toPannelWidget()->pannelName()); +#endif + continue; + } + // 这里要使用widget()->sizeHint(),因为pannel的标题会影总体布局,此处需要修改 + // TODO + QSize pannelSize = item->widget()->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + total += pannelSize.width(); + total += SeparatorSize.width(); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += QString("|-[%1]pannelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n") + .arg(debug_i__) + .arg(pannelSize.width()) + .arg(pannelSize.height()) + .arg(SeparatorSize.width()) + .arg(SeparatorSize.height()) + .arg(item->toPannelWidget()->pannelName()); +#endif + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total; + qDebug().noquote() << debug_totalSizeHintWidth__; +#endif + return (total); +} + +//============================================================= +// SARibbonCategoryLayout +//============================================================= + +SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent) + : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this)) +{ + setContentsMargins(1, 1, 1, 1); + d_ptr->mLeftScrollBtn = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent); + d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent); + d_ptr->mLeftScrollBtn->setVisible(false); + d_ptr->mRightScrollBtn->setVisible(false); + connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked); + connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked); +} + +SARibbonCategoryLayout::~SARibbonCategoryLayout() +{ + while (auto item = takePannelItem(0)) { + delete item; + } +} + +SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const +{ + return (qobject_cast< SARibbonCategory* >(parentWidget())); +} + +void SARibbonCategoryLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPannel instead"); + invalidate(); +} + +/** + * @brief 返回pannel的layout + * @param index 索引 + * @return + * @note 注意,pannel是和分割线一起的,但这个只返回一个pannel对应的layout + */ +QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const +{ + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr); + + return (item); +} + +/** + * @brief 提取layout + * @param index + * @return + */ +QLayoutItem* SARibbonCategoryLayout::takeAt(int index) +{ + QLayoutItem* r = takePannelItem(index); + invalidate(); + return r; +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(int index) +{ + if ((index >= 0) && (index < d_ptr->mItemList.size())) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index); + if (item->widget()) { + item->widget()->hide(); + } + if (item->separatorWidget) { + item->separatorWidget->hide(); + } + return (item); + } + return (nullptr); +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(SARibbonPannel* pannel) +{ + for (int i = 0; i < d_ptr->mItemList.size(); ++i) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ]; + if (item->widget() == pannel) { + return (takePannelItem(i)); + } + } + return (nullptr); +} + +/** + * @brief 移除pannel,对应的分割线也会删除 + * @param pannel + * @return + */ +bool SARibbonCategoryLayout::takePannel(SARibbonPannel* pannel) +{ + SARibbonCategoryLayoutItem* i = takePannelItem(pannel); + if (i) { + SARibbonSeparatorWidget* sp = i->separatorWidget; + if (sp) { + sp->deleteLater(); + } + delete i; + invalidate(); + return true; + } + return false; +} + +int SARibbonCategoryLayout::count() const +{ + return (d_ptr->mItemList.size()); +} + +void SARibbonCategoryLayout::addPannel(SARibbonPannel* pannel) +{ + insertPannel(d_ptr->mItemList.count(), pannel); +} + +/** + * @brief 插入一个pannel + * @param index 索引 + * @param pannel + * @return 返回对应的分割线SARibbonSeparatorWidget + * @note 在SARibbonCategoryLayout的布局中,一个pannel会携带一个分割线 + */ +void SARibbonCategoryLayout::insertPannel(int index, SARibbonPannel* pannel) +{ + index = qMax(0, index); + index = qMin(d_ptr->mItemList.count(), index); + SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(pannel); + + // 分割线 + item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget()); + // 插入list中 + d_ptr->mItemList.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); +} + +QSize SARibbonCategoryLayout::sizeHint() const +{ + if (d_ptr->mSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mSizeHint); +} + +QSize SARibbonCategoryLayout::minimumSize() const +{ + if (d_ptr->mMinSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mMinSizeHint); +} + +/** + * @brief SARibbonCategory充满整个stacked widget + * @return + */ +Qt::Orientations SARibbonCategoryLayout::expandingDirections() const +{ + return (Qt::Horizontal | Qt::Vertical); +} + +void SARibbonCategoryLayout::invalidate() +{ + d_ptr->mDirty = true; + QLayout::invalidate(); +} +/** + * @brief category的内容尺寸(把margins减去) + * @return + */ +QSize SARibbonCategoryLayout::categoryContentSize() const +{ + SARibbonCategory* category = ribbonCategory(); + QSize s = category->size(); + QMargins mag = contentsMargins(); + if (!mag.isNull()) { + s.rheight() -= (mag.top() + mag.bottom()); + s.rwidth() -= (mag.right() + mag.left()); + } + return (s); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonCategoryLayout::updateGeometryArr() +{ + SARibbonCategory* category = ribbonCategory(); + if (nullptr == category) { + return; + } + int categoryWidth = category->width(); + QMargins mag = contentsMargins(); + int height = category->height(); + int y = 0; + + if (!mag.isNull()) { + y = mag.top(); + height -= (mag.top() + mag.bottom()); + // categoryWidth不能把mag减去,减去后会导致categoryWidth不是实际的categoryWidth + // categoryWidth -= (mag.right() + mag.left()); + } + // total 是总宽,不是x坐标系,x才是坐标系 + int total = d_ptr->totalSizeHintWidth(); + + // 扩展的宽度 + int expandWidth = 0; + +// 如果total < categoryWidth,m_d->mXBase可以设置为0 +// 判断是否超过总长度 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout::updateGeometryArr" + << "\n|-category name=" << category->categoryName() // + << "\n|-category height=" << height // + << "\n|-totalSizeHintWidth=" << total // + << "\n|-y=" << y // + << "\n|-expandWidth:" << expandWidth // + << "\n|-mag=" << mag; +#endif + if (total > categoryWidth) { + // 超过总长度,需要显示滚动按钮 + if (0 == d_ptr->mXBase) { + // 已经移动到最左,需要可以向右移动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = false; + } else if (d_ptr->mXBase <= (categoryWidth - total)) { + // 已经移动到最右,需要可以向左移动 + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = true; + } else { + // 移动到中间两边都可以动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = true; + } + } else { + // 说明total 小于 categoryWidth + // 记录可以扩展的数量 + int canExpandingCount = 0; + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = false; + // 这个是避免一开始totalWidth > categorySize.width(),通过滚动按钮调整了m_d->mBaseX + // 随之调整了窗体尺寸,调整后totalWidth < categorySize.width()导致category在原来位置 + // 无法显示,必须这里把mBaseX设置为0 + + d_ptr->mXBase = 0; + // + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = qobject_cast< SARibbonPannel* >(item->widget())) { + if (p->isExpanding()) { + // pannel可扩展 + ++canExpandingCount; + } + } + } + // 计算可扩展的宽度 + if (canExpandingCount > 0) { + expandWidth = (categoryWidth - total) / canExpandingCount; + } else { + expandWidth = 0; + } + } + int x = d_ptr->mXBase; + if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) { + // 如果是居中对齐,同时没有伸缩的pannel,同时总宽度没有超过category的宽度 + x = (categoryWidth - total) / 2; + } + total = 0; // total重新计算 + // 先按照sizeHint设置所有的尺寸 + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + // 如果是hide就直接跳过 + if (item->separatorWidget) { + // pannel hide分割线也要hide + item->separatorWidget->hide(); + } + item->mWillSetGeometry = QRect(0, 0, 0, 0); + item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0); + continue; + } + SARibbonPannel* p = item->toPannelWidget(); + if (nullptr == p) { + qDebug() << "unknow widget in SARibbonCategoryLayout"; + continue; + } + // p->layout()->update(); + QSize pannelSize = p->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + if (p->isExpanding()) { + // 可扩展,就把pannel扩展到最大 + pannelSize.setWidth(pannelSize.width() + expandWidth); + } + int w = pannelSize.width(); + + item->mWillSetGeometry = QRect(x, y, w, height); + x += w; + total += w; + w = SeparatorSize.width(); + item->mWillSetSeparatorGeometry = QRect(x, y, w, height); + x += w; + total += w; + } + d_ptr->mTotalWidth = total; + d_ptr->mSizeHint = QSize(d_ptr->mTotalWidth, height); + d_ptr->mMinSizeHint = QSize(categoryWidth, height); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint + << ",Category name=" << category->categoryName(); +#endif +} + +/** + * @brief 执行layout调整 + */ +void SARibbonCategoryLayout::doLayout() +{ + if (d_ptr->mDirty) { + updateGeometryArr(); + } + SARibbonCategory* category = ribbonCategory(); + // 两个滚动按钮的位置永远不变 + d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height()); + d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height()); + QList< QWidget* > showWidgets, hideWidgets; +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + int debug_i__(0); + qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName(); +#endif + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + if (item->separatorWidget) { + hideWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() << ",will hide"; + ++debug_i__; +#endif + } else { + //! 这里不能用item->setGeometry(item->mWillSetGeometry);这样会得到一个很奇怪的显示效果 + //! 就是窗口的最左边不会移出去,而是把最右边压缩, + item->widget()->setGeometry(item->mWillSetGeometry); + // item->widget()->setFixedSize(item->mWillSetGeometry.size()); + // item->widget()->move(item->mWillSetGeometry.topLeft()); + // item->setGeometry(item->mWillSetGeometry); + showWidgets << item->widget(); + if (item->separatorWidget) { + item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry); + showWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() + << "),willSetGeometry:" << item->mWillSetGeometry + << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry; + ++debug_i__; +#endif + } + } + + d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow); + d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow); + if (d_ptr->mIsRightScrollBtnShow) { + d_ptr->mRightScrollBtn->raise(); + } + if (d_ptr->mIsLeftScrollBtnShow) { + d_ptr->mLeftScrollBtn->raise(); + } + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) { + w->show(); + } + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) { + w->hide(); + } + } +} + +/** + * @brief 返回所有pannels + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannels() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + SARibbonPannel* p = item->toPannelWidget(); + res.append(p); + } + return (res); +} + +/** + * @brief 通过ObjectName查找pannel + * @param objname + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByObjectName(const QString& objname) const +{ + for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->objectName() == objname) { + return pannel; + } + } + } + return nullptr; +} + +/** + * @brief 通过名字查找pannel + * @param title + * @return 如果有重名,只会返回第一个符合条件的 + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByName(const QString& pannelname) const +{ + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->pannelName() == pannelname) { + return (pannel); + } + } + } + return (nullptr); +} + +/** + * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr + * @param i + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByIndex(int i) const +{ + if (i >= 0 && i < d_ptr->mItemList.size()) { + return d_ptr->mItemList[ i ]->toPannelWidget(); + } + return nullptr; +} + +/** + * @brief 移动pannel + * @param from + * @param to + */ +void SARibbonCategoryLayout::movePannel(int from, int to) +{ + d_ptr->mItemList.move(from, to); + doLayout(); +} + +/** + * @brief 返回pannel的个数 + * @return + */ +int SARibbonCategoryLayout::pannelCount() const +{ + return d_ptr->mItemList.size(); +} + +/** + * @brief 查找pannel对应的索引 + * @param p + * @return 如果找不到,返回-1 + */ +int SARibbonCategoryLayout::pannelIndex(SARibbonPannel* p) const +{ + int c = pannelCount(); + + for (int i = 0; i < c; ++i) { + if (d_ptr->mItemList[ i ]->toPannelWidget() == p) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取所有的pannel + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannelList() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = i->toPannelWidget()) { + res.append(p); + } + } + return (res); +} + +/** + * @brief 执行滚动 + * @param px + */ +void SARibbonCategoryLayout::scroll(int px) +{ + QSize contentSize = categoryContentSize(); + d_ptr->mXBase += px; + if (d_ptr->mXBase > 0) { + d_ptr->mXBase = 0; + } else if ((d_ptr->mXBase + d_ptr->mTotalWidth) < contentSize.width()) { + d_ptr->mXBase = contentSize.width() - d_ptr->mTotalWidth; + } + invalidate(); +} + +/** + * @brief 判断是否滚动过 + * @return + */ +bool SARibbonCategoryLayout::isScrolled() const +{ + return (d_ptr->mXBase != 0); +} + +/** + * @brief 这个宽度是实际内容的宽度,有可能大于size.width,也有可能小于 + * @return + */ +int SARibbonCategoryLayout::categoryTotalWidth() const +{ + return d_ptr->mTotalWidth; +} + +/** + @brief 设置Category的对齐方式 + + 居中对齐会让pannel以居中进行对齐 + @param al + */ +void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al) +{ + d_ptr->mCategoryAlignment = al; +} + +/** + @brief Category的对齐方式 + @return + */ +SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const +{ + return d_ptr->mCategoryAlignment; +} + +void SARibbonCategoryLayout::onLeftScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase + width; + if (tmp > 0) { + tmp = 0; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::onRightScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase - width; + if (tmp < (width - totalWidth)) { + tmp = width - totalWidth; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName() + << ")======="; +#endif + QLayout::setGeometry(rect); + d_ptr->mDirty = false; + updateGeometryArr(); + doLayout(); +} +//============================================================= +// SARibbonCategoryLayoutItem +//============================================================= + +SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPannel* w) : QWidgetItem(w) +{ + separatorWidget = nullptr; +} + +SARibbonCategoryLayoutItem::~SARibbonCategoryLayoutItem() +{ +} + +SARibbonPannel* SARibbonCategoryLayoutItem::toPannelWidget() +{ + return qobject_cast< SARibbonPannel* >(widget()); +} diff --git a/src/SARibbonBar/SARibbonCategoryLayout.h b/src/SARibbonBar/SARibbonCategoryLayout.h index bc8e8083..839a0f95 100644 --- a/src/SARibbonBar/SARibbonCategoryLayout.h +++ b/src/SARibbonBar/SARibbonCategoryLayout.h @@ -1,95 +1,95 @@ -#ifndef SARIBBONCATEGORYLAYOUT_H -#define SARIBBONCATEGORYLAYOUT_H -#include "SARibbonGlobal.h" -#include -#include -#include -#include "SARibbonCategory.h" - -class SARibbonPannel; -class SARibbonCategoryLayoutItem; -class SARibbonSeparatorWidget; - -/** - * @brief The SARibbonCategoryLayout class - */ -class SA_RIBBON_EXPORT SARibbonCategoryLayout : public QLayout -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCategoryLayout) -public: - explicit SARibbonCategoryLayout(SARibbonCategory* parent); - ~SARibbonCategoryLayout(); - - SARibbonCategory* ribbonCategory() const; - - virtual void addItem(QLayoutItem* item) override; - virtual QLayoutItem* itemAt(int index) const override; - virtual QLayoutItem* takeAt(int index) override; - SARibbonCategoryLayoutItem* takePannelItem(int index); - SARibbonCategoryLayoutItem* takePannelItem(SARibbonPannel* pannel); - bool takePannel(SARibbonPannel* pannel); - virtual int count() const override; - void setGeometry(const QRect& rect) override; - QSize sizeHint() const override; - QSize minimumSize() const override; - Qt::Orientations expandingDirections() const override; - void invalidate() override; - // 追加一个pannel - void addPannel(SARibbonPannel* pannel); - // 插入一个pannel - void insertPannel(int index, SARibbonPannel* pannel); - // - QSize categoryContentSize() const; - // 更新尺寸 - void updateGeometryArr(); - - // 执行位置调整 - void doLayout(); - - // 返回所有pannels - QList< SARibbonPannel* > pannels() const; - - // 通过obj name获取pannel - SARibbonPannel* pannelByObjectName(const QString& objname) const; - // 通过pannel name获取pannel - SARibbonPannel* pannelByName(const QString& pannelname) const; - // 通过索引找到pannel,如果超过索引范围,会返回nullptr - SARibbonPannel* pannelByIndex(int i) const; - // 移动pannel - void movePannel(int from, int to); - // pannel的数量 - int pannelCount() const; - // pannel的索引 - int pannelIndex(SARibbonPannel* p) const; - // 获取所有的pannel - QList< SARibbonPannel* > pannelList() const; - // 执行滚轮事件 - void scroll(int px); - // 判断是否有滚动过 - bool isScrolled() const; - // category的总宽度 - int categoryTotalWidth() const; - // 设置Category的对齐方式 - void setCategoryAlignment(SARibbonAlignment al); - SARibbonAlignment categoryAlignment() const; -private Q_SLOTS: - void onLeftScrollButtonClicked(); - void onRightScrollButtonClicked(); -}; - -/** - * @brief SARibbonCategoryLayoutItem,用于标识SARibbonCategoryLayout的item - */ -class SA_RIBBON_EXPORT SARibbonCategoryLayoutItem : public QWidgetItem -{ -public: - explicit SARibbonCategoryLayoutItem(SARibbonPannel* w); - ~SARibbonCategoryLayoutItem(); - SARibbonSeparatorWidget* separatorWidget; - // 把内部的widget转换为pannel - SARibbonPannel* toPannelWidget(); - QRect mWillSetGeometry; ///< pannel将要设置的Geometry - QRect mWillSetSeparatorGeometry; ///< pannel将要设置的Separator的Geometry -}; -#endif // SARIBBONCATEGORYLAYOUT_H +#ifndef SARIBBONCATEGORYLAYOUT_H +#define SARIBBONCATEGORYLAYOUT_H +#include "SARibbonGlobal.h" +#include +#include +#include +#include "SARibbonCategory.h" + +class SARibbonPannel; +class SARibbonCategoryLayoutItem; +class SARibbonSeparatorWidget; + +/** + * @brief The SARibbonCategoryLayout class + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayout : public QLayout +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCategoryLayout) +public: + explicit SARibbonCategoryLayout(SARibbonCategory* parent); + ~SARibbonCategoryLayout(); + + SARibbonCategory* ribbonCategory() const; + + virtual void addItem(QLayoutItem* item) override; + virtual QLayoutItem* itemAt(int index) const override; + virtual QLayoutItem* takeAt(int index) override; + SARibbonCategoryLayoutItem* takePannelItem(int index); + SARibbonCategoryLayoutItem* takePannelItem(SARibbonPannel* pannel); + bool takePannel(SARibbonPannel* pannel); + virtual int count() const override; + void setGeometry(const QRect& rect) override; + QSize sizeHint() const override; + QSize minimumSize() const override; + Qt::Orientations expandingDirections() const override; + void invalidate() override; + // 追加一个pannel + void addPannel(SARibbonPannel* pannel); + // 插入一个pannel + void insertPannel(int index, SARibbonPannel* pannel); + // + QSize categoryContentSize() const; + // 更新尺寸 + void updateGeometryArr(); + + // 执行位置调整 + void doLayout(); + + // 返回所有pannels + QList< SARibbonPannel* > pannels() const; + + // 通过obj name获取pannel + SARibbonPannel* pannelByObjectName(const QString& objname) const; + // 通过pannel name获取pannel + SARibbonPannel* pannelByName(const QString& pannelname) const; + // 通过索引找到pannel,如果超过索引范围,会返回nullptr + SARibbonPannel* pannelByIndex(int i) const; + // 移动pannel + void movePannel(int from, int to); + // pannel的数量 + int pannelCount() const; + // pannel的索引 + int pannelIndex(SARibbonPannel* p) const; + // 获取所有的pannel + QList< SARibbonPannel* > pannelList() const; + // 执行滚轮事件 + void scroll(int px); + // 判断是否有滚动过 + bool isScrolled() const; + // category的总宽度 + int categoryTotalWidth() const; + // 设置Category的对齐方式 + void setCategoryAlignment(SARibbonAlignment al); + SARibbonAlignment categoryAlignment() const; +private Q_SLOTS: + void onLeftScrollButtonClicked(); + void onRightScrollButtonClicked(); +}; + +/** + * @brief SARibbonCategoryLayoutItem,用于标识SARibbonCategoryLayout的item + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayoutItem : public QWidgetItem +{ +public: + explicit SARibbonCategoryLayoutItem(SARibbonPannel* w); + ~SARibbonCategoryLayoutItem(); + SARibbonSeparatorWidget* separatorWidget; + // 把内部的widget转换为pannel + SARibbonPannel* toPannelWidget(); + QRect mWillSetGeometry; ///< pannel将要设置的Geometry + QRect mWillSetSeparatorGeometry; ///< pannel将要设置的Separator的Geometry +}; +#endif // SARIBBONCATEGORYLAYOUT_H diff --git a/src/SARibbonBar/SARibbonColorToolButton.cpp b/src/SARibbonBar/SARibbonColorToolButton.cpp index 13330144..74ab68a9 100644 --- a/src/SARibbonBar/SARibbonColorToolButton.cpp +++ b/src/SARibbonBar/SARibbonColorToolButton.cpp @@ -1,208 +1,208 @@ -#include "SARibbonColorToolButton.h" -#include -#include -#include -#include "colorWidgets/SAColorMenu.h" -//=================================================== -// SARibbonColorToolButton::PrivateData -//=================================================== -const int c_ribbonbutton_color_height = 5; ///< 颜色块的高度 - -class SARibbonColorToolButton::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton) -public: - PrivateData(SARibbonColorToolButton* p); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; - QIcon createColorIcon(const QColor& c, const QSize& size) const; - -public: - QColor mColor; ///< 记录颜色 - SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon }; ///< 颜色显示样式 - QIcon mOldIcon; ///< 记录旧的icon -}; - -SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p) -{ -} - -QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const -{ - if (opt.icon.isNull()) { // 没有有图标 - return QPixmap(); - } - // 有icon,在icon下方加入颜色 - QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1); - QPixmap pixmap = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state); - QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4)); // 宽度上,颜色块多出2px - res.fill(Qt::transparent); - QPainter painter(&res); - int xpixmap = (res.width() - pixmap.width()) / 2; - int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height()) / 2; // 这里要减去2而不是1,这样奇数偶数都不会影响 - int w = pixmap.width(); - int h = pixmap.height(); - QRect rpixmap = QRect(xpixmap, ypixmap, w, h); - painter.drawPixmap(rpixmap, pixmap); - QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1); - if (mColor.isValid()) { - painter.fillRect(colorRect, mColor); - } else { - QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - painter.setRenderHint(QPainter::Antialiasing, true); - int ss = colorRect.width() / 3; - painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); - pen.setColor(Qt::black); - painter.setPen(pen); - painter.drawRect(colorRect); - } - return res; -} - -QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const -{ - QPixmap res(size); - res.fill(Qt::transparent); - QPainter painter(&res); - if (c.isValid()) { - painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c); - } else { - QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap); - painter.setPen(pen); - painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2)); - pen.setColor(Qt::red); - painter.setPen(pen); - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - painter.setRenderHint(QPainter::Antialiasing, true); - painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1)); - } - return QIcon(res); -} - -//=================================================== -// SARibbonColorToolButton -//=================================================== - -SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent) - : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) -{ - connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); -} - -SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent) - : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) -{ - connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); -} - -SARibbonColorToolButton::~SARibbonColorToolButton() -{ -} - -/** - * @brief 获取按钮维护的颜色 - * @return - */ -QColor SARibbonColorToolButton::color() const -{ - return d_ptr->mColor; -} - -/** - * @brief 设置颜色显示的样式 - * @param s - */ -void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s) -{ - if (d_ptr->mColorStyle == s) { - return; - } - d_ptr->mColorStyle = s; - if (ColorUnderIcon == s) { - setIcon(d_ptr->mOldIcon); - } else { - d_ptr->mOldIcon = icon(); - setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32))); - } - repaint(); -} - -/** - * @brief 颜色显示的样式 - * @return - */ -SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const -{ - return d_ptr->mColorStyle; -} - -/** - * @brief 建立标准的颜色菜单 - * @return - */ -SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu() -{ - setPopupMode(QToolButton::MenuButtonPopup); - SAColorMenu* m = new SAColorMenu(this); - m->enableNoneColorAction(true); - QAction* customColor = m->customColorAction(); - if (customColor) { - customColor->setIcon(QIcon(":/image/resource/define-color.svg")); - } - connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor); - setMenu(m); - - updateRect(); - return m; -} - -/** - * @brief 设置按钮的颜色 - * - * 此时会生成一个新的icon - * @note 会发射@sa colorChanged 信号 - * @param c - */ -void SARibbonColorToolButton::setColor(const QColor& c) -{ - if (d_ptr->mColor != c) { - d_ptr->mColor = c; - if (ColorFillToIcon == colorStyle()) { - setIcon(d_ptr->createColorIcon(c, QSize(32, 32))); - } - repaint(); - Q_EMIT colorChanged(c); - } -} - -void SARibbonColorToolButton::onButtonClicked(bool checked) -{ - Q_EMIT colorClicked(d_ptr->mColor, checked); -} - -/** - * @brief 重写paintIcon函数,把颜色加到icon下面 - * @param p - * @param opt - * @param iconDrawRect - */ -void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) -{ - if (ColorUnderIcon == colorStyle()) { - // 有icon - QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); - style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); - } else { - SARibbonToolButton::paintIcon(p, opt, iconDrawRect); - } -} +#include "SARibbonColorToolButton.h" +#include +#include +#include +#include "colorWidgets/SAColorMenu.h" +//=================================================== +// SARibbonColorToolButton::PrivateData +//=================================================== +const int c_ribbonbutton_color_height = 5; ///< 颜色块的高度 + +class SARibbonColorToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton) +public: + PrivateData(SARibbonColorToolButton* p); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + QIcon createColorIcon(const QColor& c, const QSize& size) const; + +public: + QColor mColor; ///< 记录颜色 + SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon }; ///< 颜色显示样式 + QIcon mOldIcon; ///< 记录旧的icon +}; + +SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p) +{ +} + +QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return QPixmap(); + } + // 有icon,在icon下方加入颜色 + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1); + QPixmap pixmap = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state); + QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4)); // 宽度上,颜色块多出2px + res.fill(Qt::transparent); + QPainter painter(&res); + int xpixmap = (res.width() - pixmap.width()) / 2; + int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height()) / 2; // 这里要减去2而不是1,这样奇数偶数都不会影响 + int w = pixmap.width(); + int h = pixmap.height(); + QRect rpixmap = QRect(xpixmap, ypixmap, w, h); + painter.drawPixmap(rpixmap, pixmap); + QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1); + if (mColor.isValid()) { + painter.fillRect(colorRect, mColor); + } else { + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + painter.setPen(pen); + painter.drawRect(colorRect); + } + return res; +} + +QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const +{ + QPixmap res(size); + res.fill(Qt::transparent); + QPainter painter(&res); + if (c.isValid()) { + painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c); + } else { + QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2)); + pen.setColor(Qt::red); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1)); + } + return QIcon(res); +} + +//=================================================== +// SARibbonColorToolButton +//=================================================== + +SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent) + : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent) + : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::~SARibbonColorToolButton() +{ +} + +/** + * @brief 获取按钮维护的颜色 + * @return + */ +QColor SARibbonColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置颜色显示的样式 + * @param s + */ +void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s) +{ + if (d_ptr->mColorStyle == s) { + return; + } + d_ptr->mColorStyle = s; + if (ColorUnderIcon == s) { + setIcon(d_ptr->mOldIcon); + } else { + d_ptr->mOldIcon = icon(); + setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32))); + } + repaint(); +} + +/** + * @brief 颜色显示的样式 + * @return + */ +SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const +{ + return d_ptr->mColorStyle; +} + +/** + * @brief 建立标准的颜色菜单 + * @return + */ +SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu() +{ + setPopupMode(QToolButton::MenuButtonPopup); + SAColorMenu* m = new SAColorMenu(this); + m->enableNoneColorAction(true); + QAction* customColor = m->customColorAction(); + if (customColor) { + customColor->setIcon(QIcon(":/image/resource/define-color.svg")); + } + connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor); + setMenu(m); + + updateRect(); + return m; +} + +/** + * @brief 设置按钮的颜色 + * + * 此时会生成一个新的icon + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SARibbonColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + if (ColorFillToIcon == colorStyle()) { + setIcon(d_ptr->createColorIcon(c, QSize(32, 32))); + } + repaint(); + Q_EMIT colorChanged(c); + } +} + +void SARibbonColorToolButton::onButtonClicked(bool checked) +{ + Q_EMIT colorClicked(d_ptr->mColor, checked); +} + +/** + * @brief 重写paintIcon函数,把颜色加到icon下面 + * @param p + * @param opt + * @param iconDrawRect + */ +void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (ColorUnderIcon == colorStyle()) { + // 有icon + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + } else { + SARibbonToolButton::paintIcon(p, opt, iconDrawRect); + } +} diff --git a/src/SARibbonBar/SARibbonColorToolButton.h b/src/SARibbonBar/SARibbonColorToolButton.h index 3f6d6363..37f1925c 100644 --- a/src/SARibbonBar/SARibbonColorToolButton.h +++ b/src/SARibbonBar/SARibbonColorToolButton.h @@ -1,55 +1,55 @@ -#ifndef SARIBBONCOLORTOOLBUTTON_H -#define SARIBBONCOLORTOOLBUTTON_H -#include "SARibbonToolButton.h" -#include "SARibbonGlobal.h" -class SAColorMenu; -/** - * @brief Refer to the color setting button in the office, which can display the color below the icon(参考office的颜色设置按钮,可以显示颜色在图标下方) - */ -class SA_RIBBON_EXPORT SARibbonColorToolButton : public SARibbonToolButton -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonColorToolButton) -public: - /** - * @brief 颜色样式 - */ - enum ColorStyle - { - ColorUnderIcon, ///< 颜色在icon下方,这个要求必须设置icon - ColorFillToIcon ///< 颜色作为icon,这个模式下在setColor会自动生成一个颜色icon替换掉原来的icon,因此setIcon函数没有作用 - }; - -public: - explicit SARibbonColorToolButton(QWidget* parent = nullptr); - explicit SARibbonColorToolButton(QAction* defaultAction, QWidget* parent = nullptr); - ~SARibbonColorToolButton(); - // 获取颜色 - QColor color() const; - // 设置颜色显示方案 - void setColorStyle(ColorStyle s); - ColorStyle colorStyle() const; - // 建立标准的颜色菜单 - SAColorMenu* setupStandardColorMenu(); -public Q_SLOTS: - // 设置颜色,会发射colorChanged信号 - void setColor(const QColor& c); -private Q_SLOTS: - void onButtonClicked(bool checked = false); -Q_SIGNALS: - /** - * @brief 颜色被点击的响应 - * @param color - */ - void colorClicked(const QColor& color, bool checked = false); - /** - * @brief 颜色改变信号 - * @param color - */ - void colorChanged(const QColor& color); - -protected: - void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); -}; - -#endif // SARIBBONCOLORTOOLBUTTON_H +#ifndef SARIBBONCOLORTOOLBUTTON_H +#define SARIBBONCOLORTOOLBUTTON_H +#include "SARibbonToolButton.h" +#include "SARibbonGlobal.h" +class SAColorMenu; +/** + * @brief Refer to the color setting button in the office, which can display the color below the icon(参考office的颜色设置按钮,可以显示颜色在图标下方) + */ +class SA_RIBBON_EXPORT SARibbonColorToolButton : public SARibbonToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonColorToolButton) +public: + /** + * @brief 颜色样式 + */ + enum ColorStyle + { + ColorUnderIcon, ///< 颜色在icon下方,这个要求必须设置icon + ColorFillToIcon ///< 颜色作为icon,这个模式下在setColor会自动生成一个颜色icon替换掉原来的icon,因此setIcon函数没有作用 + }; + +public: + explicit SARibbonColorToolButton(QWidget* parent = nullptr); + explicit SARibbonColorToolButton(QAction* defaultAction, QWidget* parent = nullptr); + ~SARibbonColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置颜色显示方案 + void setColorStyle(ColorStyle s); + ColorStyle colorStyle() const; + // 建立标准的颜色菜单 + SAColorMenu* setupStandardColorMenu(); +public Q_SLOTS: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); +private Q_SLOTS: + void onButtonClicked(bool checked = false); +Q_SIGNALS: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); + +protected: + void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); +}; + +#endif // SARIBBONCOLORTOOLBUTTON_H diff --git a/src/SARibbonBar/SARibbonComboBox.cpp b/src/SARibbonBar/SARibbonComboBox.cpp index d97ab180..a267b30e 100644 --- a/src/SARibbonBar/SARibbonComboBox.cpp +++ b/src/SARibbonBar/SARibbonComboBox.cpp @@ -1,11 +1,11 @@ -#include "SARibbonComboBox.h" - - -SARibbonComboBox::SARibbonComboBox(QWidget *parent) - : QComboBox(parent) -{ -} - -SARibbonComboBox::~SARibbonComboBox() -{ -} +#include "SARibbonComboBox.h" + + +SARibbonComboBox::SARibbonComboBox(QWidget *parent) + : QComboBox(parent) +{ +} + +SARibbonComboBox::~SARibbonComboBox() +{ +} diff --git a/src/SARibbonBar/SARibbonComboBox.h b/src/SARibbonBar/SARibbonComboBox.h index 4cdced17..6e15e74b 100644 --- a/src/SARibbonBar/SARibbonComboBox.h +++ b/src/SARibbonBar/SARibbonComboBox.h @@ -1,19 +1,19 @@ -#ifndef SARIBBONCOMBOBOX_H -#define SARIBBONCOMBOBOX_H -#include "SARibbonGlobal.h" -#include - - - -/** - * @brief QComboBox的Ribbon显示,可以显示QIcon和windowTitle在左侧 - */ -class SA_RIBBON_EXPORT SARibbonComboBox : public QComboBox -{ - Q_OBJECT -public: - explicit SARibbonComboBox(QWidget *parent = nullptr); - ~SARibbonComboBox(); -}; - -#endif // SARIBBONCOMBOBOX_H +#ifndef SARIBBONCOMBOBOX_H +#define SARIBBONCOMBOBOX_H +#include "SARibbonGlobal.h" +#include + + + +/** + * @brief QComboBox的Ribbon显示,可以显示QIcon和windowTitle在左侧 + */ +class SA_RIBBON_EXPORT SARibbonComboBox : public QComboBox +{ + Q_OBJECT +public: + explicit SARibbonComboBox(QWidget *parent = nullptr); + ~SARibbonComboBox(); +}; + +#endif // SARIBBONCOMBOBOX_H diff --git a/src/SARibbonBar/SARibbonContextCategory.cpp b/src/SARibbonBar/SARibbonContextCategory.cpp index 17debe0c..e68fde99 100644 --- a/src/SARibbonBar/SARibbonContextCategory.cpp +++ b/src/SARibbonBar/SARibbonContextCategory.cpp @@ -1,212 +1,212 @@ -#include "SARibbonContextCategory.h" -#include -#include -#include "SARibbonElementManager.h" -#include - -/** - * @brief The SARibbonCategoryData class - */ -class SAPrivateRibbonCategoryData -{ -public: - SARibbonCategory* categoryPage; -}; - -/** - * @brief The SARibbonContextCategoryPrivate class - */ -class SARibbonContextCategory::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory) -public: - PrivateData(SARibbonContextCategory* p); - -public: - QList< SAPrivateRibbonCategoryData > mCategoryDataList; - QVariant mContextID; - QColor mContextColor; - QString mContextTitle; -}; -SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p) -{ -} - -//=================================================== -// SARibbonContextCategory -//=================================================== -SARibbonContextCategory::SARibbonContextCategory(QWidget* parent) - : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this)) -{ -} - -SARibbonContextCategory::~SARibbonContextCategory() -{ -} - -/** - * @brief 添加标签 - * @param title 标签名字 - */ -SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title) -{ - SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget()); - category->setCategoryName(title); - addCategoryPage(category); - return (category); -} - -/** - * @brief 添加标签 - * @param page - */ -void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category) -{ - if (isHaveCategory(category)) { - // cn:SARibbonContextCategory已经持有标签:%1,将跳过 - qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName()); - return; - } - category->markIsContextCategory(true); - connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged); - SAPrivateRibbonCategoryData catData; - catData.categoryPage = category; - d_ptr->mCategoryDataList.append(catData); - category->installEventFilter(this); - Q_EMIT categoryPageAdded(category); -} - -int SARibbonContextCategory::categoryCount() const -{ - return (d_ptr->mCategoryDataList.count()); -} - -void SARibbonContextCategory::setId(const QVariant& id) -{ - d_ptr->mContextID = id; -} - -QVariant SARibbonContextCategory::id() const -{ - return (d_ptr->mContextID); -} - -void SARibbonContextCategory::setContextColor(const QColor color) -{ - d_ptr->mContextColor = color; -} - -QColor SARibbonContextCategory::contextColor() const -{ - return (d_ptr->mContextColor); -} - -QWidget* SARibbonContextCategory::parentWidget() const -{ - return (qobject_cast< QWidget* >(parent())); -} - -bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e) -{ - if (nullptr == watched) { - return (false); - } - switch (e->type()) { - case QEvent::Close: { - SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched); - if (c) { -#ifdef SA_RIBBON_DEBUG_HELP_DRAW - qDebug() << " -----------> close event"; -#endif - takeCategory(c); - } - } break; - - default: - break; - } - return (false); -} - -/** - * @brief 获取上下文标签的标题 - * @return - */ -QString SARibbonContextCategory::contextTitle() const -{ - return (d_ptr->mContextTitle); -} - -/** - * @brief 设置上下文标签的标题,标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle - * @param contextTitle - * @note 此函数会触发信号@sa contextTitleChanged - */ -void SARibbonContextCategory::setContextTitle(const QString& contextTitle) -{ - d_ptr->mContextTitle = contextTitle; - Q_EMIT contextTitleChanged(contextTitle); -} - -/** - * @brief 获取对应的tab页 - * @param index - * @return - */ -SARibbonCategory* SARibbonContextCategory::categoryPage(int index) -{ - return (d_ptr->mCategoryDataList[ index ].categoryPage); -} - -/** - * @brief 获取所有的SARibbonCategory* - * @return - */ -QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const -{ - QList< SARibbonCategory* > res; - - for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->mCategoryDataList)) { - res.append(c.categoryPage); - } - return (res); -} - -/** - * @brief 移除这个category,这时SARibbonContextCategory不再管理这个category - * @param category - * @return 成功移除返回true - */ -bool SARibbonContextCategory::takeCategory(SARibbonCategory* category) -{ - for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { - if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { - d_ptr->mCategoryDataList.takeAt(i); - return (true); - } - } - return (false); -} - -/** - * @brief 判断上下文是否维护了此SARibbonCategory - * @param category - * @return - */ -bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const -{ - for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { - if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { - return (true); - } - } - return (false); -} - -void SARibbonContextCategory::onCategoryTitleChanged(const QString& title) -{ - SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender()); - if (category) { - Q_EMIT categoryTitleChanged(category, title); - } -} +#include "SARibbonContextCategory.h" +#include +#include +#include "SARibbonElementManager.h" +#include + +/** + * @brief The SARibbonCategoryData class + */ +class SAPrivateRibbonCategoryData +{ +public: + SARibbonCategory* categoryPage; +}; + +/** + * @brief The SARibbonContextCategoryPrivate class + */ +class SARibbonContextCategory::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory) +public: + PrivateData(SARibbonContextCategory* p); + +public: + QList< SAPrivateRibbonCategoryData > mCategoryDataList; + QVariant mContextID; + QColor mContextColor; + QString mContextTitle; +}; +SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonContextCategory +//=================================================== +SARibbonContextCategory::SARibbonContextCategory(QWidget* parent) + : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this)) +{ +} + +SARibbonContextCategory::~SARibbonContextCategory() +{ +} + +/** + * @brief 添加标签 + * @param title 标签名字 + */ +SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget()); + category->setCategoryName(title); + addCategoryPage(category); + return (category); +} + +/** + * @brief 添加标签 + * @param page + */ +void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category) +{ + if (isHaveCategory(category)) { + // cn:SARibbonContextCategory已经持有标签:%1,将跳过 + qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName()); + return; + } + category->markIsContextCategory(true); + connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged); + SAPrivateRibbonCategoryData catData; + catData.categoryPage = category; + d_ptr->mCategoryDataList.append(catData); + category->installEventFilter(this); + Q_EMIT categoryPageAdded(category); +} + +int SARibbonContextCategory::categoryCount() const +{ + return (d_ptr->mCategoryDataList.count()); +} + +void SARibbonContextCategory::setId(const QVariant& id) +{ + d_ptr->mContextID = id; +} + +QVariant SARibbonContextCategory::id() const +{ + return (d_ptr->mContextID); +} + +void SARibbonContextCategory::setContextColor(const QColor color) +{ + d_ptr->mContextColor = color; +} + +QColor SARibbonContextCategory::contextColor() const +{ + return (d_ptr->mContextColor); +} + +QWidget* SARibbonContextCategory::parentWidget() const +{ + return (qobject_cast< QWidget* >(parent())); +} + +bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e) +{ + if (nullptr == watched) { + return (false); + } + switch (e->type()) { + case QEvent::Close: { + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched); + if (c) { +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + qDebug() << " -----------> close event"; +#endif + takeCategory(c); + } + } break; + + default: + break; + } + return (false); +} + +/** + * @brief 获取上下文标签的标题 + * @return + */ +QString SARibbonContextCategory::contextTitle() const +{ + return (d_ptr->mContextTitle); +} + +/** + * @brief 设置上下文标签的标题,标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle + * @param contextTitle + * @note 此函数会触发信号@sa contextTitleChanged + */ +void SARibbonContextCategory::setContextTitle(const QString& contextTitle) +{ + d_ptr->mContextTitle = contextTitle; + Q_EMIT contextTitleChanged(contextTitle); +} + +/** + * @brief 获取对应的tab页 + * @param index + * @return + */ +SARibbonCategory* SARibbonContextCategory::categoryPage(int index) +{ + return (d_ptr->mCategoryDataList[ index ].categoryPage); +} + +/** + * @brief 获取所有的SARibbonCategory* + * @return + */ +QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const +{ + QList< SARibbonCategory* > res; + + for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->mCategoryDataList)) { + res.append(c.categoryPage); + } + return (res); +} + +/** + * @brief 移除这个category,这时SARibbonContextCategory不再管理这个category + * @param category + * @return 成功移除返回true + */ +bool SARibbonContextCategory::takeCategory(SARibbonCategory* category) +{ + for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { + if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { + d_ptr->mCategoryDataList.takeAt(i); + return (true); + } + } + return (false); +} + +/** + * @brief 判断上下文是否维护了此SARibbonCategory + * @param category + * @return + */ +bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const +{ + for (int i = 0; i < d_ptr->mCategoryDataList.size(); ++i) { + if (d_ptr->mCategoryDataList[ i ].categoryPage == category) { + return (true); + } + } + return (false); +} + +void SARibbonContextCategory::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender()); + if (category) { + Q_EMIT categoryTitleChanged(category, title); + } +} diff --git a/src/SARibbonBar/SARibbonContextCategory.h b/src/SARibbonBar/SARibbonContextCategory.h index 694958fe..561bef59 100644 --- a/src/SARibbonBar/SARibbonContextCategory.h +++ b/src/SARibbonBar/SARibbonContextCategory.h @@ -1,80 +1,80 @@ -#ifndef SARIBBONCONTEXTCATEGORY_H -#define SARIBBONCONTEXTCATEGORY_H -#include "SARibbonGlobal.h" -#include "SARibbonCategory.h" -#include - -/** - * @brief 管理上下文标签的类 - */ -class SA_RIBBON_EXPORT SARibbonContextCategory : public QObject -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonContextCategory) -public: - explicit SARibbonContextCategory(QWidget* parent = nullptr); - ~SARibbonContextCategory(); - //上下文目录添加下属目录 - SARibbonCategory* addCategoryPage(const QString& title); - void addCategoryPage(SARibbonCategory* category); - //获取上下文标签下管理的标签个数 - int categoryCount() const; - - //设置id - void setId(const QVariant& id); - QVariant id() const; - - //设置上下文颜色 - void setContextColor(const QColor color); - QColor contextColor() const; - - //上下文标签的内容 - QString contextTitle() const; - void setContextTitle(const QString& contextTitle); - - //获取对应的tab页 - SARibbonCategory* categoryPage(int index); - - //获取所有的SARibbonCategory* - QList< SARibbonCategory* > categoryList() const; - - //移除category - bool takeCategory(SARibbonCategory* category); - - //判断上下文是否维护了此SARibbonCategory - bool isHaveCategory(SARibbonCategory* category) const; -Q_SIGNALS: - /** - * @brief 标签加入上下文 - * @param category - */ - void categoryPageAdded(SARibbonCategory* category); - - /** - * @brief 标签从上下文移除 - * @param category - */ - void categoryPageRemoved(SARibbonCategory* category); - - /** - * @brief 上下文的标题发生改变 - * @param title - */ - void contextTitleChanged(const QString& title); - - /** - * @brief 上下文标签维护的标签页名字发生了改变 - * @param category 发生改变的上下文标签页 - * @param title 新名字 - */ - void categoryTitleChanged(SARibbonCategory* category, const QString& title); -private Q_SLOTS: - void onCategoryTitleChanged(const QString& title); - -protected: - //获取父级窗口 - QWidget* parentWidget() const; - virtual bool eventFilter(QObject* watched, QEvent* e) override; -}; - -#endif // SARIBBONCONTEXTCATEGORY_H +#ifndef SARIBBONCONTEXTCATEGORY_H +#define SARIBBONCONTEXTCATEGORY_H +#include "SARibbonGlobal.h" +#include "SARibbonCategory.h" +#include + +/** + * @brief 管理上下文标签的类 + */ +class SA_RIBBON_EXPORT SARibbonContextCategory : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonContextCategory) +public: + explicit SARibbonContextCategory(QWidget* parent = nullptr); + ~SARibbonContextCategory(); + //上下文目录添加下属目录 + SARibbonCategory* addCategoryPage(const QString& title); + void addCategoryPage(SARibbonCategory* category); + //获取上下文标签下管理的标签个数 + int categoryCount() const; + + //设置id + void setId(const QVariant& id); + QVariant id() const; + + //设置上下文颜色 + void setContextColor(const QColor color); + QColor contextColor() const; + + //上下文标签的内容 + QString contextTitle() const; + void setContextTitle(const QString& contextTitle); + + //获取对应的tab页 + SARibbonCategory* categoryPage(int index); + + //获取所有的SARibbonCategory* + QList< SARibbonCategory* > categoryList() const; + + //移除category + bool takeCategory(SARibbonCategory* category); + + //判断上下文是否维护了此SARibbonCategory + bool isHaveCategory(SARibbonCategory* category) const; +Q_SIGNALS: + /** + * @brief 标签加入上下文 + * @param category + */ + void categoryPageAdded(SARibbonCategory* category); + + /** + * @brief 标签从上下文移除 + * @param category + */ + void categoryPageRemoved(SARibbonCategory* category); + + /** + * @brief 上下文的标题发生改变 + * @param title + */ + void contextTitleChanged(const QString& title); + + /** + * @brief 上下文标签维护的标签页名字发生了改变 + * @param category 发生改变的上下文标签页 + * @param title 新名字 + */ + void categoryTitleChanged(SARibbonCategory* category, const QString& title); +private Q_SLOTS: + void onCategoryTitleChanged(const QString& title); + +protected: + //获取父级窗口 + QWidget* parentWidget() const; + virtual bool eventFilter(QObject* watched, QEvent* e) override; +}; + +#endif // SARIBBONCONTEXTCATEGORY_H diff --git a/src/SARibbonBar/SARibbonControlButton.cpp b/src/SARibbonBar/SARibbonControlButton.cpp index ce00c825..d309ef6f 100644 --- a/src/SARibbonBar/SARibbonControlButton.cpp +++ b/src/SARibbonBar/SARibbonControlButton.cpp @@ -1,16 +1,16 @@ -#include "SARibbonControlButton.h" - -SARibbonControlButton::SARibbonControlButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonControlButton::~SARibbonControlButton() -{ -} -SARibbonControlToolButton::SARibbonControlToolButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonControlToolButton::~SARibbonControlToolButton() -{ -} +#include "SARibbonControlButton.h" + +SARibbonControlButton::SARibbonControlButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonControlButton::~SARibbonControlButton() +{ +} +SARibbonControlToolButton::SARibbonControlToolButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonControlToolButton::~SARibbonControlToolButton() +{ +} diff --git a/src/SARibbonBar/SARibbonControlButton.h b/src/SARibbonBar/SARibbonControlButton.h index 777c9fbd..90c31f2a 100644 --- a/src/SARibbonBar/SARibbonControlButton.h +++ b/src/SARibbonBar/SARibbonControlButton.h @@ -1,33 +1,33 @@ -#ifndef SARIBBONCONTROLBUTTON_H -#define SARIBBONCONTROLBUTTON_H -#include -#include "SARibbonGlobal.h" -#include "SARibbonToolButton.h" - -/** - * @brief 用于SARibbonGallery的control button - * - * 为了防止外部qss的影响,单独继承一个类 - */ -class SA_RIBBON_EXPORT SARibbonControlButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonControlButton(QWidget* parent = nullptr); - ~SARibbonControlButton(); -}; - -/** - * @brief 用于SARibbonButtonGroupWidget的control button - * - * 为了防止SARibbonToolButton qss的影响,单独继承一个类 - */ -class SA_RIBBON_EXPORT SARibbonControlToolButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonControlToolButton(QWidget* parent = nullptr); - ~SARibbonControlToolButton(); -}; - -#endif // SARIBBONPANNELTOOLBUTTON_H +#ifndef SARIBBONCONTROLBUTTON_H +#define SARIBBONCONTROLBUTTON_H +#include +#include "SARibbonGlobal.h" +#include "SARibbonToolButton.h" + +/** + * @brief 用于SARibbonGallery的control button + * + * 为了防止外部qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonControlButton(QWidget* parent = nullptr); + ~SARibbonControlButton(); +}; + +/** + * @brief 用于SARibbonButtonGroupWidget的control button + * + * 为了防止SARibbonToolButton qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlToolButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonControlToolButton(QWidget* parent = nullptr); + ~SARibbonControlToolButton(); +}; + +#endif // SARIBBONPANNELTOOLBUTTON_H diff --git a/src/SARibbonBar/SARibbonCustomizeData.cpp b/src/SARibbonBar/SARibbonCustomizeData.cpp index 77bec52d..ec310e56 100644 --- a/src/SARibbonBar/SARibbonCustomizeData.cpp +++ b/src/SARibbonBar/SARibbonCustomizeData.cpp @@ -1,704 +1,704 @@ -#include "SARibbonCustomizeData.h" -#include "SARibbonBar.h" -#include -#include -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonCustomizeData -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -SARibbonCustomizeData::SARibbonCustomizeData() - : indexValue(-1) - , actionRowProportionValue(SARibbonPannelItem::Large) - , mType(UnknowActionType) - , mActionsManagerPointer(nullptr) -{ -} - -SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr) - : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), mType(type), mActionsManagerPointer(mgr) -{ -} - -/** - * @brief 获取CustomizeData的action type - * @return - */ -SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const -{ - return (mType); -} - -/** - * @brief 设置CustomizeData的action type - * @param a - */ -void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a) -{ - mType = a; -} - -/** - * @brief 判断是否是一个正常的CustomizeData - * - * 实际逻辑actionType() != UnknowActionType - * @return 有用的CustomizeData返回true - */ -bool SARibbonCustomizeData::isValid() const -{ - return (actionType() != UnknowActionType); -} - -/** - * @brief 应用SARibbonCustomizeData到SARibbonBar - * @param m - * @return 如果应用失败,返回false,如果actionType==UnknowActionType直接返回false - */ -bool SARibbonCustomizeData::apply(SARibbonBar* bar) const -{ - if (nullptr == bar) { - return (false); - } - switch (actionType()) { - case UnknowActionType: - return (false); - - case AddCategoryActionType: { - // 添加标签 - SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue); - if (nullptr == c) { - return (false); - } - c->setObjectName(categoryObjNameValue); - SARibbonCustomizeData::setCanCustomize(c); - return (true); - } - - case AddPannelActionType: { - // 添加pannel - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* p = c->insertPannel(keyValue, indexValue); - p->setObjectName(pannelObjNameValue); - SARibbonCustomizeData::setCanCustomize(p); - return (true); - } - - case AddActionActionType: { - if (nullptr == mActionsManagerPointer) { - return (false); - } - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - SARibbonCustomizeData::setCanCustomize(act); - pannel->addAction(act, actionRowProportionValue); - return (true); - } - - case RemoveCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - bar->removeCategory(c); - return (true); - } - - case RemovePannelActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - c->removePannel(pannel); - return (true); - } - - case RemoveActionActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - pannel->removeAction(act); - return (true); - } - - case ChangeCategoryOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - int currentindex = bar->categoryIndex(c); - if (-1 == currentindex) { - return (false); - } - int toindex = currentindex + indexValue; - bar->moveCategory(currentindex, toindex); - return (true); - } - - case ChangePannelOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - int pannelIndex = c->pannelIndex(pannel); - if (-1 == pannelIndex) { - return (false); - } - c->movePannel(pannelIndex, pannelIndex + indexValue); - return (true); - } - - case ChangeActionOrderActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - QAction* act = mActionsManagerPointer->action(keyValue); - if (nullptr == act) { - return (false); - } - int actindex = pannel->actionIndex(act); - if (actindex <= -1) { - return (false); - } - pannel->moveAction(actindex, actindex + indexValue); - return (true); - } - - case RenameCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - c->setCategoryName(keyValue); - return (true); - } - - case RenamePannelActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); - if (nullptr == pannel) { - return (false); - } - pannel->setPannelName(keyValue); - return (true); - } - - case VisibleCategoryActionType: { - SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); - if (nullptr == c) { - return (false); - } - if (1 == indexValue) { - bar->showCategory(c); - } else { - bar->hideCategory(c); - } - return (true); - } - - default: - break; - } - return (false); -} - -/** - * @brief 获取actionmanager指针 - * @return - */ -SARibbonActionsManager* SARibbonCustomizeData::actionManager() -{ - return (mActionsManagerPointer); -} - -/** - * @brief 设置ActionsManager - * @param mgr - */ -void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr) -{ - mActionsManagerPointer = mgr; -} - -/** - * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData - * @param title category 的标题 - * @param index category要插入的位置 - * @param objName category的object name - * @return 返回AddCategoryActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName) -{ - SARibbonCustomizeData d(AddCategoryActionType); - - d.indexValue = index; - d.keyValue = title; - d.categoryObjNameValue = objName; - return (d); -} - -/** - * @brief 创建一个AddPannelActionType的SARibbonCustomizeData - * @param title pannel的标题 - * @param index pannel的index - * @param categoryobjName pannel的category的objectname - * @param objName pannel的objname - * @return 返回AddPannelActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddPannelCustomizeData(const QString& title, - int index, - const QString& categoryobjName, - const QString& objName) -{ - SARibbonCustomizeData d(AddPannelActionType); - - d.indexValue = index; - d.keyValue = title; - d.pannelObjNameValue = objName; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 添加action - * @param key action的索引 - * @param mgr action管理器 - * @param rp 定义action的占位情况 - * @param categoryObjName action添加到的category的objname - * @param pannelObjName action添加到的category下的pannel的objname - * @param index action添加到的pannel的索引 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key, - SARibbonActionsManager* mgr, - SARibbonPannelItem::RowProportion rp, - const QString& categoryObjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(AddActionActionType, mgr); - - d.keyValue = key; - d.categoryObjNameValue = categoryObjName; - d.pannelObjNameValue = pannelObjName; - d.actionRowProportionValue = rp; - - return (d); -} - -/** - * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData - * @param newname 新名字 - * @param index category的索引 - * @return 返回RenameCategoryActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname, - const QString& categoryobjName) -{ - SARibbonCustomizeData d(RenameCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize rename category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.keyValue = newname; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 创建一个RenamePannelActionType的SARibbonCustomizeData - * @param newname pannel的名字 - * @param indexValue pannel的索引 - * @param categoryobjName pannel对应的category的object name - * @return 返回RenamePannelActionType的SARibbonCustomizeData - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRenamePannelCustomizeData(const QString& newname, - const QString& categoryobjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(RenamePannelActionType); - - if (pannelObjName.isEmpty() || categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize rename pannel," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.keyValue = newname; - d.pannelObjNameValue = pannelObjName; - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 对应ChangeCategoryOrderActionType - * @param categoryobjName 需要移动的categoryobjName - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, - int moveindex) -{ - SARibbonCustomizeData d(ChangeCategoryOrderActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change category order," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应ChangePannelOrderActionType - * @param categoryobjName 需要移动的pannel对应的categoryobjName - * @param pannelObjName 需要移动的pannelObjName - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangePannelOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - int moveindex) -{ - SARibbonCustomizeData d(ChangePannelOrderActionType); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change pannel order," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应ChangeActionOrderActionType - * @param categoryobjName 需要移动的pannel对应的categoryobjName - * @param pannelObjName 需要移动的pannelObjName - * @param key SARibbonActionsManager管理的key名 - * @param mgr SARibbonActionsManager指针 - * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr, - int moveindex) -{ - SARibbonCustomizeData d(ChangeActionOrderActionType, mgr); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize change action order," - "but get an empty category/pannel/action object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.keyValue = key; - d.indexValue = moveindex; - return (d); -} - -/** - * @brief 对应RemoveCategoryActionType - * @param categoryobjName 需要移除的objname - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName) -{ - SARibbonCustomizeData d(RemoveCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - return (d); -} - -/** - * @brief 对应RemovePannelActionType - * @param categoryobjName pannel对应的category的obj name - * @param pannelObjName pannel对应的 obj name - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemovePannelCustomizeData(const QString& categoryobjName, - const QString& pannelObjName) -{ - SARibbonCustomizeData d(RemovePannelActionType); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove pannel," - "but get an empty category/pannel object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - return (d); -} - -/** - * @brief 对应RemoveActionActionType - * @param categoryobjName pannel对应的category的obj name - * @param pannelObjName pannel对应的 obj name - * @param key SARibbonActionsManager管理的key名 - * @param mgr SARibbonActionsManager指针 - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr) -{ - SARibbonCustomizeData d(RemoveActionActionType, mgr); - - if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize remove action," - "but get an empty category/pannel/action object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.pannelObjNameValue = pannelObjName; - d.keyValue = key; - return (d); -} - -/** - * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData - * @param categoryobjName - * @param isShow - * @return - */ -SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow) -{ - SARibbonCustomizeData d(VisibleCategoryActionType); - - if (categoryobjName.isEmpty()) { - qDebug() << QObject::tr("SARibbon Warning !!! customize visible category," - "but get an empty category object name," - "if you want to customize SARibbon," - "please make sure every element has been set object name."); - } - d.categoryObjNameValue = categoryobjName; - d.indexValue = isShow ? 1 : 0; - return (d); -} - -/** - * @brief 判断外置属性,是否允许自定义 - * @param obj - * @return - */ -bool SARibbonCustomizeData::isCanCustomize(QObject* obj) -{ - QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE); - - if (v.isValid()) { - return (v.toBool()); - } - return (false); -} - -/** - * @brief 设置外置属性允许自定义 - * @param obj - * @param canbe - */ -void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe) -{ - obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe); -} - -QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex); - -QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex) -{ - QList< SARibbonCustomizeData > res; - - for (int i = 0; i < csd.size(); ++i) { - if (!willremoveIndex.contains(i)) { - res << csd[ i ]; - } - } - return (res); -} - -/** - * @brief 对QList进行简化操作 - * - * 此函数会执行如下操作: - * 1、针对同一个category/pannel连续出现的添加和删除操作进行移除(前一步添加,后一步删除) - * - * 2、针对VisibleCategoryActionType,对于连续出现的操作只保留最后一步 - * - * 3、针对RenameCategoryActionType和RenamePannelActionType操作,只保留最后一个 - * - * 4、针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并为一个动作, - * 如果合并后原地不动,则删除 - * - * @param csd - * @return 返回简化的QList - */ -QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd) -{ - int size = csd.size(); - - if (size <= 1) { - return (csd); - } - QList< SARibbonCustomizeData > res; - QList< int > willremoveIndex; // 记录要删除的index - - //! 首先针对连续出现的添加和删除操作进行优化 - for (int i = 1; i < size; ++i) { - if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) { - if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) { - willremoveIndex << i - 1 << i; - } - } else if ((csd[ i - 1 ].actionType() == AddPannelActionType) && (csd[ i ].actionType() == RemovePannelActionType)) { - if ((csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) - && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { - willremoveIndex << i - 1 << i; - } - } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) { - if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) - && (csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) - && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { - willremoveIndex << i - 1 << i; - } - } - } - res = remove_indexs(csd, willremoveIndex); - willremoveIndex.clear(); - - //! 筛选VisibleCategoryActionType,对于连续出现的操作只保留最后一步 - size = res.size(); - for (int i = 1; i < size; ++i) { - if ((res[ i - 1 ].actionType() == VisibleCategoryActionType) - && (res[ i ].actionType() == VisibleCategoryActionType)) { - if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) { - // 要保证操作的是同一个内容 - willremoveIndex << i - 1; // 删除前一个只保留最后一个 - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 针对RenameCategoryActionType和RenamePannelActionType操作,只需保留最后一个 - size = res.size(); - for (int i = 0; i < size; ++i) { - if (res[ i ].actionType() == RenameCategoryActionType) { - // 向后查询,如果查询到有同一个Category改名,把这个索引加入删除队列 - for (int j = i + 1; j < size; ++j) { - if ((res[ j ].actionType() == RenameCategoryActionType) - && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { - willremoveIndex << i; - } - } - } else if (res[ i ].actionType() == RenamePannelActionType) { - // 向后查询,如果查询到有同一个pannel改名,把这个索引加入删除队列 - for (int j = i + 1; j < size; ++j) { - if ((res[ j ].actionType() == RenamePannelActionType) - && (res[ i ].pannelObjNameValue == res[ j ].pannelObjNameValue) - && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { - willremoveIndex << i; - } - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并 - size = res.size(); - for (int i = 1; i < size; ++i) { - if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType) - && (res[ i ].actionType() == ChangeCategoryOrderActionType) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } else if ((res[ i - 1 ].actionType() == ChangePannelOrderActionType) - && (res[ i ].actionType() == ChangePannelOrderActionType) - && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType) - && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue) - && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) - && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { - // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 - res[ i ].indexValue += res[ i - 1 ].indexValue; - willremoveIndex << i - 1; - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - - //! 上一步操作可能会产生indexvalue为0的情况,此操作把indexvalue为0的删除 - size = res.size(); - for (int i = 0; i < size; ++i) { - if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePannelOrderActionType) - || (res[ i ].actionType() == ChangeActionOrderActionType)) { - if (0 == res[ i ].indexValue) { - willremoveIndex << i; - } - } - } - res = remove_indexs(res, willremoveIndex); - willremoveIndex.clear(); - return (res); -} +#include "SARibbonCustomizeData.h" +#include "SARibbonBar.h" +#include +#include +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeData +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +SARibbonCustomizeData::SARibbonCustomizeData() + : indexValue(-1) + , actionRowProportionValue(SARibbonPannelItem::Large) + , mType(UnknowActionType) + , mActionsManagerPointer(nullptr) +{ +} + +SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr) + : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), mType(type), mActionsManagerPointer(mgr) +{ +} + +/** + * @brief 获取CustomizeData的action type + * @return + */ +SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const +{ + return (mType); +} + +/** + * @brief 设置CustomizeData的action type + * @param a + */ +void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a) +{ + mType = a; +} + +/** + * @brief 判断是否是一个正常的CustomizeData + * + * 实际逻辑actionType() != UnknowActionType + * @return 有用的CustomizeData返回true + */ +bool SARibbonCustomizeData::isValid() const +{ + return (actionType() != UnknowActionType); +} + +/** + * @brief 应用SARibbonCustomizeData到SARibbonBar + * @param m + * @return 如果应用失败,返回false,如果actionType==UnknowActionType直接返回false + */ +bool SARibbonCustomizeData::apply(SARibbonBar* bar) const +{ + if (nullptr == bar) { + return (false); + } + switch (actionType()) { + case UnknowActionType: + return (false); + + case AddCategoryActionType: { + // 添加标签 + SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue); + if (nullptr == c) { + return (false); + } + c->setObjectName(categoryObjNameValue); + SARibbonCustomizeData::setCanCustomize(c); + return (true); + } + + case AddPannelActionType: { + // 添加pannel + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* p = c->insertPannel(keyValue, indexValue); + p->setObjectName(pannelObjNameValue); + SARibbonCustomizeData::setCanCustomize(p); + return (true); + } + + case AddActionActionType: { + if (nullptr == mActionsManagerPointer) { + return (false); + } + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + SARibbonCustomizeData::setCanCustomize(act); + pannel->addAction(act, actionRowProportionValue); + return (true); + } + + case RemoveCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + bar->removeCategory(c); + return (true); + } + + case RemovePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + c->removePannel(pannel); + return (true); + } + + case RemoveActionActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + pannel->removeAction(act); + return (true); + } + + case ChangeCategoryOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + int currentindex = bar->categoryIndex(c); + if (-1 == currentindex) { + return (false); + } + int toindex = currentindex + indexValue; + bar->moveCategory(currentindex, toindex); + return (true); + } + + case ChangePannelOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + int pannelIndex = c->pannelIndex(pannel); + if (-1 == pannelIndex) { + return (false); + } + c->movePannel(pannelIndex, pannelIndex + indexValue); + return (true); + } + + case ChangeActionOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = mActionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + int actindex = pannel->actionIndex(act); + if (actindex <= -1) { + return (false); + } + pannel->moveAction(actindex, actindex + indexValue); + return (true); + } + + case RenameCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + c->setCategoryName(keyValue); + return (true); + } + + case RenamePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + pannel->setPannelName(keyValue); + return (true); + } + + case VisibleCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + if (1 == indexValue) { + bar->showCategory(c); + } else { + bar->hideCategory(c); + } + return (true); + } + + default: + break; + } + return (false); +} + +/** + * @brief 获取actionmanager指针 + * @return + */ +SARibbonActionsManager* SARibbonCustomizeData::actionManager() +{ + return (mActionsManagerPointer); +} + +/** + * @brief 设置ActionsManager + * @param mgr + */ +void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr) +{ + mActionsManagerPointer = mgr; +} + +/** + * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData + * @param title category 的标题 + * @param index category要插入的位置 + * @param objName category的object name + * @return 返回AddCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName) +{ + SARibbonCustomizeData d(AddCategoryActionType); + + d.indexValue = index; + d.keyValue = title; + d.categoryObjNameValue = objName; + return (d); +} + +/** + * @brief 创建一个AddPannelActionType的SARibbonCustomizeData + * @param title pannel的标题 + * @param index pannel的index + * @param categoryobjName pannel的category的objectname + * @param objName pannel的objname + * @return 返回AddPannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddPannelCustomizeData(const QString& title, + int index, + const QString& categoryobjName, + const QString& objName) +{ + SARibbonCustomizeData d(AddPannelActionType); + + d.indexValue = index; + d.keyValue = title; + d.pannelObjNameValue = objName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 添加action + * @param key action的索引 + * @param mgr action管理器 + * @param rp 定义action的占位情况 + * @param categoryObjName action添加到的category的objname + * @param pannelObjName action添加到的category下的pannel的objname + * @param index action添加到的pannel的索引 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(AddActionActionType, mgr); + + d.keyValue = key; + d.categoryObjNameValue = categoryObjName; + d.pannelObjNameValue = pannelObjName; + d.actionRowProportionValue = rp; + + return (d); +} + +/** + * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData + * @param newname 新名字 + * @param index category的索引 + * @return 返回RenameCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname, + const QString& categoryobjName) +{ + SARibbonCustomizeData d(RenameCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 创建一个RenamePannelActionType的SARibbonCustomizeData + * @param newname pannel的名字 + * @param indexValue pannel的索引 + * @param categoryobjName pannel对应的category的object name + * @return 返回RenamePannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenamePannelCustomizeData(const QString& newname, + const QString& categoryobjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(RenamePannelActionType); + + if (pannelObjName.isEmpty() || categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.pannelObjNameValue = pannelObjName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应ChangeCategoryOrderActionType + * @param categoryobjName 需要移动的categoryobjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, + int moveindex) +{ + SARibbonCustomizeData d(ChangeCategoryOrderActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change category order," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangePannelOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangePannelOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + int moveindex) +{ + SARibbonCustomizeData d(ChangePannelOrderActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change pannel order," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangeActionOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex) +{ + SARibbonCustomizeData d(ChangeActionOrderActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change action order," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应RemoveCategoryActionType + * @param categoryobjName 需要移除的objname + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName) +{ + SARibbonCustomizeData d(RemoveCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应RemovePannelActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemovePannelCustomizeData(const QString& categoryobjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(RemovePannelActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + return (d); +} + +/** + * @brief 对应RemoveActionActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr) +{ + SARibbonCustomizeData d(RemoveActionActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove action," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + return (d); +} + +/** + * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData + * @param categoryobjName + * @param isShow + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow) +{ + SARibbonCustomizeData d(VisibleCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize visible category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = isShow ? 1 : 0; + return (d); +} + +/** + * @brief 判断外置属性,是否允许自定义 + * @param obj + * @return + */ +bool SARibbonCustomizeData::isCanCustomize(QObject* obj) +{ + QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 设置外置属性允许自定义 + * @param obj + * @param canbe + */ +void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe) +{ + obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe); +} + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex); + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex) +{ + QList< SARibbonCustomizeData > res; + + for (int i = 0; i < csd.size(); ++i) { + if (!willremoveIndex.contains(i)) { + res << csd[ i ]; + } + } + return (res); +} + +/** + * @brief 对QList进行简化操作 + * + * 此函数会执行如下操作: + * 1、针对同一个category/pannel连续出现的添加和删除操作进行移除(前一步添加,后一步删除) + * + * 2、针对VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + * + * 3、针对RenameCategoryActionType和RenamePannelActionType操作,只保留最后一个 + * + * 4、针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并为一个动作, + * 如果合并后原地不动,则删除 + * + * @param csd + * @return 返回简化的QList + */ +QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd) +{ + int size = csd.size(); + + if (size <= 1) { + return (csd); + } + QList< SARibbonCustomizeData > res; + QList< int > willremoveIndex; // 记录要删除的index + + //! 首先针对连续出现的添加和删除操作进行优化 + for (int i = 1; i < size; ++i) { + if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) { + if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddPannelActionType) && (csd[ i ].actionType() == RemovePannelActionType)) { + if ((csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) { + if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) + && (csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } + } + res = remove_indexs(csd, willremoveIndex); + willremoveIndex.clear(); + + //! 筛选VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == VisibleCategoryActionType) + && (res[ i ].actionType() == VisibleCategoryActionType)) { + if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) { + // 要保证操作的是同一个内容 + willremoveIndex << i - 1; // 删除前一个只保留最后一个 + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对RenameCategoryActionType和RenamePannelActionType操作,只需保留最后一个 + size = res.size(); + for (int i = 0; i < size; ++i) { + if (res[ i ].actionType() == RenameCategoryActionType) { + // 向后查询,如果查询到有同一个Category改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenameCategoryActionType) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } else if (res[ i ].actionType() == RenamePannelActionType) { + // 向后查询,如果查询到有同一个pannel改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenamePannelActionType) + && (res[ i ].pannelObjNameValue == res[ j ].pannelObjNameValue) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType) + && (res[ i ].actionType() == ChangeCategoryOrderActionType) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangePannelOrderActionType) + && (res[ i ].actionType() == ChangePannelOrderActionType) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType) + && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 上一步操作可能会产生indexvalue为0的情况,此操作把indexvalue为0的删除 + size = res.size(); + for (int i = 0; i < size; ++i) { + if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePannelOrderActionType) + || (res[ i ].actionType() == ChangeActionOrderActionType)) { + if (0 == res[ i ].indexValue) { + willremoveIndex << i; + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + return (res); +} diff --git a/src/SARibbonBar/SARibbonCustomizeData.h b/src/SARibbonBar/SARibbonCustomizeData.h index 8ddc90cc..6a5b24d0 100644 --- a/src/SARibbonBar/SARibbonCustomizeData.h +++ b/src/SARibbonBar/SARibbonCustomizeData.h @@ -1,152 +1,152 @@ -#ifndef SARIBBONCUSTOMIZEDATA_H -#define SARIBBONCUSTOMIZEDATA_H -#include "SARibbonGlobal.h" -#include "SARibbonActionsManager.h" -#include "SARibbonPannel.h" -#include -class SARibbonBar; -class SARibbonMainWindow; - -/** - * @brief 记录所有自定义操作的数据类 - * @note 此数据依赖于@ref SARibbonActionsManager 要在SARibbonActionsManager之后使用此类 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeData -{ -public: - enum ActionType - { - UnknowActionType = 0, ///< 未知操作 - AddCategoryActionType, ///< 添加category操作(1) - AddPannelActionType, ///< 添加pannel操作(2) - AddActionActionType, ///< 添加action操作(3) - RemoveCategoryActionType, ///< 删除category操作(4) - RemovePannelActionType, ///< 删除pannel操作(5) - RemoveActionActionType, ///< 删除action操作(6) - ChangeCategoryOrderActionType, ///< 改变category顺序的操作(7) - ChangePannelOrderActionType, ///< 改变pannel顺序的操作(8) - ChangeActionOrderActionType, ///< 改变action顺序的操作(9) - RenameCategoryActionType, ///< 对category更名操作(10) - RenamePannelActionType, ///< 对Pannel更名操作(11) - VisibleCategoryActionType ///< 对category执行隐藏/显示操作(12) - }; - SARibbonCustomizeData(); - SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr = nullptr); - // 获取CustomizeData的action type - ActionType actionType() const; - - // 设置CustomizeData的action type - void setActionType(ActionType a); - - // 判断是否是一个正常的CustomizeData - bool isValid() const; - - // 应用SARibbonCustomizeData - bool apply(SARibbonBar* bar) const; - - // 获取actionmanager指针 - SARibbonActionsManager* actionManager(); - - // 设置ActionsManager - void setActionsManager(SARibbonActionsManager* mgr); - - // 对应AddCategoryActionType - static SARibbonCustomizeData makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName); - - // 对应AddPannelActionType - static SARibbonCustomizeData makeAddPannelCustomizeData(const QString& title, - int index, - const QString& categoryobjName, - const QString& objName); - - // 对应AddActionActionType - static SARibbonCustomizeData makeAddActionCustomizeData(const QString& key, - SARibbonActionsManager* mgr, - SARibbonPannelItem::RowProportion rp, - const QString& categoryObjName, - const QString& pannelObjName); - - // 对应RenameCategoryActionType - static SARibbonCustomizeData makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName); - - // 对应RenamePannelActionType - static SARibbonCustomizeData makeRenamePannelCustomizeData(const QString& newname, - const QString& categoryobjName, - const QString& pannelObjName); - - // 对应RemoveCategoryActionType - static SARibbonCustomizeData makeRemoveCategoryCustomizeData(const QString& categoryobjName); - - // 对应ChangeCategoryOrderActionType - static SARibbonCustomizeData makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex); - - // 对应ChangePannelOrderActionType - static SARibbonCustomizeData makeChangePannelOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - int moveindex); - - // 对应ChangeActionOrderActionType - static SARibbonCustomizeData makeChangeActionOrderCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr, - int moveindex); - - // 对应RemovePannelActionType - static SARibbonCustomizeData makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName); - - // 对应RemoveActionActionType - static SARibbonCustomizeData makeRemoveActionCustomizeData(const QString& categoryobjName, - const QString& pannelObjName, - const QString& key, - SARibbonActionsManager* mgr); - - // 对应VisibleCategoryActionType - static SARibbonCustomizeData makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow); - - // 判断是否可以自定义,如果某个action不想被编辑,可以通过此函数设置 - static bool isCanCustomize(QObject* obj); - static void setCanCustomize(QObject* obj, bool canbe = true); - - // 对QList进行简化 - static QList< SARibbonCustomizeData > simplify(const QList< SARibbonCustomizeData >& csd); - -public: - /** - * @brief 记录顺序的参数 - * - * 在actionType==AddCategoryActionType时,此参数记录Category的insert位置, - * 在actionType==AddPannelActionType时,此参数记录pannel的insert位置, - * 在actionType==AddActionActionType时,此参数记录pannel的insert位置 - */ - int indexValue; - - /** - * @brief 记录标题、索引等参数 - * - * 在actionType==AddCategoryActionType时,key为category标题, - * 在actionType==AddPannelActionType时,key为pannel标题, - * 在actionType==AddActionActionType时,key为action的查询依据,基于SARibbonActionsManager::action查询 - */ - QString keyValue; - - /** - * @brief 记录categoryObjName,用于定位Category - */ - QString categoryObjNameValue; - - /** - * @brief 记录pannelObjName,saribbon的Customize索引大部分基于objname - */ - QString pannelObjNameValue; - - SARibbonPannelItem::RowProportion actionRowProportionValue; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion -private: - ActionType mType; ///< 标记这个data是category还是pannel亦或是action - SARibbonActionsManager* mActionsManagerPointer; -}; -Q_DECLARE_METATYPE(SARibbonCustomizeData) - -typedef QList< SARibbonCustomizeData > SARibbonCustomizeDataList; - -#endif // SARIBBONCUSTOMIZEDATA_H +#ifndef SARIBBONCUSTOMIZEDATA_H +#define SARIBBONCUSTOMIZEDATA_H +#include "SARibbonGlobal.h" +#include "SARibbonActionsManager.h" +#include "SARibbonPannel.h" +#include +class SARibbonBar; +class SARibbonMainWindow; + +/** + * @brief 记录所有自定义操作的数据类 + * @note 此数据依赖于@ref SARibbonActionsManager 要在SARibbonActionsManager之后使用此类 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeData +{ +public: + enum ActionType + { + UnknowActionType = 0, ///< 未知操作 + AddCategoryActionType, ///< 添加category操作(1) + AddPannelActionType, ///< 添加pannel操作(2) + AddActionActionType, ///< 添加action操作(3) + RemoveCategoryActionType, ///< 删除category操作(4) + RemovePannelActionType, ///< 删除pannel操作(5) + RemoveActionActionType, ///< 删除action操作(6) + ChangeCategoryOrderActionType, ///< 改变category顺序的操作(7) + ChangePannelOrderActionType, ///< 改变pannel顺序的操作(8) + ChangeActionOrderActionType, ///< 改变action顺序的操作(9) + RenameCategoryActionType, ///< 对category更名操作(10) + RenamePannelActionType, ///< 对Pannel更名操作(11) + VisibleCategoryActionType ///< 对category执行隐藏/显示操作(12) + }; + SARibbonCustomizeData(); + SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr = nullptr); + // 获取CustomizeData的action type + ActionType actionType() const; + + // 设置CustomizeData的action type + void setActionType(ActionType a); + + // 判断是否是一个正常的CustomizeData + bool isValid() const; + + // 应用SARibbonCustomizeData + bool apply(SARibbonBar* bar) const; + + // 获取actionmanager指针 + SARibbonActionsManager* actionManager(); + + // 设置ActionsManager + void setActionsManager(SARibbonActionsManager* mgr); + + // 对应AddCategoryActionType + static SARibbonCustomizeData makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName); + + // 对应AddPannelActionType + static SARibbonCustomizeData makeAddPannelCustomizeData(const QString& title, + int index, + const QString& categoryobjName, + const QString& objName); + + // 对应AddActionActionType + static SARibbonCustomizeData makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName); + + // 对应RenameCategoryActionType + static SARibbonCustomizeData makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName); + + // 对应RenamePannelActionType + static SARibbonCustomizeData makeRenamePannelCustomizeData(const QString& newname, + const QString& categoryobjName, + const QString& pannelObjName); + + // 对应RemoveCategoryActionType + static SARibbonCustomizeData makeRemoveCategoryCustomizeData(const QString& categoryobjName); + + // 对应ChangeCategoryOrderActionType + static SARibbonCustomizeData makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex); + + // 对应ChangePannelOrderActionType + static SARibbonCustomizeData makeChangePannelOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + int moveindex); + + // 对应ChangeActionOrderActionType + static SARibbonCustomizeData makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex); + + // 对应RemovePannelActionType + static SARibbonCustomizeData makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName); + + // 对应RemoveActionActionType + static SARibbonCustomizeData makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr); + + // 对应VisibleCategoryActionType + static SARibbonCustomizeData makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow); + + // 判断是否可以自定义,如果某个action不想被编辑,可以通过此函数设置 + static bool isCanCustomize(QObject* obj); + static void setCanCustomize(QObject* obj, bool canbe = true); + + // 对QList进行简化 + static QList< SARibbonCustomizeData > simplify(const QList< SARibbonCustomizeData >& csd); + +public: + /** + * @brief 记录顺序的参数 + * + * 在actionType==AddCategoryActionType时,此参数记录Category的insert位置, + * 在actionType==AddPannelActionType时,此参数记录pannel的insert位置, + * 在actionType==AddActionActionType时,此参数记录pannel的insert位置 + */ + int indexValue; + + /** + * @brief 记录标题、索引等参数 + * + * 在actionType==AddCategoryActionType时,key为category标题, + * 在actionType==AddPannelActionType时,key为pannel标题, + * 在actionType==AddActionActionType时,key为action的查询依据,基于SARibbonActionsManager::action查询 + */ + QString keyValue; + + /** + * @brief 记录categoryObjName,用于定位Category + */ + QString categoryObjNameValue; + + /** + * @brief 记录pannelObjName,saribbon的Customize索引大部分基于objname + */ + QString pannelObjNameValue; + + SARibbonPannelItem::RowProportion actionRowProportionValue; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +private: + ActionType mType; ///< 标记这个data是category还是pannel亦或是action + SARibbonActionsManager* mActionsManagerPointer; +}; +Q_DECLARE_METATYPE(SARibbonCustomizeData) + +typedef QList< SARibbonCustomizeData > SARibbonCustomizeDataList; + +#endif // SARIBBONCUSTOMIZEDATA_H diff --git a/src/SARibbonBar/SARibbonCustomizeDialog.cpp b/src/SARibbonBar/SARibbonCustomizeDialog.cpp index 532cf40d..c4ec77e8 100644 --- a/src/SARibbonBar/SARibbonCustomizeDialog.cpp +++ b/src/SARibbonBar/SARibbonCustomizeDialog.cpp @@ -1,187 +1,187 @@ -#include "SARibbonCustomizeDialog.h" -#include -#include -#include -#include - -/** - * @brief The SARibbonCustomizeDialogUi class - */ -class SARibbonCustomizeDialogUi { -public: - SARibbonCustomizeWidget *customWidget; - QVBoxLayout *verticalLayoutMain; - QHBoxLayout *horizontalLayoutButtonGroup; - QPushButton *pushButtonCancel; - QPushButton *pushButtonOk; - QSpacerItem *spacerItemleft; - void setupUi(SARibbonMainWindow *ribbonWindow, QWidget *customizeDialog) - { - if (customizeDialog->objectName().isEmpty()) { - customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog")); - } - customizeDialog->resize(800, 600); - verticalLayoutMain = new QVBoxLayout(customizeDialog); - verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain")); - - customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog); - customWidget->setObjectName(QStringLiteral("customWidget")); - verticalLayoutMain->addWidget(customWidget); - - horizontalLayoutButtonGroup = new QHBoxLayout(); - horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup")); - - spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - horizontalLayoutButtonGroup->addItem(spacerItemleft); - - pushButtonCancel = new QPushButton(customizeDialog); - pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel")); - horizontalLayoutButtonGroup->addWidget(pushButtonCancel); - - pushButtonOk = new QPushButton(customizeDialog); - pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel")); - horizontalLayoutButtonGroup->addWidget(pushButtonOk); - verticalLayoutMain->addItem(horizontalLayoutButtonGroup); - retranslateUi(customizeDialog); - } - - - void retranslateUi(QWidget *customizeDialog) - { - customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR)); - pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR)); - pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR)); - } -}; - - -//////////////////////////////////////////////////////////////////// - -SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow *ribbonWindow, QWidget *p, Qt::WindowFlags f) - : QDialog(p, f) - , ui(new SARibbonCustomizeDialogUi) -{ - ui->setupUi(ribbonWindow, this); - initConnection(); -} - - -/** - * @brief 设置action管理器 - * - * 等同@ref SARibbonCustomizeWidget::setupActionsManager - * @param mgr - */ - SARibbonCustomizeDialog::~SARibbonCustomizeDialog() - { - } - void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager *mgr) -{ - ui->customWidget->setupActionsManager(mgr); -} - -/** - * @brief //判断用户是否有要存储的内容,对应save动作 - * @return - */ -bool SARibbonCustomizeDialog::isApplied() const -{ - return ui->customWidget->isApplied(); -} - -/** - * @brief 判断用户是否有改动内容,对应apply动作 - * @return - */ -bool SARibbonCustomizeDialog::isCached() const -{ - return ui->customWidget->isCached(); -} - - -void SARibbonCustomizeDialog::initConnection() -{ - connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept); - connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject); -} - - -/** - * @brief 等同SARibbonCustomizeWidget::applys - * - * @ref SARibbonCustomizeWidget::applys - * @return - */ -bool SARibbonCustomizeDialog::applys() -{ - return ui->customWidget->applys(); -} - - -/** - * @brief 清除所有动作 - * - * @ref SARibbonCustomizeWidget::clear - */ -void SARibbonCustomizeDialog::clear() -{ - ui->customWidget->clear(); -} - - -/** - * @brief 转换为xml - * - * @ref SARibbonCustomizeWidget::toXml - * @param xml - * @return - */ -bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter *xml) const -{ - return (ui->customWidget->toXml(xml)); -} - - -/** - * @brief 等同SARibbonCustomizeWidget::toXml - * @ref SARibbonCustomizeWidget::toXml - * @param xmlpath - * @return - */ -bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const -{ - return (ui->customWidget->toXml(xmlpath)); -} - - -/** - * @brief 等同SARibbonCustomizeWidget::fromXml - * @param xml - */ -void SARibbonCustomizeDialog::fromXml(QXmlStreamReader *xml) -{ - ui->customWidget->fromXml(xml); -} - - -/** - * @brief 等同SARibbonCustomizeWidget::fromXml - * @param xmlpath - */ -void SARibbonCustomizeDialog::fromXml(const QString& xmlpath) -{ - ui->customWidget->fromXml(xmlpath); -} - - -/** - * @brief 返回SARibbonCustomizeWidget窗口指针 - * - * 通过SARibbonCustomizeWidget窗口可以操作更多的内容 - * - * @return SARibbonCustomizeWidget指针,参考@ref SARibbonCustomizeWidget - */ -SARibbonCustomizeWidget *SARibbonCustomizeDialog::customizeWidget() const -{ - return (ui->customWidget); -} +#include "SARibbonCustomizeDialog.h" +#include +#include +#include +#include + +/** + * @brief The SARibbonCustomizeDialogUi class + */ +class SARibbonCustomizeDialogUi { +public: + SARibbonCustomizeWidget *customWidget; + QVBoxLayout *verticalLayoutMain; + QHBoxLayout *horizontalLayoutButtonGroup; + QPushButton *pushButtonCancel; + QPushButton *pushButtonOk; + QSpacerItem *spacerItemleft; + void setupUi(SARibbonMainWindow *ribbonWindow, QWidget *customizeDialog) + { + if (customizeDialog->objectName().isEmpty()) { + customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog")); + } + customizeDialog->resize(800, 600); + verticalLayoutMain = new QVBoxLayout(customizeDialog); + verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain")); + + customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog); + customWidget->setObjectName(QStringLiteral("customWidget")); + verticalLayoutMain->addWidget(customWidget); + + horizontalLayoutButtonGroup = new QHBoxLayout(); + horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup")); + + spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayoutButtonGroup->addItem(spacerItemleft); + + pushButtonCancel = new QPushButton(customizeDialog); + pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonCancel); + + pushButtonOk = new QPushButton(customizeDialog); + pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonOk); + verticalLayoutMain->addItem(horizontalLayoutButtonGroup); + retranslateUi(customizeDialog); + } + + + void retranslateUi(QWidget *customizeDialog) + { + customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR)); + pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR)); + pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR)); + } +}; + + +//////////////////////////////////////////////////////////////////// + +SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow *ribbonWindow, QWidget *p, Qt::WindowFlags f) + : QDialog(p, f) + , ui(new SARibbonCustomizeDialogUi) +{ + ui->setupUi(ribbonWindow, this); + initConnection(); +} + + +/** + * @brief 设置action管理器 + * + * 等同@ref SARibbonCustomizeWidget::setupActionsManager + * @param mgr + */ + SARibbonCustomizeDialog::~SARibbonCustomizeDialog() + { + } + void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager *mgr) +{ + ui->customWidget->setupActionsManager(mgr); +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeDialog::isApplied() const +{ + return ui->customWidget->isApplied(); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeDialog::isCached() const +{ + return ui->customWidget->isCached(); +} + + +void SARibbonCustomizeDialog::initConnection() +{ + connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject); +} + + +/** + * @brief 等同SARibbonCustomizeWidget::applys + * + * @ref SARibbonCustomizeWidget::applys + * @return + */ +bool SARibbonCustomizeDialog::applys() +{ + return ui->customWidget->applys(); +} + + +/** + * @brief 清除所有动作 + * + * @ref SARibbonCustomizeWidget::clear + */ +void SARibbonCustomizeDialog::clear() +{ + ui->customWidget->clear(); +} + + +/** + * @brief 转换为xml + * + * @ref SARibbonCustomizeWidget::toXml + * @param xml + * @return + */ +bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter *xml) const +{ + return (ui->customWidget->toXml(xml)); +} + + +/** + * @brief 等同SARibbonCustomizeWidget::toXml + * @ref SARibbonCustomizeWidget::toXml + * @param xmlpath + * @return + */ +bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const +{ + return (ui->customWidget->toXml(xmlpath)); +} + + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xml + */ +void SARibbonCustomizeDialog::fromXml(QXmlStreamReader *xml) +{ + ui->customWidget->fromXml(xml); +} + + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xmlpath + */ +void SARibbonCustomizeDialog::fromXml(const QString& xmlpath) +{ + ui->customWidget->fromXml(xmlpath); +} + + +/** + * @brief 返回SARibbonCustomizeWidget窗口指针 + * + * 通过SARibbonCustomizeWidget窗口可以操作更多的内容 + * + * @return SARibbonCustomizeWidget指针,参考@ref SARibbonCustomizeWidget + */ +SARibbonCustomizeWidget *SARibbonCustomizeDialog::customizeWidget() const +{ + return (ui->customWidget); +} diff --git a/src/SARibbonBar/SARibbonCustomizeDialog.h b/src/SARibbonBar/SARibbonCustomizeDialog.h index 7bcaa7bd..79d261bf 100644 --- a/src/SARibbonBar/SARibbonCustomizeDialog.h +++ b/src/SARibbonBar/SARibbonCustomizeDialog.h @@ -1,58 +1,58 @@ -#ifndef SARIBBONCUSTOMIZEDIALOG_H -#define SARIBBONCUSTOMIZEDIALOG_H -#include "SARibbonGlobal.h" -#include -#include "SARibbonCustomizeWidget.h" -class SARibbonActionsManager; -class SARibbonCustomizeDialogUi; -class QXmlStreamWriter; - - -/** - * @brief SARibbonCustomizeWidget的对话框封装 - * - * 此功能依赖于@ref SARibbonActionsManager ,SARibbonActionsManager建议作为mianwindow的成员变量, - * SARibbonActionsManager可以快速绑定所有QAction,详细见SARibbonActionsManager的说明 - * - * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 - * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeDialog::fromXml 同步配置文件,这样再次修改后的配置文件就一致 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeDialog : public QDialog -{ - Q_OBJECT -public: - explicit SARibbonCustomizeDialog(SARibbonMainWindow *ribbonWindow, QWidget *p = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); - ~SARibbonCustomizeDialog(); - //设置action管理器 - void setupActionsManager(SARibbonActionsManager *mgr); - - //判断用户是否有要存储的内容,对应save动作 - bool isApplied() const; - - //判断用户是否有改动内容,对应apply动作 - bool isCached() const; - - //应用所有的设定 - bool applys(); - - //清除所有动作 - void clear(); - - //转换为xml - bool toXml(QXmlStreamWriter *xml) const; - bool toXml(const QString& xmlpath) const; - - //从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - void fromXml(QXmlStreamReader *xml); - void fromXml(const QString& xmlpath); - - //返回SARibbonCustomizeWidget窗口指针 - SARibbonCustomizeWidget *customizeWidget() const; - -private: - void initConnection(); - - SARibbonCustomizeDialogUi *ui; -}; - -#endif // SARIBBONCUSTOMIZEDIALOG_H +#ifndef SARIBBONCUSTOMIZEDIALOG_H +#define SARIBBONCUSTOMIZEDIALOG_H +#include "SARibbonGlobal.h" +#include +#include "SARibbonCustomizeWidget.h" +class SARibbonActionsManager; +class SARibbonCustomizeDialogUi; +class QXmlStreamWriter; + + +/** + * @brief SARibbonCustomizeWidget的对话框封装 + * + * 此功能依赖于@ref SARibbonActionsManager ,SARibbonActionsManager建议作为mianwindow的成员变量, + * SARibbonActionsManager可以快速绑定所有QAction,详细见SARibbonActionsManager的说明 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeDialog::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeDialog : public QDialog +{ + Q_OBJECT +public: + explicit SARibbonCustomizeDialog(SARibbonMainWindow *ribbonWindow, QWidget *p = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~SARibbonCustomizeDialog(); + //设置action管理器 + void setupActionsManager(SARibbonActionsManager *mgr); + + //判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + //判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + //应用所有的设定 + bool applys(); + + //清除所有动作 + void clear(); + + //转换为xml + bool toXml(QXmlStreamWriter *xml) const; + bool toXml(const QString& xmlpath) const; + + //从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader *xml); + void fromXml(const QString& xmlpath); + + //返回SARibbonCustomizeWidget窗口指针 + SARibbonCustomizeWidget *customizeWidget() const; + +private: + void initConnection(); + + SARibbonCustomizeDialogUi *ui; +}; + +#endif // SARIBBONCUSTOMIZEDIALOG_H diff --git a/src/SARibbonBar/SARibbonCustomizeWidget.cpp b/src/SARibbonBar/SARibbonCustomizeWidget.cpp index 632c22d4..159e7f5a 100644 --- a/src/SARibbonBar/SARibbonCustomizeWidget.cpp +++ b/src/SARibbonBar/SARibbonCustomizeWidget.cpp @@ -1,1513 +1,1513 @@ -#include "SARibbonCustomizeWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "SARibbonMainWindow.h" -#include "SARibbonPannel.h" -#include -#include -#include -#include -#include -#include -#include -#include "SARibbonCustomizeData.h" -#include "SARibbonBar.h" -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -/// SARibbonCustomizeWidget -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds) -{ - if (cds.size() <= 0) { - return (false); - } - - xml->writeStartElement("sa-ribbon-customize"); - for (const SARibbonCustomizeData& d : cds) { - xml->writeStartElement("customize-data"); - xml->writeAttribute("type", QString::number(d.actionType())); - xml->writeAttribute("index", QString::number(d.indexValue)); - xml->writeAttribute("key", d.keyValue); - xml->writeAttribute("category", d.categoryObjNameValue); - xml->writeAttribute("pannel", d.pannelObjNameValue); - xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue)); - - xml->writeEndElement(); - } - xml->writeEndElement(); - if (xml->hasError()) { - qWarning() << "write has error"; - } - return (true); -} - -QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr) -{ - // 先找到"sa-ribbon-customize" - while (!xml->atEnd()) { - - if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) { - break; - } - xml->readNext(); - } - QList< SARibbonCustomizeData > res; - - // 开始遍历"customize-data" - while (!xml->atEnd()) { - if (xml->isStartElement() && (xml->name().toString() == "customize-data")) { - // 首先读取属性type - SARibbonCustomizeData d; - QXmlStreamAttributes attrs = xml->attributes(); - if (!attrs.hasAttribute("type")) { - // 说明异常,跳过这个 - xml->readNextStartElement(); - continue; - } - bool isOk = false; - int v = xml->attributes().value("type").toInt(&isOk); - if (!isOk) { - // 说明异常,跳过这个 - xml->readNextStartElement(); - continue; - } - d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v)); - // 开始读取子对象 - if (attrs.hasAttribute("index")) { - v = xml->attributes().value("index").toInt(&isOk); - if (isOk) { - d.indexValue = v; - } - } - if (attrs.hasAttribute("key")) { - d.keyValue = attrs.value("key").toString(); - } - if (attrs.hasAttribute("category")) { - d.categoryObjNameValue = attrs.value("category").toString(); - } - if (attrs.hasAttribute("pannel")) { - d.pannelObjNameValue = attrs.value("pannel").toString(); - } - if (attrs.hasAttribute("row-prop")) { - v = xml->attributes().value("row-prop").toInt(&isOk); - if (isOk) { - d.actionRowProportionValue = static_cast< SARibbonPannelItem::RowProportion >(v); - } - } - d.setActionsManager(mgr); - res.append(d); - } - xml->readNext(); - } - if (xml->hasError()) { - qWarning() << xml->errorString(); - } - return (res); -} - -int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) -{ - int c = 0; - - for (const SARibbonCustomizeData& d : cds) { - if (d.apply(bar)) { - ++c; - } - } - return (c); -} - -int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) -{ - int c = 0; - // todo 支持反向操作 - return (c); -} - -bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr) -{ - QFile f(filePath); - - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - return (false); - } - f.seek(0); - QXmlStreamReader xml(&f); - - return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr)); -} - -/** - * @brief 构建SARibbonCustomizeWidget的Ui - */ -class SARibbonCustomizeWidgetUi -{ -public: - QHBoxLayout* horizontalLayoutMain; - QVBoxLayout* verticalLayoutSelect; - QLabel* labelSelectAction; - QHBoxLayout* horizontalLayoutSearch; - QComboBox* comboBoxActionIndex; - QLineEdit* lineEditSearchAction; - QListView* listViewSelect; - QVBoxLayout* verticalLayoutMidButtons; - QSpacerItem* verticalSpacerUp; - QPushButton* pushButtonAdd; - QPushButton* pushButtonDelete; - QPushButton* pushButtonReset; - QSpacerItem* verticalSpacerDown; - QLabel* labelProportion; - QComboBox* comboBoxActionProportion; - QVBoxLayout* verticalLayoutResult; - QLabel* labelCustomize; - QHBoxLayout* horizontalLayoutCategorySelect; - QRadioButton* radioButtonMainCategory; - QRadioButton* radioButtonAllCategory; - QButtonGroup* radioButtonGroup; - QTreeView* treeViewResult; - QHBoxLayout* horizontalLayoutActionOptBtns; - QPushButton* pushButtonNewCategory; - QPushButton* pushButtonNewPannel; - QPushButton* pushButtonRename; - QVBoxLayout* verticalLayoutRightButtons; - QSpacerItem* verticalSpacerUp2; - QToolButton* toolButtonUp; - QToolButton* toolButtonDown; - QSpacerItem* verticalSpacerDown2; - - void setupUi(QWidget* customizeWidget) - { - if (customizeWidget->objectName().isEmpty()) { - customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget")); - } - customizeWidget->resize(800, 600); - horizontalLayoutMain = new QHBoxLayout(customizeWidget); - horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain")); - verticalLayoutSelect = new QVBoxLayout(); - verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect")); - labelSelectAction = new QLabel(customizeWidget); - labelSelectAction->setObjectName(QStringLiteral("labelSelectAction")); - - verticalLayoutSelect->addWidget(labelSelectAction); - - horizontalLayoutSearch = new QHBoxLayout(); - horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch")); - comboBoxActionIndex = new QComboBox(customizeWidget); - comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex")); - comboBoxActionIndex->setEditable(false); - - horizontalLayoutSearch->addWidget(comboBoxActionIndex); - - lineEditSearchAction = new QLineEdit(customizeWidget); - lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction")); - - horizontalLayoutSearch->addWidget(lineEditSearchAction); - - verticalLayoutSelect->addLayout(horizontalLayoutSearch); - - listViewSelect = new QListView(customizeWidget); - listViewSelect->setObjectName(QStringLiteral("listViewSelect")); - - verticalLayoutSelect->addWidget(listViewSelect); - - horizontalLayoutMain->addLayout(verticalLayoutSelect); - - verticalLayoutMidButtons = new QVBoxLayout(); - verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons")); - verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutMidButtons->addItem(verticalSpacerUp); - - pushButtonAdd = new QPushButton(customizeWidget); - pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd")); - pushButtonAdd->setEnabled(false); - - verticalLayoutMidButtons->addWidget(pushButtonAdd); - - pushButtonDelete = new QPushButton(customizeWidget); - pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete")); - pushButtonDelete->setEnabled(false); - - verticalLayoutMidButtons->addWidget(pushButtonDelete); - - verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutMidButtons->addItem(verticalSpacerDown); - - labelProportion = new QLabel(customizeWidget); - labelProportion->setObjectName(QStringLiteral("labelProportion")); - verticalLayoutMidButtons->addWidget(labelProportion); - - comboBoxActionProportion = new QComboBox(customizeWidget); - comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion")); - comboBoxActionProportion->setEditable(false); - verticalLayoutMidButtons->addWidget(comboBoxActionProportion); - horizontalLayoutMain->addLayout(verticalLayoutMidButtons); - - verticalLayoutResult = new QVBoxLayout(); - verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult")); - labelCustomize = new QLabel(customizeWidget); - labelCustomize->setObjectName(QStringLiteral("labelCustomize")); - - verticalLayoutResult->addWidget(labelCustomize); - - horizontalLayoutCategorySelect = new QHBoxLayout(); - horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect")); - radioButtonMainCategory = new QRadioButton(customizeWidget); - radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory")); - radioButtonMainCategory->setChecked(false); - - horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory); - - radioButtonAllCategory = new QRadioButton(customizeWidget); - radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory")); - radioButtonAllCategory->setChecked(true); - - horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory); - - radioButtonGroup = new QButtonGroup(customizeWidget); - radioButtonGroup->addButton(radioButtonMainCategory); - radioButtonGroup->addButton(radioButtonAllCategory); - - verticalLayoutResult->addLayout(horizontalLayoutCategorySelect); - - treeViewResult = new QTreeView(customizeWidget); - treeViewResult->setObjectName(QStringLiteral("treeViewResult")); - treeViewResult->setHeaderHidden(true); - treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection); - treeViewResult->setAnimated(true); // 支持动画 - treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不允许直接在item上重命名 - - verticalLayoutResult->addWidget(treeViewResult); - - horizontalLayoutActionOptBtns = new QHBoxLayout(); - horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns")); - pushButtonNewCategory = new QPushButton(customizeWidget); - pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory); - - pushButtonNewPannel = new QPushButton(customizeWidget); - pushButtonNewPannel->setObjectName(QStringLiteral("pushButtonNewPannel")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonNewPannel); - - pushButtonRename = new QPushButton(customizeWidget); - pushButtonRename->setObjectName(QStringLiteral("pushButtonRename")); - - horizontalLayoutActionOptBtns->addWidget(pushButtonRename); - - pushButtonReset = new QPushButton(customizeWidget); - pushButtonReset->setObjectName(QStringLiteral("pushButtonReset")); - horizontalLayoutActionOptBtns->addWidget(pushButtonReset); - - verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns); - - horizontalLayoutMain->addLayout(verticalLayoutResult); - - verticalLayoutRightButtons = new QVBoxLayout(); - verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons")); - verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutRightButtons->addItem(verticalSpacerUp2); - - toolButtonUp = new QToolButton(customizeWidget); - toolButtonUp->setObjectName(QStringLiteral("pushButtonUp")); - toolButtonUp->setArrowType(Qt::UpArrow); - toolButtonUp->setAutoRaise(true); - - verticalLayoutRightButtons->addWidget(toolButtonUp); - - toolButtonDown = new QToolButton(customizeWidget); - toolButtonDown->setObjectName(QStringLiteral("pushButtonDown")); - toolButtonDown->setArrowType(Qt::DownArrow); - toolButtonDown->setAutoRaise(true); - - verticalLayoutRightButtons->addWidget(toolButtonDown); - - verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - verticalLayoutRightButtons->addItem(verticalSpacerDown2); - - horizontalLayoutMain->addLayout(verticalLayoutRightButtons); - - retranslateUi(customizeWidget); - } // setupUi - - void retranslateUi(QWidget* customizeWidget) - { - customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR)); - labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR)); // cn:请选择 - lineEditSearchAction->setInputMask(QString()); - lineEditSearchAction->setText(QString()); - lineEditSearchAction->setPlaceholderText( - QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR)); // cn:查找命令 - pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR)); // cn:添加 >> - pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR)); // cn:<< 移除 - labelCustomize->setText( - QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR)); // cn:自定义功能区 - radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR)); // cn:主选项卡 - radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR)); // cn:所有选项卡 - pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR)); // cn:新建选项卡 - pushButtonNewPannel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR)); // cn:新建组 - pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR)); // cn:重命名 - pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR)); // cn:重置 - labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR)); // cn:比例 - } // retranslateUi -}; - -/** - * @brief 管理SARibbonCustomizeWidget的业务逻辑 - */ -class SARibbonCustomizeWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget) -public: - SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory }; ///< 显示类型 - SARibbonBar* mRibbonBar { nullptr }; ///< 保存SARibbonMainWindow的指针 - SARibbonActionsManager* mActionMgr { nullptr }; ///< action管理器 - SARibbonActionsManagerModel* mAcionModel { nullptr }; ///< action管理器对应的model - QStandardItemModel* mRibbonModel { nullptr }; ///< 用于很成ribbon的树 - int mCustomizeCategoryCount { 0 }; ///< 记录自定义Category的个数 - int mCustomizePannelCount { 0 }; ///< 记录自定义Pannel的个数 -public: - PrivateData(SARibbonCustomizeWidget* p); - void updateModel(); - - QList< SARibbonCustomizeData > mCustomizeDatasCache; ///< 缓存记录所有的自定义动作 - QList< SARibbonCustomizeData > mCustomizeDatasApplied; ///< 应用后的所有的自定义动作 - QList< SARibbonCustomizeData > mOldCustomizeDatas; ///< 记录旧的自定义动作,本地文件缓存 - // 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch_suf - static QString makeRandomObjName(const QString& pre); - - int itemLevel(QStandardItem* item) const; - - // - bool isCustomizeItem(QStandardItem* item) const; - - // 把item转换为category - SARibbonCategory* itemToCategory(QStandardItem* item) const; - - // 把item转换为SARibbonPannel - SARibbonPannel* itemToPannel(QStandardItem* item) const; - - // 获取item对应的object name - QString itemObjectName(QStandardItem* item) const; - - // 判断是否可以自定义 - bool isItemCanCustomize(QStandardItem* item) const; - - // 从item转为action - QAction* itemToAction(QStandardItem* item) const; -}; - -SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p) - : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p)) -{ -} - -void SARibbonCustomizeWidget::PrivateData::updateModel() -{ - if (mRibbonBar == nullptr) { - return; - } - mRibbonModel->clear(); - SARibbonBar* ribbonbar = mRibbonBar; - QList< SARibbonCategory* > categorys = ribbonbar->categoryPages(); - - for (const SARibbonCategory* c : qAsConst(categorys)) { - if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) { - // 如果是只显示主内容,如果是上下文标签就忽略 - continue; - } - QStandardItem* ci = new QStandardItem(); - if (c->isContextCategory()) { - ci->setText(QString("[%1]").arg(c->categoryName())); - } else { - ci->setText(c->categoryName()); - } - if (c->isCanCustomize() && !c->isContextCategory()) { - // 上下文标签不做显示隐藏处理 - ci->setCheckable(true); - ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked); - ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ci->setData(0, SARibbonCustomizeWidget::LevelRole); - ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole); - QList< SARibbonPannel* > pannels = c->pannelList(); - for (const SARibbonPannel* p : qAsConst(pannels)) { - QStandardItem* pi = new QStandardItem(p->pannelName()); - pi->setData(1, SARibbonCustomizeWidget::LevelRole); - pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole); - if (p->isCanCustomize()) { - pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ci->appendRow(pi); - const QList< SARibbonPannelItem* >& items = p->ribbonPannelItem(); - for (SARibbonPannelItem* i : qAsConst(items)) { - if (i->action->isSeparator()) { - continue; - } - QStandardItem* ii = new QStandardItem(); - // if (i->customWidget) { - // //如果是自定义窗口 - // if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) { - // delete ii; - // continue; //如果窗口啥也没有,就跳过 - // } - // ii->setText(i->widget()->windowTitle()); - // ii->setIcon(i->widget()->windowIcon()); - // if (SARibbonCustomizeData::isCanCustomize(i->widget())) { - // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 - // } - // } else { - // //不是自定义,说明是action - // ii->setText(i->action->text()); - // ii->setIcon(i->action->icon()); - // if (SARibbonCustomizeData::isCanCustomize(i->action)) { - // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 - // } - // } - ii->setText(i->action->text()); - ii->setIcon(i->action->icon()); - if (SARibbonCustomizeData::isCanCustomize(i->action)) { - ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 - } - ii->setData(2, SARibbonCustomizeWidget::LevelRole); - ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole); - pi->appendRow(ii); - } - } - mRibbonModel->appendRow(ci); - } -} - -/** - * @brief 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch - * @param pre 前缀 - * @return - */ -QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre) -{ - return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch())); -} - -/** - * @brief 获取item的level - * @param item - * @return - */ -int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const -{ - return (item->data(SARibbonCustomizeWidget::LevelRole).toInt()); -} - -/** - * @brief 判断itemn为自定义的item,自定义的item都带有CustomizeRole角色 - * @param item - * @return - */ -bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const -{ - if (nullptr == item) { - return (false); - } - return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid()); -} - -/** - * @brief 把item转换为category - * @param item - * @return无法转换返回nullptr - */ -SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const -{ - int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); - - if (level != 0) { - return (nullptr); - } - qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); - - return (reinterpret_cast< SARibbonCategory* >(p)); -} - -/** - * @brief 把item转换为pannel - * @param item - * @return 无法转换返回nullptr - */ -SARibbonPannel* SARibbonCustomizeWidget::PrivateData::itemToPannel(QStandardItem* item) const -{ - int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); - - if (level != 1) { - return (nullptr); - } - qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); - - return (reinterpret_cast< SARibbonPannel* >(p)); -} - -/** - * @brief 获取item对应的objectname - * @param item - * @return 如果无法获取,返回一个空的QString - */ -QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const -{ - QString objName; - - if (isCustomizeItem(item)) { - // 说明是自定义的 - objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString(); - } else { - // 说明这个是非自定义的 - int level = itemLevel(item); - if (0 == level) { - SARibbonCategory* category = itemToCategory(item); - if (category) { - objName = category->objectName(); - } - } else if (1 == level) { - SARibbonPannel* pannel = itemToPannel(item); - if (pannel) { - objName = pannel->objectName(); - } - } - } - return (objName); -} - -/** - * @brief 判断item是否可自定义 - * @param item - * @return - */ -bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const -{ - if (nullptr == item) { - return (false); - } - QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole); - - if (v.isValid()) { - return (v.toBool()); - } - return (false); -} - -/** - * @brief 从item 转为action - * @param item - * @return - */ -QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const -{ - if (2 != itemLevel(item)) { - return (nullptr); - } - // 这里要非常注意,SARibbonCustomizeWidget::CustomizeRole为true时,说明这个是自定义的内容, - // 这时PointerRole里存放的是action指针,不是SARibbonPannelItem - QAction* act = nullptr; - - if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) { - act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); - } else { - SARibbonPannelItem* pi = reinterpret_cast< SARibbonPannelItem* >( - item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); - act = (pi->action); - } - return (act); -} - -//=================================================== -// SARibbonCustomizeWidget -//=================================================== -/** - * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget - * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针 - * @param parent 用于界面生成的parent,可以和SARibbonMainWindow一样 - * @param f 同QWidget::QWidget的第二个参数 - */ -SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f) - : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) -{ - init(ribbonWindow->ribbonBar()); -} - -SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f) - : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) -{ - init(ribbonbar); -} - -void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar) -{ - d_ptr->mRibbonBar = ribbonbar; - ui->setupUi(this); - ui->listViewSelect->setModel(d_ptr->mAcionModel); - ui->treeViewResult->setModel(d_ptr->mRibbonModel); - initConnection(); - updateModel(); -} - -SARibbonCustomizeWidget::~SARibbonCustomizeWidget() -{ - delete ui; -} - -void SARibbonCustomizeWidget::initConnection() -{ - // 这个需要qt5.8以上支持 - // connect(ui->comboBoxActionIndex, QOverload::of(&QComboBox::currentIndexChanged) - // , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); - connect(ui->comboBoxActionIndex, - static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged), - this, - &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); - // 这个需要qt5.8以上支持 - // connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked) - // , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); - connect(ui->radioButtonGroup, - static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked), - this, - &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); - connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked); - connect(ui->pushButtonNewPannel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPannelClicked); - connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked); - connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked); - connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked); - connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked); - connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked); - connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked); - connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked); - connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged); - connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited); - connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked); -} - -/** - * @brief 设置action管理器 - * @param mgr - */ -void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr) -{ - d_ptr->mActionMgr = mgr; - if (d_ptr->mActionMgr) { - d_ptr->mAcionModel->uninstallActionsManager(); - } - d_ptr->mAcionModel->setupActionsManager(mgr); - // 更新左边复选框 - QList< int > tags = mgr->actionTags(); - - ui->comboBoxActionIndex->clear(); - for (int tag : qAsConst(tags)) { - if (mgr->tagName(tag).isEmpty()) - continue; - ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag); - } -} - -/** - * @brief //判断用户是否有要存储的内容,对应save动作 - * @return - */ -bool SARibbonCustomizeWidget::isApplied() const -{ - return (d_ptr->mCustomizeDatasApplied.size() > 0); -} - -/** - * @brief 判断用户是否有改动内容,对应apply动作 - * @return - */ -bool SARibbonCustomizeWidget::isCached() const -{ - return (d_ptr->mCustomizeDatasCache.size() > 0); -} - -/** - * @brief 获取model - * @return - */ -const QStandardItemModel* SARibbonCustomizeWidget::model() const -{ - return (d_ptr->mRibbonModel); -} - -/** - * @brief 根据当前的radiobutton选项来更新model - */ -void SARibbonCustomizeWidget::updateModel() -{ - updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory); - if (d_ptr->mRibbonBar) { - SARibbonBar* bar = d_ptr->mRibbonBar; - if (bar) { - ui->comboBoxActionProportion->clear(); - if (bar->isTwoRowStyle()) { - ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); - ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); - } else { - ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); - ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPannelItem::Medium); - ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); - } - } - } -} - -/** - * @brief 更新model - */ -void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type) -{ - d_ptr->mShowType = type; - d_ptr->updateModel(); -} - -/** - * @brief 应用所有设定 - * @return 应用成功返回true - * @note 所有设定有一个应用成功都会返回true - */ -bool SARibbonCustomizeWidget::applys() -{ - simplify(); - if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) { - // 将临时操作存入已应用操作,并清空临时操作 - makeActionsApplied(); - clearCache(); - return true; - } else { - return false; - } -} - -/** - * @brief 转换为xml - * - * 此函数仅会写element,不会写document相关内容,因此如果需要写document, - * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() - * - * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); - * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, - * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: - * @code - * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口 - * dlg.setupActionsManager(m_actMgr); - * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { - * dlg.applys(); - * QByteArray str; - * QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造,遇到中文会异常 - * xml.setAutoFormatting(true); - * xml.setAutoFormattingIndent(2); - * xml.setCodec("utf-8");//在writeStartDocument之前指定编码 - * xml.writeStartDocument(); - * bool isok = dlg.toXml(&xml); - * xml.writeEndDocument(); - * if (isok) { - * QFile f("customize.xml"); - * if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) { - * QTextStream s(&f); - * s.setCodec("utf-8");//指定编码输出 - * s << str; - * s.flush(); - * } - * m_edit->append("write xml:");//m_edit的定义为:QTextEdit *m_edit; - * m_edit->append(str); - * } - * } - * @endcode - * @return 如果出现异常,返回false,如果没有自定义数据也会返回false - * @see sa_customize_datas_to_xml - */ -bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const -{ - QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas; - - if (isApplied()) - res << d_ptr->mCustomizeDatasApplied; - if (isCached()) - res << d_ptr->mCustomizeDatasCache; - - res = SARibbonCustomizeData::simplify(res); - return (sa_customize_datas_to_xml(xml, res)); -} - -/** - * @brief 把配置写入文件中 - * @param xmlpath - * @return - */ -bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const -{ - QFile f(xmlpath); - - if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - return (false); - } - QXmlStreamWriter xml(&f); - - xml.setAutoFormatting(true); - xml.setAutoFormattingIndent(2); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. - xml.setCodec("utf-8"); // 在writeStartDocument之前指定编码 -#endif - xml.writeStartDocument(); - bool isOK = toXml(&xml); - - xml.writeEndDocument(); - f.close(); - return (isOK); -} - -/** - * @brief 从xml中加载QList - * - * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - * @param xml - * @return - * @note 此函数要在@ref setupActionsManager 函数之后调用 - */ -void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml) -{ - QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr); - - d_ptr->mOldCustomizeDatas = cds; -} - -/** - * @brief 从xml中加载QList - * - * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - * @param xmlpath - * @note 此函数要在@ref setupActionsManager 函数之后调用 - * @note 如果程序启动后加载了自定义配置,再调用此窗口时需要调用此函数,把原来的配置加载进来, - * 在生成新动作时会把旧动作保存,但在调用applys时不会调用此加载的动作 - */ -void SARibbonCustomizeWidget::fromXml(const QString& xmlpath) -{ - QFile f(xmlpath); - - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - return; - } - f.seek(0); - QXmlStreamReader xml(&f); - - fromXml(&xml); -} - -/** - * @brief 应用xml配置 - * - * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: - * @code - * //只能调用一次 - * static bool has_call = false; - * if (!has_call) { - * QFile f("customize.xml"); - * if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) { - * return; - * } - * f.seek(0); - * QXmlStreamReader xml(&f); - * has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr); - * } - * @endcode - * @param xml - * @param bar SARibbonBar - * @return 所有设定有一个应用成功都会返回true - * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file - */ -bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr) -{ - // 先找到sa-ribbon-customize标签 - QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr); - - return (sa_customize_datas_apply(cds, bar) > 0); -} - -/** - * @brief 清除已应用的动作 - */ -void SARibbonCustomizeWidget::makeActionsApplied() -{ - d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache; -} - -/** - * @brief 清除applied的动作,cancel操作后需要清空已应用的动作 - */ -void SARibbonCustomizeWidget::clearApplied() -{ - d_ptr->mCustomizeDatasApplied.clear(); -} - -/** - * @brief 清除缓存动作 - * - * 在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 - */ -void SARibbonCustomizeWidget::clearCache() -{ - d_ptr->mCustomizeDatasCache.clear(); -} - -/** - * @brief 清除所有动作,不包含本地读取的数据 - */ -void SARibbonCustomizeWidget::clear() -{ - clearApplied(); - clearCache(); -} - -/** - * @brief 精简 - */ -void SARibbonCustomizeWidget::simplify() -{ - d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache); -} - -/** - * @brief 获取当前界面选中的行属性 - * @return - */ -SARibbonPannelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const -{ - return (static_cast< SARibbonPannelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt())); -} - -/** - * @brief 获取listview中选中的action - * @return 如果没有选中action,返回nullptr - * @note 如果要获取treeview选中的action,使用@ref itemToAction 函数 - */ -QAction* SARibbonCustomizeWidget::selectedAction() const -{ - QItemSelectionModel* m = ui->listViewSelect->selectionModel(); - - if ((nullptr == m) || !m->hasSelection()) { - return (nullptr); - } - QModelIndex i = m->currentIndex(); - - return (d_ptr->mAcionModel->indexToAction(i)); -} - -/** - * @brief 把item转换为action - * @param item - * @return 如果没有action可转换,返回nullptr - */ -QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const -{ - return (d_ptr->itemToAction(item)); -} - -/** - * @brief 获取ribbon tree选中的item - * @return - */ -QStandardItem* SARibbonCustomizeWidget::selectedItem() const -{ - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if ((nullptr == m) || !m->hasSelection()) { - return (nullptr); - } - QModelIndex i = m->currentIndex(); - - return (d_ptr->mRibbonModel->itemFromIndex(i)); -} - -/** - * @brief 获取选中的ribbon tree 的level - * @return -1为选中异常,0代表选中了category 1代表选中了pannel 2代表选中了action - */ -int SARibbonCustomizeWidget::selectedRibbonLevel() const -{ - QStandardItem* item = selectedItem(); - - if (item) { - return (itemLevel(item)); - } - return (-1); -} - -/** - * @brief 获取StandardItem 的level - * @param item - * @return - */ -int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const -{ - return (d_ptr->itemLevel(item)); -} - -/** - * @brief 设置某个item被选中 - * @param item - */ -void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible) -{ - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if (nullptr == m) { - return; - } - if (m) { - m->clearSelection(); - m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent); - } - if (ensureVisible) { - ui->treeViewResult->scrollTo(item->index()); - } -} - -/** - * @brief 判断itemn能否改动,可以改动返回true - * @param item - * @return - */ -bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const -{ - return (d_ptr->isItemCanCustomize(item)); -} - -bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const -{ - return (isItemCanCustomize(selectedItem())); -} - -/** - * @brief 判断itemn能否改动,可以改动返回true - * @param item - * @return - */ -bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const -{ - return (d_ptr->isCustomizeItem(item)); -} - -bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const -{ - return (isCustomizeItem(selectedItem())); -} - -void SARibbonCustomizeWidget::removeItem(QStandardItem* item) -{ - if (item->parent()) { - item->parent()->removeRow(item->row()); - } else { - d_ptr->mRibbonModel->removeRow(item->row()); - } -} - -void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index) -{ - int tag = ui->comboBoxActionIndex->itemData(index).toInt(); - - d_ptr->mAcionModel->setFilter(tag); -} - -void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b) -{ - updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory); -} - -void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked() -{ - int row = d_ptr->mRibbonModel->rowCount(); - QItemSelectionModel* m = ui->treeViewResult->selectionModel(); - - if (m && m->hasSelection()) { - QModelIndex i = m->currentIndex(); - while (i.parent().isValid()) { - i = i.parent(); - } - // 获取选中的最顶层item - row = i.row() + 1; - } - QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount))); - - ni->setData(0, SARibbonCustomizeWidget::LevelRole); - d_ptr->mRibbonModel->insertRow(row, ni); - // 设置新增的为选中 - setSelectItem(ni); - // 把动作插入动作列表中 - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData(ni->text(), - ni->row(), - SARibbonCustomizeWidget::PrivateData::makeRandomObjName( - "category")); - - d_ptr->mCustomizeDatasCache.append(d); - ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); - ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); -} - -void SARibbonCustomizeWidget::onPushButtonNewPannelClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - int level = selectedRibbonLevel(); - - QStandardItem* ni = new QStandardItem(tr("new pannel[customize]%1").arg(++(d_ptr->mCustomizePannelCount))); - - ni->setData(1, SARibbonCustomizeWidget::LevelRole); - - if (0 == level) { - // 说明是category,插入到最后 - item->appendRow(ni); - } else if (1 == level) { - // 说明选择的是pannel,插入到此pannel之后 - QStandardItem* categoryItem = item->parent(); - if (nullptr == categoryItem) { - return; - } - categoryItem->insertRow(item->row() + 1, ni); - } else { - // 不符合就删除退出 - delete ni; - ni = nullptr; - return; - } - // 查找category的object name - QStandardItem* categoryItem = ni->parent(); - QString categoryObjName = ""; - - categoryObjName = d_ptr->itemObjectName(categoryItem); - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPannelCustomizeData(ni->text(), - ni->row(), - categoryObjName, - SARibbonCustomizeWidget::PrivateData::makeRandomObjName( - "pannel")); - - d_ptr->mCustomizeDatasCache.append(d); - ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); - ni->setData(d.pannelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); - setSelectItem(ni); -} - -void SARibbonCustomizeWidget::onPushButtonRenameClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - bool ok; - QString text = ""; - - text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok); - - if (!ok || text.isEmpty()) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 改Category名 - QString cateObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else if (1 == level) { - QString cateObjName = d_ptr->itemObjectName(item->parent()); - QString pannelObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePannelCustomizeData(text, cateObjName, pannelObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else { - // action 不允许改名 - return; - } - item->setText(text); -} - -void SARibbonCustomizeWidget::onPushButtonAddClicked() -{ - QAction* act = selectedAction(); - QStandardItem* item = selectedItem(); - - if ((nullptr == act) || (nullptr == item)) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 选中category不进行操作 - return; - } else if (2 == level) { - // 选中action,添加到这个action之后,把item设置为pannel - item = item->parent(); - } - QString pannelObjName = d_ptr->itemObjectName(item); - QString categoryObjName = d_ptr->itemObjectName(item->parent()); - QString key = d_ptr->mActionMgr->key(act); - - SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData(key, - d_ptr->mActionMgr, - selectedRowProportion(), - categoryObjName, - pannelObjName); - - d_ptr->mCustomizeDatasCache.append(d); - - QStandardItem* actItem = new QStandardItem(act->icon(), act->text()); - - actItem->setData(2, SARibbonCustomizeWidget::LevelRole); - actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole - actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole); - actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole); - actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole); // 把action指针传入 - item->appendRow(actItem); -} - -void SARibbonCustomizeWidget::onPushButtonDeleteClicked() -{ - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - if (!isItemCanCustomize(item)) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 删除category - SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item)); - d_ptr->mCustomizeDatasCache.append(d); - } else if (1 == level) { - // 删除pannel - QString catObjName = d_ptr->itemObjectName(item->parent()); - QString pannelObjName = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePannelCustomizeData(catObjName, pannelObjName); - d_ptr->mCustomizeDatasCache.append(d); - } else if (2 == level) { - // 删除Action - QString catObjName = d_ptr->itemObjectName(item->parent()->parent()); - QString pannelObjName = d_ptr->itemObjectName(item->parent()); - QAction* act = itemToAction(item); - QString key = d_ptr->mActionMgr->key(act); - if (key.isEmpty() || catObjName.isEmpty() || pannelObjName.isEmpty()) { - return; - } - - SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, - pannelObjName, - key, - d_ptr->mActionMgr); - d_ptr->mCustomizeDatasCache.append(d); - } - // 执行删除操作 - removeItem(item); - // 删除后重新识别 - ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0); - ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize()); -} - -void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index) -{ - // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 - // 点击了listview,判断treeview的状态 - Q_UNUSED(index); - ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0); - ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize()); -} - -void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index) -{ - Q_UNUSED(index); - // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 - QStandardItem* item = selectedItem(); - - if (nullptr == item) { - return; - } - int level = itemLevel(item); - - ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item)); - ui->pushButtonDelete->setEnabled(isItemCanCustomize(item)); // 有CustomizeRole,必有CanCustomizeRole - ui->pushButtonRename->setEnabled( - level != 2 || isItemCanCustomize(item)); // QAction 不能改名 , 有CustomizeRole,必有CanCustomizeRole -} - -void SARibbonCustomizeWidget::onToolButtonUpClicked() -{ - QStandardItem* item = selectedItem(); - - if ((nullptr == item) || (0 == item->row())) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 移动category - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), - -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = d_ptr->mRibbonModel->takeItem(r); - d_ptr->mRibbonModel->removeRow(r); - d_ptr->mRibbonModel->insertRow(r - 1, item); - } else if (1 == level) { - QStandardItem* paritem = item->parent(); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), - d_ptr->itemObjectName(item), - -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = paritem->takeChild(r); - paritem->removeRow(r); - paritem->insertRow(r - 1, item); - } else if (2 == level) { - QStandardItem* pannelItem = item->parent(); - QStandardItem* categoryItem = pannelItem->parent(); - QAction* act = itemToAction(item); - if (!act) { - return; - } - QString key = d_ptr->mActionMgr->key(act); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), - d_ptr->itemObjectName(pannelItem), - key, - d_ptr->mActionMgr, - -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = pannelItem->takeChild(r); - pannelItem->removeRow(r); - pannelItem->insertRow(r - 1, item); - } - - // 保持焦点,方便连续操作 - setSelectItem(item); - onTreeViewResultClicked(item->index()); -} - -void SARibbonCustomizeWidget::onToolButtonDownClicked() -{ - QStandardItem* item = selectedItem(); - - if (item == nullptr) { - return; - } - int count = 0; - - if (item->parent()) { - count = item->parent()->rowCount(); - } else { - count = d_ptr->mRibbonModel->rowCount(); - } - if ((nullptr == item) || ((count - 1) == item->row())) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - // 移动category - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), - 1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = d_ptr->mRibbonModel->takeItem(item->row()); - d_ptr->mRibbonModel->removeRow(r); - d_ptr->mRibbonModel->insertRow(r + 1, item); - } else if (1 == level) { - QStandardItem* paritem = item->parent(); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), - d_ptr->itemObjectName(item), - 1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = paritem->takeChild(r); - paritem->removeRow(r); - paritem->insertRow(r + 1, item); - } else if (2 == level) { - QStandardItem* pannelItem = item->parent(); - QStandardItem* categoryItem = pannelItem->parent(); - QAction* act = itemToAction(item); - if (!act) { - return; - } - QString key = d_ptr->mActionMgr->key(act); - SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), - d_ptr->itemObjectName(pannelItem), - key, - d_ptr->mActionMgr, - -1); - d_ptr->mCustomizeDatasCache.append(d); - int r = item->row(); - item = pannelItem->takeChild(r); - pannelItem->removeRow(r); - pannelItem->insertRow(r + 1, item); - } - - // 保持焦点,方便连续操作 - setSelectItem(item); - onTreeViewResultClicked(item->index()); -} - -void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item) -{ - if (item == nullptr) { - return; - } - int level = itemLevel(item); - - if (0 == level) { - if (item->isCheckable()) { - QString objname = d_ptr->itemObjectName(item); - SARibbonCustomizeData d = SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, - item->checkState() - == Qt::Checked); - d_ptr->mCustomizeDatasCache.append(d); - } - } -} - -void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text) -{ - d_ptr->mAcionModel->search(text); -} - -void SARibbonCustomizeWidget::onPushButtonResetClicked() -{ - int btn = QMessageBox::warning(this, - tr("Warning"), - tr("Are you sure reset all customize setting?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - - if (btn == QMessageBox::Yes) { - clear(); - } -} +#include "SARibbonCustomizeWidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SARibbonMainWindow.h" +#include "SARibbonPannel.h" +#include +#include +#include +#include +#include +#include +#include +#include "SARibbonCustomizeData.h" +#include "SARibbonBar.h" +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeWidget +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds) +{ + if (cds.size() <= 0) { + return (false); + } + + xml->writeStartElement("sa-ribbon-customize"); + for (const SARibbonCustomizeData& d : cds) { + xml->writeStartElement("customize-data"); + xml->writeAttribute("type", QString::number(d.actionType())); + xml->writeAttribute("index", QString::number(d.indexValue)); + xml->writeAttribute("key", d.keyValue); + xml->writeAttribute("category", d.categoryObjNameValue); + xml->writeAttribute("pannel", d.pannelObjNameValue); + xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue)); + + xml->writeEndElement(); + } + xml->writeEndElement(); + if (xml->hasError()) { + qWarning() << "write has error"; + } + return (true); +} + +QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr) +{ + // 先找到"sa-ribbon-customize" + while (!xml->atEnd()) { + + if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) { + break; + } + xml->readNext(); + } + QList< SARibbonCustomizeData > res; + + // 开始遍历"customize-data" + while (!xml->atEnd()) { + if (xml->isStartElement() && (xml->name().toString() == "customize-data")) { + // 首先读取属性type + SARibbonCustomizeData d; + QXmlStreamAttributes attrs = xml->attributes(); + if (!attrs.hasAttribute("type")) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + bool isOk = false; + int v = xml->attributes().value("type").toInt(&isOk); + if (!isOk) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v)); + // 开始读取子对象 + if (attrs.hasAttribute("index")) { + v = xml->attributes().value("index").toInt(&isOk); + if (isOk) { + d.indexValue = v; + } + } + if (attrs.hasAttribute("key")) { + d.keyValue = attrs.value("key").toString(); + } + if (attrs.hasAttribute("category")) { + d.categoryObjNameValue = attrs.value("category").toString(); + } + if (attrs.hasAttribute("pannel")) { + d.pannelObjNameValue = attrs.value("pannel").toString(); + } + if (attrs.hasAttribute("row-prop")) { + v = xml->attributes().value("row-prop").toInt(&isOk); + if (isOk) { + d.actionRowProportionValue = static_cast< SARibbonPannelItem::RowProportion >(v); + } + } + d.setActionsManager(mgr); + res.append(d); + } + xml->readNext(); + } + if (xml->hasError()) { + qWarning() << xml->errorString(); + } + return (res); +} + +int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + + for (const SARibbonCustomizeData& d : cds) { + if (d.apply(bar)) { + ++c; + } + } + return (c); +} + +int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + // todo 支持反向操作 + return (c); +} + +bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + QFile f(filePath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return (false); + } + f.seek(0); + QXmlStreamReader xml(&f); + + return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr)); +} + +/** + * @brief 构建SARibbonCustomizeWidget的Ui + */ +class SARibbonCustomizeWidgetUi +{ +public: + QHBoxLayout* horizontalLayoutMain; + QVBoxLayout* verticalLayoutSelect; + QLabel* labelSelectAction; + QHBoxLayout* horizontalLayoutSearch; + QComboBox* comboBoxActionIndex; + QLineEdit* lineEditSearchAction; + QListView* listViewSelect; + QVBoxLayout* verticalLayoutMidButtons; + QSpacerItem* verticalSpacerUp; + QPushButton* pushButtonAdd; + QPushButton* pushButtonDelete; + QPushButton* pushButtonReset; + QSpacerItem* verticalSpacerDown; + QLabel* labelProportion; + QComboBox* comboBoxActionProportion; + QVBoxLayout* verticalLayoutResult; + QLabel* labelCustomize; + QHBoxLayout* horizontalLayoutCategorySelect; + QRadioButton* radioButtonMainCategory; + QRadioButton* radioButtonAllCategory; + QButtonGroup* radioButtonGroup; + QTreeView* treeViewResult; + QHBoxLayout* horizontalLayoutActionOptBtns; + QPushButton* pushButtonNewCategory; + QPushButton* pushButtonNewPannel; + QPushButton* pushButtonRename; + QVBoxLayout* verticalLayoutRightButtons; + QSpacerItem* verticalSpacerUp2; + QToolButton* toolButtonUp; + QToolButton* toolButtonDown; + QSpacerItem* verticalSpacerDown2; + + void setupUi(QWidget* customizeWidget) + { + if (customizeWidget->objectName().isEmpty()) { + customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget")); + } + customizeWidget->resize(800, 600); + horizontalLayoutMain = new QHBoxLayout(customizeWidget); + horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain")); + verticalLayoutSelect = new QVBoxLayout(); + verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect")); + labelSelectAction = new QLabel(customizeWidget); + labelSelectAction->setObjectName(QStringLiteral("labelSelectAction")); + + verticalLayoutSelect->addWidget(labelSelectAction); + + horizontalLayoutSearch = new QHBoxLayout(); + horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch")); + comboBoxActionIndex = new QComboBox(customizeWidget); + comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex")); + comboBoxActionIndex->setEditable(false); + + horizontalLayoutSearch->addWidget(comboBoxActionIndex); + + lineEditSearchAction = new QLineEdit(customizeWidget); + lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction")); + + horizontalLayoutSearch->addWidget(lineEditSearchAction); + + verticalLayoutSelect->addLayout(horizontalLayoutSearch); + + listViewSelect = new QListView(customizeWidget); + listViewSelect->setObjectName(QStringLiteral("listViewSelect")); + + verticalLayoutSelect->addWidget(listViewSelect); + + horizontalLayoutMain->addLayout(verticalLayoutSelect); + + verticalLayoutMidButtons = new QVBoxLayout(); + verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons")); + verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerUp); + + pushButtonAdd = new QPushButton(customizeWidget); + pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd")); + pushButtonAdd->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonAdd); + + pushButtonDelete = new QPushButton(customizeWidget); + pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete")); + pushButtonDelete->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonDelete); + + verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerDown); + + labelProportion = new QLabel(customizeWidget); + labelProportion->setObjectName(QStringLiteral("labelProportion")); + verticalLayoutMidButtons->addWidget(labelProportion); + + comboBoxActionProportion = new QComboBox(customizeWidget); + comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion")); + comboBoxActionProportion->setEditable(false); + verticalLayoutMidButtons->addWidget(comboBoxActionProportion); + horizontalLayoutMain->addLayout(verticalLayoutMidButtons); + + verticalLayoutResult = new QVBoxLayout(); + verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult")); + labelCustomize = new QLabel(customizeWidget); + labelCustomize->setObjectName(QStringLiteral("labelCustomize")); + + verticalLayoutResult->addWidget(labelCustomize); + + horizontalLayoutCategorySelect = new QHBoxLayout(); + horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect")); + radioButtonMainCategory = new QRadioButton(customizeWidget); + radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory")); + radioButtonMainCategory->setChecked(false); + + horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory); + + radioButtonAllCategory = new QRadioButton(customizeWidget); + radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory")); + radioButtonAllCategory->setChecked(true); + + horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory); + + radioButtonGroup = new QButtonGroup(customizeWidget); + radioButtonGroup->addButton(radioButtonMainCategory); + radioButtonGroup->addButton(radioButtonAllCategory); + + verticalLayoutResult->addLayout(horizontalLayoutCategorySelect); + + treeViewResult = new QTreeView(customizeWidget); + treeViewResult->setObjectName(QStringLiteral("treeViewResult")); + treeViewResult->setHeaderHidden(true); + treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection); + treeViewResult->setAnimated(true); // 支持动画 + treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不允许直接在item上重命名 + + verticalLayoutResult->addWidget(treeViewResult); + + horizontalLayoutActionOptBtns = new QHBoxLayout(); + horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns")); + pushButtonNewCategory = new QPushButton(customizeWidget); + pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory); + + pushButtonNewPannel = new QPushButton(customizeWidget); + pushButtonNewPannel->setObjectName(QStringLiteral("pushButtonNewPannel")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewPannel); + + pushButtonRename = new QPushButton(customizeWidget); + pushButtonRename->setObjectName(QStringLiteral("pushButtonRename")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonRename); + + pushButtonReset = new QPushButton(customizeWidget); + pushButtonReset->setObjectName(QStringLiteral("pushButtonReset")); + horizontalLayoutActionOptBtns->addWidget(pushButtonReset); + + verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns); + + horizontalLayoutMain->addLayout(verticalLayoutResult); + + verticalLayoutRightButtons = new QVBoxLayout(); + verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons")); + verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerUp2); + + toolButtonUp = new QToolButton(customizeWidget); + toolButtonUp->setObjectName(QStringLiteral("pushButtonUp")); + toolButtonUp->setArrowType(Qt::UpArrow); + toolButtonUp->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonUp); + + toolButtonDown = new QToolButton(customizeWidget); + toolButtonDown->setObjectName(QStringLiteral("pushButtonDown")); + toolButtonDown->setArrowType(Qt::DownArrow); + toolButtonDown->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonDown); + + verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerDown2); + + horizontalLayoutMain->addLayout(verticalLayoutRightButtons); + + retranslateUi(customizeWidget); + } // setupUi + + void retranslateUi(QWidget* customizeWidget) + { + customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR)); + labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR)); // cn:请选择 + lineEditSearchAction->setInputMask(QString()); + lineEditSearchAction->setText(QString()); + lineEditSearchAction->setPlaceholderText( + QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR)); // cn:查找命令 + pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR)); // cn:添加 >> + pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR)); // cn:<< 移除 + labelCustomize->setText( + QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR)); // cn:自定义功能区 + radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR)); // cn:主选项卡 + radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR)); // cn:所有选项卡 + pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR)); // cn:新建选项卡 + pushButtonNewPannel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR)); // cn:新建组 + pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR)); // cn:重命名 + pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR)); // cn:重置 + labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR)); // cn:比例 + } // retranslateUi +}; + +/** + * @brief 管理SARibbonCustomizeWidget的业务逻辑 + */ +class SARibbonCustomizeWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget) +public: + SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory }; ///< 显示类型 + SARibbonBar* mRibbonBar { nullptr }; ///< 保存SARibbonMainWindow的指针 + SARibbonActionsManager* mActionMgr { nullptr }; ///< action管理器 + SARibbonActionsManagerModel* mAcionModel { nullptr }; ///< action管理器对应的model + QStandardItemModel* mRibbonModel { nullptr }; ///< 用于很成ribbon的树 + int mCustomizeCategoryCount { 0 }; ///< 记录自定义Category的个数 + int mCustomizePannelCount { 0 }; ///< 记录自定义Pannel的个数 +public: + PrivateData(SARibbonCustomizeWidget* p); + void updateModel(); + + QList< SARibbonCustomizeData > mCustomizeDatasCache; ///< 缓存记录所有的自定义动作 + QList< SARibbonCustomizeData > mCustomizeDatasApplied; ///< 应用后的所有的自定义动作 + QList< SARibbonCustomizeData > mOldCustomizeDatas; ///< 记录旧的自定义动作,本地文件缓存 + // 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch_suf + static QString makeRandomObjName(const QString& pre); + + int itemLevel(QStandardItem* item) const; + + // + bool isCustomizeItem(QStandardItem* item) const; + + // 把item转换为category + SARibbonCategory* itemToCategory(QStandardItem* item) const; + + // 把item转换为SARibbonPannel + SARibbonPannel* itemToPannel(QStandardItem* item) const; + + // 获取item对应的object name + QString itemObjectName(QStandardItem* item) const; + + // 判断是否可以自定义 + bool isItemCanCustomize(QStandardItem* item) const; + + // 从item转为action + QAction* itemToAction(QStandardItem* item) const; +}; + +SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p) + : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p)) +{ +} + +void SARibbonCustomizeWidget::PrivateData::updateModel() +{ + if (mRibbonBar == nullptr) { + return; + } + mRibbonModel->clear(); + SARibbonBar* ribbonbar = mRibbonBar; + QList< SARibbonCategory* > categorys = ribbonbar->categoryPages(); + + for (const SARibbonCategory* c : qAsConst(categorys)) { + if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) { + // 如果是只显示主内容,如果是上下文标签就忽略 + continue; + } + QStandardItem* ci = new QStandardItem(); + if (c->isContextCategory()) { + ci->setText(QString("[%1]").arg(c->categoryName())); + } else { + ci->setText(c->categoryName()); + } + if (c->isCanCustomize() && !c->isContextCategory()) { + // 上下文标签不做显示隐藏处理 + ci->setCheckable(true); + ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked); + ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->setData(0, SARibbonCustomizeWidget::LevelRole); + ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole); + QList< SARibbonPannel* > pannels = c->pannelList(); + for (const SARibbonPannel* p : qAsConst(pannels)) { + QStandardItem* pi = new QStandardItem(p->pannelName()); + pi->setData(1, SARibbonCustomizeWidget::LevelRole); + pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole); + if (p->isCanCustomize()) { + pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->appendRow(pi); + const QList< SARibbonPannelItem* >& items = p->ribbonPannelItem(); + for (SARibbonPannelItem* i : qAsConst(items)) { + if (i->action->isSeparator()) { + continue; + } + QStandardItem* ii = new QStandardItem(); + // if (i->customWidget) { + // //如果是自定义窗口 + // if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) { + // delete ii; + // continue; //如果窗口啥也没有,就跳过 + // } + // ii->setText(i->widget()->windowTitle()); + // ii->setIcon(i->widget()->windowIcon()); + // if (SARibbonCustomizeData::isCanCustomize(i->widget())) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } else { + // //不是自定义,说明是action + // ii->setText(i->action->text()); + // ii->setIcon(i->action->icon()); + // if (SARibbonCustomizeData::isCanCustomize(i->action)) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } + ii->setText(i->action->text()); + ii->setIcon(i->action->icon()); + if (SARibbonCustomizeData::isCanCustomize(i->action)) { + ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ii->setData(2, SARibbonCustomizeWidget::LevelRole); + ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole); + pi->appendRow(ii); + } + } + mRibbonModel->appendRow(ci); + } +} + +/** + * @brief 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch + * @param pre 前缀 + * @return + */ +QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre) +{ + return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch())); +} + +/** + * @brief 获取item的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const +{ + return (item->data(SARibbonCustomizeWidget::LevelRole).toInt()); +} + +/** + * @brief 判断itemn为自定义的item,自定义的item都带有CustomizeRole角色 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid()); +} + +/** + * @brief 把item转换为category + * @param item + * @return无法转换返回nullptr + */ +SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 0) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonCategory* >(p)); +} + +/** + * @brief 把item转换为pannel + * @param item + * @return 无法转换返回nullptr + */ +SARibbonPannel* SARibbonCustomizeWidget::PrivateData::itemToPannel(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 1) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonPannel* >(p)); +} + +/** + * @brief 获取item对应的objectname + * @param item + * @return 如果无法获取,返回一个空的QString + */ +QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const +{ + QString objName; + + if (isCustomizeItem(item)) { + // 说明是自定义的 + objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString(); + } else { + // 说明这个是非自定义的 + int level = itemLevel(item); + if (0 == level) { + SARibbonCategory* category = itemToCategory(item); + if (category) { + objName = category->objectName(); + } + } else if (1 == level) { + SARibbonPannel* pannel = itemToPannel(item); + if (pannel) { + objName = pannel->objectName(); + } + } + } + return (objName); +} + +/** + * @brief 判断item是否可自定义 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 从item 转为action + * @param item + * @return + */ +QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const +{ + if (2 != itemLevel(item)) { + return (nullptr); + } + // 这里要非常注意,SARibbonCustomizeWidget::CustomizeRole为true时,说明这个是自定义的内容, + // 这时PointerRole里存放的是action指针,不是SARibbonPannelItem + QAction* act = nullptr; + + if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) { + act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + } else { + SARibbonPannelItem* pi = reinterpret_cast< SARibbonPannelItem* >( + item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + act = (pi->action); + } + return (act); +} + +//=================================================== +// SARibbonCustomizeWidget +//=================================================== +/** + * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget + * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针 + * @param parent 用于界面生成的parent,可以和SARibbonMainWindow一样 + * @param f 同QWidget::QWidget的第二个参数 + */ +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonWindow->ribbonBar()); +} + +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonbar); +} + +void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar) +{ + d_ptr->mRibbonBar = ribbonbar; + ui->setupUi(this); + ui->listViewSelect->setModel(d_ptr->mAcionModel); + ui->treeViewResult->setModel(d_ptr->mRibbonModel); + initConnection(); + updateModel(); +} + +SARibbonCustomizeWidget::~SARibbonCustomizeWidget() +{ + delete ui; +} + +void SARibbonCustomizeWidget::initConnection() +{ + // 这个需要qt5.8以上支持 + // connect(ui->comboBoxActionIndex, QOverload::of(&QComboBox::currentIndexChanged) + // , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + connect(ui->comboBoxActionIndex, + static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged), + this, + &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + // 这个需要qt5.8以上支持 + // connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked) + // , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->radioButtonGroup, + static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked), + this, + &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked); + connect(ui->pushButtonNewPannel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPannelClicked); + connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked); + connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked); + connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked); + connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked); + connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked); + connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked); + connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked); + connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged); + connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited); + connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked); +} + +/** + * @brief 设置action管理器 + * @param mgr + */ +void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr) +{ + d_ptr->mActionMgr = mgr; + if (d_ptr->mActionMgr) { + d_ptr->mAcionModel->uninstallActionsManager(); + } + d_ptr->mAcionModel->setupActionsManager(mgr); + // 更新左边复选框 + QList< int > tags = mgr->actionTags(); + + ui->comboBoxActionIndex->clear(); + for (int tag : qAsConst(tags)) { + if (mgr->tagName(tag).isEmpty()) + continue; + ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag); + } +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeWidget::isApplied() const +{ + return (d_ptr->mCustomizeDatasApplied.size() > 0); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeWidget::isCached() const +{ + return (d_ptr->mCustomizeDatasCache.size() > 0); +} + +/** + * @brief 获取model + * @return + */ +const QStandardItemModel* SARibbonCustomizeWidget::model() const +{ + return (d_ptr->mRibbonModel); +} + +/** + * @brief 根据当前的radiobutton选项来更新model + */ +void SARibbonCustomizeWidget::updateModel() +{ + updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory); + if (d_ptr->mRibbonBar) { + SARibbonBar* bar = d_ptr->mRibbonBar; + if (bar) { + ui->comboBoxActionProportion->clear(); + if (bar->isTwoRowStyle()) { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } else { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPannelItem::Medium); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } + } + } +} + +/** + * @brief 更新model + */ +void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type) +{ + d_ptr->mShowType = type; + d_ptr->updateModel(); +} + +/** + * @brief 应用所有设定 + * @return 应用成功返回true + * @note 所有设定有一个应用成功都会返回true + */ +bool SARibbonCustomizeWidget::applys() +{ + simplify(); + if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) { + // 将临时操作存入已应用操作,并清空临时操作 + makeActionsApplied(); + clearCache(); + return true; + } else { + return false; + } +} + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @code + * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口 + * dlg.setupActionsManager(m_actMgr); + * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { + * dlg.applys(); + * QByteArray str; + * QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造,遇到中文会异常 + * xml.setAutoFormatting(true); + * xml.setAutoFormattingIndent(2); + * xml.setCodec("utf-8");//在writeStartDocument之前指定编码 + * xml.writeStartDocument(); + * bool isok = dlg.toXml(&xml); + * xml.writeEndDocument(); + * if (isok) { + * QFile f("customize.xml"); + * if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) { + * QTextStream s(&f); + * s.setCodec("utf-8");//指定编码输出 + * s << str; + * s.flush(); + * } + * m_edit->append("write xml:");//m_edit的定义为:QTextEdit *m_edit; + * m_edit->append(str); + * } + * } + * @endcode + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + * @see sa_customize_datas_to_xml + */ +bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const +{ + QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas; + + if (isApplied()) + res << d_ptr->mCustomizeDatasApplied; + if (isCached()) + res << d_ptr->mCustomizeDatasCache; + + res = SARibbonCustomizeData::simplify(res); + return (sa_customize_datas_to_xml(xml, res)); +} + +/** + * @brief 把配置写入文件中 + * @param xmlpath + * @return + */ +bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { + return (false); + } + QXmlStreamWriter xml(&f); + + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(2); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. + xml.setCodec("utf-8"); // 在writeStartDocument之前指定编码 +#endif + xml.writeStartDocument(); + bool isOK = toXml(&xml); + + xml.writeEndDocument(); + f.close(); + return (isOK); +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xml + * @return + * @note 此函数要在@ref setupActionsManager 函数之后调用 + */ +void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml) +{ + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr); + + d_ptr->mOldCustomizeDatas = cds; +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xmlpath + * @note 此函数要在@ref setupActionsManager 函数之后调用 + * @note 如果程序启动后加载了自定义配置,再调用此窗口时需要调用此函数,把原来的配置加载进来, + * 在生成新动作时会把旧动作保存,但在调用applys时不会调用此加载的动作 + */ +void SARibbonCustomizeWidget::fromXml(const QString& xmlpath) +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + f.seek(0); + QXmlStreamReader xml(&f); + + fromXml(&xml); +} + +/** + * @brief 应用xml配置 + * + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * //只能调用一次 + * static bool has_call = false; + * if (!has_call) { + * QFile f("customize.xml"); + * if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) { + * return; + * } + * f.seek(0); + * QXmlStreamReader xml(&f); + * has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr); + * } + * @endcode + * @param xml + * @param bar SARibbonBar + * @return 所有设定有一个应用成功都会返回true + * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file + */ +bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + // 先找到sa-ribbon-customize标签 + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr); + + return (sa_customize_datas_apply(cds, bar) > 0); +} + +/** + * @brief 清除已应用的动作 + */ +void SARibbonCustomizeWidget::makeActionsApplied() +{ + d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache; +} + +/** + * @brief 清除applied的动作,cancel操作后需要清空已应用的动作 + */ +void SARibbonCustomizeWidget::clearApplied() +{ + d_ptr->mCustomizeDatasApplied.clear(); +} + +/** + * @brief 清除缓存动作 + * + * 在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + */ +void SARibbonCustomizeWidget::clearCache() +{ + d_ptr->mCustomizeDatasCache.clear(); +} + +/** + * @brief 清除所有动作,不包含本地读取的数据 + */ +void SARibbonCustomizeWidget::clear() +{ + clearApplied(); + clearCache(); +} + +/** + * @brief 精简 + */ +void SARibbonCustomizeWidget::simplify() +{ + d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache); +} + +/** + * @brief 获取当前界面选中的行属性 + * @return + */ +SARibbonPannelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const +{ + return (static_cast< SARibbonPannelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt())); +} + +/** + * @brief 获取listview中选中的action + * @return 如果没有选中action,返回nullptr + * @note 如果要获取treeview选中的action,使用@ref itemToAction 函数 + */ +QAction* SARibbonCustomizeWidget::selectedAction() const +{ + QItemSelectionModel* m = ui->listViewSelect->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mAcionModel->indexToAction(i)); +} + +/** + * @brief 把item转换为action + * @param item + * @return 如果没有action可转换,返回nullptr + */ +QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const +{ + return (d_ptr->itemToAction(item)); +} + +/** + * @brief 获取ribbon tree选中的item + * @return + */ +QStandardItem* SARibbonCustomizeWidget::selectedItem() const +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mRibbonModel->itemFromIndex(i)); +} + +/** + * @brief 获取选中的ribbon tree 的level + * @return -1为选中异常,0代表选中了category 1代表选中了pannel 2代表选中了action + */ +int SARibbonCustomizeWidget::selectedRibbonLevel() const +{ + QStandardItem* item = selectedItem(); + + if (item) { + return (itemLevel(item)); + } + return (-1); +} + +/** + * @brief 获取StandardItem 的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const +{ + return (d_ptr->itemLevel(item)); +} + +/** + * @brief 设置某个item被选中 + * @param item + */ +void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible) +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (nullptr == m) { + return; + } + if (m) { + m->clearSelection(); + m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent); + } + if (ensureVisible) { + ui->treeViewResult->scrollTo(item->index()); + } +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const +{ + return (d_ptr->isItemCanCustomize(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const +{ + return (isItemCanCustomize(selectedItem())); +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const +{ + return (d_ptr->isCustomizeItem(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const +{ + return (isCustomizeItem(selectedItem())); +} + +void SARibbonCustomizeWidget::removeItem(QStandardItem* item) +{ + if (item->parent()) { + item->parent()->removeRow(item->row()); + } else { + d_ptr->mRibbonModel->removeRow(item->row()); + } +} + +void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index) +{ + int tag = ui->comboBoxActionIndex->itemData(index).toInt(); + + d_ptr->mAcionModel->setFilter(tag); +} + +void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b) +{ + updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory); +} + +void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked() +{ + int row = d_ptr->mRibbonModel->rowCount(); + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (m && m->hasSelection()) { + QModelIndex i = m->currentIndex(); + while (i.parent().isValid()) { + i = i.parent(); + } + // 获取选中的最顶层item + row = i.row() + 1; + } + QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount))); + + ni->setData(0, SARibbonCustomizeWidget::LevelRole); + d_ptr->mRibbonModel->insertRow(row, ni); + // 设置新增的为选中 + setSelectItem(ni); + // 把动作插入动作列表中 + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData(ni->text(), + ni->row(), + SARibbonCustomizeWidget::PrivateData::makeRandomObjName( + "category")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); +} + +void SARibbonCustomizeWidget::onPushButtonNewPannelClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = selectedRibbonLevel(); + + QStandardItem* ni = new QStandardItem(tr("new pannel[customize]%1").arg(++(d_ptr->mCustomizePannelCount))); + + ni->setData(1, SARibbonCustomizeWidget::LevelRole); + + if (0 == level) { + // 说明是category,插入到最后 + item->appendRow(ni); + } else if (1 == level) { + // 说明选择的是pannel,插入到此pannel之后 + QStandardItem* categoryItem = item->parent(); + if (nullptr == categoryItem) { + return; + } + categoryItem->insertRow(item->row() + 1, ni); + } else { + // 不符合就删除退出 + delete ni; + ni = nullptr; + return; + } + // 查找category的object name + QStandardItem* categoryItem = ni->parent(); + QString categoryObjName = ""; + + categoryObjName = d_ptr->itemObjectName(categoryItem); + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPannelCustomizeData(ni->text(), + ni->row(), + categoryObjName, + SARibbonCustomizeWidget::PrivateData::makeRandomObjName( + "pannel")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.pannelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); + setSelectItem(ni); +} + +void SARibbonCustomizeWidget::onPushButtonRenameClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + bool ok; + QString text = ""; + + text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok); + + if (!ok || text.isEmpty()) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 改Category名 + QString cateObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + QString cateObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePannelCustomizeData(text, cateObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else { + // action 不允许改名 + return; + } + item->setText(text); +} + +void SARibbonCustomizeWidget::onPushButtonAddClicked() +{ + QAction* act = selectedAction(); + QStandardItem* item = selectedItem(); + + if ((nullptr == act) || (nullptr == item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 选中category不进行操作 + return; + } else if (2 == level) { + // 选中action,添加到这个action之后,把item设置为pannel + item = item->parent(); + } + QString pannelObjName = d_ptr->itemObjectName(item); + QString categoryObjName = d_ptr->itemObjectName(item->parent()); + QString key = d_ptr->mActionMgr->key(act); + + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData(key, + d_ptr->mActionMgr, + selectedRowProportion(), + categoryObjName, + pannelObjName); + + d_ptr->mCustomizeDatasCache.append(d); + + QStandardItem* actItem = new QStandardItem(act->icon(), act->text()); + + actItem->setData(2, SARibbonCustomizeWidget::LevelRole); + actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole); + actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole); + actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole); // 把action指针传入 + item->appendRow(actItem); +} + +void SARibbonCustomizeWidget::onPushButtonDeleteClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + if (!isItemCanCustomize(item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 删除category + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item)); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + // 删除pannel + QString catObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePannelCustomizeData(catObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (2 == level) { + // 删除Action + QString catObjName = d_ptr->itemObjectName(item->parent()->parent()); + QString pannelObjName = d_ptr->itemObjectName(item->parent()); + QAction* act = itemToAction(item); + QString key = d_ptr->mActionMgr->key(act); + if (key.isEmpty() || catObjName.isEmpty() || pannelObjName.isEmpty()) { + return; + } + + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, + pannelObjName, + key, + d_ptr->mActionMgr); + d_ptr->mCustomizeDatasCache.append(d); + } + // 执行删除操作 + removeItem(item); + // 删除后重新识别 + ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize()); +} + +void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index) +{ + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + // 点击了listview,判断treeview的状态 + Q_UNUSED(index); + ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize()); +} + +void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index) +{ + Q_UNUSED(index); + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = itemLevel(item); + + ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item)); + ui->pushButtonDelete->setEnabled(isItemCanCustomize(item)); // 有CustomizeRole,必有CanCustomizeRole + ui->pushButtonRename->setEnabled( + level != 2 || isItemCanCustomize(item)); // QAction 不能改名 , 有CustomizeRole,必有CanCustomizeRole +} + +void SARibbonCustomizeWidget::onToolButtonUpClicked() +{ + QStandardItem* item = selectedItem(); + + if ((nullptr == item) || (0 == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(r); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r - 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), + d_ptr->itemObjectName(item), + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r - 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), + d_ptr->itemObjectName(pannelItem), + key, + d_ptr->mActionMgr, + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r - 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onToolButtonDownClicked() +{ + QStandardItem* item = selectedItem(); + + if (item == nullptr) { + return; + } + int count = 0; + + if (item->parent()) { + count = item->parent()->rowCount(); + } else { + count = d_ptr->mRibbonModel->rowCount(); + } + if ((nullptr == item) || ((count - 1) == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), + 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(item->row()); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r + 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), + d_ptr->itemObjectName(item), + 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r + 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), + d_ptr->itemObjectName(pannelItem), + key, + d_ptr->mActionMgr, + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r + 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item) +{ + if (item == nullptr) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + if (item->isCheckable()) { + QString objname = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, + item->checkState() + == Qt::Checked); + d_ptr->mCustomizeDatasCache.append(d); + } + } +} + +void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text) +{ + d_ptr->mAcionModel->search(text); +} + +void SARibbonCustomizeWidget::onPushButtonResetClicked() +{ + int btn = QMessageBox::warning(this, + tr("Warning"), + tr("Are you sure reset all customize setting?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (btn == QMessageBox::Yes) { + clear(); + } +} diff --git a/src/SARibbonBar/SARibbonCustomizeWidget.h b/src/SARibbonBar/SARibbonCustomizeWidget.h index 44e4017d..8a1d32b1 100644 --- a/src/SARibbonBar/SARibbonCustomizeWidget.h +++ b/src/SARibbonBar/SARibbonCustomizeWidget.h @@ -1,209 +1,209 @@ -#ifndef SARIBBONCUSTOMIZEWIDGET_H -#define SARIBBONCUSTOMIZEWIDGET_H -#include "SARibbonGlobal.h" -#include -#include "SARibbonActionsManager.h" -#include "SARibbonPannel.h" -#include "SARibbonCustomizeData.h" - -// SARibbonCustomizeWidget 特有 -class SARibbonCustomizeWidgetUi; -class SARibbonMainWindow; -class SARibbonBar; -// -class QStandardItemModel; -class QStandardItem; -class QAbstractButton; -// -class QXmlStreamWriter; -class QXmlStreamReader; - -/** - * @brief 自定义界面窗口 - * - * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 - * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeWidget::fromXml 同步配置文件,这样再次修改后的配置文件就一致 - */ -class SA_RIBBON_EXPORT SARibbonCustomizeWidget : public QWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonCustomizeWidget) -public: - //保留接口 - explicit SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); - //对于不使用SARibbonMainWindow的情况,使用此构造函数 - explicit SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); - ~SARibbonCustomizeWidget(); - - /** - * @brief 定义ribbon树的显示类型 - */ - enum RibbonTreeShowType - { - ShowAllCategory, ///< 显示所有Category,包括contextcategory - ShowMainCategory ///< 显示主要的category,不包含上下文 - }; - - /** - * @brief QStandardItem对应的role - */ - enum ItemRole - { - LevelRole = Qt::UserRole + 1, ///< 代表这是层级,有0:category 1:pannel 2:item - PointerRole = Qt::UserRole + 2, ///< 代表这是存放指针。根据LevelRole来进行转 - CanCustomizeRole = Qt::UserRole + 3, ///< 代表个item是可以自定义的.bool - CustomizeRole = Qt::UserRole + 4, ///< 代表这个是自定义的item,bool,主要用于那些自己添加的标签和pannel,有此角色必有CanCustomizeRole - CustomizeObjNameRole = Qt::UserRole + 5 ///< 记录了临时的自定义内容的obj名 QString - }; - - //设置action管理器 - void setupActionsManager(SARibbonActionsManager* mgr); - - //判断用户是否有要存储的内容,对应save动作 - bool isApplied() const; - - //判断用户是否有改动内容,对应apply动作 - bool isCached() const; - - //获取model - const QStandardItemModel* model() const; - - //根据当前的radiobutton选项来更新model - void updateModel(); - - //更新model - void updateModel(RibbonTreeShowType type); - - //应用所有的设定 - bool applys(); - - //转换为xml - bool toXml(QXmlStreamWriter* xml) const; - bool toXml(const QString& xmlpath) const; - - //从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 - void fromXml(QXmlStreamReader* xml); - void fromXml(const QString& xmlpath); - - //应用xml配置,可以结合customize_datas_from_xml和customize_datas_apply函数 - static bool fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr); - - //缓存应用的动作,这些动作不会被clear清除,用于本地存储 - void makeActionsApplied(); - - //清除applied的动作,cancel操作后需要清空已应用的动作 - void clearApplied(); - //清除缓存动作,在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 - void clearCache(); - //清除所有动作,不包含本地读取的数据 - void clear(); - -protected: - //把QList进行裁剪,把一些动作合并 - void simplify(); - - SARibbonPannelItem::RowProportion selectedRowProportion() const; - - QAction* selectedAction() const; - QAction* itemToAction(QStandardItem* item) const; - - QStandardItem* selectedItem() const; - - //获取选中的ribbon tree 的level - int selectedRibbonLevel() const; - - //根据选中的item判断 - int itemLevel(QStandardItem* item) const; - - //设置某个item被选中 - void setSelectItem(QStandardItem* item, bool ensureVisible = true); - - //判断itemn能否改动,可以改动返回true - bool isItemCanCustomize(QStandardItem* item) const; - bool isSelectedItemCanCustomize() const; - - //判断item是否是自定义的item - bool isCustomizeItem(QStandardItem* item) const; - bool isSelectedItemIsCustomize() const; - - //删除一个item - void removeItem(QStandardItem* item); - -private Q_SLOTS: - void onComboBoxActionIndexCurrentIndexChanged(int index); - void onRadioButtonGroupButtonClicked(QAbstractButton* b); - void onPushButtonNewCategoryClicked(); - void onPushButtonNewPannelClicked(); - void onPushButtonRenameClicked(); - void onPushButtonAddClicked(); - void onPushButtonDeleteClicked(); - void onListViewSelectClicked(const QModelIndex& index); - void onTreeViewResultClicked(const QModelIndex& index); - void onToolButtonUpClicked(); - void onToolButtonDownClicked(); - void onItemChanged(QStandardItem* item); - void onLineEditSearchActionTextEdited(const QString& text); - void onPushButtonResetClicked(); - -private: - void init(SARibbonBar* ribbonbar); - void initConnection(); - -private: - SARibbonCustomizeWidgetUi* ui; -}; - -/** - * @brief 转换为xml - * - * 此函数仅会写element,不会写document相关内容,因此如果需要写document, - * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() - * @param xml QXmlStreamWriter指针 - * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); - * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, - * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: - * @param cds 基于QList生成的步骤 - * @return 如果出现异常,返回false,如果没有自定义数据也会返回false - */ -bool SA_RIBBON_EXPORT sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds); - -/** - * @brief 通过xml获取QList - * @param xml - * @return QList - */ -QList< SARibbonCustomizeData > SA_RIBBON_EXPORT sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr); - -/** - * @brief 应用QList - * @param cds - * @param w SARibbonBar指针 - * @return 成功应用的个数 - */ -int SA_RIBBON_EXPORT sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); - -/** - * @brief 反向取消应用 - * @param cds - * @param w SARibbonBar指针 - * @return 成功应用的个数 - */ -int SA_RIBBON_EXPORT sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); - -/** - * @brief 直接加载xml自定义ribbon配置文件用于ribbon的自定义显示 - * @param filePath xml配置文件 - * @param w 主窗体 - * @param mgr action管理器 - * @return 成功返回true - * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: - * @code - * static bool has_call = false; - * if (!has_call) { - * has_call = sa_apply_customize_from_xml_file("customize.xml", this, m_actMgr); - * } - * @endcode - */ -bool SA_RIBBON_EXPORT sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr); - -#endif // SARIBBONCUSTOMIZEWIDGET_H +#ifndef SARIBBONCUSTOMIZEWIDGET_H +#define SARIBBONCUSTOMIZEWIDGET_H +#include "SARibbonGlobal.h" +#include +#include "SARibbonActionsManager.h" +#include "SARibbonPannel.h" +#include "SARibbonCustomizeData.h" + +// SARibbonCustomizeWidget 特有 +class SARibbonCustomizeWidgetUi; +class SARibbonMainWindow; +class SARibbonBar; +// +class QStandardItemModel; +class QStandardItem; +class QAbstractButton; +// +class QXmlStreamWriter; +class QXmlStreamReader; + +/** + * @brief 自定义界面窗口 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeWidget::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeWidget : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCustomizeWidget) +public: + //保留接口 + explicit SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + //对于不使用SARibbonMainWindow的情况,使用此构造函数 + explicit SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~SARibbonCustomizeWidget(); + + /** + * @brief 定义ribbon树的显示类型 + */ + enum RibbonTreeShowType + { + ShowAllCategory, ///< 显示所有Category,包括contextcategory + ShowMainCategory ///< 显示主要的category,不包含上下文 + }; + + /** + * @brief QStandardItem对应的role + */ + enum ItemRole + { + LevelRole = Qt::UserRole + 1, ///< 代表这是层级,有0:category 1:pannel 2:item + PointerRole = Qt::UserRole + 2, ///< 代表这是存放指针。根据LevelRole来进行转 + CanCustomizeRole = Qt::UserRole + 3, ///< 代表个item是可以自定义的.bool + CustomizeRole = Qt::UserRole + 4, ///< 代表这个是自定义的item,bool,主要用于那些自己添加的标签和pannel,有此角色必有CanCustomizeRole + CustomizeObjNameRole = Qt::UserRole + 5 ///< 记录了临时的自定义内容的obj名 QString + }; + + //设置action管理器 + void setupActionsManager(SARibbonActionsManager* mgr); + + //判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + //判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + //获取model + const QStandardItemModel* model() const; + + //根据当前的radiobutton选项来更新model + void updateModel(); + + //更新model + void updateModel(RibbonTreeShowType type); + + //应用所有的设定 + bool applys(); + + //转换为xml + bool toXml(QXmlStreamWriter* xml) const; + bool toXml(const QString& xmlpath) const; + + //从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader* xml); + void fromXml(const QString& xmlpath); + + //应用xml配置,可以结合customize_datas_from_xml和customize_datas_apply函数 + static bool fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr); + + //缓存应用的动作,这些动作不会被clear清除,用于本地存储 + void makeActionsApplied(); + + //清除applied的动作,cancel操作后需要清空已应用的动作 + void clearApplied(); + //清除缓存动作,在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + void clearCache(); + //清除所有动作,不包含本地读取的数据 + void clear(); + +protected: + //把QList进行裁剪,把一些动作合并 + void simplify(); + + SARibbonPannelItem::RowProportion selectedRowProportion() const; + + QAction* selectedAction() const; + QAction* itemToAction(QStandardItem* item) const; + + QStandardItem* selectedItem() const; + + //获取选中的ribbon tree 的level + int selectedRibbonLevel() const; + + //根据选中的item判断 + int itemLevel(QStandardItem* item) const; + + //设置某个item被选中 + void setSelectItem(QStandardItem* item, bool ensureVisible = true); + + //判断itemn能否改动,可以改动返回true + bool isItemCanCustomize(QStandardItem* item) const; + bool isSelectedItemCanCustomize() const; + + //判断item是否是自定义的item + bool isCustomizeItem(QStandardItem* item) const; + bool isSelectedItemIsCustomize() const; + + //删除一个item + void removeItem(QStandardItem* item); + +private Q_SLOTS: + void onComboBoxActionIndexCurrentIndexChanged(int index); + void onRadioButtonGroupButtonClicked(QAbstractButton* b); + void onPushButtonNewCategoryClicked(); + void onPushButtonNewPannelClicked(); + void onPushButtonRenameClicked(); + void onPushButtonAddClicked(); + void onPushButtonDeleteClicked(); + void onListViewSelectClicked(const QModelIndex& index); + void onTreeViewResultClicked(const QModelIndex& index); + void onToolButtonUpClicked(); + void onToolButtonDownClicked(); + void onItemChanged(QStandardItem* item); + void onLineEditSearchActionTextEdited(const QString& text); + void onPushButtonResetClicked(); + +private: + void init(SARibbonBar* ribbonbar); + void initConnection(); + +private: + SARibbonCustomizeWidgetUi* ui; +}; + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * @param xml QXmlStreamWriter指针 + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @param cds 基于QList生成的步骤 + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + */ +bool SA_RIBBON_EXPORT sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds); + +/** + * @brief 通过xml获取QList + * @param xml + * @return QList + */ +QList< SARibbonCustomizeData > SA_RIBBON_EXPORT sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr); + +/** + * @brief 应用QList + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 反向取消应用 + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 直接加载xml自定义ribbon配置文件用于ribbon的自定义显示 + * @param filePath xml配置文件 + * @param w 主窗体 + * @param mgr action管理器 + * @return 成功返回true + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * static bool has_call = false; + * if (!has_call) { + * has_call = sa_apply_customize_from_xml_file("customize.xml", this, m_actMgr); + * } + * @endcode + */ +bool SA_RIBBON_EXPORT sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr); + +#endif // SARIBBONCUSTOMIZEWIDGET_H diff --git a/src/SARibbonBar/SARibbonElementFactory.cpp b/src/SARibbonBar/SARibbonElementFactory.cpp index 261c2ae1..8b2e71b2 100644 --- a/src/SARibbonBar/SARibbonElementFactory.cpp +++ b/src/SARibbonBar/SARibbonElementFactory.cpp @@ -1,118 +1,118 @@ -#include "SARibbonElementFactory.h" -#include -#include -#include "SARibbonApplicationButton.h" -#include "SARibbonBar.h" -#include "SARibbonTabBar.h" -#include "SARibbonCategory.h" -#include "SARibbonContextCategory.h" -#include "SARibbonSeparatorWidget.h" -#include "SARibbonGallery.h" -#include "SARibbonGalleryGroup.h" -#include "SARibbonToolButton.h" -#include "SARibbonControlButton.h" -#include "SARibbonButtonGroupWidget.h" -#include "SARibbonStackedWidget.h" -#include "SARibbonQuickAccessBar.h" -#include "SARibbonPannelOptionButton.h" -#include "SARibbonPannelLayout.h" -#include "SARibbonSystemButtonBar.h" - -SARibbonElementFactory::SARibbonElementFactory() -{ -} - -SARibbonElementFactory::~SARibbonElementFactory() -{ -} - -SARibbonBar* SARibbonElementFactory::createRibbonBar(QWidget* parent) -{ - return (new SARibbonBar(parent)); -} - -SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent) -{ - return (new SARibbonTabBar(parent)); -} - -SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent) -{ - return (new SARibbonApplicationButton(parent)); -} - -SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent) -{ - return (new SARibbonCategory(parent)); -} - -SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent) -{ - return (new SARibbonContextCategory(parent)); -} - -SARibbonPannel* SARibbonElementFactory::createRibbonPannel(QWidget* parent) -{ - return (new SARibbonPannel(parent)); -} - -SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent) -{ - return (new SARibbonSeparatorWidget(parent)); -} - -SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent) -{ - return (new SARibbonGallery(parent)); -} - -SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent) -{ - return (new SARibbonGalleryGroup(parent)); -} - -SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent) -{ - return (new SARibbonToolButton(parent)); -} - -SARibbonControlButton* SARibbonElementFactory::createRibbonControlButton(QWidget* parent) -{ - return (new SARibbonControlButton(parent)); -} - -SARibbonControlToolButton* SARibbonElementFactory::createRibbonControlToolButton(QWidget* parent) -{ - return (new SARibbonControlToolButton(parent)); -} - -SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent) -{ - return (new SARibbonStackedWidget(parent)); -} - -SARibbonButtonGroupWidget* SARibbonElementFactory::craeteButtonGroupWidget(QWidget* parent) -{ - return (new SARibbonButtonGroupWidget(parent)); -} - -SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent) -{ - return (new SARibbonQuickAccessBar(parent)); -} - -SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent) -{ - return (new SARibbonSystemButtonBar(parent)); -} - -/** - * @brief 创建SARibbonPannelOptionButton - * @param pannel 附属的pannel - * @return - * @sa SARibbonPannelOptionButton - */ -SARibbonPannelOptionButton* SARibbonElementFactory::createRibbonPannelOptionButton(SARibbonPannel* pannel) -{ - return (new SARibbonPannelOptionButton(pannel)); -} +#include "SARibbonElementFactory.h" +#include +#include +#include "SARibbonApplicationButton.h" +#include "SARibbonBar.h" +#include "SARibbonTabBar.h" +#include "SARibbonCategory.h" +#include "SARibbonContextCategory.h" +#include "SARibbonSeparatorWidget.h" +#include "SARibbonGallery.h" +#include "SARibbonGalleryGroup.h" +#include "SARibbonToolButton.h" +#include "SARibbonControlButton.h" +#include "SARibbonButtonGroupWidget.h" +#include "SARibbonStackedWidget.h" +#include "SARibbonQuickAccessBar.h" +#include "SARibbonPannelOptionButton.h" +#include "SARibbonPannelLayout.h" +#include "SARibbonSystemButtonBar.h" + +SARibbonElementFactory::SARibbonElementFactory() +{ +} + +SARibbonElementFactory::~SARibbonElementFactory() +{ +} + +SARibbonBar* SARibbonElementFactory::createRibbonBar(QWidget* parent) +{ + return (new SARibbonBar(parent)); +} + +SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent) +{ + return (new SARibbonTabBar(parent)); +} + +SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent) +{ + return (new SARibbonApplicationButton(parent)); +} + +SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent) +{ + return (new SARibbonCategory(parent)); +} + +SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent) +{ + return (new SARibbonContextCategory(parent)); +} + +SARibbonPannel* SARibbonElementFactory::createRibbonPannel(QWidget* parent) +{ + return (new SARibbonPannel(parent)); +} + +SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent) +{ + return (new SARibbonSeparatorWidget(parent)); +} + +SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent) +{ + return (new SARibbonGallery(parent)); +} + +SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent) +{ + return (new SARibbonGalleryGroup(parent)); +} + +SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent) +{ + return (new SARibbonToolButton(parent)); +} + +SARibbonControlButton* SARibbonElementFactory::createRibbonControlButton(QWidget* parent) +{ + return (new SARibbonControlButton(parent)); +} + +SARibbonControlToolButton* SARibbonElementFactory::createRibbonControlToolButton(QWidget* parent) +{ + return (new SARibbonControlToolButton(parent)); +} + +SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent) +{ + return (new SARibbonStackedWidget(parent)); +} + +SARibbonButtonGroupWidget* SARibbonElementFactory::craeteButtonGroupWidget(QWidget* parent) +{ + return (new SARibbonButtonGroupWidget(parent)); +} + +SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent) +{ + return (new SARibbonQuickAccessBar(parent)); +} + +SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent) +{ + return (new SARibbonSystemButtonBar(parent)); +} + +/** + * @brief 创建SARibbonPannelOptionButton + * @param pannel 附属的pannel + * @return + * @sa SARibbonPannelOptionButton + */ +SARibbonPannelOptionButton* SARibbonElementFactory::createRibbonPannelOptionButton(SARibbonPannel* pannel) +{ + return (new SARibbonPannelOptionButton(pannel)); +} diff --git a/src/SARibbonBar/SARibbonElementFactory.h b/src/SARibbonBar/SARibbonElementFactory.h index 31fdc41e..c147d198 100644 --- a/src/SARibbonBar/SARibbonElementFactory.h +++ b/src/SARibbonBar/SARibbonElementFactory.h @@ -1,59 +1,59 @@ -#ifndef SARIBBONELEMENTFACTORY_H -#define SARIBBONELEMENTFACTORY_H -#include "SARibbonGlobal.h" -#include -#include -#include -#include -#include "SARibbonBar.h" -#include "SARibbonPannel.h" -class QWidget; -class SARibbonBar; -class SARibbonTabBar; -class SARibbonApplicationButton; -class SARibbonCategory; -class SARibbonContextCategory; -class SARibbonPannel; -class SARibbonSeparatorWidget; -class SARibbonGallery; -class SARibbonGalleryGroup; -class SARibbonToolButton; -class SARibbonControlButton; -class SARibbonControlToolButton; -class SARibbonButtonGroupWidget; -class SARibbonStackedWidget; -class SARibbonQuickAccessBar; -class SARibbonPannelOptionButton; -class SARibbonSystemButtonBar; - -/// -/// \brief SARibbon的子元素创建的工厂,SARibbon内部创建子元素都通过SARibbonElementFactory来创建 -/// -/// 由于SARibbonBar是一个复合控件,很多子窗口组合而成,有些部件的创建如果想继承,那么就需要这个工厂类来处理 -/// 如SARibbonCategory,可以重载此类的createRibbonCategory,返回重载的类的实例 -/// -class SA_RIBBON_EXPORT SARibbonElementFactory -{ -public: - SARibbonElementFactory(); - virtual ~SARibbonElementFactory(); - virtual SARibbonBar* createRibbonBar(QWidget* parent); - virtual SARibbonTabBar* createRibbonTabBar(QWidget* parent); - virtual SARibbonApplicationButton* createRibbonApplicationButton(QWidget* parent); - virtual SARibbonCategory* createRibbonCategory(QWidget* parent); - virtual SARibbonContextCategory* createRibbonContextCategory(QWidget* parent); - virtual SARibbonPannel* createRibbonPannel(QWidget* parent); - virtual SARibbonSeparatorWidget* createRibbonSeparatorWidget(QWidget* parent); - virtual SARibbonGallery* createRibbonGallery(QWidget* parent); - virtual SARibbonGalleryGroup* createRibbonGalleryGroup(QWidget* parent); - virtual SARibbonToolButton* createRibbonToolButton(QWidget* parent); - virtual SARibbonControlButton* createRibbonControlButton(QWidget* parent); - virtual SARibbonControlToolButton* createRibbonControlToolButton(QWidget* parent); - virtual SARibbonStackedWidget* createRibbonStackedWidget(SARibbonBar* parent); - virtual SARibbonButtonGroupWidget* craeteButtonGroupWidget(QWidget* parent); - virtual SARibbonQuickAccessBar* createQuickAccessBar(QWidget* parent); - virtual SARibbonSystemButtonBar* createWindowButtonGroup(QWidget* parent); - virtual SARibbonPannelOptionButton* createRibbonPannelOptionButton(SARibbonPannel* pannel); -}; - -#endif // SARIBBONELEMENTFACTORY_H +#ifndef SARIBBONELEMENTFACTORY_H +#define SARIBBONELEMENTFACTORY_H +#include "SARibbonGlobal.h" +#include +#include +#include +#include +#include "SARibbonBar.h" +#include "SARibbonPannel.h" +class QWidget; +class SARibbonBar; +class SARibbonTabBar; +class SARibbonApplicationButton; +class SARibbonCategory; +class SARibbonContextCategory; +class SARibbonPannel; +class SARibbonSeparatorWidget; +class SARibbonGallery; +class SARibbonGalleryGroup; +class SARibbonToolButton; +class SARibbonControlButton; +class SARibbonControlToolButton; +class SARibbonButtonGroupWidget; +class SARibbonStackedWidget; +class SARibbonQuickAccessBar; +class SARibbonPannelOptionButton; +class SARibbonSystemButtonBar; + +/// +/// \brief SARibbon的子元素创建的工厂,SARibbon内部创建子元素都通过SARibbonElementFactory来创建 +/// +/// 由于SARibbonBar是一个复合控件,很多子窗口组合而成,有些部件的创建如果想继承,那么就需要这个工厂类来处理 +/// 如SARibbonCategory,可以重载此类的createRibbonCategory,返回重载的类的实例 +/// +class SA_RIBBON_EXPORT SARibbonElementFactory +{ +public: + SARibbonElementFactory(); + virtual ~SARibbonElementFactory(); + virtual SARibbonBar* createRibbonBar(QWidget* parent); + virtual SARibbonTabBar* createRibbonTabBar(QWidget* parent); + virtual SARibbonApplicationButton* createRibbonApplicationButton(QWidget* parent); + virtual SARibbonCategory* createRibbonCategory(QWidget* parent); + virtual SARibbonContextCategory* createRibbonContextCategory(QWidget* parent); + virtual SARibbonPannel* createRibbonPannel(QWidget* parent); + virtual SARibbonSeparatorWidget* createRibbonSeparatorWidget(QWidget* parent); + virtual SARibbonGallery* createRibbonGallery(QWidget* parent); + virtual SARibbonGalleryGroup* createRibbonGalleryGroup(QWidget* parent); + virtual SARibbonToolButton* createRibbonToolButton(QWidget* parent); + virtual SARibbonControlButton* createRibbonControlButton(QWidget* parent); + virtual SARibbonControlToolButton* createRibbonControlToolButton(QWidget* parent); + virtual SARibbonStackedWidget* createRibbonStackedWidget(SARibbonBar* parent); + virtual SARibbonButtonGroupWidget* craeteButtonGroupWidget(QWidget* parent); + virtual SARibbonQuickAccessBar* createQuickAccessBar(QWidget* parent); + virtual SARibbonSystemButtonBar* createWindowButtonGroup(QWidget* parent); + virtual SARibbonPannelOptionButton* createRibbonPannelOptionButton(SARibbonPannel* pannel); +}; + +#endif // SARIBBONELEMENTFACTORY_H diff --git a/src/SARibbonBar/SARibbonElementManager.cpp b/src/SARibbonBar/SARibbonElementManager.cpp index 385f7dd0..f9d76cad 100644 --- a/src/SARibbonBar/SARibbonElementManager.cpp +++ b/src/SARibbonBar/SARibbonElementManager.cpp @@ -1,27 +1,27 @@ -#include "SARibbonElementManager.h" - -SARibbonElementManager::SARibbonElementManager() -{ - mFactory.reset(new SARibbonElementFactory()); -} - -SARibbonElementManager::~SARibbonElementManager() -{ -} - -SARibbonElementManager* SARibbonElementManager::instance() -{ - static SARibbonElementManager s_instance; - - return (&(s_instance)); -} - -SARibbonElementFactory* SARibbonElementManager::factory() -{ - return (mFactory.data()); -} - -void SARibbonElementManager::setupFactory(SARibbonElementFactory* fac) -{ - mFactory.reset(fac); -} +#include "SARibbonElementManager.h" + +SARibbonElementManager::SARibbonElementManager() +{ + mFactory.reset(new SARibbonElementFactory()); +} + +SARibbonElementManager::~SARibbonElementManager() +{ +} + +SARibbonElementManager* SARibbonElementManager::instance() +{ + static SARibbonElementManager s_instance; + + return (&(s_instance)); +} + +SARibbonElementFactory* SARibbonElementManager::factory() +{ + return (mFactory.data()); +} + +void SARibbonElementManager::setupFactory(SARibbonElementFactory* fac) +{ + mFactory.reset(fac); +} diff --git a/src/SARibbonBar/SARibbonElementManager.h b/src/SARibbonBar/SARibbonElementManager.h index 7c0c0530..f1a01e98 100644 --- a/src/SARibbonBar/SARibbonElementManager.h +++ b/src/SARibbonBar/SARibbonElementManager.h @@ -1,52 +1,52 @@ -#ifndef SARIBBONELEMENTMANAGER_H -#define SARIBBONELEMENTMANAGER_H -#include "SARibbonElementFactory.h" -#include "SARibbonGlobal.h" - -/** - @brief 此类是一个全局单例,用于管理SARibbonElementFactory - - 如果你有自己的某个部件要重写,首先你需要有自己的ElementFactory - @code - class MyRibbonElementFactory : public SARibbonElementFactory{ - public: - ... - virtual SARibbonPannel* createRibbonPannel(QWidget* parent){ - return new MyRibbonPannel(parent); - } - }; - @endcode - - 然后,你需要在ribbonbar创建之前把自己的ElementFactory设置进去 - - 这个一般会在main函数中进行 - - @code - SARibbonElementManager::instance()->setupFactory(new MyRibbonElementFactory); - @endcode - - 这样,SARibbon创建的pannel就是你自己重写的MyRibbonPannel - - */ -class SA_RIBBON_EXPORT SARibbonElementManager -{ -protected: - SARibbonElementManager(); - -public: - virtual ~SARibbonElementManager(); - static SARibbonElementManager* instance(); - SARibbonElementFactory* factory(); - void setupFactory(SARibbonElementFactory* fac); - -private: - QScopedPointer< SARibbonElementFactory > mFactory; -}; -#ifndef RibbonSubElementMgr -#define RibbonSubElementMgr SARibbonElementManager::instance() -#endif -#ifndef RibbonSubElementFactory -#define RibbonSubElementFactory SARibbonElementManager::instance()->factory() -#endif - -#endif // SARIBBONELEMENTMANAGER_H +#ifndef SARIBBONELEMENTMANAGER_H +#define SARIBBONELEMENTMANAGER_H +#include "SARibbonElementFactory.h" +#include "SARibbonGlobal.h" + +/** + @brief 此类是一个全局单例,用于管理SARibbonElementFactory + + 如果你有自己的某个部件要重写,首先你需要有自己的ElementFactory + @code + class MyRibbonElementFactory : public SARibbonElementFactory{ + public: + ... + virtual SARibbonPannel* createRibbonPannel(QWidget* parent){ + return new MyRibbonPannel(parent); + } + }; + @endcode + + 然后,你需要在ribbonbar创建之前把自己的ElementFactory设置进去 + + 这个一般会在main函数中进行 + + @code + SARibbonElementManager::instance()->setupFactory(new MyRibbonElementFactory); + @endcode + + 这样,SARibbon创建的pannel就是你自己重写的MyRibbonPannel + + */ +class SA_RIBBON_EXPORT SARibbonElementManager +{ +protected: + SARibbonElementManager(); + +public: + virtual ~SARibbonElementManager(); + static SARibbonElementManager* instance(); + SARibbonElementFactory* factory(); + void setupFactory(SARibbonElementFactory* fac); + +private: + QScopedPointer< SARibbonElementFactory > mFactory; +}; +#ifndef RibbonSubElementMgr +#define RibbonSubElementMgr SARibbonElementManager::instance() +#endif +#ifndef RibbonSubElementFactory +#define RibbonSubElementFactory SARibbonElementManager::instance()->factory() +#endif + +#endif // SARIBBONELEMENTMANAGER_H diff --git a/src/SARibbonBar/SARibbonGallery.cpp b/src/SARibbonBar/SARibbonGallery.cpp index 1b6f488d..4575fca5 100644 --- a/src/SARibbonBar/SARibbonGallery.cpp +++ b/src/SARibbonBar/SARibbonGallery.cpp @@ -1,421 +1,421 @@ -#include "SARibbonGallery.h" -#include "SARibbonControlButton.h" -#include -#include -#define ICON_ARROW_UP QIcon(":/image/resource/ArrowUp.png") -#define ICON_ARROW_DOWN QIcon(":/image/resource/ArrowDown.png") -#define ICON_ARROW_MORE QIcon(":/image/resource/ArrowMore.png") -#include -#include -#include -#include -#include -#include "SARibbonElementManager.h" -#include - -/** - * @brief The SARibbonGalleryPrivate class - */ -class SARibbonGallery::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery) -public: - static int sGalleryButtonMaximumWidth; - SARibbonGalleryButton* mButtonUp { nullptr }; - SARibbonGalleryButton* mButtonDown { nullptr }; - SARibbonGalleryButton* mButtonMore { nullptr }; -#if 0 - SARibbonMenu *popupWidget; -#else - SARibbonGalleryViewport* mPopupWidget { nullptr }; -#endif - SARibbonGalleryGroup* mViewportGroup { nullptr }; - QBoxLayout* mButtonLayout { nullptr }; - QBoxLayout* mLayout { nullptr }; - PrivateData(SARibbonGallery* p) : q_ptr(p) - { - } - - void init() - { - mButtonUp = new SARibbonGalleryButton(q_ptr); - mButtonDown = new SARibbonGalleryButton(q_ptr); - mButtonMore = new SARibbonGalleryButton(q_ptr); - mButtonUp->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonDown->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonMore->setToolButtonStyle(Qt::ToolButtonIconOnly); - mButtonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - mButtonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp")); - mButtonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown")); - mButtonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore")); - mButtonUp->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonDown->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonMore->setMaximumWidth(sGalleryButtonMaximumWidth); - mButtonUp->setIcon(ICON_ARROW_UP); - mButtonDown->setIcon(ICON_ARROW_DOWN); - mButtonMore->setIcon(ICON_ARROW_MORE); - q_ptr->connect(mButtonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp); - q_ptr->connect(mButtonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown); - q_ptr->connect(mButtonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail); - // 信号转发 - q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered); - mPopupWidget = nullptr; - mViewportGroup = nullptr; - mButtonLayout = new QBoxLayout(QBoxLayout::TopToBottom); - mButtonLayout->setSpacing(0); - mButtonLayout->setContentsMargins(0, 0, 0, 0); - mButtonLayout->addWidget(mButtonUp); - mButtonLayout->addWidget(mButtonDown); - mButtonLayout->addWidget(mButtonMore); - mLayout = new QBoxLayout(QBoxLayout::RightToLeft); - mLayout->setSpacing(0); - mLayout->setContentsMargins(0, 0, 0, 0); - mLayout->addLayout(mButtonLayout); - mLayout->addStretch(); - q_ptr->setLayout(mLayout); - } - - bool isValid() const - { - return (q_ptr != nullptr); - } - - void createPopupWidget() - { - if (nullptr == mPopupWidget) { -#if 0 - popupWidget = new SARibbonMenu(Parent); -#else - mPopupWidget = new SARibbonGalleryViewport(q_ptr); -#endif - } - } - - void setViewPort(SARibbonGalleryGroup* v) - { - if (nullptr == mViewportGroup) { - mViewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr); - mLayout->addWidget(mViewportGroup, 1); - } - mViewportGroup->setRecalcGridSizeBlock(true); - mViewportGroup->setGalleryGroupStyle(v->galleryGroupStyle()); - mViewportGroup->setDisplayRow(v->displayRow()); - mViewportGroup->setSpacing(v->spacing()); - mViewportGroup->setGridMaximumWidth(v->gridMaximumWidth()); - mViewportGroup->setGridMinimumWidth(v->gridMinimumWidth()); - mViewportGroup->setRecalcGridSizeBlock(false); - mViewportGroup->recalcGridSize(mViewportGroup->height()); - mViewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - mViewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - mViewportGroup->setModel(v->model()); - mViewportGroup->show(); - } -}; - -// 静态变量初始化 - -/** - * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth - */ -int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15; - -//=================================================== -// SARibbonGalleryButton -//=================================================== - -SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent) -{ -} - -SARibbonGalleryButton::~SARibbonGalleryButton() -{ -} -//=================================================== -// SARibbonGalleryViewport -//=================================================== - -SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent) -{ - setWindowFlags(Qt::Popup); - QPalette pl = palette(); - pl.setBrush(QPalette::Window, pl.brush(QPalette::Base)); - setPalette(pl); - m_layout = new QVBoxLayout(this); - m_layout->setSpacing(0); - m_layout->setContentsMargins(0, 0, 0, 0); -} - -/** - * @brief 添加窗口不带标题 - * @param w - */ -void SARibbonGalleryViewport::addWidget(QWidget* w) -{ - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(w); -} - -/** - * @brief 添加窗口,带标题 - * @param w - * @param title - */ -void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title) -{ - QLabel* label = new QLabel(this); - label->setText(title); - label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(label); - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - m_layout->addWidget(w); - _widgetToTitleLable[ w ] = label; -} - -/** - * @brief 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 - * @param g - * @return 如果没有管理group,将返回nullptr - */ -QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w) -{ - return _widgetToTitleLable.value(w, nullptr); -} - -/** - * @brief 移除窗口 - * @param w - */ -void SARibbonGalleryViewport::removeWidget(QWidget* w) -{ - QLabel* label = titleLabel(w); - if (label) { - m_layout->removeWidget(label); - } - m_layout->removeWidget(w); -} - -/** - * @brief widget的标题改变 - * @param g - * @param title - */ -void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title) -{ - QLabel* l = titleLabel(w); - if (l) { - l->setText(title); - } -} - -//=================================================== -// SARibbonGallery -//=================================================== - -SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this)) -{ - d_ptr->init(); - setFrameShape(QFrame::Box); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setMinimumWidth(200); -} - -SARibbonGallery::~SARibbonGallery() -{ -} - -QSize SARibbonGallery::sizeHint() const -{ - return (QSize(100, 62)); -} - -/** - * @brief 获取一个空白SARibbonGalleryGroup - * @return - */ -SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup() -{ - SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); - addGalleryGroup(group); - return (group); -} - -/** - * @brief 添加一组SARibbonGalleryGroup - * @param group - */ -void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group) -{ - group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - SARibbonGalleryViewport* viewport = ensureGetPopupViewPort(); - viewport->addWidget(group, group->groupTitle()); - connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked); - connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) { - viewport->onTitleChanged(group, t); - }); - connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered); - connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered); - setCurrentViewGroup(group); -} - -/** - * @brief 添加一组actions - * @param title actions组的名字 - * @param actions - * @return 返回SARibbonGalleryGroup,用户可以通过修改SARibbonGalleryGroup属性控制其显示方法 - */ -SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions) -{ - SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); - if (!title.isEmpty()) { - group->setGroupTitle(title); - } - group->addActionItemList(actions); - addGalleryGroup(group); - return (group); -} - -void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group) -{ - d_ptr->setViewPort(group); - QApplication::postEvent(this, new QResizeEvent(size(), size())); -} - -SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const -{ - return (d_ptr->mViewportGroup); -} - -/** - * @brief 获取弹出窗口 - * @return - */ -SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const -{ - return d_ptr->mPopupWidget; -} - -/** - * @brief 设置最右边三个控制按钮的最大宽度(默认15) - * @param w - */ -void SARibbonGallery::setGalleryButtonMaximumWidth(int w) -{ - SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w; -} - -/** - * @brief 上翻页 - */ -void SARibbonGallery::pageDown() -{ - if (d_ptr->mViewportGroup) { - QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); - int v = vscrollBar->value(); - v += vscrollBar->singleStep(); - vscrollBar->setValue(v); - } -} - -/** - * @brief 下翻页 - */ -void SARibbonGallery::pageUp() -{ - if (d_ptr->mViewportGroup) { - QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); - int v = vscrollBar->value(); - v -= vscrollBar->singleStep(); - vscrollBar->setValue(v); - } -} - -/** - * @brief 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 - */ -void SARibbonGallery::showMoreDetail() -{ - if (nullptr == d_ptr->mPopupWidget) { - return; - } - QSize popupMenuSize = d_ptr->mPopupWidget->sizeHint(); - QPoint start = mapToGlobal(QPoint(0, 0)); - - int width = d_ptr->mViewportGroup->width(); // viewport - - width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); // scrollbar - d_ptr->mPopupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height()); - d_ptr->mPopupWidget->show(); -} - -void SARibbonGallery::onItemClicked(const QModelIndex& index) -{ - QObject* obj = sender(); - SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj); - if (group) { - setCurrentViewGroup(group); - SARibbonGalleryGroup* curGroup = currentViewGroup(); - curGroup->scrollTo(index); - curGroup->setCurrentIndex(index); - } -} - -void SARibbonGallery::onTriggered(QAction* action) -{ - Q_UNUSED(action); - // 点击后关闭弹出窗口 - if (d_ptr->mPopupWidget) { - if (d_ptr->mPopupWidget->isVisible()) { - d_ptr->mPopupWidget->hide(); - } - } -} - -SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort() -{ - if (nullptr == d_ptr->mPopupWidget) { - d_ptr->createPopupWidget(); - } - return (d_ptr->mPopupWidget); -} - -void SARibbonGallery::resizeEvent(QResizeEvent* event) -{ - QFrame::resizeEvent(event); - // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸 - int h = layout()->contentsRect().height(); - if (d_ptr->mViewportGroup) { - h = d_ptr->mViewportGroup->height(); - d_ptr->mViewportGroup->recalcGridSize(); - } - if (d_ptr->mPopupWidget) { - QLayout* lay = d_ptr->mPopupWidget->layout(); - if (!lay) { - return; - } - int c = lay->count(); - for (int i = 0; i < c; ++i) { - QLayoutItem* item = lay->itemAt(i); - if (!item) { - continue; - } - QWidget* w = item->widget(); - if (!w) { - continue; - } - SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w); - if (!g) { - continue; - } - g->recalcGridSize(h); - } - } -} - -void SARibbonGallery::paintEvent(QPaintEvent* event) -{ - QFrame::paintEvent(event); -} - - +#include "SARibbonGallery.h" +#include "SARibbonControlButton.h" +#include +#include +#define ICON_ARROW_UP QIcon(":/image/resource/ArrowUp.png") +#define ICON_ARROW_DOWN QIcon(":/image/resource/ArrowDown.png") +#define ICON_ARROW_MORE QIcon(":/image/resource/ArrowMore.png") +#include +#include +#include +#include +#include +#include "SARibbonElementManager.h" +#include + +/** + * @brief The SARibbonGalleryPrivate class + */ +class SARibbonGallery::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery) +public: + static int sGalleryButtonMaximumWidth; + SARibbonGalleryButton* mButtonUp { nullptr }; + SARibbonGalleryButton* mButtonDown { nullptr }; + SARibbonGalleryButton* mButtonMore { nullptr }; +#if 0 + SARibbonMenu *popupWidget; +#else + SARibbonGalleryViewport* mPopupWidget { nullptr }; +#endif + SARibbonGalleryGroup* mViewportGroup { nullptr }; + QBoxLayout* mButtonLayout { nullptr }; + QBoxLayout* mLayout { nullptr }; + PrivateData(SARibbonGallery* p) : q_ptr(p) + { + } + + void init() + { + mButtonUp = new SARibbonGalleryButton(q_ptr); + mButtonDown = new SARibbonGalleryButton(q_ptr); + mButtonMore = new SARibbonGalleryButton(q_ptr); + mButtonUp->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonDown->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonMore->setToolButtonStyle(Qt::ToolButtonIconOnly); + mButtonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + mButtonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp")); + mButtonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown")); + mButtonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore")); + mButtonUp->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonDown->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonMore->setMaximumWidth(sGalleryButtonMaximumWidth); + mButtonUp->setIcon(ICON_ARROW_UP); + mButtonDown->setIcon(ICON_ARROW_DOWN); + mButtonMore->setIcon(ICON_ARROW_MORE); + q_ptr->connect(mButtonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp); + q_ptr->connect(mButtonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown); + q_ptr->connect(mButtonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail); + // 信号转发 + q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered); + mPopupWidget = nullptr; + mViewportGroup = nullptr; + mButtonLayout = new QBoxLayout(QBoxLayout::TopToBottom); + mButtonLayout->setSpacing(0); + mButtonLayout->setContentsMargins(0, 0, 0, 0); + mButtonLayout->addWidget(mButtonUp); + mButtonLayout->addWidget(mButtonDown); + mButtonLayout->addWidget(mButtonMore); + mLayout = new QBoxLayout(QBoxLayout::RightToLeft); + mLayout->setSpacing(0); + mLayout->setContentsMargins(0, 0, 0, 0); + mLayout->addLayout(mButtonLayout); + mLayout->addStretch(); + q_ptr->setLayout(mLayout); + } + + bool isValid() const + { + return (q_ptr != nullptr); + } + + void createPopupWidget() + { + if (nullptr == mPopupWidget) { +#if 0 + popupWidget = new SARibbonMenu(Parent); +#else + mPopupWidget = new SARibbonGalleryViewport(q_ptr); +#endif + } + } + + void setViewPort(SARibbonGalleryGroup* v) + { + if (nullptr == mViewportGroup) { + mViewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr); + mLayout->addWidget(mViewportGroup, 1); + } + mViewportGroup->setRecalcGridSizeBlock(true); + mViewportGroup->setGalleryGroupStyle(v->galleryGroupStyle()); + mViewportGroup->setDisplayRow(v->displayRow()); + mViewportGroup->setSpacing(v->spacing()); + mViewportGroup->setGridMaximumWidth(v->gridMaximumWidth()); + mViewportGroup->setGridMinimumWidth(v->gridMinimumWidth()); + mViewportGroup->setRecalcGridSizeBlock(false); + mViewportGroup->recalcGridSize(mViewportGroup->height()); + mViewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + mViewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + mViewportGroup->setModel(v->model()); + mViewportGroup->show(); + } +}; + +// 静态变量初始化 + +/** + * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth + */ +int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15; + +//=================================================== +// SARibbonGalleryButton +//=================================================== + +SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonGalleryButton::~SARibbonGalleryButton() +{ +} +//=================================================== +// SARibbonGalleryViewport +//=================================================== + +SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent) +{ + setWindowFlags(Qt::Popup); + QPalette pl = palette(); + pl.setBrush(QPalette::Window, pl.brush(QPalette::Base)); + setPalette(pl); + m_layout = new QVBoxLayout(this); + m_layout->setSpacing(0); + m_layout->setContentsMargins(0, 0, 0, 0); +} + +/** + * @brief 添加窗口不带标题 + * @param w + */ +void SARibbonGalleryViewport::addWidget(QWidget* w) +{ + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); +} + +/** + * @brief 添加窗口,带标题 + * @param w + * @param title + */ +void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title) +{ + QLabel* label = new QLabel(this); + label->setText(title); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(label); + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); + _widgetToTitleLable[ w ] = label; +} + +/** + * @brief 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + * @param g + * @return 如果没有管理group,将返回nullptr + */ +QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w) +{ + return _widgetToTitleLable.value(w, nullptr); +} + +/** + * @brief 移除窗口 + * @param w + */ +void SARibbonGalleryViewport::removeWidget(QWidget* w) +{ + QLabel* label = titleLabel(w); + if (label) { + m_layout->removeWidget(label); + } + m_layout->removeWidget(w); +} + +/** + * @brief widget的标题改变 + * @param g + * @param title + */ +void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title) +{ + QLabel* l = titleLabel(w); + if (l) { + l->setText(title); + } +} + +//=================================================== +// SARibbonGallery +//=================================================== + +SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this)) +{ + d_ptr->init(); + setFrameShape(QFrame::Box); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumWidth(200); +} + +SARibbonGallery::~SARibbonGallery() +{ +} + +QSize SARibbonGallery::sizeHint() const +{ + return (QSize(100, 62)); +} + +/** + * @brief 获取一个空白SARibbonGalleryGroup + * @return + */ +SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup() +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + addGalleryGroup(group); + return (group); +} + +/** + * @brief 添加一组SARibbonGalleryGroup + * @param group + */ +void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group) +{ + group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + SARibbonGalleryViewport* viewport = ensureGetPopupViewPort(); + viewport->addWidget(group, group->groupTitle()); + connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked); + connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) { + viewport->onTitleChanged(group, t); + }); + connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered); + connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered); + setCurrentViewGroup(group); +} + +/** + * @brief 添加一组actions + * @param title actions组的名字 + * @param actions + * @return 返回SARibbonGalleryGroup,用户可以通过修改SARibbonGalleryGroup属性控制其显示方法 + */ +SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions) +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + if (!title.isEmpty()) { + group->setGroupTitle(title); + } + group->addActionItemList(actions); + addGalleryGroup(group); + return (group); +} + +void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group) +{ + d_ptr->setViewPort(group); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const +{ + return (d_ptr->mViewportGroup); +} + +/** + * @brief 获取弹出窗口 + * @return + */ +SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const +{ + return d_ptr->mPopupWidget; +} + +/** + * @brief 设置最右边三个控制按钮的最大宽度(默认15) + * @param w + */ +void SARibbonGallery::setGalleryButtonMaximumWidth(int w) +{ + SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w; +} + +/** + * @brief 上翻页 + */ +void SARibbonGallery::pageDown() +{ + if (d_ptr->mViewportGroup) { + QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v += vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 下翻页 + */ +void SARibbonGallery::pageUp() +{ + if (d_ptr->mViewportGroup) { + QScrollBar* vscrollBar = d_ptr->mViewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v -= vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + */ +void SARibbonGallery::showMoreDetail() +{ + if (nullptr == d_ptr->mPopupWidget) { + return; + } + QSize popupMenuSize = d_ptr->mPopupWidget->sizeHint(); + QPoint start = mapToGlobal(QPoint(0, 0)); + + int width = d_ptr->mViewportGroup->width(); // viewport + + width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); // scrollbar + d_ptr->mPopupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height()); + d_ptr->mPopupWidget->show(); +} + +void SARibbonGallery::onItemClicked(const QModelIndex& index) +{ + QObject* obj = sender(); + SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj); + if (group) { + setCurrentViewGroup(group); + SARibbonGalleryGroup* curGroup = currentViewGroup(); + curGroup->scrollTo(index); + curGroup->setCurrentIndex(index); + } +} + +void SARibbonGallery::onTriggered(QAction* action) +{ + Q_UNUSED(action); + // 点击后关闭弹出窗口 + if (d_ptr->mPopupWidget) { + if (d_ptr->mPopupWidget->isVisible()) { + d_ptr->mPopupWidget->hide(); + } + } +} + +SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort() +{ + if (nullptr == d_ptr->mPopupWidget) { + d_ptr->createPopupWidget(); + } + return (d_ptr->mPopupWidget); +} + +void SARibbonGallery::resizeEvent(QResizeEvent* event) +{ + QFrame::resizeEvent(event); + // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸 + int h = layout()->contentsRect().height(); + if (d_ptr->mViewportGroup) { + h = d_ptr->mViewportGroup->height(); + d_ptr->mViewportGroup->recalcGridSize(); + } + if (d_ptr->mPopupWidget) { + QLayout* lay = d_ptr->mPopupWidget->layout(); + if (!lay) { + return; + } + int c = lay->count(); + for (int i = 0; i < c; ++i) { + QLayoutItem* item = lay->itemAt(i); + if (!item) { + continue; + } + QWidget* w = item->widget(); + if (!w) { + continue; + } + SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w); + if (!g) { + continue; + } + g->recalcGridSize(h); + } + } +} + +void SARibbonGallery::paintEvent(QPaintEvent* event) +{ + QFrame::paintEvent(event); +} + + diff --git a/src/SARibbonBar/SARibbonGallery.h b/src/SARibbonBar/SARibbonGallery.h index 09cb1b71..f9929ab1 100644 --- a/src/SARibbonBar/SARibbonGallery.h +++ b/src/SARibbonBar/SARibbonGallery.h @@ -1,125 +1,125 @@ -#ifndef SARIBBONGALLERY_H -#define SARIBBONGALLERY_H -#include "SARibbonGlobal.h" -#include -#include -#include "SARibbonGalleryGroup.h" -#include -class QLabel; -class QVBoxLayout; -class SARibbonGalleryViewport; - -/** - * @brief 针对SARibbonGallery控件的按钮 - */ -class SA_RIBBON_EXPORT SARibbonGalleryButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonGalleryButton(QWidget* parent = nullptr); - ~SARibbonGalleryButton(); -}; - -/** - * @brief Gallery控件 - * - * Gallery控件是由一个当前激活的@sa SARibbonGalleryGroup 和弹出的 @sa SARibbonGalleryViewport 组成 - * - * @sa SARibbonGalleryGroup 是继承@sa QListView actions通过icon展示出来,相关的属性可以按照QListView设置 - * - * @sa SARibbonGalleryViewport 是一个内部有垂直布局的窗体,在弹出激活时,把管理的SARibbonGalleryGroup都展示出来 - * - * 示例如下: - * @code - * SARibbonGallery* gallery = pannel1->addGallery(); - * QList< QAction* > galleryActions; - * ...create many actions ... - * SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); - * galleryActions.clear(); - * ...create many actions ... - * gallery->addCategoryActions(tr("Apps"), galleryActions); - * gallery->setCurrentViewGroup(group1); - * @endcode - */ -class SA_RIBBON_EXPORT SARibbonGallery : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonGallery) -public: - explicit SARibbonGallery(QWidget* parent = nullptr); - virtual ~SARibbonGallery(); - virtual QSize sizeHint() const override; - // 添加一个GalleryGroup - SARibbonGalleryGroup* addGalleryGroup(); - // 添加一个GalleryGroup - void addGalleryGroup(SARibbonGalleryGroup* group); - // 快速添加一组actions - SARibbonGalleryGroup* addCategoryActions(const QString& title, QList< QAction* > actions); - // 设置当前显示的SARibbonGalleryGroup - void setCurrentViewGroup(SARibbonGalleryGroup* group); - // 获取当前显示的SARibbonGalleryGroup - SARibbonGalleryGroup* currentViewGroup() const; - // 获取弹出窗口指针 - SARibbonGalleryViewport* getPopupViewPort() const; - -public: - // 设置最右边三个控制按钮的最大宽度(默认15) - static void setGalleryButtonMaximumWidth(int w); -Q_SIGNALS: - /** - * @brief 转发管理的SARibbonGalleryGroup::triggered - * 所有加入SARibbonGallery的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup - * @param action - */ - void triggered(QAction* action); - /** - * @brief 转发管理的SARibbonGalleryGroup::hovered - * @note 此属性需要确保SARibbonGalleryGroup::setMouseTracking(true) - * @param action - */ - void hovered(QAction* action); - -public Q_SLOTS: - // 上翻页 - virtual void pageUp(); - // 下翻页 - virtual void pageDown(); - // 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 - virtual void showMoreDetail(); -protected Q_SLOTS: - void onItemClicked(const QModelIndex& index); - virtual void onTriggered(QAction* action); - -private: - SARibbonGalleryViewport* ensureGetPopupViewPort(); - -protected: - void resizeEvent(QResizeEvent* event) override; - void paintEvent(QPaintEvent* event) override; -}; - -/// -/// \brief SARibbonGallery的Viewport class -/// -class SARibbonGalleryViewport : public QWidget -{ - Q_OBJECT -public: - SARibbonGalleryViewport(QWidget* parent); - // 添加窗口不带标题 - void addWidget(QWidget* w); - // 添加窗口,带标题 - void addWidget(QWidget* w, const QString& title); - // 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 - QLabel* titleLabel(QWidget* w); - // 移除窗口 - void removeWidget(QWidget* w); -public Q_SLOTS: - void onTitleChanged(QWidget* w, const QString& title); - -private: - QVBoxLayout* m_layout; - QMap< QWidget*, QLabel* > _widgetToTitleLable; ///< QWidget和lable的对应 -}; - -#endif // SARIBBONGALLERY_H +#ifndef SARIBBONGALLERY_H +#define SARIBBONGALLERY_H +#include "SARibbonGlobal.h" +#include +#include +#include "SARibbonGalleryGroup.h" +#include +class QLabel; +class QVBoxLayout; +class SARibbonGalleryViewport; + +/** + * @brief 针对SARibbonGallery控件的按钮 + */ +class SA_RIBBON_EXPORT SARibbonGalleryButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonGalleryButton(QWidget* parent = nullptr); + ~SARibbonGalleryButton(); +}; + +/** + * @brief Gallery控件 + * + * Gallery控件是由一个当前激活的@sa SARibbonGalleryGroup 和弹出的 @sa SARibbonGalleryViewport 组成 + * + * @sa SARibbonGalleryGroup 是继承@sa QListView actions通过icon展示出来,相关的属性可以按照QListView设置 + * + * @sa SARibbonGalleryViewport 是一个内部有垂直布局的窗体,在弹出激活时,把管理的SARibbonGalleryGroup都展示出来 + * + * 示例如下: + * @code + * SARibbonGallery* gallery = pannel1->addGallery(); + * QList< QAction* > galleryActions; + * ...create many actions ... + * SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); + * galleryActions.clear(); + * ...create many actions ... + * gallery->addCategoryActions(tr("Apps"), galleryActions); + * gallery->setCurrentViewGroup(group1); + * @endcode + */ +class SA_RIBBON_EXPORT SARibbonGallery : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonGallery) +public: + explicit SARibbonGallery(QWidget* parent = nullptr); + virtual ~SARibbonGallery(); + virtual QSize sizeHint() const override; + // 添加一个GalleryGroup + SARibbonGalleryGroup* addGalleryGroup(); + // 添加一个GalleryGroup + void addGalleryGroup(SARibbonGalleryGroup* group); + // 快速添加一组actions + SARibbonGalleryGroup* addCategoryActions(const QString& title, QList< QAction* > actions); + // 设置当前显示的SARibbonGalleryGroup + void setCurrentViewGroup(SARibbonGalleryGroup* group); + // 获取当前显示的SARibbonGalleryGroup + SARibbonGalleryGroup* currentViewGroup() const; + // 获取弹出窗口指针 + SARibbonGalleryViewport* getPopupViewPort() const; + +public: + // 设置最右边三个控制按钮的最大宽度(默认15) + static void setGalleryButtonMaximumWidth(int w); +Q_SIGNALS: + /** + * @brief 转发管理的SARibbonGalleryGroup::triggered + * 所有加入SARibbonGallery的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @param action + */ + void triggered(QAction* action); + /** + * @brief 转发管理的SARibbonGalleryGroup::hovered + * @note 此属性需要确保SARibbonGalleryGroup::setMouseTracking(true) + * @param action + */ + void hovered(QAction* action); + +public Q_SLOTS: + // 上翻页 + virtual void pageUp(); + // 下翻页 + virtual void pageDown(); + // 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + virtual void showMoreDetail(); +protected Q_SLOTS: + void onItemClicked(const QModelIndex& index); + virtual void onTriggered(QAction* action); + +private: + SARibbonGalleryViewport* ensureGetPopupViewPort(); + +protected: + void resizeEvent(QResizeEvent* event) override; + void paintEvent(QPaintEvent* event) override; +}; + +/// +/// \brief SARibbonGallery的Viewport class +/// +class SARibbonGalleryViewport : public QWidget +{ + Q_OBJECT +public: + SARibbonGalleryViewport(QWidget* parent); + // 添加窗口不带标题 + void addWidget(QWidget* w); + // 添加窗口,带标题 + void addWidget(QWidget* w, const QString& title); + // 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + QLabel* titleLabel(QWidget* w); + // 移除窗口 + void removeWidget(QWidget* w); +public Q_SLOTS: + void onTitleChanged(QWidget* w, const QString& title); + +private: + QVBoxLayout* m_layout; + QMap< QWidget*, QLabel* > _widgetToTitleLable; ///< QWidget和lable的对应 +}; + +#endif // SARIBBONGALLERY_H diff --git a/src/SARibbonBar/SARibbonGlobal.h b/src/SARibbonBar/SARibbonGlobal.h index 1d051ddb..36f62c8a 100644 --- a/src/SARibbonBar/SARibbonGlobal.h +++ b/src/SARibbonBar/SARibbonGlobal.h @@ -1,149 +1,149 @@ -#ifndef SARIBBONGLOBAL_H -#define SARIBBONGLOBAL_H -#include -#include -#include -#include "SARibbonBarVersionInfo.h" -class QWidget; - -#ifndef SA_RIBBON_BAR_NO_EXPORT -#if defined(SA_RIBBON_BAR_MAKE_LIB) // 定义此宏将构建library -#define SA_RIBBON_EXPORT Q_DECL_EXPORT -#else -#define SA_RIBBON_EXPORT Q_DECL_IMPORT -#endif -#endif -#ifndef SA_RIBBON_EXPORT -#define SA_RIBBON_EXPORT -#endif - -/** - * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 - */ -#ifndef SA_RIBBON_DECLARE_PRIVATE -#define SA_RIBBON_DECLARE_PRIVATE(classname) \ - class PrivateData; \ - friend class classname::PrivateData; \ - std::unique_ptr< PrivateData > d_ptr; -#endif -/** - * @def 模仿Q_DECLARE_PUBLIC - */ -#ifndef SA_RIBBON_DECLARE_PUBLIC -#define SA_RIBBON_DECLARE_PUBLIC(classname) \ - friend class classname; \ - classname* q_ptr { nullptr }; \ - PrivateData(const PrivateData&) = delete; \ - PrivateData& operator=(const PrivateData&) = delete; -#endif - -/** - * @brief 定义Ribbon的对其方式,目前支持左对齐和居中对其 - * - * @note 如果你编译器提示:成员声明的限定名称非法,那么留意一下文件换行是否为LF,如果是把文件换行改为CRLF - */ -enum class SARibbonAlignment -{ - AlignLeft, ///< 左对齐,tab栏左对齐,同时category也是左对齐 - AlignCenter ///< 居中对其,tab栏居中对齐,同时category也是居中对齐 -}; - -/** - * @brief ribbon主题 - * - * 注意,由于有些qss的尺寸,在C++代码中无法获取到,因此针对用户自定义的qss主题,有些尺寸是需要手动设置进去的 - * - * 例如ribbon tab的margin信息,在QTabBar是无法获取到,而这个影响了SARibbonContextCategory的绘制, - * 因此,在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - */ -enum class SARibbonTheme -{ - RibbonThemeOffice2013, ///< office2013主题 - RibbonThemeOffice2016Blue, ///< office2016-蓝色主题 - RibbonThemeOffice2021Blue, ///< office2021-蓝色主题 - RibbonThemeWindows7, ///< win7主题 - RibbonThemeDark, ///< 暗色主题 - RibbonThemeDark2 -}; - -/** - * @def 属性,用于标记是否可以进行自定义,用于动态设置到@ref SARibbonCategory 和@ref SARibbonPannel - * 值为bool,在为true时,可以通过@ref SARibbonCustomizeWidget 改变这个SARibbonCategory和SARibbonPannel的布局, - * 默认不会有此属性,仅在有此属性且为true时才会在SARibbonCustomizeWidget中能显示为可设置 - */ -#ifndef SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE -#define SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE "_sa_isCanCustomize" -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) -#ifndef SA_FONTMETRICS_WIDTH -#define SA_FONTMETRICS_WIDTH(fm, str) fm.horizontalAdvance(str) -#endif -#else -#ifndef SA_FONTMETRICS_WIDTH -#define SA_FONTMETRICS_WIDTH(fm, str) fm.width(str) -#endif -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT -#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPosition().toPoint() -#endif -#else -#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT -#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPos() -#endif -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#ifndef SA_HOVEREVENT_POS_POINT -#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->position().toPoint() -#endif -#else -#ifndef SA_HOVEREVENT_POS_POINT -#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->pos() -#endif -#endif - -/** - @def 定义此宏,将打印和尺寸刷新相关的信息 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_SIZE_HINT -#define SA_DEBUG_PRINT_SIZE_HINT 0 -#endif - -/** - @def 定义此宏,将打印事件 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_EVENT -#define SA_DEBUG_PRINT_EVENT 0 -#endif - -/** - @def 定义此宏,qDebug将支持SARibbonBar的属性打印 - - 仅用于调试 - */ -#ifndef SA_DEBUG_PRINT_SARIBBONBAR -#define SA_DEBUG_PRINT_SARIBBONBAR 0 -#endif - -/** - * @brief 全局的设置ribbon theme函数 - * - * 之所以提供此函数,是因为在某些情况下,SARibbonBar不用在SARibbonMainWindow情况下的时候,也需要设置主题, - * 但主题设置是在SARibbonMainWindow下的为了能在非SARibbonMainWindow下也能设置主题,这里提供@sa sa_set_ribbon_theme函数, - * 可以这样使用: - * @code - * @endcode - * - * @param w - * @param theme - */ -void SA_RIBBON_EXPORT sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme); - -#endif // SARIBBONGLOBAL_H +#ifndef SARIBBONGLOBAL_H +#define SARIBBONGLOBAL_H +#include +#include +#include +#include "SARibbonBarVersionInfo.h" +class QWidget; + +#ifndef SA_RIBBON_BAR_NO_EXPORT +#if defined(SA_RIBBON_BAR_MAKE_LIB) // 定义此宏将构建library +#define SA_RIBBON_EXPORT Q_DECL_EXPORT +#else +#define SA_RIBBON_EXPORT Q_DECL_IMPORT +#endif +#endif +#ifndef SA_RIBBON_EXPORT +#define SA_RIBBON_EXPORT +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_RIBBON_DECLARE_PRIVATE +#define SA_RIBBON_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + std::unique_ptr< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_RIBBON_DECLARE_PUBLIC +#define SA_RIBBON_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; \ + PrivateData(const PrivateData&) = delete; \ + PrivateData& operator=(const PrivateData&) = delete; +#endif + +/** + * @brief 定义Ribbon的对其方式,目前支持左对齐和居中对其 + * + * @note 如果你编译器提示:成员声明的限定名称非法,那么留意一下文件换行是否为LF,如果是把文件换行改为CRLF + */ +enum class SARibbonAlignment +{ + AlignLeft, ///< 左对齐,tab栏左对齐,同时category也是左对齐 + AlignCenter ///< 居中对其,tab栏居中对齐,同时category也是居中对齐 +}; + +/** + * @brief ribbon主题 + * + * 注意,由于有些qss的尺寸,在C++代码中无法获取到,因此针对用户自定义的qss主题,有些尺寸是需要手动设置进去的 + * + * 例如ribbon tab的margin信息,在QTabBar是无法获取到,而这个影响了SARibbonContextCategory的绘制, + * 因此,在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + */ +enum class SARibbonTheme +{ + RibbonThemeOffice2013, ///< office2013主题 + RibbonThemeOffice2016Blue, ///< office2016-蓝色主题 + RibbonThemeOffice2021Blue, ///< office2021-蓝色主题 + RibbonThemeWindows7, ///< win7主题 + RibbonThemeDark, ///< 暗色主题 + RibbonThemeDark2 +}; + +/** + * @def 属性,用于标记是否可以进行自定义,用于动态设置到@ref SARibbonCategory 和@ref SARibbonPannel + * 值为bool,在为true时,可以通过@ref SARibbonCustomizeWidget 改变这个SARibbonCategory和SARibbonPannel的布局, + * 默认不会有此属性,仅在有此属性且为true时才会在SARibbonCustomizeWidget中能显示为可设置 + */ +#ifndef SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE +#define SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE "_sa_isCanCustomize" +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.horizontalAdvance(str) +#endif +#else +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.width(str) +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPosition().toPoint() +#endif +#else +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPos() +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->position().toPoint() +#endif +#else +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->pos() +#endif +#endif + +/** + @def 定义此宏,将打印和尺寸刷新相关的信息 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SIZE_HINT +#define SA_DEBUG_PRINT_SIZE_HINT 0 +#endif + +/** + @def 定义此宏,将打印事件 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_EVENT +#define SA_DEBUG_PRINT_EVENT 0 +#endif + +/** + @def 定义此宏,qDebug将支持SARibbonBar的属性打印 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SARIBBONBAR +#define SA_DEBUG_PRINT_SARIBBONBAR 0 +#endif + +/** + * @brief 全局的设置ribbon theme函数 + * + * 之所以提供此函数,是因为在某些情况下,SARibbonBar不用在SARibbonMainWindow情况下的时候,也需要设置主题, + * 但主题设置是在SARibbonMainWindow下的为了能在非SARibbonMainWindow下也能设置主题,这里提供@sa sa_set_ribbon_theme函数, + * 可以这样使用: + * @code + * @endcode + * + * @param w + * @param theme + */ +void SA_RIBBON_EXPORT sa_set_ribbon_theme(QWidget* w, SARibbonTheme theme); + +#endif // SARIBBONGLOBAL_H diff --git a/src/SARibbonBar/SARibbonLineWidgetContainer.cpp b/src/SARibbonBar/SARibbonLineWidgetContainer.cpp index 98c9f55b..9fa56518 100644 --- a/src/SARibbonBar/SARibbonLineWidgetContainer.cpp +++ b/src/SARibbonBar/SARibbonLineWidgetContainer.cpp @@ -1,57 +1,57 @@ -#include "SARibbonLineWidgetContainer.h" -#include - -SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget *par) : QWidget(par) - , m_innerWidget(nullptr) -{ - m_labelPrefix = new QLabel(this); - m_labelSuffix = new QLabel(this); - QHBoxLayout *lay = new QHBoxLayout(); - - lay->setContentsMargins(0, 0, 0, 0); - lay->setSpacing(0); - lay->addWidget(m_labelPrefix); - lay->addWidget(m_labelSuffix); - setLayout(lay); -} - -SARibbonLineWidgetContainer::~SARibbonLineWidgetContainer() -{ -} - - -void SARibbonLineWidgetContainer::setWidget(QWidget *innerWidget) -{ - QHBoxLayout *lay = static_cast(layout()); - - if (m_innerWidget) { - lay->replaceWidget(m_innerWidget, innerWidget); - }else{ - lay->insertWidget(1, innerWidget); - } - m_innerWidget = innerWidget; -} - - -void SARibbonLineWidgetContainer::setPrefix(const QString& str) -{ - m_labelPrefix->setText(str); -} - - -void SARibbonLineWidgetContainer::setSuffix(const QString& str) -{ - m_labelSuffix->setText(str); -} - - -QLabel *SARibbonLineWidgetContainer::labelPrefix() const -{ - return (m_labelPrefix); -} - - -QLabel *SARibbonLineWidgetContainer::labelSuffix() const -{ - return (m_labelSuffix); -} +#include "SARibbonLineWidgetContainer.h" +#include + +SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget *par) : QWidget(par) + , m_innerWidget(nullptr) +{ + m_labelPrefix = new QLabel(this); + m_labelSuffix = new QLabel(this); + QHBoxLayout *lay = new QHBoxLayout(); + + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + lay->addWidget(m_labelPrefix); + lay->addWidget(m_labelSuffix); + setLayout(lay); +} + +SARibbonLineWidgetContainer::~SARibbonLineWidgetContainer() +{ +} + + +void SARibbonLineWidgetContainer::setWidget(QWidget *innerWidget) +{ + QHBoxLayout *lay = static_cast(layout()); + + if (m_innerWidget) { + lay->replaceWidget(m_innerWidget, innerWidget); + }else{ + lay->insertWidget(1, innerWidget); + } + m_innerWidget = innerWidget; +} + + +void SARibbonLineWidgetContainer::setPrefix(const QString& str) +{ + m_labelPrefix->setText(str); +} + + +void SARibbonLineWidgetContainer::setSuffix(const QString& str) +{ + m_labelSuffix->setText(str); +} + + +QLabel *SARibbonLineWidgetContainer::labelPrefix() const +{ + return (m_labelPrefix); +} + + +QLabel *SARibbonLineWidgetContainer::labelSuffix() const +{ + return (m_labelSuffix); +} diff --git a/src/SARibbonBar/SARibbonLineWidgetContainer.h b/src/SARibbonBar/SARibbonLineWidgetContainer.h index 5f979d40..8930fdbe 100644 --- a/src/SARibbonBar/SARibbonLineWidgetContainer.h +++ b/src/SARibbonBar/SARibbonLineWidgetContainer.h @@ -1,44 +1,44 @@ -#ifndef SARIBBONLINEWIDGETCONTAINER_H -#define SARIBBONLINEWIDGETCONTAINER_H - -#include -#include -#include -#include "SARibbonGlobal.h" - -/** - * @brief 一个窗口容器,把窗口放置中间,前面后面都可以设置文本,主要用于放置在pannel上的小窗口 - * - * 实现如下效果: - * - * PrefixLabel|_Widget_|SuffixLabel - * - */ -class SA_RIBBON_EXPORT SARibbonLineWidgetContainer : public QWidget -{ -public: - explicit SARibbonLineWidgetContainer(QWidget* par = nullptr); - ~SARibbonLineWidgetContainer(); - //设置widget,不允许设置一个nullptr - void setWidget(QWidget* innerWidget); - - //设置前缀 - void setPrefix(const QString& str); - - //设置后缀 - void setSuffix(const QString& str); - - //前缀文本框 - QLabel* labelPrefix() const; - - //后缀文本框 - QLabel* labelSuffix() const; - -private: - //两个文本 - QLabel* m_labelPrefix; - QLabel* m_labelSuffix; - QWidget* m_innerWidget; -}; - -#endif // SARIBBONWIDGETCONTAINER_H +#ifndef SARIBBONLINEWIDGETCONTAINER_H +#define SARIBBONLINEWIDGETCONTAINER_H + +#include +#include +#include +#include "SARibbonGlobal.h" + +/** + * @brief 一个窗口容器,把窗口放置中间,前面后面都可以设置文本,主要用于放置在pannel上的小窗口 + * + * 实现如下效果: + * + * PrefixLabel|_Widget_|SuffixLabel + * + */ +class SA_RIBBON_EXPORT SARibbonLineWidgetContainer : public QWidget +{ +public: + explicit SARibbonLineWidgetContainer(QWidget* par = nullptr); + ~SARibbonLineWidgetContainer(); + //设置widget,不允许设置一个nullptr + void setWidget(QWidget* innerWidget); + + //设置前缀 + void setPrefix(const QString& str); + + //设置后缀 + void setSuffix(const QString& str); + + //前缀文本框 + QLabel* labelPrefix() const; + + //后缀文本框 + QLabel* labelSuffix() const; + +private: + //两个文本 + QLabel* m_labelPrefix; + QLabel* m_labelSuffix; + QWidget* m_innerWidget; +}; + +#endif // SARIBBONWIDGETCONTAINER_H diff --git a/src/SARibbonBar/SARibbonMenu.cpp b/src/SARibbonBar/SARibbonMenu.cpp index e1424a2e..49436d3c 100644 --- a/src/SARibbonBar/SARibbonMenu.cpp +++ b/src/SARibbonBar/SARibbonMenu.cpp @@ -1,50 +1,50 @@ -#include "SARibbonMenu.h" -#include - -SARibbonMenu::SARibbonMenu(QWidget *parent) : QMenu(parent) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); -} - - -SARibbonMenu::SARibbonMenu(const QString& title, QWidget *parent) : QMenu(title, parent) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); -} - -SARibbonMenu::~SARibbonMenu() -{ -} - - -QAction *SARibbonMenu::addRibbonMenu(SARibbonMenu *menu) -{ - return (QMenu::addMenu(menu)); -} - - -SARibbonMenu *SARibbonMenu::addRibbonMenu(const QString& title) -{ - SARibbonMenu *menu = new SARibbonMenu(title, this); - - return (menu); -} - - -SARibbonMenu *SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title) -{ - SARibbonMenu *menu = new SARibbonMenu(title, this); - - menu->setIcon(icon); - return (menu); -} - - -QAction *SARibbonMenu::addWidget(QWidget *w) -{ - QWidgetAction *action = new QWidgetAction(this); - - action->setDefaultWidget(w); - addAction(action); - return (action); -} +#include "SARibbonMenu.h" +#include + +SARibbonMenu::SARibbonMenu(QWidget *parent) : QMenu(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + + +SARibbonMenu::SARibbonMenu(const QString& title, QWidget *parent) : QMenu(title, parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +SARibbonMenu::~SARibbonMenu() +{ +} + + +QAction *SARibbonMenu::addRibbonMenu(SARibbonMenu *menu) +{ + return (QMenu::addMenu(menu)); +} + + +SARibbonMenu *SARibbonMenu::addRibbonMenu(const QString& title) +{ + SARibbonMenu *menu = new SARibbonMenu(title, this); + + return (menu); +} + + +SARibbonMenu *SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title) +{ + SARibbonMenu *menu = new SARibbonMenu(title, this); + + menu->setIcon(icon); + return (menu); +} + + +QAction *SARibbonMenu::addWidget(QWidget *w) +{ + QWidgetAction *action = new QWidgetAction(this); + + action->setDefaultWidget(w); + addAction(action); + return (action); +} diff --git a/src/SARibbonBar/SARibbonMenu.h b/src/SARibbonBar/SARibbonMenu.h index 060a7355..ab9ca188 100644 --- a/src/SARibbonBar/SARibbonMenu.h +++ b/src/SARibbonBar/SARibbonMenu.h @@ -1,23 +1,23 @@ -#ifndef SARIBBONMENU_H -#define SARIBBONMENU_H -#include "SARibbonGlobal.h" -#include - -/// -/// \brief 用在ribbon的menu -/// 可以通过StyleSheet设置样式而不影响QMenu -/// -class SA_RIBBON_EXPORT SARibbonMenu : public QMenu -{ - Q_OBJECT -public: - explicit SARibbonMenu(QWidget* parent = nullptr); - explicit SARibbonMenu(const QString& title, QWidget* parent = nullptr); - ~SARibbonMenu(); - QAction* addRibbonMenu(SARibbonMenu* menu); - SARibbonMenu* addRibbonMenu(const QString& title); - SARibbonMenu* addRibbonMenu(const QIcon& icon, const QString& title); - QAction* addWidget(QWidget* w); -}; - -#endif // SARIBBONMENU_H +#ifndef SARIBBONMENU_H +#define SARIBBONMENU_H +#include "SARibbonGlobal.h" +#include + +/// +/// \brief 用在ribbon的menu +/// 可以通过StyleSheet设置样式而不影响QMenu +/// +class SA_RIBBON_EXPORT SARibbonMenu : public QMenu +{ + Q_OBJECT +public: + explicit SARibbonMenu(QWidget* parent = nullptr); + explicit SARibbonMenu(const QString& title, QWidget* parent = nullptr); + ~SARibbonMenu(); + QAction* addRibbonMenu(SARibbonMenu* menu); + SARibbonMenu* addRibbonMenu(const QString& title); + SARibbonMenu* addRibbonMenu(const QIcon& icon, const QString& title); + QAction* addWidget(QWidget* w); +}; + +#endif // SARIBBONMENU_H diff --git a/src/SARibbonBar/SARibbonPannel.cpp b/src/SARibbonBar/SARibbonPannel.cpp index f7f02ed4..61874e75 100644 --- a/src/SARibbonBar/SARibbonPannel.cpp +++ b/src/SARibbonBar/SARibbonPannel.cpp @@ -1,1013 +1,1013 @@ -#include "SARibbonPannel.h" -#include "SARibbonCategory.h" -#include "SARibbonElementManager.h" -#include "SARibbonGallery.h" -#include "SARibbonPannelLayout.h" -#include "SARibbonPannelOptionButton.h" -#include "SARibbonToolButton.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SARibbonPannel_DEBUG_PRINT -#define SARibbonPannel_DEBUG_PRINT 0 -#endif - -#define HELP_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - QPen _pen(Qt::DashLine); \ - _pen.setColor(Qt::blue); \ - p.setPen(_pen); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) -//=============================================================== -// SARibbonPannelLabel -//=============================================================== - -SARibbonPannelLabel::SARibbonPannelLabel(QWidget* parent) : QLabel(parent) -{ -} - -//=============================================================== -// SARibbonPannel::PrivateData -//=============================================================== - -class SARibbonPannel::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonPannel) -public: - PrivateData(SARibbonPannel* p); - // 根据m_pannelLayoutMode返回gridLayout应该增加的行数 - int rowadded(); - void createLayout(); - // 获取layout - SARibbonPannelLayout* pannelLayout() const; - // 返回最后一个添加的action对应的button,前提是最后一个是toolbutton,否则返回nullptr - SARibbonToolButton* lastAddActionButton(); - // 重置labe的字体,这个主要是为了让pannel的标题字体能适应标题高度 - void resetTitleLabelFont(); - // 标题 - QString pannelName() const; - void setPannelName(const QString& title); - -public: - bool m_isCanCustomize { true }; ///< 记录是否可自定义 - SARibbonPannel::PannelLayoutMode m_pannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< pannel的布局模式,默认为3行模式ThreeRowMode - SARibbonPannelOptionButton* m_optionActionButton { nullptr }; ///< 标题栏的y距离 - SARibbonPannelLabel* m_label { nullptr }; -}; - -SARibbonPannel::PrivateData::PrivateData(SARibbonPannel* p) : q_ptr(p) -{ - createLayout(); -} - -int SARibbonPannel::PrivateData::rowadded() -{ - switch (m_pannelLayoutMode) { - case SARibbonPannel::ThreeRowMode: - return (2); - - case SARibbonPannel::TwoRowMode: - return (3); - - default: - break; - } - return (2); -} - -void SARibbonPannel::PrivateData::createLayout() -{ - m_label = new SARibbonPannelLabel(q_ptr); - m_label->setAlignment(Qt::AlignCenter); - SARibbonPannelLayout* lay = new SARibbonPannelLayout(q_ptr); - lay->setPannelTitleLabel(m_label); - lay->setSpacing(2); - lay->setContentsMargins(2, 2, 2, 2); -} - -SARibbonPannelLayout* SARibbonPannel::PrivateData::pannelLayout() const -{ - return qobject_cast< SARibbonPannelLayout* >(q_ptr->layout()); -} - -SARibbonToolButton* SARibbonPannel::PrivateData::lastAddActionButton() -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (qobject_cast< SARibbonToolButton* >(lay->lastWidget())); - } - return (nullptr); -} - -/** - * @brief 重置label的字体 - * - * @note 此函数必须在布局设置后调用 - */ -void SARibbonPannel::PrivateData::resetTitleLabelFont() -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - int h = lay->pannelTitleHeight(); - QFont f = q_ptr->font(); - f.setPixelSize(h * 0.8); - if (m_label) { - m_label->setFont(f); - } - } -} - -QString SARibbonPannel::PrivateData::pannelName() const -{ - if (m_label) { - return m_label->text(); - } - return (QString()); -} - -void SARibbonPannel::PrivateData::setPannelName(const QString& title) -{ - if (m_label) { - m_label->setText(title); - } -} - -//================================================== -// SARibbonPannel -//================================================== - -SARibbonPannel::SARibbonPannel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) -{ - setPannelLayoutMode(ThreeRowMode); -} - -SARibbonPannel::SARibbonPannel(const QString& name, QWidget* parent) - : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) -{ - setPannelLayoutMode(ThreeRowMode); - setPannelName(name); -} - -SARibbonPannel::~SARibbonPannel() -{ -} - -/** - * @brief 把action的行属性设置进action中,action自身携带了行属性 - * @param action - * @param rp - */ -void SARibbonPannel::setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp)); -} - -/** - * @brief 获取action的行属性 - * @param action - * @return - */ -SARibbonPannelItem::RowProportion SARibbonPannel::getActionRowProportionProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok); - - if (isok) { - return (static_cast< SARibbonPannelItem::RowProportion >(r)); - } - return (SARibbonPannelItem::Large); -} - -/** - * @brief 设置action的ToolButtonPopupMode属性 - * @param action - * @param popMode - */ -void SARibbonPannel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode)); -} - -/** - * @brief 获取action的ToolButtonPopupMode属性 - * @param action - * @return - */ -QToolButton::ToolButtonPopupMode SARibbonPannel::getActionToolButtonPopupModeProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok); - - if (isok) { - return (static_cast< QToolButton::ToolButtonPopupMode >(r)); - } - return (QToolButton::InstantPopup); -} - -/** - * @brief 设置action的ToolButtonStyle属性 - * @param action - * @param buttonStyle - */ -void SARibbonPannel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle) -{ - Q_CHECK_PTR(action); - action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle)); -} - -/** - * @brief 获取action的ToolButtonStyle属性 - * @param action - * @return - */ -Qt::ToolButtonStyle SARibbonPannel::getActionToolButtonStyleProperty(QAction* action) -{ - bool isok = false; - int r = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok); - - if (isok) { - return (static_cast< Qt::ToolButtonStyle >(r)); - } - return (Qt::ToolButtonIconOnly); -} - -/** - * @brief 添加action - * - * action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - * @param action - * @param rp 指定action的行占比 - * @param popMode 菜单弹出样式 - */ -void SARibbonPannel::addAction(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(action); - setActionRowProportionProperty(action, rp); - addAction(action); -} - -/** - * @brief 添加一个action - * @param act - * @param popMode 按钮的样式 - * @param rp action在pannel中的占位情况,默认是大图标 - */ -void SARibbonPannel::addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) -{ - Q_CHECK_PTR(act); - setActionRowProportionProperty(act, rp); - setActionToolButtonPopupModeProperty(act, popMode); - addAction(act); -} - -/** - @brief 添加大图标 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addLargeAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Large); -} - -/** - @brief 在三栏模式下,强制加为2栏action - @note 在两行模式下,Medium和Small等价 - 主要应用在ThreeRowMode下 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addMediumAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Medium); -} - -/** - @brief 添加小图标 - - action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton - @param action - */ -void SARibbonPannel::addSmallAction(QAction* action) -{ - addAction(action, SARibbonPannelItem::Small); -} - -void SARibbonPannel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Small); -} - -void SARibbonPannel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Large); -} - -void SARibbonPannel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) -{ - addAction(action, popMode, SARibbonPannelItem::Medium); -} - -/** - * @brief 生成并添加一个action - * - * 如果不对此action做操作,SARibbonPannel将管理此action - * - * @note action的父对象将设置为SARibbonPannel,SARibbonPannel在删除时将会删除子对象,会把这个action也删除, - * 如果不想此action也删除,需要对action重新设置父对象 - * - * @param text action的文字 - * @param icon action的图标 - * @param popMode 按钮的样式 - * @param rp action在pannel中的占位情况,默认是大图标 - * @return 返回添加的action - */ -QAction* SARibbonPannel::addAction(const QString& text, - const QIcon& icon, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp) -{ - QAction* action = new QAction(icon, text, this); - addAction(action, popMode, rp); - return (action); -} - -/** - * @brief 添加一个普通菜单 - * @param menu - * @param rp - * @param popMode,菜单弹出模式,默认InstantPopup模式 - * @return - */ -void SARibbonPannel::addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode) -{ - Q_CHECK_PTR(menu); - QAction* action = menu->menuAction(); - action->setIcon(menu->icon()); - action->setText(menu->title()); - action->setObjectName("action." + menu->objectName()); - addAction(action, popMode, rp); -} - -void SARibbonPannel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) -{ - addMenu(menu, SARibbonPannelItem::Large, popMode); -} - -void SARibbonPannel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) -{ - addMenu(menu, SARibbonPannelItem::Small, popMode); -} - -/** - * @brief 添加窗口 - * - * @param w - * @param rp - * @return 返回action(QWidgetAction) - * @note SARibbonPannel并不会管理此窗口内存,在delete SARibbonPannel时,此窗口如果父对象不是SARibbonPannel将不会被删除 - */ -QAction* SARibbonPannel::addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp) -{ - QWidgetAction* action = new QWidgetAction(this); - - action->setDefaultWidget(w); - action->setIcon(w->windowIcon()); - action->setText(w->windowTitle()); - action->setObjectName("action." + w->objectName()); - w->setAttribute(Qt::WA_Hover); - setActionRowProportionProperty(action, rp); - addAction(action); - return (action); -} - -/** - * @brief 添加窗口,占用ribbon的一行 - * @param w - * @return 返回action(QWidgetAction) - */ -QAction* SARibbonPannel::addSmallWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Small)); -} - -/** - * @brief 添加窗口,占用ribbon的一行 - * @param w - * @return - */ -QAction* SARibbonPannel::addMediumWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Medium)); -} - -/** - * @brief 添加窗口,占用所有行 - * @param w - * @return 返回action(QWidgetAction) - */ -QAction* SARibbonPannel::addLargeWidget(QWidget* w) -{ - return (addWidget(w, SARibbonPannelItem::Large)); -} - -/** - * @brief SARibbonPannel::addGallery - * @return - * @note SARibbonPannel将拥有SARibbonGallery的管理权 - */ -SARibbonGallery* SARibbonPannel::addGallery(bool expanding) -{ - SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this); - - addWidget(gallery, SARibbonPannelItem::Large); - if (expanding) { - setExpanding(expanding); - } - return (gallery); -} - -/** - * @brief 添加分割线 - * @param top 上边距 @default 6 - * @param bottom 下边距 @default 6 - */ -QAction* SARibbonPannel::addSeparator() -{ - QAction* action = new QAction(this); - - action->setSeparator(true); - setActionRowProportionProperty(action, SARibbonPannelItem::Large); - addAction(action); - return (action); -} - -/** - * @brief 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr - * @param action - * @return 如果action没有对应的button,就返回nullptr - */ -SARibbonToolButton* SARibbonPannel::actionToRibbonToolButton(QAction* action) -{ -#if 0 - SARibbonPannelLayout* lay = qobject_cast< SARibbonPannelLayout* >(layout()); - - if (lay) { - int index = lay->indexOf(action); - if (index == -1) { - return (nullptr); - } - QLayoutItem* item = lay->takeAt(index); - SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(item ? item->widget() : nullptr); - return (btn); - } - return (nullptr); -#else - for (auto obj : qAsConst(children())) { - if (obj->isWidgetType()) { - if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) { - if (btn->defaultAction() == action) { - return btn; - } - } - } - } - return (nullptr); -#endif -} - -/** - * @brief 获取pannel下面的所有toolbutton - * @return - */ -QList< SARibbonToolButton* > SARibbonPannel::ribbonToolButtons() const -{ - const QObjectList& objs = children(); - QList< SARibbonToolButton* > res; - - for (QObject* o : objs) { - SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o); - if (b) { - res.append(b); - } - } - return (res); -} - -/** - * @brief 设置PannelLayoutMode - * @param mode - */ -void SARibbonPannel::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode mode) -{ - if (d_ptr->m_pannelLayoutMode == mode) { - return; - } - d_ptr->m_pannelLayoutMode = mode; - updateItemGeometry(); -} - -SARibbonPannel::PannelLayoutMode SARibbonPannel::pannelLayoutMode() const -{ - return (d_ptr->m_pannelLayoutMode); -} - -/** - * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - * @param action - * @note 要去除OptionAction直接传入nullptr即可 - * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction - */ -void SARibbonPannel::setOptionAction(QAction* action) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setOptionAction(action); - } -} - -/** - * @brief 判断是否存在OptionAction - * @return 存在返回true - */ -bool SARibbonPannel::isHaveOptionAction() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->isHaveOptionAction(); - } - return false; -} - -QSize SARibbonPannel::sizeHint() const -{ - int shWidth = 500; - int shHeight = 100; - if (QLayout* lay = layout()) { - QSize laySize = layout()->sizeHint(); - shWidth = laySize.width(); - shHeight = laySize.height(); - } - return QSize(shWidth, shHeight); -} - -QSize SARibbonPannel::minimumSizeHint() const -{ - return (layout()->minimumSize()); -} - -void SARibbonPannel::setToolButtonIconSize(const QSize& s) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setToolButtonIconSize(s); - } - // 对已经管理的QToolButton设置为iconsize - iterate([ s ](SARibbonToolButton* t) -> bool { - t->setIconSize(s); - return true; - }); -} - -QSize SARibbonPannel::toolButtonIconSize() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->toolButtonIconSize(); - } - return QSize(); -} - -/** - * @brief 遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 - * @param fp - * @return 返回false说明迭代中途退出,返回true则表示迭代完全 - */ -bool SARibbonPannel::iterate(SARibbonPannel::FpRibbonToolButtonIterate fp) -{ - const QObjectList& ols = children(); - for (QObject* obj : ols) { - if (SARibbonToolButton* t = qobject_cast< SARibbonToolButton* >(obj)) { - if (!fp(t)) { - return false; - } - } - } - return true; -} - -/** - * @brief 把pannel设置为扩展模式,此时会撑大水平区域 - * @param isExpanding - */ -void SARibbonPannel::setExpanding(bool isExpanding) -{ - setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed); -} - -/** - * @brief 判断此pannel是否为(水平)扩展模式 - * @return 是扩展模式返回true - */ -bool SARibbonPannel::isExpanding() const -{ - QSizePolicy sp = sizePolicy(); - - return (sp.horizontalPolicy() == QSizePolicy::Expanding); -} - -/** - @brief 设置标题栏的高度 - @param h - */ -void SARibbonPannel::setTitleHeight(int h) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->setPannelTitleHeight(h); - } -} - -/** - * @brief 标题栏高度 - * @return - */ -int SARibbonPannel::titleHeight() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->pannelTitleHeight(); - } - return -1; -} - -/** - * @brief 判断是否显示标题,只有标题的高度被设置,才会显示标题 - * @return - */ -bool SARibbonPannel::isEnableShowTitle() const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->isEnableShowPannelTitle(); - } - return false; -} - -/** - * @brief 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 - * @param on - */ -void SARibbonPannel::setEnableShowTitle(bool on) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return lay->setEnableShowPannelTitle(on); - } -} - -/** - * @brief action对应的布局index,此操作一般用于移动,其他意义不大 - * @param act - * @return 没有查到返回-1 - */ -int SARibbonPannel::actionIndex(QAction* act) const -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (lay->indexByAction(act)); - } - return (-1); -} - -/** - * @brief 移动action - * @param from 要移动action的位置,通过@ref actionIndex 获取 - * @param to 要移动的位置 - */ -void SARibbonPannel::moveAction(int from, int to) -{ - if (SARibbonPannelLayout* lay = pannelLayout()) { - return (lay->move(from, to)); - } - updateGeometry(); // 通知layout进行重新布局 -} - -/** - * @brief 判断是否可以自定义 - * @return - */ -bool SARibbonPannel::isCanCustomize() const -{ - return (d_ptr->m_isCanCustomize); -} - -/** - * @brief 设置是否可以自定义 - * @param b - */ -void SARibbonPannel::setCanCustomize(bool b) -{ - d_ptr->m_isCanCustomize = b; -} - -/** - * @brief pannel名称 - * @return - */ -QString SARibbonPannel::pannelName() const -{ - return (d_ptr->pannelName()); -} - -/** - * @brief pannel的名称 - * @param title 标题 - * @note 此函数会触发@sa pannelNameChanged 信号 - */ -void SARibbonPannel::setPannelName(const QString& title) -{ - QString oldName = pannelName(); - if (oldName != title) { - d_ptr->setPannelName(title); - Q_EMIT pannelNameChanged(title); - } -} - -/** - * @brief 获取大图标的高度 - * @return - */ -int SARibbonPannel::largeButtonHeight() const -{ - const QMargins& mag = contentsMargins(); - return height() - mag.top() - mag.bottom() - titleHeight(); -} - -/** - * @brief 获取pannel layout - * @return - */ -SARibbonPannelLayout* SARibbonPannel::pannelLayout() const -{ - return d_ptr->pannelLayout(); -} - -/** - * @brief 更新布局 - */ -void SARibbonPannel::updateItemGeometry() -{ -#if SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "SARibbonPannel updateItemGeometry,pannelName=" << pannelName(); -#endif - // 此函数需要添加,否则SARibbonBar::setEnableWordWrap无法刷新按钮 - resetToolButtonSize(); - if (SARibbonPannelLayout* lay = pannelLayout()) { - lay->updateGeomArray(); - } -} - -/** - @brief 获取category指针,如果没有parent,或者不在category管理,返回nullptr - @return - */ -SARibbonCategory* SARibbonPannel::category() const -{ - return qobject_cast< SARibbonCategory* >(parent()); -} - -/** - @brief 获取ribbonBar指针,如果没有返回nullptr - @return - */ -SARibbonBar* SARibbonPannel::ribbonBar() const -{ - if (SARibbonCategory* c = category()) { - return c->ribbonBar(); - } - return nullptr; -} - -/** - * @brief 设置按钮之间的间隔 - * @param n - */ -void SARibbonPannel::setSpacing(int n) -{ - if (auto lay = layout()) { - lay->setSpacing(n); - } -} - -/** - * @brief 按钮之间的间隔 - * @return - */ -int SARibbonPannel::spacing() const -{ - if (auto lay = layout()) { - return lay->spacing(); - } - return 0; -} - -/** - * @brief pannel高度推荐 - * @param fm - * @param layMode - * @param pannelTitleHeight - * @return - */ -int SARibbonPannel::pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight) -{ - int textH = fm.lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 - switch (layMode) { - case SARibbonPannel::ThreeRowMode: { - // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 - return textH * 4.8 + pannelTitleHeight; - } break; - case SARibbonPannel::TwoRowMode: { - // 3=2*1.6 - return textH * 3.2 + pannelTitleHeight; - } break; - default: { - qWarning() << "unknow SARibbonPannel::PannelLayoutMode:" << layMode; - } - } - return (textH * 4.8 + pannelTitleHeight); -} - -/** - * @brief 重置按钮的尺寸,在布局改变后(尤其高度变更),按钮的尺寸需要手动变更 - */ -void SARibbonPannel::resetToolButtonSize() -{ - QList< SARibbonToolButton* > btns = ribbonToolButtons(); - - for (SARibbonToolButton* b : qAsConst(btns)) { - if ((nullptr == b)) { - continue; - } - b->updateRect(); - } -} - -bool SARibbonPannel::event(QEvent* e) -{ -#if SA_DEBUG_PRINT_EVENT - if (e->type() != QEvent::Paint) { - qDebug() << "SARibbonPannel event(" << e->type() << "),name=" << pannelName(); - } -#endif - // if (SARibbonPannelLayout* lay = pannelLayout()) { - // if (lay->isDirty() && e->type() == QEvent::LayoutRequest) { - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - // lay->m_dirty = false; - // } - // } - return QWidget::event(e); -} - -/** - * @brief 处理action的事件 - * - * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 - * - * ActionAdded时向布局请求,添加action,布局中同时触发了@ref SARibbonPannelLayout::createItem 函数 - * 此函数用于生成窗口,例如QRibbonToolButton - * - * ActionChanged时会让布局重新计算尺寸,并向category请求重新布局,有可能category的所有要重新调整尺寸 - * - * ActionRemoved会移除布局管理的QLayoutItem,并进行内存清理,这时窗口也会隐藏,同时销毁 - * - * @param e - * @note 所有action事件都会向category请求重新布局 - * - */ -void SARibbonPannel::actionEvent(QActionEvent* e) -{ - QAction* action = e->action(); - QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action); - - switch (e->type()) { - case QEvent::ActionAdded: { - SARibbonPannelLayout* lay = pannelLayout(); - if (nullptr != widgetAction) { - if (widgetAction->parent() != this) { - widgetAction->setParent(this); - } - } - int index = layout()->count(); - if (e->before()) { - // 说明是插入 - index = lay->indexByAction(e->before()); - if (-1 == index) { - index = layout()->count(); // 找不到的时候就插入到最后 - } - } - lay->insertAction(index, action, getActionRowProportionProperty(action)); - // 由于pannel的尺寸发生变化,需要让category也调整 - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - } break; - - case QEvent::ActionChanged: { - // 让布局重新绘制 - layout()->invalidate(); - - // updateGeometry(); - // 由于pannel的尺寸发生变化,需要让category也调整 - if (QWidget* parw = parentWidget()) { - if (QLayout* pl = parw->layout()) { - pl->invalidate(); - } - //! 强制发送一个resizeevent,让Category能重绘,如果没有这个函数,发现Category的layout虽然设置了invalidate(标记缓存失效) - //! 但并没有按顺序在pannel尺寸更新后更新Category的尺寸,导致有些pannel的尺寸识别出现异常 - //! 重打印信息上看,pannel的尺寸有进行更新,category的尺寸也进行了更新,但更新的次数和调用invalidate的次数不一样,需要手动触发ResizeEvent - //! 尝试过调用QEvent::LayoutRequest没有效果: - //! @code - //! QEvent* el = new QEvent(QEvent::LayoutRequest); - //! QApplication::postEvent(parw, el); - //! @endcode - //! - //! 调用parw->updateGeometry();也没有效果,目前看使用resizeevent是最有效果的 - //! - QResizeEvent* ersize = new QResizeEvent(parw->size(), QSize()); - QApplication::postEvent(parw, ersize); - } - } break; - - case QEvent::ActionRemoved: { - SARibbonPannelLayout* lay = pannelLayout(); - action->disconnect(this); - int index = lay->indexByAction(action); - if (index != -1) { - QLayoutItem* item = lay->takeAt(index); - delete item; - } - // 由于pannel的尺寸发生变化,需要让category也调整 - // if (QWidget* parw = parentWidget()) { - // if (QLayout* pl = parw->layout()) { - // pl->invalidate(); - // } - // } - } break; - - default: - break; - } -} - -/** - * @brief 追踪字体改变事件,把新字体设置到childwidget中 - * @param e - */ -void SARibbonPannel::changeEvent(QEvent* e) -{ - if (nullptr == e) { - return; - } - if (e->type() == QEvent::FontChange) { - QFont f = font(); - QList< QWidget* > listWidgets = findChildren< QWidget* >(); - for (QWidget* w : listWidgets) { - w->setFont(f); - } - if (QLayout* lay = layout()) { - lay->invalidate(); - } - d_ptr->resetTitleLabelFont(); - } - QWidget::changeEvent(e); -} - -/** - * @brief 获取布局item - * @return - */ -const QList< SARibbonPannelItem* >& SARibbonPannel::ribbonPannelItem() const -{ - return (pannelLayout()->mItems); -} - -/** - * @brief 获取最近添加的按钮 - * @return 刚才添加的按钮的指针 - */ -SARibbonToolButton* SARibbonPannel::lastAddActionButton() -{ - return d_ptr->lastAddActionButton(); -} +#include "SARibbonPannel.h" +#include "SARibbonCategory.h" +#include "SARibbonElementManager.h" +#include "SARibbonGallery.h" +#include "SARibbonPannelLayout.h" +#include "SARibbonPannelOptionButton.h" +#include "SARibbonToolButton.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SARibbonPannel_DEBUG_PRINT +#define SARibbonPannel_DEBUG_PRINT 0 +#endif + +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) +//=============================================================== +// SARibbonPannelLabel +//=============================================================== + +SARibbonPannelLabel::SARibbonPannelLabel(QWidget* parent) : QLabel(parent) +{ +} + +//=============================================================== +// SARibbonPannel::PrivateData +//=============================================================== + +class SARibbonPannel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonPannel) +public: + PrivateData(SARibbonPannel* p); + // 根据m_pannelLayoutMode返回gridLayout应该增加的行数 + int rowadded(); + void createLayout(); + // 获取layout + SARibbonPannelLayout* pannelLayout() const; + // 返回最后一个添加的action对应的button,前提是最后一个是toolbutton,否则返回nullptr + SARibbonToolButton* lastAddActionButton(); + // 重置labe的字体,这个主要是为了让pannel的标题字体能适应标题高度 + void resetTitleLabelFont(); + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + +public: + bool m_isCanCustomize { true }; ///< 记录是否可自定义 + SARibbonPannel::PannelLayoutMode m_pannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< pannel的布局模式,默认为3行模式ThreeRowMode + SARibbonPannelOptionButton* m_optionActionButton { nullptr }; ///< 标题栏的y距离 + SARibbonPannelLabel* m_label { nullptr }; +}; + +SARibbonPannel::PrivateData::PrivateData(SARibbonPannel* p) : q_ptr(p) +{ + createLayout(); +} + +int SARibbonPannel::PrivateData::rowadded() +{ + switch (m_pannelLayoutMode) { + case SARibbonPannel::ThreeRowMode: + return (2); + + case SARibbonPannel::TwoRowMode: + return (3); + + default: + break; + } + return (2); +} + +void SARibbonPannel::PrivateData::createLayout() +{ + m_label = new SARibbonPannelLabel(q_ptr); + m_label->setAlignment(Qt::AlignCenter); + SARibbonPannelLayout* lay = new SARibbonPannelLayout(q_ptr); + lay->setPannelTitleLabel(m_label); + lay->setSpacing(2); + lay->setContentsMargins(2, 2, 2, 2); +} + +SARibbonPannelLayout* SARibbonPannel::PrivateData::pannelLayout() const +{ + return qobject_cast< SARibbonPannelLayout* >(q_ptr->layout()); +} + +SARibbonToolButton* SARibbonPannel::PrivateData::lastAddActionButton() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (qobject_cast< SARibbonToolButton* >(lay->lastWidget())); + } + return (nullptr); +} + +/** + * @brief 重置label的字体 + * + * @note 此函数必须在布局设置后调用 + */ +void SARibbonPannel::PrivateData::resetTitleLabelFont() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + int h = lay->pannelTitleHeight(); + QFont f = q_ptr->font(); + f.setPixelSize(h * 0.8); + if (m_label) { + m_label->setFont(f); + } + } +} + +QString SARibbonPannel::PrivateData::pannelName() const +{ + if (m_label) { + return m_label->text(); + } + return (QString()); +} + +void SARibbonPannel::PrivateData::setPannelName(const QString& title) +{ + if (m_label) { + m_label->setText(title); + } +} + +//================================================== +// SARibbonPannel +//================================================== + +SARibbonPannel::SARibbonPannel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); +} + +SARibbonPannel::SARibbonPannel(const QString& name, QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); + setPannelName(name); +} + +SARibbonPannel::~SARibbonPannel() +{ +} + +/** + * @brief 把action的行属性设置进action中,action自身携带了行属性 + * @param action + * @param rp + */ +void SARibbonPannel::setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp)); +} + +/** + * @brief 获取action的行属性 + * @param action + * @return + */ +SARibbonPannelItem::RowProportion SARibbonPannel::getActionRowProportionProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok); + + if (isok) { + return (static_cast< SARibbonPannelItem::RowProportion >(r)); + } + return (SARibbonPannelItem::Large); +} + +/** + * @brief 设置action的ToolButtonPopupMode属性 + * @param action + * @param popMode + */ +void SARibbonPannel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode)); +} + +/** + * @brief 获取action的ToolButtonPopupMode属性 + * @param action + * @return + */ +QToolButton::ToolButtonPopupMode SARibbonPannel::getActionToolButtonPopupModeProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok); + + if (isok) { + return (static_cast< QToolButton::ToolButtonPopupMode >(r)); + } + return (QToolButton::InstantPopup); +} + +/** + * @brief 设置action的ToolButtonStyle属性 + * @param action + * @param buttonStyle + */ +void SARibbonPannel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle)); +} + +/** + * @brief 获取action的ToolButtonStyle属性 + * @param action + * @return + */ +Qt::ToolButtonStyle SARibbonPannel::getActionToolButtonStyleProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok); + + if (isok) { + return (static_cast< Qt::ToolButtonStyle >(r)); + } + return (Qt::ToolButtonIconOnly); +} + +/** + * @brief 添加action + * + * action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + * @param action + * @param rp 指定action的行占比 + * @param popMode 菜单弹出样式 + */ +void SARibbonPannel::addAction(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + setActionRowProportionProperty(action, rp); + addAction(action); +} + +/** + * @brief 添加一个action + * @param act + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + */ +void SARibbonPannel::addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(act); + setActionRowProportionProperty(act, rp); + setActionToolButtonPopupModeProperty(act, popMode); + addAction(act); +} + +/** + @brief 添加大图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addLargeAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Large); +} + +/** + @brief 在三栏模式下,强制加为2栏action + @note 在两行模式下,Medium和Small等价 + 主要应用在ThreeRowMode下 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addMediumAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Medium); +} + +/** + @brief 添加小图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addSmallAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Large); +} + +void SARibbonPannel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Medium); +} + +/** + * @brief 生成并添加一个action + * + * 如果不对此action做操作,SARibbonPannel将管理此action + * + * @note action的父对象将设置为SARibbonPannel,SARibbonPannel在删除时将会删除子对象,会把这个action也删除, + * 如果不想此action也删除,需要对action重新设置父对象 + * + * @param text action的文字 + * @param icon action的图标 + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + * @return 返回添加的action + */ +QAction* SARibbonPannel::addAction(const QString& text, + const QIcon& icon, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp) +{ + QAction* action = new QAction(icon, text, this); + addAction(action, popMode, rp); + return (action); +} + +/** + * @brief 添加一个普通菜单 + * @param menu + * @param rp + * @param popMode,菜单弹出模式,默认InstantPopup模式 + * @return + */ +void SARibbonPannel::addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(menu); + QAction* action = menu->menuAction(); + action->setIcon(menu->icon()); + action->setText(menu->title()); + action->setObjectName("action." + menu->objectName()); + addAction(action, popMode, rp); +} + +void SARibbonPannel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Large, popMode); +} + +void SARibbonPannel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Small, popMode); +} + +/** + * @brief 添加窗口 + * + * @param w + * @param rp + * @return 返回action(QWidgetAction) + * @note SARibbonPannel并不会管理此窗口内存,在delete SARibbonPannel时,此窗口如果父对象不是SARibbonPannel将不会被删除 + */ +QAction* SARibbonPannel::addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp) +{ + QWidgetAction* action = new QWidgetAction(this); + + action->setDefaultWidget(w); + action->setIcon(w->windowIcon()); + action->setText(w->windowTitle()); + action->setObjectName("action." + w->objectName()); + w->setAttribute(Qt::WA_Hover); + setActionRowProportionProperty(action, rp); + addAction(action); + return (action); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addSmallWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Small)); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return + */ +QAction* SARibbonPannel::addMediumWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Medium)); +} + +/** + * @brief 添加窗口,占用所有行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addLargeWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Large)); +} + +/** + * @brief SARibbonPannel::addGallery + * @return + * @note SARibbonPannel将拥有SARibbonGallery的管理权 + */ +SARibbonGallery* SARibbonPannel::addGallery(bool expanding) +{ + SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this); + + addWidget(gallery, SARibbonPannelItem::Large); + if (expanding) { + setExpanding(expanding); + } + return (gallery); +} + +/** + * @brief 添加分割线 + * @param top 上边距 @default 6 + * @param bottom 下边距 @default 6 + */ +QAction* SARibbonPannel::addSeparator() +{ + QAction* action = new QAction(this); + + action->setSeparator(true); + setActionRowProportionProperty(action, SARibbonPannelItem::Large); + addAction(action); + return (action); +} + +/** + * @brief 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + * @param action + * @return 如果action没有对应的button,就返回nullptr + */ +SARibbonToolButton* SARibbonPannel::actionToRibbonToolButton(QAction* action) +{ +#if 0 + SARibbonPannelLayout* lay = qobject_cast< SARibbonPannelLayout* >(layout()); + + if (lay) { + int index = lay->indexOf(action); + if (index == -1) { + return (nullptr); + } + QLayoutItem* item = lay->takeAt(index); + SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(item ? item->widget() : nullptr); + return (btn); + } + return (nullptr); +#else + for (auto obj : qAsConst(children())) { + if (obj->isWidgetType()) { + if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) { + if (btn->defaultAction() == action) { + return btn; + } + } + } + } + return (nullptr); +#endif +} + +/** + * @brief 获取pannel下面的所有toolbutton + * @return + */ +QList< SARibbonToolButton* > SARibbonPannel::ribbonToolButtons() const +{ + const QObjectList& objs = children(); + QList< SARibbonToolButton* > res; + + for (QObject* o : objs) { + SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o); + if (b) { + res.append(b); + } + } + return (res); +} + +/** + * @brief 设置PannelLayoutMode + * @param mode + */ +void SARibbonPannel::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode mode) +{ + if (d_ptr->m_pannelLayoutMode == mode) { + return; + } + d_ptr->m_pannelLayoutMode = mode; + updateItemGeometry(); +} + +SARibbonPannel::PannelLayoutMode SARibbonPannel::pannelLayoutMode() const +{ + return (d_ptr->m_pannelLayoutMode); +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannel::setOptionAction(QAction* action) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setOptionAction(action); + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannel::isHaveOptionAction() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isHaveOptionAction(); + } + return false; +} + +QSize SARibbonPannel::sizeHint() const +{ + int shWidth = 500; + int shHeight = 100; + if (QLayout* lay = layout()) { + QSize laySize = layout()->sizeHint(); + shWidth = laySize.width(); + shHeight = laySize.height(); + } + return QSize(shWidth, shHeight); +} + +QSize SARibbonPannel::minimumSizeHint() const +{ + return (layout()->minimumSize()); +} + +void SARibbonPannel::setToolButtonIconSize(const QSize& s) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setToolButtonIconSize(s); + } + // 对已经管理的QToolButton设置为iconsize + iterate([ s ](SARibbonToolButton* t) -> bool { + t->setIconSize(s); + return true; + }); +} + +QSize SARibbonPannel::toolButtonIconSize() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->toolButtonIconSize(); + } + return QSize(); +} + +/** + * @brief 遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 + * @param fp + * @return 返回false说明迭代中途退出,返回true则表示迭代完全 + */ +bool SARibbonPannel::iterate(SARibbonPannel::FpRibbonToolButtonIterate fp) +{ + const QObjectList& ols = children(); + for (QObject* obj : ols) { + if (SARibbonToolButton* t = qobject_cast< SARibbonToolButton* >(obj)) { + if (!fp(t)) { + return false; + } + } + } + return true; +} + +/** + * @brief 把pannel设置为扩展模式,此时会撑大水平区域 + * @param isExpanding + */ +void SARibbonPannel::setExpanding(bool isExpanding) +{ + setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +/** + * @brief 判断此pannel是否为(水平)扩展模式 + * @return 是扩展模式返回true + */ +bool SARibbonPannel::isExpanding() const +{ + QSizePolicy sp = sizePolicy(); + + return (sp.horizontalPolicy() == QSizePolicy::Expanding); +} + +/** + @brief 设置标题栏的高度 + @param h + */ +void SARibbonPannel::setTitleHeight(int h) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setPannelTitleHeight(h); + } +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonPannel::titleHeight() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->pannelTitleHeight(); + } + return -1; +} + +/** + * @brief 判断是否显示标题,只有标题的高度被设置,才会显示标题 + * @return + */ +bool SARibbonPannel::isEnableShowTitle() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isEnableShowPannelTitle(); + } + return false; +} + +/** + * @brief 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + * @param on + */ +void SARibbonPannel::setEnableShowTitle(bool on) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->setEnableShowPannelTitle(on); + } +} + +/** + * @brief action对应的布局index,此操作一般用于移动,其他意义不大 + * @param act + * @return 没有查到返回-1 + */ +int SARibbonPannel::actionIndex(QAction* act) const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->indexByAction(act)); + } + return (-1); +} + +/** + * @brief 移动action + * @param from 要移动action的位置,通过@ref actionIndex 获取 + * @param to 要移动的位置 + */ +void SARibbonPannel::moveAction(int from, int to) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->move(from, to)); + } + updateGeometry(); // 通知layout进行重新布局 +} + +/** + * @brief 判断是否可以自定义 + * @return + */ +bool SARibbonPannel::isCanCustomize() const +{ + return (d_ptr->m_isCanCustomize); +} + +/** + * @brief 设置是否可以自定义 + * @param b + */ +void SARibbonPannel::setCanCustomize(bool b) +{ + d_ptr->m_isCanCustomize = b; +} + +/** + * @brief pannel名称 + * @return + */ +QString SARibbonPannel::pannelName() const +{ + return (d_ptr->pannelName()); +} + +/** + * @brief pannel的名称 + * @param title 标题 + * @note 此函数会触发@sa pannelNameChanged 信号 + */ +void SARibbonPannel::setPannelName(const QString& title) +{ + QString oldName = pannelName(); + if (oldName != title) { + d_ptr->setPannelName(title); + Q_EMIT pannelNameChanged(title); + } +} + +/** + * @brief 获取大图标的高度 + * @return + */ +int SARibbonPannel::largeButtonHeight() const +{ + const QMargins& mag = contentsMargins(); + return height() - mag.top() - mag.bottom() - titleHeight(); +} + +/** + * @brief 获取pannel layout + * @return + */ +SARibbonPannelLayout* SARibbonPannel::pannelLayout() const +{ + return d_ptr->pannelLayout(); +} + +/** + * @brief 更新布局 + */ +void SARibbonPannel::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonPannel updateItemGeometry,pannelName=" << pannelName(); +#endif + // 此函数需要添加,否则SARibbonBar::setEnableWordWrap无法刷新按钮 + resetToolButtonSize(); + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->updateGeomArray(); + } +} + +/** + @brief 获取category指针,如果没有parent,或者不在category管理,返回nullptr + @return + */ +SARibbonCategory* SARibbonPannel::category() const +{ + return qobject_cast< SARibbonCategory* >(parent()); +} + +/** + @brief 获取ribbonBar指针,如果没有返回nullptr + @return + */ +SARibbonBar* SARibbonPannel::ribbonBar() const +{ + if (SARibbonCategory* c = category()) { + return c->ribbonBar(); + } + return nullptr; +} + +/** + * @brief 设置按钮之间的间隔 + * @param n + */ +void SARibbonPannel::setSpacing(int n) +{ + if (auto lay = layout()) { + lay->setSpacing(n); + } +} + +/** + * @brief 按钮之间的间隔 + * @return + */ +int SARibbonPannel::spacing() const +{ + if (auto lay = layout()) { + return lay->spacing(); + } + return 0; +} + +/** + * @brief pannel高度推荐 + * @param fm + * @param layMode + * @param pannelTitleHeight + * @return + */ +int SARibbonPannel::pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight) +{ + int textH = fm.lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 + switch (layMode) { + case SARibbonPannel::ThreeRowMode: { + // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 + return textH * 4.8 + pannelTitleHeight; + } break; + case SARibbonPannel::TwoRowMode: { + // 3=2*1.6 + return textH * 3.2 + pannelTitleHeight; + } break; + default: { + qWarning() << "unknow SARibbonPannel::PannelLayoutMode:" << layMode; + } + } + return (textH * 4.8 + pannelTitleHeight); +} + +/** + * @brief 重置按钮的尺寸,在布局改变后(尤其高度变更),按钮的尺寸需要手动变更 + */ +void SARibbonPannel::resetToolButtonSize() +{ + QList< SARibbonToolButton* > btns = ribbonToolButtons(); + + for (SARibbonToolButton* b : qAsConst(btns)) { + if ((nullptr == b)) { + continue; + } + b->updateRect(); + } +} + +bool SARibbonPannel::event(QEvent* e) +{ +#if SA_DEBUG_PRINT_EVENT + if (e->type() != QEvent::Paint) { + qDebug() << "SARibbonPannel event(" << e->type() << "),name=" << pannelName(); + } +#endif + // if (SARibbonPannelLayout* lay = pannelLayout()) { + // if (lay->isDirty() && e->type() == QEvent::LayoutRequest) { + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + // lay->m_dirty = false; + // } + // } + return QWidget::event(e); +} + +/** + * @brief 处理action的事件 + * + * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 + * + * ActionAdded时向布局请求,添加action,布局中同时触发了@ref SARibbonPannelLayout::createItem 函数 + * 此函数用于生成窗口,例如QRibbonToolButton + * + * ActionChanged时会让布局重新计算尺寸,并向category请求重新布局,有可能category的所有要重新调整尺寸 + * + * ActionRemoved会移除布局管理的QLayoutItem,并进行内存清理,这时窗口也会隐藏,同时销毁 + * + * @param e + * @note 所有action事件都会向category请求重新布局 + * + */ +void SARibbonPannel::actionEvent(QActionEvent* e) +{ + QAction* action = e->action(); + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action); + + switch (e->type()) { + case QEvent::ActionAdded: { + SARibbonPannelLayout* lay = pannelLayout(); + if (nullptr != widgetAction) { + if (widgetAction->parent() != this) { + widgetAction->setParent(this); + } + } + int index = layout()->count(); + if (e->before()) { + // 说明是插入 + index = lay->indexByAction(e->before()); + if (-1 == index) { + index = layout()->count(); // 找不到的时候就插入到最后 + } + } + lay->insertAction(index, action, getActionRowProportionProperty(action)); + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + case QEvent::ActionChanged: { + // 让布局重新绘制 + layout()->invalidate(); + + // updateGeometry(); + // 由于pannel的尺寸发生变化,需要让category也调整 + if (QWidget* parw = parentWidget()) { + if (QLayout* pl = parw->layout()) { + pl->invalidate(); + } + //! 强制发送一个resizeevent,让Category能重绘,如果没有这个函数,发现Category的layout虽然设置了invalidate(标记缓存失效) + //! 但并没有按顺序在pannel尺寸更新后更新Category的尺寸,导致有些pannel的尺寸识别出现异常 + //! 重打印信息上看,pannel的尺寸有进行更新,category的尺寸也进行了更新,但更新的次数和调用invalidate的次数不一样,需要手动触发ResizeEvent + //! 尝试过调用QEvent::LayoutRequest没有效果: + //! @code + //! QEvent* el = new QEvent(QEvent::LayoutRequest); + //! QApplication::postEvent(parw, el); + //! @endcode + //! + //! 调用parw->updateGeometry();也没有效果,目前看使用resizeevent是最有效果的 + //! + QResizeEvent* ersize = new QResizeEvent(parw->size(), QSize()); + QApplication::postEvent(parw, ersize); + } + } break; + + case QEvent::ActionRemoved: { + SARibbonPannelLayout* lay = pannelLayout(); + action->disconnect(this); + int index = lay->indexByAction(action); + if (index != -1) { + QLayoutItem* item = lay->takeAt(index); + delete item; + } + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + default: + break; + } +} + +/** + * @brief 追踪字体改变事件,把新字体设置到childwidget中 + * @param e + */ +void SARibbonPannel::changeEvent(QEvent* e) +{ + if (nullptr == e) { + return; + } + if (e->type() == QEvent::FontChange) { + QFont f = font(); + QList< QWidget* > listWidgets = findChildren< QWidget* >(); + for (QWidget* w : listWidgets) { + w->setFont(f); + } + if (QLayout* lay = layout()) { + lay->invalidate(); + } + d_ptr->resetTitleLabelFont(); + } + QWidget::changeEvent(e); +} + +/** + * @brief 获取布局item + * @return + */ +const QList< SARibbonPannelItem* >& SARibbonPannel::ribbonPannelItem() const +{ + return (pannelLayout()->mItems); +} + +/** + * @brief 获取最近添加的按钮 + * @return 刚才添加的按钮的指针 + */ +SARibbonToolButton* SARibbonPannel::lastAddActionButton() +{ + return d_ptr->lastAddActionButton(); +} diff --git a/src/SARibbonBar/SARibbonPannel.h b/src/SARibbonBar/SARibbonPannel.h index fef405d3..32d48396 100644 --- a/src/SARibbonBar/SARibbonPannel.h +++ b/src/SARibbonBar/SARibbonPannel.h @@ -1,237 +1,237 @@ -#ifndef SARIBBONPANNEL_H -#define SARIBBONPANNEL_H -#include "SARibbonGlobal.h" -#include "SARibbonPannelItem.h" -#include "SARibbonPannelLayout.h" -#include "SARibbonToolButton.h" -#include -#include -#include -class SARibbonMenu; -class SARibbonGallery; -class QGridLayout; -class SARibbonPannelOptionButton; -class SARibbonPannelLayout; -class SARibbonCategory; -class SARibbonBar; - -/** - * @brief SARibbonPannel的标题label,此类用于qss - */ -class SA_RIBBON_EXPORT SARibbonPannelLabel : public QLabel -{ - Q_OBJECT -public: - SARibbonPannelLabel(QWidget* parent = nullptr); -}; - -/** - * @brief pannel页窗口,pannel是ribbon的面板用于承放控件 - * - * ribbon的pannel分为两行模式和三行模式,以office为代表的ribbon为3行模式,以WPS为代表的“紧凑派”就是2行模式, - * SARibbon可通过SARibbonBar的@ref SARibbonBar::RibbonStyle 来指定模式(通过函数@ref SARibbonBar::setRibbonStyle ) - * - * 在pannel中,可以通过@ref setExpanding 函数指定pannel水平扩展,如果pannel里面没有能水平扩展的控件,将会留白,因此, - * 建议在pannel里面有水平扩展的控件如(@ref SARibbonGallery )才指定这个函数 - * - * pannel的布局通过@ref SARibbonPannelLayout 来实现,如果有其他布局,可以通过继承@ref - * SARibbonElementCreateDelegate::createRibbonPannel 函数返回带有自己布局的pannel,但你必须继承对应的虚函数 - */ -class SA_RIBBON_EXPORT SARibbonPannel : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonPannel) - friend class SARibbonCategory; - friend class SARibbonCategoryPrivate; - friend class SARibbonCustomizeWidgetPrivate; - friend class SARibbonPannelLayout; - Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) - Q_PROPERTY(bool isExpanding READ isExpanding WRITE setExpanding) - Q_PROPERTY(QString pannelName READ pannelName WRITE setPannelName) -public: - enum PannelLayoutMode - { - ThreeRowMode, ///< 三行布局模式,office就是三行布局模式,pannel能布置3行小toolbutton,默认模式 - TwoRowMode ///< 两行布局模式,wps的后续布局模式就是两行布局模式,pannel能布置2行小toolbutton - }; - Q_ENUM(PannelLayoutMode) -public: - using FpRibbonToolButtonIterate = std::function< bool(SARibbonToolButton*) >; - -public: - explicit SARibbonPannel(QWidget* parent = nullptr); - explicit SARibbonPannel(const QString& name, QWidget* parent = nullptr); - ~SARibbonPannel() Q_DECL_OVERRIDE; - using QWidget::addAction; - - // 把action加入到pannel - void addAction(QAction* action, SARibbonPannelItem::RowProportion rp); - // 生成并添加一个action - void addAction(QAction* act, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); - // 把action加入到pannel,并以大图标显示 - void addLargeAction(QAction* action); - // 把action加入到pannel,在三行模式下会以中图标显示 - void addMediumAction(QAction* action); - // 把action加入到pannel,并以小图标显示 - void addSmallAction(QAction* action); - - // 把action加入到pannel,并以小图标显示 - void addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 把action加入到pannel,并以大图标显示 - void addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 把action加入到pannel,在三行模式下会以中图标显示 - void addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); - - QAction* addAction(const QString& text, - const QIcon& icon, - QToolButton::ToolButtonPopupMode popMode, - SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); - - // 添加menu - void addMenu(QMenu* menu, - SARibbonPannelItem::RowProportion rp, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加普通大菜单 - void addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加普通小按钮菜单 - void addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - - // 添加窗口 - QAction* addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp); - - // 添加窗口,占用ribbon的一行 - QAction* addSmallWidget(QWidget* w); - - // 添加窗口,占用ribbon的一行 - QAction* addMediumWidget(QWidget* w); - - // 添加窗口,占用所有行 - QAction* addLargeWidget(QWidget* w); - - // 添加一个Gallery - SARibbonGallery* addGallery(bool expanding = true); - - // 添加分割线 - QAction* addSeparator(); - - // 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr - SARibbonToolButton* actionToRibbonToolButton(QAction* action); - - // 设置操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - // OptionAction也会触发actionTriggered信号 - void setOptionAction(QAction* action); - - // 判断是否存在OptionAction - bool isHaveOptionAction() const; - - // 获取所有的buttons - QList< SARibbonToolButton* > ribbonToolButtons() const; - - // 获取PannelLayoutMode - PannelLayoutMode pannelLayoutMode() const; - void setPannelLayoutMode(PannelLayoutMode mode); - // 更新按钮的尺寸,这个函数在pannel的布局状态变换后需要调用刷新 - void resetToolButtonSize(); - - // 判断是否为2行模式 - bool isTwoRow() const - { - return (TwoRowMode == pannelLayoutMode()); - } - - // 把pannel设置为扩展模式,此时会撑大水平区域 - void setExpanding(bool isExpanding = true); - - // 是否是扩展模式 - bool isExpanding() const; - - // 标题栏高度 - void setTitleHeight(int h); - int titleHeight() const; - - // 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 - bool isEnableShowTitle() const; - void setEnableShowTitle(bool on); - - // action对应的布局index,此操作一般用于移动moveAction,其他意义不大 - int actionIndex(QAction* act) const; - - // 移动action - void moveAction(int from, int to); - - // 判断是否可以自定义 - bool isCanCustomize() const; - void setCanCustomize(bool b); - - // 标题 - QString pannelName() const; - void setPannelName(const QString& title); - - // 大图标的高度 - int largeButtonHeight() const; - // 获取布局对应的item,此函数目前仅仅在自定义过程中用到 - const QList< SARibbonPannelItem* >& ribbonPannelItem() const; - // 获取pannel layout - SARibbonPannelLayout* pannelLayout() const; - // 更新布局 - void updateItemGeometry(); - // 获取category指针,如果没有parent,或者不在category管理,返回nullptr - SARibbonCategory* category() const; - // 获取ribbonBar指针,如果没有返回nullptr - SARibbonBar* ribbonBar() const; - // 设置按钮之间的间隔 - void setSpacing(int n); - int spacing() const; - // - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - // 设置按钮的icon尺寸,large action不受此尺寸影响 - void setToolButtonIconSize(const QSize& s); - QSize toolButtonIconSize() const; - // 此函数会遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 - bool iterate(FpRibbonToolButtonIterate fp); - - // 获取最近添加的按钮 - SARibbonToolButton* lastAddActionButton(); - -Q_SIGNALS: - - /** - * @brief 等同于QToolBar::actionTriggered - * @param action - */ - void actionTriggered(QAction* action); - - /** - * @brief pannel的标题发生了改变 - * @param n - */ - void pannelNameChanged(const QString& n); - -public: - // pannel高度推荐 - static int pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight); - // 把action的行属性设置进action中,action自身携带了行属性 - static void setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp); - // 获取action的行属性 - static SARibbonPannelItem::RowProportion getActionRowProportionProperty(QAction* action); - // 把action的PopupMode属性设置进action中,action自身携带了PopupMode属性 - static void setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode); - // 获取action的PopupMode属性 - static QToolButton::ToolButtonPopupMode getActionToolButtonPopupModeProperty(QAction* action); - // 把action的ToolButtonStyle属性设置进action中,action自身携带了ToolButtonStyle属性 - static void setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle); - // 获取action的ToolButtonStyle属性 - static Qt::ToolButtonStyle getActionToolButtonStyleProperty(QAction* action); - -protected: - virtual bool event(QEvent* e) Q_DECL_OVERRIDE; - virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; - virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; -}; - -#endif // SARIBBONPANNEL_H +#ifndef SARIBBONPANNEL_H +#define SARIBBONPANNEL_H +#include "SARibbonGlobal.h" +#include "SARibbonPannelItem.h" +#include "SARibbonPannelLayout.h" +#include "SARibbonToolButton.h" +#include +#include +#include +class SARibbonMenu; +class SARibbonGallery; +class QGridLayout; +class SARibbonPannelOptionButton; +class SARibbonPannelLayout; +class SARibbonCategory; +class SARibbonBar; + +/** + * @brief SARibbonPannel的标题label,此类用于qss + */ +class SA_RIBBON_EXPORT SARibbonPannelLabel : public QLabel +{ + Q_OBJECT +public: + SARibbonPannelLabel(QWidget* parent = nullptr); +}; + +/** + * @brief pannel页窗口,pannel是ribbon的面板用于承放控件 + * + * ribbon的pannel分为两行模式和三行模式,以office为代表的ribbon为3行模式,以WPS为代表的“紧凑派”就是2行模式, + * SARibbon可通过SARibbonBar的@ref SARibbonBar::RibbonStyle 来指定模式(通过函数@ref SARibbonBar::setRibbonStyle ) + * + * 在pannel中,可以通过@ref setExpanding 函数指定pannel水平扩展,如果pannel里面没有能水平扩展的控件,将会留白,因此, + * 建议在pannel里面有水平扩展的控件如(@ref SARibbonGallery )才指定这个函数 + * + * pannel的布局通过@ref SARibbonPannelLayout 来实现,如果有其他布局,可以通过继承@ref + * SARibbonElementCreateDelegate::createRibbonPannel 函数返回带有自己布局的pannel,但你必须继承对应的虚函数 + */ +class SA_RIBBON_EXPORT SARibbonPannel : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonPannel) + friend class SARibbonCategory; + friend class SARibbonCategoryPrivate; + friend class SARibbonCustomizeWidgetPrivate; + friend class SARibbonPannelLayout; + Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) + Q_PROPERTY(bool isExpanding READ isExpanding WRITE setExpanding) + Q_PROPERTY(QString pannelName READ pannelName WRITE setPannelName) +public: + enum PannelLayoutMode + { + ThreeRowMode, ///< 三行布局模式,office就是三行布局模式,pannel能布置3行小toolbutton,默认模式 + TwoRowMode ///< 两行布局模式,wps的后续布局模式就是两行布局模式,pannel能布置2行小toolbutton + }; + Q_ENUM(PannelLayoutMode) +public: + using FpRibbonToolButtonIterate = std::function< bool(SARibbonToolButton*) >; + +public: + explicit SARibbonPannel(QWidget* parent = nullptr); + explicit SARibbonPannel(const QString& name, QWidget* parent = nullptr); + ~SARibbonPannel() Q_DECL_OVERRIDE; + using QWidget::addAction; + + // 把action加入到pannel + void addAction(QAction* action, SARibbonPannelItem::RowProportion rp); + // 生成并添加一个action + void addAction(QAction* act, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action); + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action); + + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + + QAction* addAction(const QString& text, + const QIcon& icon, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + + // 添加menu + void addMenu(QMenu* menu, + SARibbonPannelItem::RowProportion rp, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通大菜单 + void addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通小按钮菜单 + void addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加窗口 + QAction* addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp); + + // 添加窗口,占用ribbon的一行 + QAction* addSmallWidget(QWidget* w); + + // 添加窗口,占用ribbon的一行 + QAction* addMediumWidget(QWidget* w); + + // 添加窗口,占用所有行 + QAction* addLargeWidget(QWidget* w); + + // 添加一个Gallery + SARibbonGallery* addGallery(bool expanding = true); + + // 添加分割线 + QAction* addSeparator(); + + // 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + SARibbonToolButton* actionToRibbonToolButton(QAction* action); + + // 设置操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + // OptionAction也会触发actionTriggered信号 + void setOptionAction(QAction* action); + + // 判断是否存在OptionAction + bool isHaveOptionAction() const; + + // 获取所有的buttons + QList< SARibbonToolButton* > ribbonToolButtons() const; + + // 获取PannelLayoutMode + PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(PannelLayoutMode mode); + // 更新按钮的尺寸,这个函数在pannel的布局状态变换后需要调用刷新 + void resetToolButtonSize(); + + // 判断是否为2行模式 + bool isTwoRow() const + { + return (TwoRowMode == pannelLayoutMode()); + } + + // 把pannel设置为扩展模式,此时会撑大水平区域 + void setExpanding(bool isExpanding = true); + + // 是否是扩展模式 + bool isExpanding() const; + + // 标题栏高度 + void setTitleHeight(int h); + int titleHeight() const; + + // 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + bool isEnableShowTitle() const; + void setEnableShowTitle(bool on); + + // action对应的布局index,此操作一般用于移动moveAction,其他意义不大 + int actionIndex(QAction* act) const; + + // 移动action + void moveAction(int from, int to); + + // 判断是否可以自定义 + bool isCanCustomize() const; + void setCanCustomize(bool b); + + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + + // 大图标的高度 + int largeButtonHeight() const; + // 获取布局对应的item,此函数目前仅仅在自定义过程中用到 + const QList< SARibbonPannelItem* >& ribbonPannelItem() const; + // 获取pannel layout + SARibbonPannelLayout* pannelLayout() const; + // 更新布局 + void updateItemGeometry(); + // 获取category指针,如果没有parent,或者不在category管理,返回nullptr + SARibbonCategory* category() const; + // 获取ribbonBar指针,如果没有返回nullptr + SARibbonBar* ribbonBar() const; + // 设置按钮之间的间隔 + void setSpacing(int n); + int spacing() const; + // + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + // 设置按钮的icon尺寸,large action不受此尺寸影响 + void setToolButtonIconSize(const QSize& s); + QSize toolButtonIconSize() const; + // 此函数会遍历SARibbonPannel下的所有RibbonToolButton,执行函数指针(bool(SARibbonRibbonToolButton*)),函数指针返回false则停止迭代 + bool iterate(FpRibbonToolButtonIterate fp); + + // 获取最近添加的按钮 + SARibbonToolButton* lastAddActionButton(); + +Q_SIGNALS: + + /** + * @brief 等同于QToolBar::actionTriggered + * @param action + */ + void actionTriggered(QAction* action); + + /** + * @brief pannel的标题发生了改变 + * @param n + */ + void pannelNameChanged(const QString& n); + +public: + // pannel高度推荐 + static int pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight); + // 把action的行属性设置进action中,action自身携带了行属性 + static void setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp); + // 获取action的行属性 + static SARibbonPannelItem::RowProportion getActionRowProportionProperty(QAction* action); + // 把action的PopupMode属性设置进action中,action自身携带了PopupMode属性 + static void setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 获取action的PopupMode属性 + static QToolButton::ToolButtonPopupMode getActionToolButtonPopupModeProperty(QAction* action); + // 把action的ToolButtonStyle属性设置进action中,action自身携带了ToolButtonStyle属性 + static void setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle); + // 获取action的ToolButtonStyle属性 + static Qt::ToolButtonStyle getActionToolButtonStyleProperty(QAction* action); + +protected: + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; +}; + +#endif // SARIBBONPANNEL_H diff --git a/src/SARibbonBar/SARibbonPannelItem.cpp b/src/SARibbonBar/SARibbonPannelItem.cpp index 9e43cdf3..26987885 100644 --- a/src/SARibbonBar/SARibbonPannelItem.cpp +++ b/src/SARibbonBar/SARibbonPannelItem.cpp @@ -1,15 +1,15 @@ -#include "SARibbonPannelItem.h" - -SARibbonPannelItem::SARibbonPannelItem(QWidget* widget) - : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large) -{ -} - -SARibbonPannelItem::~SARibbonPannelItem() -{ -} - -bool SARibbonPannelItem::isEmpty() const -{ - return (action == nullptr || !action->isVisible()); -} +#include "SARibbonPannelItem.h" + +SARibbonPannelItem::SARibbonPannelItem(QWidget* widget) + : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large) +{ +} + +SARibbonPannelItem::~SARibbonPannelItem() +{ +} + +bool SARibbonPannelItem::isEmpty() const +{ + return (action == nullptr || !action->isVisible()); +} diff --git a/src/SARibbonBar/SARibbonPannelItem.h b/src/SARibbonBar/SARibbonPannelItem.h index 59813888..b18f6cb2 100644 --- a/src/SARibbonBar/SARibbonPannelItem.h +++ b/src/SARibbonBar/SARibbonPannelItem.h @@ -1,50 +1,50 @@ -#ifndef SARIBBONPANNELITEM_H -#define SARIBBONPANNELITEM_H -#include "SARibbonGlobal.h" -#include -#include -class SARibbonToolButton; -/** - * @brief 是对pannel所有子窗口的抽象,参考qt的toolbar - * - * 参考qt的toolbar,pannel所有子窗口内容都通过QAction进行抽象,包括gallery这些窗口,也是通过QAction进行抽象 - * QAction最终会转换为SARibbonPannelItem,每个SARibbonPannelItem都含有一个widget,SARibbonPannel的布局 - * 就基于SARibbonPannelItem - * - * 无窗口的action会在内部生成一个SARibbonToolButton, - */ -class SA_RIBBON_EXPORT SARibbonPannelItem : public QWidgetItem -{ -public: - /** - * @brief 定义了行的占比,ribbon中有large,media和small三种占比 - */ - enum RowProportion - { - None, ///< 为定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small - Large, ///< 大占比,一个widget的高度会充满整个pannel - Medium, ///< 中占比,在@ref SARibbonPannel::pannelLayoutMode 为 @ref SARibbonPannel::ThreeRowMode 时才会起作用,且要同一列里两个都是Medium时,会在三行中占据两行 - Small ///< 小占比,占SARibbonPannel的一行,Medium在不满足条件时也会变为Small,但不会变为Large - }; - explicit SARibbonPannelItem(QWidget* widget); - ~SARibbonPannelItem(); - - bool isEmpty() const Q_DECL_OVERRIDE; - - short rowIndex; ///< 记录当前item属于第几行,hide模式下为-1 - int columnIndex; ///< 记录当前item属于第几列,hide模式下为-1 - QRect itemWillSetGeometry; ///< 在调用SARibbonPannelLayout::updateGeomArray会更新这个此处,实际设置的时候会QWidgetItem::setGeometry设置Geometry - QAction* action; /// < 记录action,参考QToolBarLayoutItem - bool customWidget; ///< 对于没有窗口的action,实际也会有一个SARibbonToolButton,在销毁时要delete掉 - SARibbonPannelItem::RowProportion rowProportion; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion -}; -#ifndef SA_ActionPropertyName_RowProportion -#define SA_ActionPropertyName_RowProportion "_sa_RowProportion" -#endif -#ifndef SA_ActionPropertyName_ToolButtonPopupMode -#define SA_ActionPropertyName_ToolButtonPopupMode "_sa_ToolButtonPopupMode" -#endif -#ifndef SA_ActionPropertyName_ToolButtonStyle -#define SA_ActionPropertyName_ToolButtonStyle "_sa_ToolButtonStyle" -#endif -#endif // SARIBBONPANNELITEM_H +#ifndef SARIBBONPANNELITEM_H +#define SARIBBONPANNELITEM_H +#include "SARibbonGlobal.h" +#include +#include +class SARibbonToolButton; +/** + * @brief 是对pannel所有子窗口的抽象,参考qt的toolbar + * + * 参考qt的toolbar,pannel所有子窗口内容都通过QAction进行抽象,包括gallery这些窗口,也是通过QAction进行抽象 + * QAction最终会转换为SARibbonPannelItem,每个SARibbonPannelItem都含有一个widget,SARibbonPannel的布局 + * 就基于SARibbonPannelItem + * + * 无窗口的action会在内部生成一个SARibbonToolButton, + */ +class SA_RIBBON_EXPORT SARibbonPannelItem : public QWidgetItem +{ +public: + /** + * @brief 定义了行的占比,ribbon中有large,media和small三种占比 + */ + enum RowProportion + { + None, ///< 为定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small + Large, ///< 大占比,一个widget的高度会充满整个pannel + Medium, ///< 中占比,在@ref SARibbonPannel::pannelLayoutMode 为 @ref SARibbonPannel::ThreeRowMode 时才会起作用,且要同一列里两个都是Medium时,会在三行中占据两行 + Small ///< 小占比,占SARibbonPannel的一行,Medium在不满足条件时也会变为Small,但不会变为Large + }; + explicit SARibbonPannelItem(QWidget* widget); + ~SARibbonPannelItem(); + + bool isEmpty() const Q_DECL_OVERRIDE; + + short rowIndex; ///< 记录当前item属于第几行,hide模式下为-1 + int columnIndex; ///< 记录当前item属于第几列,hide模式下为-1 + QRect itemWillSetGeometry; ///< 在调用SARibbonPannelLayout::updateGeomArray会更新这个此处,实际设置的时候会QWidgetItem::setGeometry设置Geometry + QAction* action; /// < 记录action,参考QToolBarLayoutItem + bool customWidget; ///< 对于没有窗口的action,实际也会有一个SARibbonToolButton,在销毁时要delete掉 + SARibbonPannelItem::RowProportion rowProportion; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +}; +#ifndef SA_ActionPropertyName_RowProportion +#define SA_ActionPropertyName_RowProportion "_sa_RowProportion" +#endif +#ifndef SA_ActionPropertyName_ToolButtonPopupMode +#define SA_ActionPropertyName_ToolButtonPopupMode "_sa_ToolButtonPopupMode" +#endif +#ifndef SA_ActionPropertyName_ToolButtonStyle +#define SA_ActionPropertyName_ToolButtonStyle "_sa_ToolButtonStyle" +#endif +#endif // SARIBBONPANNELITEM_H diff --git a/src/SARibbonBar/SARibbonPannelLayout.cpp b/src/SARibbonBar/SARibbonPannelLayout.cpp index 9b62f648..d8939684 100644 --- a/src/SARibbonBar/SARibbonPannelLayout.cpp +++ b/src/SARibbonBar/SARibbonPannelLayout.cpp @@ -1,952 +1,952 @@ -#include "SARibbonPannelLayout.h" -#include "SARibbonPannelOptionButton.h" -#include "SARibbonSeparatorWidget.h" -#include "SARibbonElementManager.h" -#include -#include -#include "SARibbonPannel.h" -#include "SARibbonPannelItem.h" -#define SARibbonPannelLayout_DEBUG_PRINT 1 -#define HELP_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - QPen _pen(Qt::DashLine); \ - _pen.setColor(Qt::blue); \ - p.setPen(_pen); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) - -SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true) -{ - setSpacing(1); - SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p); - - if (!tb) { - return; - } -} - -SARibbonPannelLayout::~SARibbonPannelLayout() -{ - // 参考QToolBarLayout - while (!mItems.isEmpty()) { - SARibbonPannelItem* item = mItems.takeFirst(); - if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) { - if (item->customWidget) { - widgetAction->releaseWidget(item->widget()); - } - } - delete item; - } -} - -/** - * @brief 通过action查找索引,用于actionEvent添加action用 - * @param action - * @return 没有查到返回-1 - */ -int SARibbonPannelLayout::indexByAction(QAction* action) const -{ - for (int i = 0; i < mItems.count(); ++i) { - if (mItems.at(i)->action == action) { - return (i); - } - } - return (-1); -} - -/** - * @brief 获取ribbonpannel - * @return - */ -SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const -{ - return qobject_cast< SARibbonPannel* >(parentWidget()); -} - -void SARibbonPannelLayout::addItem(QLayoutItem* item) -{ - Q_UNUSED(item); - qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead"); - return; -} - -/** - * @brief SARibbonPannel主要通过此函数来添加action - * @param act - * @param rp 布局策略 - */ -void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp) -{ - index = qMax(0, index); - index = qMin(mItems.count(), index); - SARibbonPannelItem* item = createItem(act, rp); - - if (item) { - mItems.insert(index, item); - // 标记需要重新计算尺寸 - invalidate(); - } -} - -/** - * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 - * @param action - * @note 要去除OptionAction直接传入nullptr即可 - * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction - */ -void SARibbonPannelLayout::setOptionAction(QAction* action) -{ - SARibbonPannel* p = ribbonPannel(); - if (!p) { - return; - } - if (action) { - // 创建option action - if (nullptr == mOptionActionBtn) { - mOptionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p); - QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered); - // 确保m_optionActionBtn在label之上 - if (mTitleLabel) { - mTitleLabel->stackUnder(mOptionActionBtn); - } - } - mOptionActionBtn->setDefaultAction(action); - if (action->icon().isNull()) { - mOptionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); - } - // 标记需要重新计算尺寸 - invalidate(); - } else { - // 取消option action - if (mOptionActionBtn) { - mOptionActionBtn->hide(); - mOptionActionBtn->deleteLater(); - mOptionActionBtn = nullptr; - // 标记需要重新计算尺寸 - invalidate(); - } - } -} - -/** - * @brief 判断是否存在OptionAction - * @return 存在返回true - */ -bool SARibbonPannelLayout::isHaveOptionAction() const -{ - return (mOptionActionBtn != nullptr); -} - -QLayoutItem* SARibbonPannelLayout::itemAt(int index) const -{ - if ((index < 0) || (index >= mItems.count())) { - return (nullptr); - } - return (mItems.at(index)); -} - -QLayoutItem* SARibbonPannelLayout::takeAt(int index) -{ - if ((index < 0) || (index >= mItems.count())) { - return (nullptr); - } - SARibbonPannelItem* item = mItems.takeAt(index); - - QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action); - - if ((widgetAction != 0) && item->customWidget) { - widgetAction->releaseWidget(item->widget()); - } else { - // destroy the QToolButton/QToolBarSeparator - item->widget()->hide(); - item->widget()->deleteLater(); - } - - invalidate(); - return (item); -} - -int SARibbonPannelLayout::count() const -{ - return (mItems.count()); -} - -bool SARibbonPannelLayout::isEmpty() const -{ - - return (mItems.isEmpty()); -} - -void SARibbonPannelLayout::invalidate() -{ - mDirty = true; - QLayout::invalidate(); -} - -Qt::Orientations SARibbonPannelLayout::expandingDirections() const -{ - return (Qt::Horizontal); -} - -QSize SARibbonPannelLayout::minimumSize() const -{ - return (mSizeHint); -} - -QSize SARibbonPannelLayout::sizeHint() const -{ -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonPannel* pannel = ribbonPannel()) { - qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint; - } -#endif - return (mSizeHint); -} - -/** - * @brief 通过action获取SARibbonPannelItem - * @param action - * @return 如果没有返回nullptr - */ -SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const -{ - int index = indexByAction(action); - - if (index >= 0) { - return (mItems[ index ]); - } - return (nullptr); -} - -/** - * @brief 获取最后一个添加的item - * @return 如果没有返回nullptr - */ -SARibbonPannelItem* SARibbonPannelLayout::lastItem() const -{ - if (mItems.isEmpty()) { - return (nullptr); - } - return (mItems.last()); -} - -/** - * @brief 获取最后生成的窗口 - * @return 如果无窗口或者item为空,返回nullptr - */ -QWidget* SARibbonPannelLayout::lastWidget() const -{ - SARibbonPannelItem* item = lastItem(); - - if (item) { - return (item->widget()); - } - return (nullptr); -} - -/** - * @brief 移动两个item - * @param from - * @param to - * @note 移动完后所有都失效,需要重新布局 - */ -void SARibbonPannelLayout::move(int from, int to) -{ - if (from == to) { - return; - } - if (to < 0) { - to = 0; - } - if (to >= count()) { - to = count() - 1; - } - mItems.move(from, to); - invalidate(); -} - -/** - * @brief 判断是否需要重新布局 - * @return - */ -bool SARibbonPannelLayout::isDirty() const -{ - return (mDirty); -} - -void SARibbonPannelLayout::updateGeomArray() -{ - updateGeomArray(geometry()); -} - -/** - * @brief 布局所有action - */ -void SARibbonPannelLayout::doLayout() -{ -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonPannel* pannel = ribbonPannel()) { - qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName(); - } -#endif - if (isDirty()) { - updateGeomArray(); - } - QList< QWidget* > showWidgets, hideWidgets; - SARibbonPannel* pannel = ribbonPannel(); - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if (item->isEmpty()) { - hideWidgets << item->widget(); - } else { - // 在category发现item->setGeometry有点奇怪的现象,这里统一使用item->widget->setgeo - // item->setGeometry(item->itemWillSetGeometry); - if (item->widget()) { - item->widget()->setGeometry(item->itemWillSetGeometry); - } - showWidgets << item->widget(); - } - } - - // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 - for (QWidget* w : qAsConst(showWidgets)) { - if (!w->isVisible()) - w->show(); - } - for (QWidget* w : qAsConst(hideWidgets)) { - if (w->isVisible()) - w->hide(); - } - // 布局label - if (mTitleLabel) { - if (isEnableShowPannelTitle()) { - mTitleLabel->setGeometry(mTitleLabelGeometry); - if (!mTitleLabel->isVisibleTo(pannel)) { - mTitleLabel->show(); - } - } else { - if (mTitleLabel->isVisibleTo(pannel)) { - mTitleLabel->hide(); - } - } - } - // 布局m_optionActionBtn - if (mOptionActionBtn) { - mOptionActionBtn->setGeometry(mOptionActionBtnGeometry); - mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height())); - } -} - -/** - * @brief 把action转换为item - * - * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action) - * - * 对于普通QAction,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考SARibbonPannelItem::RowProportion, - * @param action - * @param rp 行高占比情况 - * @return 转换的SARibbonPannelItem - * @note 每个SARibbonPannelItem最终都会携带一个widget,传入的是QWidgetAction的话,会直接使用QWidgetAction带的widget, - * 否则会内部生成一个SARibbonToolButton - * - */ -SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp) -{ - bool customWidget = false; - QWidget* widget = nullptr; - SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); - - if (!pannel) { - // 在没有pannel这个函数会返回nullptr - return (nullptr); - } - if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) { - widget = widgetAction->requestWidget(pannel); - if (widget != nullptr) { - widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); - customWidget = true; // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton - } - } else if (action->isSeparator()) { - SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel); - widget = sep; - } - // 不是widget,自动生成SARibbonToolbutton - if (!widget) { - SARibbonToolButton::RibbonButtonType buttonType = ((rp == SARibbonPannelItem::Large) - ? SARibbonToolButton::LargeButton - : SARibbonToolButton::SmallButton); - - SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel); - button->setFocusPolicy(Qt::NoFocus); - button->setButtonType(buttonType); - button->setDefaultAction(action); - button->setIconSize(mDefaultToolButtonIconSize); - // 属性设置 - QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action); - button->setPopupMode(popMode); - // 根据QAction的属性设置按钮的大小 - - QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered); - widget = button; - } - // 这时总会有widget - widget->hide(); - SARibbonPannelItem* result = new SARibbonPannelItem(widget); - - result->rowProportion = rp; - result->customWidget = customWidget; - result->action = action; - return (result); -} - -/** - * @brief 更新尺寸 - */ -void SARibbonPannelLayout::updateGeomArray(const QRect& setrect) -{ - SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); - - if (!pannel) { - return; - } - - const int height = setrect.height(); - const QMargins& mag = contentsMargins(); - const int spacing = this->spacing(); - const int spacingRow = 1; // 高度间距,也就是行间距,不同行之间的距离 - int x = mag.left(); - const int yBegin = mag.top(); - int titleH = (mTitleHeight >= 0) ? mTitleHeight : 0; // 防止负数影响 - int titleSpace = (mTitleHeight >= 0) ? mTitleSpace : 0; // 对于没有标题的情况,spacing就不生效 - if (!isEnableShowPannelTitle()) { - titleH = 0; - titleSpace = 0; - } - // 获取pannel的布局模式 3行或者2行 - // rowcount 是ribbon的行,有2行和3行两种 - const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2; - // largeHeight是对应large占比的高度 - const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace; - const int yTitleBegin = height - mag.bottom() - titleH; - mLargeHeight = largeHeight; - // 计算smallHeight的高度 - const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount; - // Medium行的y位置 - const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3)); - const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow) - : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight); - // Small行的y位置 - const int ySmallRow0 = yBegin; - const int ySmallRow1 = yBegin + smallHeight + spacingRow; - const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow); - // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行, - // item->rowIndex主要用于SARibbonPannelItem::Medium - short row = 0; - int column = 0; - // 记录每列最大的宽度 - int columMaxWidth = 0; - // 记录总宽度 - int totalWidth = 0; - - int itemCount = mItems.count(); - -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - QString debug_print__log__; -#endif - // 本列第一、二行占比 - SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None; - SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item - for (int i = 0; i < itemCount; ++i) { - SARibbonPannelItem* item = mItems.at(i); - if (item->isEmpty()) { - // 如果是hide就直接跳过 - item->rowIndex = -1; - item->columnIndex = -1; - continue; - } - - QSize hint = item->sizeHint(); -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) { - auto ss__ = tb->sizeHint(); - debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n") - .arg(i) - .arg(ss__.width()) - .arg(ss__.height()) - .arg(tb->text()); - } -#endif - Qt::Orientations exp = item->expandingDirections(); - if (item->widget()) { - // 有窗口是水平扩展,则标记为扩展 - if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { - mExpandFlag = true; - } - } - SARibbonPannelItem::RowProportion rp = item->rowProportion; - if (SARibbonPannelItem::None == rp) { - // 为定义行占比但是垂直扩展,就定义为Large占比,否则就是small占比 - if (exp & Qt::Vertical) { - rp = SARibbonPannelItem::Large; - } else { - rp = SARibbonPannelItem::Small; - } - } - // 开始根据占比和layoutmode来布局 - switch (rp) { - case SARibbonPannelItem::Large: { - // !!在Large,如果不是处于新列的第一行,就需要进行换列处理 - // 把large一直设置在下一列的开始 - if (row != 0) { - x += (columMaxWidth + spacing); - ++column; - } - // - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight); - columMaxWidth = hint.width(); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } break; - - case SARibbonPannelItem::Medium: { - // 2行模式下Medium和small等价 - if (2 == rowCount) { - if (0 == row) { - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - // 下个row为1 - row = 1; - // x不变 - } else { - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } else { - // 3行模式 - if (0 == row) { - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - row = 1; - // x不变 - } else if (1 == row) { - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } else { - // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium - // 这时需要先换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - ++column; - // 换列后此时等价于0 == row - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Medium; - columMaxWidth = hint.width(); - row = 1; - } - } - } break; - - case SARibbonPannelItem::Small: { - if (0 == row) { - // 第一行 - item->rowIndex = 0; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight); - thisColumnRP0 = SARibbonPannelItem::Small; - columMaxWidth = hint.width(); - // 下个row为1 - row = 1; - // x不变 - } else if (1 == row) { - // 第二行 - item->rowIndex = 1; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight); - if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { - // 三行模式,并且第一行是Medium - item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); - } - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 这里要看两行还是三行,确定是否要换列 - if (2 == rowCount) { - // 两行模式,换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } else { - // 三行模式,继续增加行数 - row = 2; - // x不变 - } - if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { - // 三行模式,并且第一行是Medium,换列 - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } else { - // 第三行 - item->rowIndex = 2; - item->columnIndex = column; - item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight); - // 和上个进行比较得到最长宽度 - columMaxWidth = qMax(columMaxWidth, hint.width()); - // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 - x += (columMaxWidth + spacing); - row = 0; - columMaxWidth = 0; - ++column; - } - } break; - - default: - // 不可能出现 - break; - } - lastGeomItem = item; - } - // 最后一个元素,更新列数 - // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常 - if (lastGeomItem) { // 最后一个元素,更新totalWidth - if (lastGeomItem->columnIndex != column) { - // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引 - mColumnCount = column; - // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth要减去已经加入的spacing - totalWidth = x - spacing + mag.right(); - } else { - // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1 - mColumnCount = column + 1; - // 由于最后一个元素未触发换列,需要计算totalWidth - totalWidth = x + columMaxWidth + mag.right(); - } - } - - // 在设置完所有窗口后,再设置扩展属性的窗口 - if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) { - // 说明可以设置扩展属性的窗口 - recalcExpandGeomArray(setrect); - } - // 布局label - bool isTitleWidthThanPannel = false; - if (isEnableShowPannelTitle()) { - mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH); - // 这里要确认标题宽度是否大于totalWidth,如果大于,则要把标题的宽度作为totalwidth - QFontMetrics fm = mTitleLabel->fontMetrics(); - int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName()); - textWidth += 4; - if (totalWidth < textWidth) { - totalWidth = textWidth; - isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度 - } - } - // 布局optionActionButton - - if (isHaveOptionAction()) { - QSize optBtnSize = optionActionButtonSize(); - if (isEnableShowPannelTitle()) { - // 有标题 - mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(), - mTitleLabelGeometry.y(), - mTitleLabelGeometry.height(), - mTitleLabelGeometry.height()); - - // 特殊情况,如果pannel的标题长度大于totalWidth,那么说明totalWidth比较短 - // 这时候,optionActionBtn的宽度要加上到标题宽度上 - if (isTitleWidthThanPannel) { - // 由于文字是居中对齐,因此要扩展2个按钮的宽度 - totalWidth += (2 * titleH); - } - } else { - // 无标题 - mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(), - setrect.bottom() - optBtnSize.height() - mag.bottom(), - optBtnSize.width(), - optBtnSize.height()); - totalWidth += optBtnSize.width(); - } - } - // 刷新sizeHint - int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH); - this->mSizeHint = QSize(totalWidth, heightHint); -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName() - << "\n| | |-size hint =" << this->m_sizeHint // - << "\n| | |-totalWidth=" << totalWidth // - << "\n| | |-last x=" << x // - << "\n| | |-columMaxWidth=" << columMaxWidth // - << "\n| | |-spacing=" << spacing // - << "\n| | |-mag=" << mag // - << "\n| | |-largeHeight=" << largeHeight // - << "\n| | |-smallHeight=" << smallHeight // - ; - qDebug().noquote() << debug_print__log__; -#endif -} - -void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect) -{ - // 计算能扩展的尺寸 - int expandwidth = setrect.width() - this->mSizeHint.width(); - - if (expandwidth <= 0) { - // 没有必要设置 - return; - } - // 列扩展信息 - struct _columnExpandInfo - { - int oldColumnWidth = 0; ///< 原来的列宽 - int columnMaximumWidth = -1; ///< 列的最大宽度 - int columnExpandedWidth = 0; ///< 扩展后列的宽度 - QList< SARibbonPannelItem* > expandItems; - }; - // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展 - QMap< int, _columnExpandInfo > columnExpandInfo; - - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) { - // 只获取可见的 - QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex); - if (i == columnExpandInfo.end()) { - i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo()); - } - i.value().expandItems.append(item); - } - } - if (columnExpandInfo.size() <= 0) { - // 没有需要扩展的就退出 - return; - } - // 获取完可扩展的列和控件后,计算对应的列的尺寸 - // 计算能扩展的尺寸 - int oneColCanexpandWidth = expandwidth / columnExpandInfo.size(); - - for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) { - int& oldColumnWidth = i.value().oldColumnWidth; - int& columnMaximumWidth = i.value().columnMaximumWidth; - this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth); - if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) { - // 如果小于0说明没有这个列,这种属于异常,删除继续 - // oldColumnWidth > columnMaximumWidth也是异常 - i = columnExpandInfo.erase(i); - continue; - } - // 开始调整 - int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了 - if (colwidth >= columnMaximumWidth) { - // 过最大宽度要求 - i.value().columnExpandedWidth = columnMaximumWidth; - } else { - i.value().columnExpandedWidth = colwidth; - } - ++i; - } - // 从新调整尺寸 - // 由于会涉及其他列的变更,因此需要所有都遍历一下 - for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) { - int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth; - for (SARibbonPannelItem* item : qAsConst(mItems)) { - if (item->isEmpty() || (item->columnIndex < i.key())) { - // 之前的列不用管 - continue; - } - if (item->columnIndex == i.key()) { - // 此列的扩展 - if (i.value().expandItems.contains(item)) { - // 此列需要扩展的item才扩展尺寸 - item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth); - } else { - // 此列不扩展的模块保持原来的尺寸 - continue; - } - } else { - // 后面的移动 - item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen); - } - } - } -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect - << ") pannelName=" << ribbonPannel()->pannelName() // - << ",expandwidth=" << expandwidth // - ; -#endif -} - -/** - * @brief 根据列数,计算窗口的宽度,以及最大宽度 - * @param colindex - * @param width 如果传入没有这个列,返回-1 - * @param maximum 如果传入没有这个列,返回-1 - */ -void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const -{ - width = -1; - maximum = -1; - for (SARibbonPannelItem* item : mItems) { - if (!item->isEmpty() && (item->columnIndex == colindex)) { - width = qMax(width, item->itemWillSetGeometry.width()); - maximum = qMax(maximum, item->widget()->maximumWidth()); - } - } -} - -SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const -{ - return mTitleLabel; -} - -void SARibbonPannelLayout::setToolButtonIconSize(const QSize& s) -{ - mDefaultToolButtonIconSize = s; -} - -QSize SARibbonPannelLayout::toolButtonIconSize() const -{ - return mDefaultToolButtonIconSize; -} - -/** - * @brief 获取optionAction 按钮尺寸 - * @return - */ -QSize SARibbonPannelLayout::optionActionButtonSize() const -{ - return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight)); -} - -void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel) -{ - mTitleLabel = newTitleLabel; - // 确保m_optionActionBtn在label之上 - if (mOptionActionBtn) { - if (mTitleLabel) { - mTitleLabel->stackUnder(mOptionActionBtn); - } - } -} - -/** - * @brief 标题区域和按钮的间隔 - * @return - */ -int SARibbonPannelLayout::pannelTitleSpace() const -{ - return mTitleSpace; -} - -/** - * @brief 设置标题区域和按钮的间隔 - * @param newTitleSpace - */ -void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace) -{ - if (mTitleSpace == newTitleSpace) { - return; - } - mTitleSpace = newTitleSpace; - invalidate(); -} - -/** - * @brief 标题高度 - * @return - */ -int SARibbonPannelLayout::pannelTitleHeight() const -{ - return mTitleHeight; -} - -/** - * @brief 设置标题高度 - * @param newTitleHeight - */ -void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight) -{ - if (mTitleHeight == newTitleHeight) { - return; - } - mTitleHeight = newTitleHeight; - invalidate(); -} - -/** - * @brief 判断是否存在标题 - * @return - */ -bool SARibbonPannelLayout::isEnableShowPannelTitle() const -{ - return mEnableShowTitle; -} - -/** - * @brief 设置显示标题 - * @param on - */ -void SARibbonPannelLayout::setEnableShowPannelTitle(bool on) -{ - if (mEnableShowTitle == on) { - return; - } - mEnableShowTitle = on; - invalidate(); -} - -/** - * @brief 大按钮的高度 - * @return - */ -int SARibbonPannelLayout::largeButtonHeight() const -{ - return mLargeHeight; -} - -void SARibbonPannelLayout::setGeometry(const QRect& rect) -{ - QRect old = geometry(); - if (old == rect) { - return; - } -#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")======="; -#endif - QLayout::setGeometry(rect); - mDirty = false; - updateGeomArray(rect); - doLayout(); -} +#include "SARibbonPannelLayout.h" +#include "SARibbonPannelOptionButton.h" +#include "SARibbonSeparatorWidget.h" +#include "SARibbonElementManager.h" +#include +#include +#include "SARibbonPannel.h" +#include "SARibbonPannelItem.h" +#define SARibbonPannelLayout_DEBUG_PRINT 1 +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true) +{ + setSpacing(1); + SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p); + + if (!tb) { + return; + } +} + +SARibbonPannelLayout::~SARibbonPannelLayout() +{ + // 参考QToolBarLayout + while (!mItems.isEmpty()) { + SARibbonPannelItem* item = mItems.takeFirst(); + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) { + if (item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } + } + delete item; + } +} + +/** + * @brief 通过action查找索引,用于actionEvent添加action用 + * @param action + * @return 没有查到返回-1 + */ +int SARibbonPannelLayout::indexByAction(QAction* action) const +{ + for (int i = 0; i < mItems.count(); ++i) { + if (mItems.at(i)->action == action) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取ribbonpannel + * @return + */ +SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const +{ + return qobject_cast< SARibbonPannel* >(parentWidget()); +} + +void SARibbonPannelLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead"); + return; +} + +/** + * @brief SARibbonPannel主要通过此函数来添加action + * @param act + * @param rp 布局策略 + */ +void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp) +{ + index = qMax(0, index); + index = qMin(mItems.count(), index); + SARibbonPannelItem* item = createItem(act, rp); + + if (item) { + mItems.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); + } +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannelLayout::setOptionAction(QAction* action) +{ + SARibbonPannel* p = ribbonPannel(); + if (!p) { + return; + } + if (action) { + // 创建option action + if (nullptr == mOptionActionBtn) { + mOptionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p); + QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered); + // 确保m_optionActionBtn在label之上 + if (mTitleLabel) { + mTitleLabel->stackUnder(mOptionActionBtn); + } + } + mOptionActionBtn->setDefaultAction(action); + if (action->icon().isNull()) { + mOptionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); + } + // 标记需要重新计算尺寸 + invalidate(); + } else { + // 取消option action + if (mOptionActionBtn) { + mOptionActionBtn->hide(); + mOptionActionBtn->deleteLater(); + mOptionActionBtn = nullptr; + // 标记需要重新计算尺寸 + invalidate(); + } + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannelLayout::isHaveOptionAction() const +{ + return (mOptionActionBtn != nullptr); +} + +QLayoutItem* SARibbonPannelLayout::itemAt(int index) const +{ + if ((index < 0) || (index >= mItems.count())) { + return (nullptr); + } + return (mItems.at(index)); +} + +QLayoutItem* SARibbonPannelLayout::takeAt(int index) +{ + if ((index < 0) || (index >= mItems.count())) { + return (nullptr); + } + SARibbonPannelItem* item = mItems.takeAt(index); + + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action); + + if ((widgetAction != 0) && item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } else { + // destroy the QToolButton/QToolBarSeparator + item->widget()->hide(); + item->widget()->deleteLater(); + } + + invalidate(); + return (item); +} + +int SARibbonPannelLayout::count() const +{ + return (mItems.count()); +} + +bool SARibbonPannelLayout::isEmpty() const +{ + + return (mItems.isEmpty()); +} + +void SARibbonPannelLayout::invalidate() +{ + mDirty = true; + QLayout::invalidate(); +} + +Qt::Orientations SARibbonPannelLayout::expandingDirections() const +{ + return (Qt::Horizontal); +} + +QSize SARibbonPannelLayout::minimumSize() const +{ + return (mSizeHint); +} + +QSize SARibbonPannelLayout::sizeHint() const +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint; + } +#endif + return (mSizeHint); +} + +/** + * @brief 通过action获取SARibbonPannelItem + * @param action + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const +{ + int index = indexByAction(action); + + if (index >= 0) { + return (mItems[ index ]); + } + return (nullptr); +} + +/** + * @brief 获取最后一个添加的item + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::lastItem() const +{ + if (mItems.isEmpty()) { + return (nullptr); + } + return (mItems.last()); +} + +/** + * @brief 获取最后生成的窗口 + * @return 如果无窗口或者item为空,返回nullptr + */ +QWidget* SARibbonPannelLayout::lastWidget() const +{ + SARibbonPannelItem* item = lastItem(); + + if (item) { + return (item->widget()); + } + return (nullptr); +} + +/** + * @brief 移动两个item + * @param from + * @param to + * @note 移动完后所有都失效,需要重新布局 + */ +void SARibbonPannelLayout::move(int from, int to) +{ + if (from == to) { + return; + } + if (to < 0) { + to = 0; + } + if (to >= count()) { + to = count() - 1; + } + mItems.move(from, to); + invalidate(); +} + +/** + * @brief 判断是否需要重新布局 + * @return + */ +bool SARibbonPannelLayout::isDirty() const +{ + return (mDirty); +} + +void SARibbonPannelLayout::updateGeomArray() +{ + updateGeomArray(geometry()); +} + +/** + * @brief 布局所有action + */ +void SARibbonPannelLayout::doLayout() +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName(); + } +#endif + if (isDirty()) { + updateGeomArray(); + } + QList< QWidget* > showWidgets, hideWidgets; + SARibbonPannel* pannel = ribbonPannel(); + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + } else { + // 在category发现item->setGeometry有点奇怪的现象,这里统一使用item->widget->setgeo + // item->setGeometry(item->itemWillSetGeometry); + if (item->widget()) { + item->widget()->setGeometry(item->itemWillSetGeometry); + } + showWidgets << item->widget(); + } + } + + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) + w->show(); + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) + w->hide(); + } + // 布局label + if (mTitleLabel) { + if (isEnableShowPannelTitle()) { + mTitleLabel->setGeometry(mTitleLabelGeometry); + if (!mTitleLabel->isVisibleTo(pannel)) { + mTitleLabel->show(); + } + } else { + if (mTitleLabel->isVisibleTo(pannel)) { + mTitleLabel->hide(); + } + } + } + // 布局m_optionActionBtn + if (mOptionActionBtn) { + mOptionActionBtn->setGeometry(mOptionActionBtnGeometry); + mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height())); + } +} + +/** + * @brief 把action转换为item + * + * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action) + * + * 对于普通QAction,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考SARibbonPannelItem::RowProportion, + * @param action + * @param rp 行高占比情况 + * @return 转换的SARibbonPannelItem + * @note 每个SARibbonPannelItem最终都会携带一个widget,传入的是QWidgetAction的话,会直接使用QWidgetAction带的widget, + * 否则会内部生成一个SARibbonToolButton + * + */ +SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + bool customWidget = false; + QWidget* widget = nullptr; + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + // 在没有pannel这个函数会返回nullptr + return (nullptr); + } + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) { + widget = widgetAction->requestWidget(pannel); + if (widget != nullptr) { + widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); + customWidget = true; // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton + } + } else if (action->isSeparator()) { + SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel); + widget = sep; + } + // 不是widget,自动生成SARibbonToolbutton + if (!widget) { + SARibbonToolButton::RibbonButtonType buttonType = ((rp == SARibbonPannelItem::Large) + ? SARibbonToolButton::LargeButton + : SARibbonToolButton::SmallButton); + + SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel); + button->setFocusPolicy(Qt::NoFocus); + button->setButtonType(buttonType); + button->setDefaultAction(action); + button->setIconSize(mDefaultToolButtonIconSize); + // 属性设置 + QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action); + button->setPopupMode(popMode); + // 根据QAction的属性设置按钮的大小 + + QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered); + widget = button; + } + // 这时总会有widget + widget->hide(); + SARibbonPannelItem* result = new SARibbonPannelItem(widget); + + result->rowProportion = rp; + result->customWidget = customWidget; + result->action = action; + return (result); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonPannelLayout::updateGeomArray(const QRect& setrect) +{ + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + return; + } + + const int height = setrect.height(); + const QMargins& mag = contentsMargins(); + const int spacing = this->spacing(); + const int spacingRow = 1; // 高度间距,也就是行间距,不同行之间的距离 + int x = mag.left(); + const int yBegin = mag.top(); + int titleH = (mTitleHeight >= 0) ? mTitleHeight : 0; // 防止负数影响 + int titleSpace = (mTitleHeight >= 0) ? mTitleSpace : 0; // 对于没有标题的情况,spacing就不生效 + if (!isEnableShowPannelTitle()) { + titleH = 0; + titleSpace = 0; + } + // 获取pannel的布局模式 3行或者2行 + // rowcount 是ribbon的行,有2行和3行两种 + const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2; + // largeHeight是对应large占比的高度 + const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace; + const int yTitleBegin = height - mag.bottom() - titleH; + mLargeHeight = largeHeight; + // 计算smallHeight的高度 + const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount; + // Medium行的y位置 + const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3)); + const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow) + : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight); + // Small行的y位置 + const int ySmallRow0 = yBegin; + const int ySmallRow1 = yBegin + smallHeight + spacingRow; + const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow); + // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行, + // item->rowIndex主要用于SARibbonPannelItem::Medium + short row = 0; + int column = 0; + // 记录每列最大的宽度 + int columMaxWidth = 0; + // 记录总宽度 + int totalWidth = 0; + + int itemCount = mItems.count(); + +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + QString debug_print__log__; +#endif + // 本列第一、二行占比 + SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None; + SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item + for (int i = 0; i < itemCount; ++i) { + SARibbonPannelItem* item = mItems.at(i); + if (item->isEmpty()) { + // 如果是hide就直接跳过 + item->rowIndex = -1; + item->columnIndex = -1; + continue; + } + + QSize hint = item->sizeHint(); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) { + auto ss__ = tb->sizeHint(); + debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n") + .arg(i) + .arg(ss__.width()) + .arg(ss__.height()) + .arg(tb->text()); + } +#endif + Qt::Orientations exp = item->expandingDirections(); + if (item->widget()) { + // 有窗口是水平扩展,则标记为扩展 + if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { + mExpandFlag = true; + } + } + SARibbonPannelItem::RowProportion rp = item->rowProportion; + if (SARibbonPannelItem::None == rp) { + // 为定义行占比但是垂直扩展,就定义为Large占比,否则就是small占比 + if (exp & Qt::Vertical) { + rp = SARibbonPannelItem::Large; + } else { + rp = SARibbonPannelItem::Small; + } + } + // 开始根据占比和layoutmode来布局 + switch (rp) { + case SARibbonPannelItem::Large: { + // !!在Large,如果不是处于新列的第一行,就需要进行换列处理 + // 把large一直设置在下一列的开始 + if (row != 0) { + x += (columMaxWidth + spacing); + ++column; + } + // + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight); + columMaxWidth = hint.width(); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } break; + + case SARibbonPannelItem::Medium: { + // 2行模式下Medium和small等价 + if (2 == rowCount) { + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 3行模式 + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + // x不变 + } else if (1 == row) { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium + // 这时需要先换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + ++column; + // 换列后此时等价于0 == row + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + } + } + } break; + + case SARibbonPannelItem::Small: { + if (0 == row) { + // 第一行 + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Small; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else if (1 == row) { + // 第二行 + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight); + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + } + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 这里要看两行还是三行,确定是否要换列 + if (2 == rowCount) { + // 两行模式,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 三行模式,继续增加行数 + row = 2; + // x不变 + } + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 第三行 + item->rowIndex = 2; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } break; + + default: + // 不可能出现 + break; + } + lastGeomItem = item; + } + // 最后一个元素,更新列数 + // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常 + if (lastGeomItem) { // 最后一个元素,更新totalWidth + if (lastGeomItem->columnIndex != column) { + // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引 + mColumnCount = column; + // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth要减去已经加入的spacing + totalWidth = x - spacing + mag.right(); + } else { + // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1 + mColumnCount = column + 1; + // 由于最后一个元素未触发换列,需要计算totalWidth + totalWidth = x + columMaxWidth + mag.right(); + } + } + + // 在设置完所有窗口后,再设置扩展属性的窗口 + if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) { + // 说明可以设置扩展属性的窗口 + recalcExpandGeomArray(setrect); + } + // 布局label + bool isTitleWidthThanPannel = false; + if (isEnableShowPannelTitle()) { + mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH); + // 这里要确认标题宽度是否大于totalWidth,如果大于,则要把标题的宽度作为totalwidth + QFontMetrics fm = mTitleLabel->fontMetrics(); + int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName()); + textWidth += 4; + if (totalWidth < textWidth) { + totalWidth = textWidth; + isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度 + } + } + // 布局optionActionButton + + if (isHaveOptionAction()) { + QSize optBtnSize = optionActionButtonSize(); + if (isEnableShowPannelTitle()) { + // 有标题 + mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(), + mTitleLabelGeometry.y(), + mTitleLabelGeometry.height(), + mTitleLabelGeometry.height()); + + // 特殊情况,如果pannel的标题长度大于totalWidth,那么说明totalWidth比较短 + // 这时候,optionActionBtn的宽度要加上到标题宽度上 + if (isTitleWidthThanPannel) { + // 由于文字是居中对齐,因此要扩展2个按钮的宽度 + totalWidth += (2 * titleH); + } + } else { + // 无标题 + mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(), + setrect.bottom() - optBtnSize.height() - mag.bottom(), + optBtnSize.width(), + optBtnSize.height()); + totalWidth += optBtnSize.width(); + } + } + // 刷新sizeHint + int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH); + this->mSizeHint = QSize(totalWidth, heightHint); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName() + << "\n| | |-size hint =" << this->m_sizeHint // + << "\n| | |-totalWidth=" << totalWidth // + << "\n| | |-last x=" << x // + << "\n| | |-columMaxWidth=" << columMaxWidth // + << "\n| | |-spacing=" << spacing // + << "\n| | |-mag=" << mag // + << "\n| | |-largeHeight=" << largeHeight // + << "\n| | |-smallHeight=" << smallHeight // + ; + qDebug().noquote() << debug_print__log__; +#endif +} + +void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect) +{ + // 计算能扩展的尺寸 + int expandwidth = setrect.width() - this->mSizeHint.width(); + + if (expandwidth <= 0) { + // 没有必要设置 + return; + } + // 列扩展信息 + struct _columnExpandInfo + { + int oldColumnWidth = 0; ///< 原来的列宽 + int columnMaximumWidth = -1; ///< 列的最大宽度 + int columnExpandedWidth = 0; ///< 扩展后列的宽度 + QList< SARibbonPannelItem* > expandItems; + }; + // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展 + QMap< int, _columnExpandInfo > columnExpandInfo; + + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) { + // 只获取可见的 + QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex); + if (i == columnExpandInfo.end()) { + i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo()); + } + i.value().expandItems.append(item); + } + } + if (columnExpandInfo.size() <= 0) { + // 没有需要扩展的就退出 + return; + } + // 获取完可扩展的列和控件后,计算对应的列的尺寸 + // 计算能扩展的尺寸 + int oneColCanexpandWidth = expandwidth / columnExpandInfo.size(); + + for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) { + int& oldColumnWidth = i.value().oldColumnWidth; + int& columnMaximumWidth = i.value().columnMaximumWidth; + this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth); + if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) { + // 如果小于0说明没有这个列,这种属于异常,删除继续 + // oldColumnWidth > columnMaximumWidth也是异常 + i = columnExpandInfo.erase(i); + continue; + } + // 开始调整 + int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了 + if (colwidth >= columnMaximumWidth) { + // 过最大宽度要求 + i.value().columnExpandedWidth = columnMaximumWidth; + } else { + i.value().columnExpandedWidth = colwidth; + } + ++i; + } + // 从新调整尺寸 + // 由于会涉及其他列的变更,因此需要所有都遍历一下 + for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) { + int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth; + for (SARibbonPannelItem* item : qAsConst(mItems)) { + if (item->isEmpty() || (item->columnIndex < i.key())) { + // 之前的列不用管 + continue; + } + if (item->columnIndex == i.key()) { + // 此列的扩展 + if (i.value().expandItems.contains(item)) { + // 此列需要扩展的item才扩展尺寸 + item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth); + } else { + // 此列不扩展的模块保持原来的尺寸 + continue; + } + } else { + // 后面的移动 + item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen); + } + } + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect + << ") pannelName=" << ribbonPannel()->pannelName() // + << ",expandwidth=" << expandwidth // + ; +#endif +} + +/** + * @brief 根据列数,计算窗口的宽度,以及最大宽度 + * @param colindex + * @param width 如果传入没有这个列,返回-1 + * @param maximum 如果传入没有这个列,返回-1 + */ +void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const +{ + width = -1; + maximum = -1; + for (SARibbonPannelItem* item : mItems) { + if (!item->isEmpty() && (item->columnIndex == colindex)) { + width = qMax(width, item->itemWillSetGeometry.width()); + maximum = qMax(maximum, item->widget()->maximumWidth()); + } + } +} + +SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const +{ + return mTitleLabel; +} + +void SARibbonPannelLayout::setToolButtonIconSize(const QSize& s) +{ + mDefaultToolButtonIconSize = s; +} + +QSize SARibbonPannelLayout::toolButtonIconSize() const +{ + return mDefaultToolButtonIconSize; +} + +/** + * @brief 获取optionAction 按钮尺寸 + * @return + */ +QSize SARibbonPannelLayout::optionActionButtonSize() const +{ + return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight)); +} + +void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel) +{ + mTitleLabel = newTitleLabel; + // 确保m_optionActionBtn在label之上 + if (mOptionActionBtn) { + if (mTitleLabel) { + mTitleLabel->stackUnder(mOptionActionBtn); + } + } +} + +/** + * @brief 标题区域和按钮的间隔 + * @return + */ +int SARibbonPannelLayout::pannelTitleSpace() const +{ + return mTitleSpace; +} + +/** + * @brief 设置标题区域和按钮的间隔 + * @param newTitleSpace + */ +void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace) +{ + if (mTitleSpace == newTitleSpace) { + return; + } + mTitleSpace = newTitleSpace; + invalidate(); +} + +/** + * @brief 标题高度 + * @return + */ +int SARibbonPannelLayout::pannelTitleHeight() const +{ + return mTitleHeight; +} + +/** + * @brief 设置标题高度 + * @param newTitleHeight + */ +void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight) +{ + if (mTitleHeight == newTitleHeight) { + return; + } + mTitleHeight = newTitleHeight; + invalidate(); +} + +/** + * @brief 判断是否存在标题 + * @return + */ +bool SARibbonPannelLayout::isEnableShowPannelTitle() const +{ + return mEnableShowTitle; +} + +/** + * @brief 设置显示标题 + * @param on + */ +void SARibbonPannelLayout::setEnableShowPannelTitle(bool on) +{ + if (mEnableShowTitle == on) { + return; + } + mEnableShowTitle = on; + invalidate(); +} + +/** + * @brief 大按钮的高度 + * @return + */ +int SARibbonPannelLayout::largeButtonHeight() const +{ + return mLargeHeight; +} + +void SARibbonPannelLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")======="; +#endif + QLayout::setGeometry(rect); + mDirty = false; + updateGeomArray(rect); + doLayout(); +} diff --git a/src/SARibbonBar/SARibbonPannelLayout.h b/src/SARibbonBar/SARibbonPannelLayout.h index 993eeaf9..d45abf20 100644 --- a/src/SARibbonBar/SARibbonPannelLayout.h +++ b/src/SARibbonBar/SARibbonPannelLayout.h @@ -1,113 +1,113 @@ -#ifndef SARIBBONPANNELLAYOUT_H -#define SARIBBONPANNELLAYOUT_H -#include "SARibbonGlobal.h" -#include -#include "SARibbonPannelItem.h" -class QToolButton; -class SARibbonPannel; -class SARibbonPannelLabel; -/** - * @brief 针对SARibbonPannel的布局 - * - * SARibbonPannelLayout实际是一个列布局,每一列有2~3行,看窗口定占几行 - * - * 核心函数: @ref SARibbonPannelLayout::createItem - * - * @note QLayout::contentsMargins 函数不会启作用,如果要设置contentsMargins,使用@sa setPannelContentsMargins - */ -class SA_RIBBON_EXPORT SARibbonPannelLayout : public QLayout -{ - Q_OBJECT - friend class SARibbonPannel; - -public: - explicit SARibbonPannelLayout(QWidget* p = nullptr); - ~SARibbonPannelLayout(); - // SARibbonPannelLayout additem 无效 - void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; - // QLayout 所必须的重载函数 - QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; - QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; - int count() const Q_DECL_OVERRIDE; - bool isEmpty() const Q_DECL_OVERRIDE; - void invalidate() Q_DECL_OVERRIDE; - Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; - void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; - QSize minimumSize() const Q_DECL_OVERRIDE; - QSize sizeHint() const Q_DECL_OVERRIDE; - - // 获取ribbonpannel - SARibbonPannel* ribbonPannel() const; - // SARibbonPannel主要通过此函数来添加action - void insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); - // 设置OptionAction,如果要去除,传入nullptr指针即可 - void setOptionAction(QAction* action); - bool isHaveOptionAction() const; - // 通过action获取SARibbonPannelItem - SARibbonPannelItem* pannelItem(QAction* action) const; - // 获取最后一个添加的item - SARibbonPannelItem* lastItem() const; - // 获取最后生成的窗口 - QWidget* lastWidget() const; - // 移动两个item - void move(int from, int to); - // 判断是否需要重新布局 - bool isDirty() const; - // 更新尺寸 - void updateGeomArray(); - // 通过action获取SARibbonPannelItem的索引 - int indexByAction(QAction* action) const; - // 标题高度 - int pannelTitleHeight() const; - void setPannelTitleHeight(int newTitleHeight); - // 判断是否存在标题 - bool isEnableShowPannelTitle() const; - void setEnableShowPannelTitle(bool on); - // 返回大按钮的高度 - int largeButtonHeight() const; - // 标题区域和按钮的间隔 - int pannelTitleSpace() const; - void setPannelTitleSpace(int newTitleSpace); - // pannel 标题的label - SARibbonPannelLabel* pannelTitleLabel() const; - // 设置按钮的icon尺寸,large action不受此尺寸影响 - void setToolButtonIconSize(const QSize& s); - QSize toolButtonIconSize() const; - -protected: - // 获取optionAction 按钮尺寸 - QSize optionActionButtonSize() const; - // 布局action - void doLayout(); - // 把action转换为item,对于纯Action,此函数会创建SARibbonToolButton, - // rp用于告诉Layout生成什么样的窗口,详细见SARibbonPannelItem::RowProportion - SARibbonPannelItem* createItem(QAction* action, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); - void updateGeomArray(const QRect& setrect); - // 重新计算扩展条码,此函数必须在updateGeomArray函数之后调用 - void recalcExpandGeomArray(const QRect& setrect); - // 返回optionActionButton的尺寸 - -private: - // 根据列数,计算窗口的宽度,以及最大宽度 - void columnWidthInfo(int colindex, int& width, int& maximum) const; - // 设置titlelabel - void setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel); - -private: - QList< SARibbonPannelItem* > mItems; - int mColumnCount { 0 }; ///< 记录有多少列 - bool mExpandFlag { false }; ///< 标记是否是会扩展的 - QSize mSizeHint; ///< sizeHint返回的尺寸 - QSize mDefaultToolButtonIconSize { 22, 22 }; ///< 记录默认的按钮图标尺寸 - bool mDirty { true }; ///< 用于标记是否需要刷新元素,参考QToolBarLayout源码 - int mLargeHeight { 0 }; ///< 记录大图标的高度 - int mTitleHeight { 15 }; ///< 标题区域高度 - int mTitleSpace { 2 }; ///< 标题区域和按钮的间隔 - bool mEnableShowTitle { true }; ///< 是否运行显示pannel标题 - SARibbonPannelLabel* mTitleLabel { nullptr }; ///< titlelabel指针 - QRect mTitleLabelGeometry; ///< titlelabel的位置 - QToolButton* mOptionActionBtn { nullptr }; ///< optionAction对应的button - QRect mOptionActionBtnGeometry; ///< optionAction的位置 -}; - -#endif // SARIBBONPANNELLAYOUT_H +#ifndef SARIBBONPANNELLAYOUT_H +#define SARIBBONPANNELLAYOUT_H +#include "SARibbonGlobal.h" +#include +#include "SARibbonPannelItem.h" +class QToolButton; +class SARibbonPannel; +class SARibbonPannelLabel; +/** + * @brief 针对SARibbonPannel的布局 + * + * SARibbonPannelLayout实际是一个列布局,每一列有2~3行,看窗口定占几行 + * + * 核心函数: @ref SARibbonPannelLayout::createItem + * + * @note QLayout::contentsMargins 函数不会启作用,如果要设置contentsMargins,使用@sa setPannelContentsMargins + */ +class SA_RIBBON_EXPORT SARibbonPannelLayout : public QLayout +{ + Q_OBJECT + friend class SARibbonPannel; + +public: + explicit SARibbonPannelLayout(QWidget* p = nullptr); + ~SARibbonPannelLayout(); + // SARibbonPannelLayout additem 无效 + void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; + // QLayout 所必须的重载函数 + QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; + QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; + int count() const Q_DECL_OVERRIDE; + bool isEmpty() const Q_DECL_OVERRIDE; + void invalidate() Q_DECL_OVERRIDE; + Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; + void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; + QSize minimumSize() const Q_DECL_OVERRIDE; + QSize sizeHint() const Q_DECL_OVERRIDE; + + // 获取ribbonpannel + SARibbonPannel* ribbonPannel() const; + // SARibbonPannel主要通过此函数来添加action + void insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + // 设置OptionAction,如果要去除,传入nullptr指针即可 + void setOptionAction(QAction* action); + bool isHaveOptionAction() const; + // 通过action获取SARibbonPannelItem + SARibbonPannelItem* pannelItem(QAction* action) const; + // 获取最后一个添加的item + SARibbonPannelItem* lastItem() const; + // 获取最后生成的窗口 + QWidget* lastWidget() const; + // 移动两个item + void move(int from, int to); + // 判断是否需要重新布局 + bool isDirty() const; + // 更新尺寸 + void updateGeomArray(); + // 通过action获取SARibbonPannelItem的索引 + int indexByAction(QAction* action) const; + // 标题高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int newTitleHeight); + // 判断是否存在标题 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + // 返回大按钮的高度 + int largeButtonHeight() const; + // 标题区域和按钮的间隔 + int pannelTitleSpace() const; + void setPannelTitleSpace(int newTitleSpace); + // pannel 标题的label + SARibbonPannelLabel* pannelTitleLabel() const; + // 设置按钮的icon尺寸,large action不受此尺寸影响 + void setToolButtonIconSize(const QSize& s); + QSize toolButtonIconSize() const; + +protected: + // 获取optionAction 按钮尺寸 + QSize optionActionButtonSize() const; + // 布局action + void doLayout(); + // 把action转换为item,对于纯Action,此函数会创建SARibbonToolButton, + // rp用于告诉Layout生成什么样的窗口,详细见SARibbonPannelItem::RowProportion + SARibbonPannelItem* createItem(QAction* action, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + void updateGeomArray(const QRect& setrect); + // 重新计算扩展条码,此函数必须在updateGeomArray函数之后调用 + void recalcExpandGeomArray(const QRect& setrect); + // 返回optionActionButton的尺寸 + +private: + // 根据列数,计算窗口的宽度,以及最大宽度 + void columnWidthInfo(int colindex, int& width, int& maximum) const; + // 设置titlelabel + void setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel); + +private: + QList< SARibbonPannelItem* > mItems; + int mColumnCount { 0 }; ///< 记录有多少列 + bool mExpandFlag { false }; ///< 标记是否是会扩展的 + QSize mSizeHint; ///< sizeHint返回的尺寸 + QSize mDefaultToolButtonIconSize { 22, 22 }; ///< 记录默认的按钮图标尺寸 + bool mDirty { true }; ///< 用于标记是否需要刷新元素,参考QToolBarLayout源码 + int mLargeHeight { 0 }; ///< 记录大图标的高度 + int mTitleHeight { 15 }; ///< 标题区域高度 + int mTitleSpace { 2 }; ///< 标题区域和按钮的间隔 + bool mEnableShowTitle { true }; ///< 是否运行显示pannel标题 + SARibbonPannelLabel* mTitleLabel { nullptr }; ///< titlelabel指针 + QRect mTitleLabelGeometry; ///< titlelabel的位置 + QToolButton* mOptionActionBtn { nullptr }; ///< optionAction对应的button + QRect mOptionActionBtnGeometry; ///< optionAction的位置 +}; + +#endif // SARIBBONPANNELLAYOUT_H diff --git a/src/SARibbonBar/SARibbonPannelOptionButton.cpp b/src/SARibbonBar/SARibbonPannelOptionButton.cpp index 4a7da415..7db387a4 100644 --- a/src/SARibbonBar/SARibbonPannelOptionButton.cpp +++ b/src/SARibbonBar/SARibbonPannelOptionButton.cpp @@ -1,16 +1,16 @@ -#include "SARibbonPannelOptionButton.h" -#include - -SARibbonPannelOptionButton::SARibbonPannelOptionButton(QWidget* parent) : QToolButton(parent) -{ - setAutoRaise(true); - setCheckable(false); - setToolButtonStyle(Qt::ToolButtonIconOnly); - setIconSize(QSize(10, 10)); - static QIcon s_default_icon = QIcon(":/image/resource/ribbonPannelOptionButton.png"); - setIcon(s_default_icon); -} - -SARibbonPannelOptionButton::~SARibbonPannelOptionButton() -{ -} +#include "SARibbonPannelOptionButton.h" +#include + +SARibbonPannelOptionButton::SARibbonPannelOptionButton(QWidget* parent) : QToolButton(parent) +{ + setAutoRaise(true); + setCheckable(false); + setToolButtonStyle(Qt::ToolButtonIconOnly); + setIconSize(QSize(10, 10)); + static QIcon s_default_icon = QIcon(":/image/resource/ribbonPannelOptionButton.png"); + setIcon(s_default_icon); +} + +SARibbonPannelOptionButton::~SARibbonPannelOptionButton() +{ +} diff --git a/src/SARibbonBar/SARibbonPannelOptionButton.h b/src/SARibbonBar/SARibbonPannelOptionButton.h index 1f0da0f0..4753696f 100644 --- a/src/SARibbonBar/SARibbonPannelOptionButton.h +++ b/src/SARibbonBar/SARibbonPannelOptionButton.h @@ -1,24 +1,24 @@ -#ifndef SARIBBONPANNELOPTIONBUTTON_H -#define SARIBBONPANNELOPTIONBUTTON_H -#include -#include "SARibbonGlobal.h" -class QAction; - -/** - * @brief Pannel右下角的操作按钮 - * - * 此按钮和一个action关联,使用@ref SARibbonPannel::addOptionAction 函数用于生成此按钮,正常来说 - * 用户并不需要直接操作此类,仅仅用于样式设计 - * 如果一定要重载此按钮,可以通过重载@ref SARibbonElementFactory - * 的 @ref SARibbonElementFactory::createRibbonPannelOptionButton来实现新的OptionButton - * - */ -class SA_RIBBON_EXPORT SARibbonPannelOptionButton : public QToolButton -{ - Q_OBJECT -public: - explicit SARibbonPannelOptionButton(QWidget* parent = nullptr); - ~SARibbonPannelOptionButton(); -}; - -#endif // SAROBBONPANNELOPTIONBUTTON_H +#ifndef SARIBBONPANNELOPTIONBUTTON_H +#define SARIBBONPANNELOPTIONBUTTON_H +#include +#include "SARibbonGlobal.h" +class QAction; + +/** + * @brief Pannel右下角的操作按钮 + * + * 此按钮和一个action关联,使用@ref SARibbonPannel::addOptionAction 函数用于生成此按钮,正常来说 + * 用户并不需要直接操作此类,仅仅用于样式设计 + * 如果一定要重载此按钮,可以通过重载@ref SARibbonElementFactory + * 的 @ref SARibbonElementFactory::createRibbonPannelOptionButton来实现新的OptionButton + * + */ +class SA_RIBBON_EXPORT SARibbonPannelOptionButton : public QToolButton +{ + Q_OBJECT +public: + explicit SARibbonPannelOptionButton(QWidget* parent = nullptr); + ~SARibbonPannelOptionButton(); +}; + +#endif // SAROBBONPANNELOPTIONBUTTON_H diff --git a/src/SARibbonBar/SARibbonSeparatorWidget.cpp b/src/SARibbonBar/SARibbonSeparatorWidget.cpp index 32725252..c49a0fd8 100644 --- a/src/SARibbonBar/SARibbonSeparatorWidget.cpp +++ b/src/SARibbonBar/SARibbonSeparatorWidget.cpp @@ -1,26 +1,26 @@ -#include "SARibbonSeparatorWidget.h" -#include -#include -#include -#include -#include - -SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent) -{ - setFrameShape(QFrame::VLine); - setFrameShadow(QFrame::Plain); - - if(QScreen* screen = QApplication::primaryScreen()){ - qreal dpr = screen->physicalDotsPerInch() / screen->logicalDotsPerInch(); - int scaledLineWidth = qRound(1.0 * dpr); // 假设基础 lineWidth 是 1 - setLineWidth(scaledLineWidth); - // qDebug() << "SARibbonSeparatorWidget:" << scaledLineWidth; - }else{ - setLineWidth(1); - } - -} - -SARibbonSeparatorWidget::~SARibbonSeparatorWidget() -{ -} +#include "SARibbonSeparatorWidget.h" +#include +#include +#include +#include +#include + +SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent) +{ + setFrameShape(QFrame::VLine); + setFrameShadow(QFrame::Plain); + + if(QScreen* screen = QApplication::primaryScreen()){ + qreal dpr = screen->physicalDotsPerInch() / screen->logicalDotsPerInch(); + int scaledLineWidth = qRound(1.0 * dpr); // 假设基础 lineWidth 是 1 + setLineWidth(scaledLineWidth); + // qDebug() << "SARibbonSeparatorWidget:" << scaledLineWidth; + }else{ + setLineWidth(1); + } + +} + +SARibbonSeparatorWidget::~SARibbonSeparatorWidget() +{ +} diff --git a/src/SARibbonBar/SARibbonSeparatorWidget.h b/src/SARibbonBar/SARibbonSeparatorWidget.h index d650d982..20097307 100644 --- a/src/SARibbonBar/SARibbonSeparatorWidget.h +++ b/src/SARibbonBar/SARibbonSeparatorWidget.h @@ -1,18 +1,18 @@ -#ifndef SARIBBONSEPARATORWIDGET_H -#define SARIBBONSEPARATORWIDGET_H -#include "SARibbonGlobal.h" -#include -#include - -/// -/// \brief 用于显示分割线 -/// -class SA_RIBBON_EXPORT SARibbonSeparatorWidget : public QFrame -{ - Q_OBJECT -public: - explicit SARibbonSeparatorWidget(QWidget* parent = nullptr); - ~SARibbonSeparatorWidget(); -}; - -#endif // SARIBBONSEPARATORWIDGET_H +#ifndef SARIBBONSEPARATORWIDGET_H +#define SARIBBONSEPARATORWIDGET_H +#include "SARibbonGlobal.h" +#include +#include + +/// +/// \brief 用于显示分割线 +/// +class SA_RIBBON_EXPORT SARibbonSeparatorWidget : public QFrame +{ + Q_OBJECT +public: + explicit SARibbonSeparatorWidget(QWidget* parent = nullptr); + ~SARibbonSeparatorWidget(); +}; + +#endif // SARIBBONSEPARATORWIDGET_H diff --git a/src/SARibbonBar/SARibbonSystemButtonBar.h b/src/SARibbonBar/SARibbonSystemButtonBar.h index 29543ecf..ed17d3db 100644 --- a/src/SARibbonBar/SARibbonSystemButtonBar.h +++ b/src/SARibbonBar/SARibbonSystemButtonBar.h @@ -1,88 +1,88 @@ -#ifndef SARIBBONSYSTEMBUTTONBAR_H -#define SARIBBONSYSTEMBUTTONBAR_H -#include "SARibbonGlobal.h" -#include -#include - -/** - * @brief 窗口的最大最小化按钮 - * @note 内部有个SARibbonButtonGroupWidget,其ObjectName = SASystemButtonGroup - * 如果需要qss,可以进行特化处理: - * - * @code - * SARibbonButtonGroupWidget#SASystemButtonGroup - * @endcode - * - */ -class SA_RIBBON_EXPORT SARibbonSystemButtonBar : public QFrame -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonSystemButtonBar) -public: - explicit SARibbonSystemButtonBar(QWidget* parent); - explicit SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags); - ~SARibbonSystemButtonBar(); - void setupMinimizeButton(bool on); - void setupMaximizeButton(bool on); - void setupCloseButton(bool on); - // 以最顶层的widget的WindowFlag作为WindowFlag - void updateWindowFlag(); - void updateWindowFlag(Qt::WindowFlags flags); - // 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 - void setButtonWidthStretch(int close = 4, int max = 3, int min = 3); - // 标题栏高度 - void setWindowTitleHeight(int h); - int windowTitleHeight() const; - // 设置标准系统按钮的宽度 - void setWindowButtonWidth(int w); - int windowButtonWidth() const; - // 设置Qt::WindowStates - void setWindowStates(Qt::WindowStates s); - // 仅获取按钮的状态 - Qt::WindowFlags windowButtonFlags() const; - // 三个标准系统窗口按钮 - QAbstractButton* minimizeButton() const; - QAbstractButton* maximizeButton() const; - QAbstractButton* closeButton() const; - - // 图标尺寸 - void setIconSize(const QSize& ic); - QSize iconSize() const; - // 生成并添加一个action - QAction* addAction(QAction* a, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addAction(const QString& text, - const QIcon& icon, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); - QAction* addMenu(QMenu* menu, - Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, - QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); - QAction* addSeparator(); - QAction* addWidget(QWidget* w); - - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - // 捕获mainwindow的事件 - bool eventFilter(QObject* obj, QEvent* event) override; - -protected: - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - -protected Q_SLOTS: - Q_SLOT void closeWindow(); - Q_SLOT void minimizeWindow(); - Q_SLOT void maximizeWindow(); -}; - -/** - * @brief The SARibbonSystemToolButton class - */ -class SARibbonSystemToolButton : public QToolButton -{ - Q_OBJECT -public: - SARibbonSystemToolButton(QWidget* p = nullptr); -}; - -#endif // SARIBBONSYSTEMBUTTONBAR_H +#ifndef SARIBBONSYSTEMBUTTONBAR_H +#define SARIBBONSYSTEMBUTTONBAR_H +#include "SARibbonGlobal.h" +#include +#include + +/** + * @brief 窗口的最大最小化按钮 + * @note 内部有个SARibbonButtonGroupWidget,其ObjectName = SASystemButtonGroup + * 如果需要qss,可以进行特化处理: + * + * @code + * SARibbonButtonGroupWidget#SASystemButtonGroup + * @endcode + * + */ +class SA_RIBBON_EXPORT SARibbonSystemButtonBar : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonSystemButtonBar) +public: + explicit SARibbonSystemButtonBar(QWidget* parent); + explicit SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags); + ~SARibbonSystemButtonBar(); + void setupMinimizeButton(bool on); + void setupMaximizeButton(bool on); + void setupCloseButton(bool on); + // 以最顶层的widget的WindowFlag作为WindowFlag + void updateWindowFlag(); + void updateWindowFlag(Qt::WindowFlags flags); + // 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 + void setButtonWidthStretch(int close = 4, int max = 3, int min = 3); + // 标题栏高度 + void setWindowTitleHeight(int h); + int windowTitleHeight() const; + // 设置标准系统按钮的宽度 + void setWindowButtonWidth(int w); + int windowButtonWidth() const; + // 设置Qt::WindowStates + void setWindowStates(Qt::WindowStates s); + // 仅获取按钮的状态 + Qt::WindowFlags windowButtonFlags() const; + // 三个标准系统窗口按钮 + QAbstractButton* minimizeButton() const; + QAbstractButton* maximizeButton() const; + QAbstractButton* closeButton() const; + + // 图标尺寸 + void setIconSize(const QSize& ic); + QSize iconSize() const; + // 生成并添加一个action + QAction* addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addMenu(QMenu* menu, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + QAction* addSeparator(); + QAction* addWidget(QWidget* w); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + // 捕获mainwindow的事件 + bool eventFilter(QObject* obj, QEvent* event) override; + +protected: + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + +protected Q_SLOTS: + Q_SLOT void closeWindow(); + Q_SLOT void minimizeWindow(); + Q_SLOT void maximizeWindow(); +}; + +/** + * @brief The SARibbonSystemToolButton class + */ +class SARibbonSystemToolButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonSystemToolButton(QWidget* p = nullptr); +}; + +#endif // SARIBBONSYSTEMBUTTONBAR_H diff --git a/src/SARibbonBar/SARibbonToolButton.cpp b/src/SARibbonBar/SARibbonToolButton.cpp index ea2d53cc..df0e55b0 100644 --- a/src/SARibbonBar/SARibbonToolButton.cpp +++ b/src/SARibbonBar/SARibbonToolButton.cpp @@ -1,1229 +1,1229 @@ -#include "SARibbonToolButton.h" -#include "SARibbonPannel.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @def 定义文字换行时2行文本的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR 2.05 - -/** - * @def 定义文字不换行时单行文本的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR 1.2 - -/** - * @def 定义小按钮的矩形高度系数,此系数决定文字区域的高度 - * - * fontMetrics.lineSpacing*系数 = 文本区域高度 - */ -#define SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR 1.4 - -/** - * @def 文本宽度估算时的宽度比高度系数 - * - * 超过此系数的宽度时,开始尝试换行,例如按钮高度为h,如果单行文本的宽度大于h*系数,则触发换行估算 - */ -#define SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO 1.4 - -/** - * @def 开启此宏会打印一些常见信息 - */ -#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0 - -#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0 - -#if SARIBBONTOOLBUTTON_DEBUG_DRAW -#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) \ - do { \ - p.save(); \ - p.setPen(Qt::red); \ - p.setBrush(QBrush()); \ - p.drawRect(rect); \ - p.restore(); \ - } while (0) - -#else -#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) -#endif -namespace SA -{ - -QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt) -{ - debug << "==============" << "\nQStyleOption(" << (QStyleOption)opt << ")" - << "\n QStyleOptionComplex:" - "\n subControls(" - << opt.subControls - << " ) " - "\n activeSubControls(" - << opt.activeSubControls - << "\n QStyleOptionToolButton" - "\n features(" - << opt.features - << ")" - "\n toolButtonStyle(" - << opt.toolButtonStyle << ")"; - - return (debug); -} -} - -//=================================================== -// SARibbonToolButtonProxyStyle -//=================================================== - -class SARibbonToolButtonProxyStyle : public QProxyStyle -{ -public: - void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override - { - if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight - || pe == PE_IndicatorArrowLeft) { - if (opt->rect.width() <= 1 || opt->rect.height() <= 1) - return; - - QRect r = opt->rect; - int size = qMin(r.height(), r.width()); - QPixmap pixmap; - qreal pixelRatio = p->device()->devicePixelRatio(); - int border = qRound(pixelRatio * (size / 4)); - int sqsize = qRound(pixelRatio * (2 * (size / 2))); - QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - QPainter imagePainter(&image); - - QPolygon a; - switch (pe) { - case PE_IndicatorArrowUp: - a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2); - break; - case PE_IndicatorArrowDown: - a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2); - break; - case PE_IndicatorArrowRight: - a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); - break; - case PE_IndicatorArrowLeft: - a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); - break; - default: - break; - } - - int bsx = 0; - int bsy = 0; - - if (opt->state & State_Sunken) { - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget); - } - - QRect bounds = a.boundingRect(); - int sx = sqsize / 2 - bounds.center().x() - 1; - int sy = sqsize / 2 - bounds.center().y() - 1; - imagePainter.translate(sx + bsx, sy + bsy); - imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4)); - imagePainter.setBrush(Qt::NoBrush); - - if (!(opt->state & State_Enabled)) { - imagePainter.translate(1, 1); - imagePainter.setPen(QPen(opt->palette.light().color(), 1.4)); - imagePainter.drawPolyline(a); - imagePainter.translate(-1, -1); - imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4)); - } - - imagePainter.drawPolyline(a); - imagePainter.end(); - pixmap = QPixmap::fromImage(image); - pixmap.setDevicePixelRatio(pixelRatio); - - int xOffset = r.x() + (r.width() - size) / 2; - int yOffset = r.y() + (r.height() - size) / 2; - p->drawPixmap(xOffset, yOffset, pixmap); - } else { - QProxyStyle::drawPrimitive(pe, opt, p, widget); - } - } -}; - -//=================================================== -// SARibbonToolButton::PrivateData -//=================================================== - -class SARibbonToolButton::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton) -public: - PrivateData(SARibbonToolButton* p); - // 根据鼠标位置更新按钮的信息 - void updateStatusByMousePosition(const QPoint& pos); - // 更新绘图相关的尺寸 - void updateDrawRect(const QStyleOptionToolButton& opt); - // 更新SizeHint - void updateSizeHint(const QStyleOptionToolButton& opt); - // 计算涉及到的rect尺寸 - void calcDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 计算小按钮模式下的尺寸 - void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 计算大按钮模式下的尺寸 - void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const; - // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况) - QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const; - // 判断是否有Indicator - bool hasIndicator(const QStyleOptionToolButton& opt) const; - // 计算sizehint - QSize calcSizeHint(const QStyleOptionToolButton& opt); - QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt); - QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt); - - // 计算文本绘制矩形的高度 - int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const; - // 估算一个最优的文本宽度 - int estimateLargeButtonTextWidth(int buttonHeight, - int textDrawRectHeight, - const QString& text, - const QFontMetrics& fm, - float widthHeightRatio = SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO, - int maxTrycount = 3); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; - // 获取文字的对其方式 - int getTextAlignment() const; - // 确认文字是否确切要换行显示 - bool isTextNeedWrap() const; - // 仅仅对\n进行剔除,和QString::simplified不一样 - static QString simplified(const QString& str); - -public: - bool mMouseOnSubControl { false }; ///< 这个用于标记MenuButtonPopup模式下,鼠标在文本区域 - bool mMenuButtonPressed { false }; ///< 由于Indicator改变,因此hitButton不能用QToolButton的hitButton - bool mWordWrap { false }; ///< 标记是否文字换行 @default false - SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton }; - int mSpacing { 1 }; ///< 按钮和边框的距离 - int mIndicatorLen { 8 }; ///< Indicator的长度 - QRect mDrawIconRect; ///< 记录icon的绘制位置 - QRect mDrawTextRect; ///< 记录text的绘制位置 - QRect mDrawIndicatorArrowRect; ///< 记录IndicatorArrow的绘制位置 - QSize mSizeHint; ///< 保存计算好的sizehint - bool mIsTextNeedWrap { false }; ///< 标记文字是否需要换行显示 -public: - static bool s_enableWordWrap; ///< 在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 -}; - -// 静态参数初始化 -bool SARibbonToolButton::PrivateData::s_enableWordWrap = false; - -SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p) -{ - auto proxy = new SARibbonToolButtonProxyStyle(); - proxy->setParent(p); // take ownership to avoid memleak - p->setStyle(proxy); -} - -/** - * @brief 根据鼠标的位置更新状态,主要用于判断鼠标是否位于subcontrol - * - * 此函数主要应用在action menu模式下 - * @param pos - */ -void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos) -{ - bool isMouseOnSubControl(false); - if (SARibbonToolButton::LargeButton == mButtonType) { - isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos); - } else { - // 小按钮模式就和普通toolbutton一样 - isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos); - } - - if (mMouseOnSubControl != isMouseOnSubControl) { - mMouseOnSubControl = isMouseOnSubControl; - // 从icon变到text过程中刷新一次 - q_ptr->update(); - } -} - -/** - * @brief 更新绘图的几个关键尺寸 - * - * 包括: - * - * - DrawIconRect 绘制图标的矩形区域 - * - * - DrawTextRect 绘制文本的矩形区域 - * - * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域 - * - * @param opt - */ -void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt) -{ - if (!mSizeHint.isValid()) { - updateSizeHint(opt); - } - // 先更新IndicatorLen - mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr); - if (mIndicatorLen < 3) { - if (SARibbonToolButton::LargeButton == mButtonType) { - mIndicatorLen = 8; - } else { - mIndicatorLen = 12; // 小按钮模式下设置为10 - } - } - calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen); -} - -/** - * @brief 更新sizehint - * @param opt - */ -void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt) -{ - mSizeHint = calcSizeHint(opt); -} - -/** - * @brief 计算绘图的几个关键区域 - * @param opt - * @param iconRect 绘制图标的矩形区域 - * @param textRect 绘制文本的矩形区域 - * @param indicatorArrowRect 绘制菜单下箭头的矩形区域 - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); - - } else { - calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); - } -} - -/** - * @brief 计算小按钮模式下的绘制尺寸 - * @param opt - * @param iconRect - * @param textRect - * @param indicatorArrowRect - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - switch (opt.toolButtonStyle) { - case Qt::ToolButtonIconOnly: { - if (hasIndicator(opt)) { - // 在仅有图标的小模式显示时,预留一个下拉箭头位置 - iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); - indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, - iconRect.y(), - indicatorLen, - iconRect.height()); - } else { - iconRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); - indicatorArrowRect = QRect(); - } - // 文本区域为空 - textRect = QRect(); - } break; - case Qt::ToolButtonTextOnly: { - if (hasIndicator(opt)) { - // 在仅有图标的小模式显示时,预留一个下拉箭头位置 - textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); - indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height()); - } else { - textRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); - indicatorArrowRect = QRect(); - } - // 绘图区域为空 - iconRect = QRect(); - } break; - default: { - bool hasInd = hasIndicator(opt); - // icon Beside和under都是一样的 - QRect buttonRect = q_ptr->rect(); - buttonRect.adjust(spacing, spacing, -spacing, -spacing); - // 先设置IconRect - if (opt.icon.isNull()) { - // 没有图标 - iconRect = QRect(); - } else { - QSize iconSize = adjustIconSize(buttonRect, opt.iconSize); - iconRect = QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height())); - } - // 后设置TextRect - if (opt.text.isEmpty()) { - textRect = QRect(); - } else { - // 分有菜单和没菜单两种情况 - int adjx = iconRect.isValid() ? (iconRect.width() + spacing) - : 0; // 在buttonRect上变换,因此如果没有图标是不用偏移spacing - if (hasInd) { - textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0); - } else { - textRect = buttonRect.adjusted(adjx, 0, 0, 0); // 在buttonRect上变换,因此如果没有图标是不用偏移spacing - } - } - // 最后设置Indicator - if (hasInd) { - if (textRect.isValid()) { - indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, - textRect.y(), - indicatorLen, - textRect.height()); - } else if (iconRect.isValid()) { - indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, - iconRect.y(), - indicatorLen, - iconRect.height()); - } else { - indicatorArrowRect = buttonRect; - } - } else { - indicatorArrowRect = QRect(); - } - } - } -} - -/** - * @brief 计算大按钮模式下的绘制尺寸(普通) - * @param opt - * @param iconRect - * @param textRect - * @param indicatorArrowRect - * @param spacing - * @param indicatorLen - */ -void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& indicatorArrowRect, - int spacing, - int indicatorLen) const -{ - //! 3行模式的图标比较大,文字换行情况下,indicator会动态调整 - // 先获取文字矩形的高度 - int textHeight = calcTextDrawRectHeight(opt); - bool hIndicator = hasIndicator(opt); - if (!hIndicator) { - // 没有菜单,把len设置为0 - indicatorLen = 0; - indicatorArrowRect = QRect(); - } - // 这里要判断文字是否要换行显示,换行显示的文字的indicatorArrowRect所处的位置不一样 - // 先布置textRect - if (isEnableWordWrap()) { - // 在换行模式下 - if (isTextNeedWrap()) { - // 如果文字的确换行,indicator放在最右边 - textRect = QRect(spacing, - opt.rect.bottom() - spacing - textHeight, - opt.rect.width() - 2 * spacing - indicatorLen, - textHeight); - if (hIndicator) { - indicatorArrowRect = QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2); - } - } else { - // 如果文字不需要换行,indicator在下板行 - textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing, textHeight); - if (hIndicator) { - int dy = textRect.height() / 2; - dy += (dy - indicatorLen) / 2; - indicatorArrowRect = QRect(textRect.x(), textRect.y() + dy, textRect.width(), indicatorLen); - } - } - } else { - // 文字不换行,indicator放在最右边 - int y = opt.rect.bottom() - spacing - textHeight; - if (hIndicator) { - // 先布置indicator - indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight); - textRect = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight); - } else { - textRect = QRect(spacing, y, opt.rect.width() - 2 * spacing, textHeight); - } - } - // 剩下就是icon区域 - iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing); -} - -/** - * @brief 适应iconsize - * @param buttonRect - * @param originIconSize - * @return - */ -QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const -{ - QSize iconSize = originIconSize; - if (iconSize.height() > buttonRect.height()) { - // 说明图标的icon过大,要匹配到buttonRect - iconSize.setHeight(buttonRect.height()); - // 等比例设置宽度 - iconSize.setWidth(originIconSize.width() * iconSize.height() / originIconSize.height()); - } - return iconSize; -} - -/** - * @brief 判断是否有Indicator - * @param opt - * @return - */ -bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const -{ - return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu)); -} - -/** - * @brief 计算sizehint - * - * 此函数非常关键,因为所有尺寸计算都是基于原始的rect来的 - * @param opt - * @return - */ -QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt) -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - return calcLargeButtonSizeHint(opt); - } - return calcSmallButtonSizeHint(opt); -} - -QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt) -{ - int w = 0, h = 0; - - switch (opt.toolButtonStyle) { - case Qt::ToolButtonIconOnly: { - w = opt.iconSize.width() + 2 * mSpacing; - h = opt.iconSize.height() + 2 * mSpacing; - } break; - case Qt::ToolButtonTextOnly: { - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); - textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); - textSize.setHeight(calcTextDrawRectHeight(opt)); - w = textSize.width() + 2 * mSpacing; - h = textSize.height() + 2 * mSpacing; - } break; - default: { - // 先加入icon的尺寸 - w = opt.iconSize.width() + 2 * mSpacing; - h = opt.iconSize.height() + 2 * mSpacing; - // 再加入文本的长度 - if (!opt.text.isEmpty()) { - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); - textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); - textSize.setHeight(calcTextDrawRectHeight(opt)); - w += mSpacing; - w += textSize.width(); - h = qMax(h, (textSize.height() + (2 * mSpacing))); - } else { - // 没有文本的时候也要设置一下高度 - QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " "); - h = qMax(h, (textSize.height() + (2 * mSpacing))); - } - } - } - if (hasIndicator(opt)) { - // 存在indicator的按钮,宽度尺寸要扩展 - w += mIndicatorLen; - } - if (w < 16) { - w = 16; - } - //! Qt6.4 取消了QApplication::globalStrut - return QSize(w, h).expandedTo(QSize(2, 2)); -} - -QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt) -{ - int w = 0; - int h = opt.fontMetrics.lineSpacing() * 4.8; // 3*1.6 - int minW = h * 0.75; // 最小宽度,在pannel里面的按钮,最小宽度要和icon适应 - - if (SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(q_ptr->parent())) { - // 对于建立在SARibbonPannel的基础上的大按钮,把高度设置为SARibbonPannel计算的大按钮高度 - h = pannel->largeButtonHeight(); - } - int textHeight = calcTextDrawRectHeight(opt); - // 估算字体的宽度作为宽度 - w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics); - w += (2 * mSpacing); - // 判断是否需要加上indicator - if (isEnableWordWrap() && isTextNeedWrap()) { - w += mIndicatorLen; - } - -#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text - << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5 // - << "\n| | | |-textHeight=" << textHeight // - << "\n| | | |-mDrawIconRect=" << mDrawIconRect // - << "\n| | | |-minW=" << minW // - << "\n| | | |-w=" << w // - ; -#endif - //! Qt6.4 取消了QApplication::globalStrut - return QSize(w, h).expandedTo(QSize(minW, textHeight)); -} - -/** - * @brief 计算文本高度 - * @param opt - * @return - */ -int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const -{ - if (SARibbonToolButton::LargeButton == mButtonType) { - if (isEnableWordWrap()) { - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR + opt.fontMetrics.leading(); - } else { - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR; - } - } - // 小按钮 - return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR; -} - -/** - * @brief 估算一个最优的文字尺寸,在可以换行的情况下会进行换行,且只会换一行 - * @param buttonHeight 按钮的高度 - * @param textDrawRectHeight 文本绘制的高度 - * @param fm QFontMetrics - * @param widthHeightRatio 宽高比,宽度/高度的比值,如果大于这个比值,则会进行尝试换行以获取更低的宽度 - * @param maxTrycount 尝试次数 - * @return - */ -int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight, - int textDrawRectHeight, - const QString& text, - const QFontMetrics& fm, - float widthHeightRatio, - int maxTrycount) -{ - QSize textSize; - int space = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2; - int hintMaxWidth = buttonHeight * widthHeightRatio; ///< 建议的宽度 - if (isEnableWordWrap()) { - textSize = fm.size(Qt::TextShowMnemonic, text); - textSize.setWidth(textSize.width() + space); - - if (textSize.height() > fm.lineSpacing() * 1.1) { - //! 说明文字带有换行符,是用户手动换行,这种情况就直接返回字体尺寸,不进行估算 - mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 - return textSize.width(); - } - - // 这时候需要估算文本的长度 - if (textSize.width() <= hintMaxWidth) { - // 范围合理,直接返回 - mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 - return textSize.width(); - } - - //! 大于宽高比尝试进行文字换行 - //! 这里先对文本长度逐渐加长估算,一直到和原来长度一致为止 - int trycount = 0; - int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; - // 对于英文字体,直接宽度减半是无法满足完全显示两行的,需要进行预估 - QRect textRect(0, 0, textSize.width(), textDrawRectHeight); - do { - //! 先计算两行文本的紧凑矩形 - //! 从一半开始逐渐递增 - //! 第1次为 w/2 + w/2 * (0/3) - //! 第2次为 w/2 + w/2 * (1/3) - //! 第3次为 w/2 + w/2 * (2/3) - textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount)); - textRect = fm.boundingRect(textRect, alignment, text); - if (textRect.height() <= (fm.lineSpacing() * 2)) { - // 保证在两行 - mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 - return textRect.width(); - } - ++trycount; -#if SARIBBONTOOLBUTTON_DEBUG_DRAW - if (trycount > 1) { - qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount - << ",textRect=" << textRect; - } -#endif - } while (trycount < 3); - // 到这里说明前面的尝试失败,最终使用原始的长度 - return textSize.width(); - } - - //! 说明是不换行 - - mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 - // 文字不换行情况下,做simplified处理 - textSize = fm.size(Qt::TextShowMnemonic, simplified(text)); - textSize.setWidth(textSize.width() + space); - if (textSize.width() < hintMaxWidth) { - // 范围合理,直接返回 - return textSize.width(); - } - if (textSize.width() > q_ptr->maximumWidth()) { - // 超出了极限,就返回极限 - return q_ptr->maximumWidth(); - } - return hintMaxWidth; -} - -QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const -{ - if (opt.icon.isNull()) { // 没有有图标 - return (QPixmap()); - } - QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - // 添加高分屏支持 - QSize pxiampSize = iconsize - QSize(2, 2); - return opt.icon.pixmap(pxiampSize, mode, state); -} - -int SARibbonToolButton::PrivateData::getTextAlignment() const -{ - int alignment = Qt::TextShowMnemonic; - if (SARibbonToolButton::LargeButton == mButtonType) { - if (isEnableWordWrap()) { - alignment |= Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter; // 换行的情况下,顶部对齐 - } else { - alignment |= Qt::AlignCenter; - } - } else { - alignment |= Qt::AlignCenter; - } - return alignment; -} - -/** - * @brief 确认文字是否确切要换行显示 - * @return - */ -bool SARibbonToolButton::PrivateData::isTextNeedWrap() const -{ - return mIsTextNeedWrap; -} - -/** - * @brief 仅仅对\n进行剔除 - * @param str - * @return - */ -QString SARibbonToolButton::PrivateData::simplified(const QString& str) -{ - QString res = str; - res.remove('\n'); - return res; -} -//=================================================== -// SARibbonToolButton -//=================================================== - -SARibbonToolButton::SARibbonToolButton(QWidget* parent) - : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) -{ - setAutoRaise(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setButtonType(SmallButton); - setMouseTracking(true); -} - -SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent) - : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) -{ - setAutoRaise(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setDefaultAction(defaultAction); - setButtonType(SmallButton); - setMouseTracking(true); -} - -SARibbonToolButton::~SARibbonToolButton() -{ -} - -/** - * @brief 鼠标移动事件 - * - * 由于Ribbon的Indicator和正常的Toolbutton不一样,因此无法用QStyleOptionToolButton的activeSubControls的状态 - * - * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态 - * @param e - */ -void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e) -{ - d_ptr->updateStatusByMousePosition(e->pos()); - QToolButton::mouseMoveEvent(e); -} - -/** - * @brief SARibbonToolButton::mousePressEvent - * @param e - */ -void SARibbonToolButton::mousePressEvent(QMouseEvent* e) -{ - if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) { - d_ptr->updateStatusByMousePosition(e->pos()); - if (d_ptr->mMouseOnSubControl) { - d_ptr->mMenuButtonPressed = true; - showMenu(); - // showmenu结束后,在判断当前的鼠标位置是否是在subcontrol - d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos())); - return; - } - } - d_ptr->mMenuButtonPressed = false; - //! 注意这里要用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent - //! QToolButton的mousePressEvent主要是为了弹出菜单,这里弹出菜单的方式是不一样的,因此不能执行QToolButton的mousePressEvent - QAbstractButton::mousePressEvent(e); -} - -void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e) -{ - d_ptr->mMenuButtonPressed = false; - QToolButton::mouseReleaseEvent(e); -} - -void SARibbonToolButton::focusOutEvent(QFocusEvent* e) -{ - d_ptr->mMouseOnSubControl = false; - QToolButton::focusOutEvent(e); -} - -void SARibbonToolButton::leaveEvent(QEvent* e) -{ - d_ptr->mMouseOnSubControl = false; - QToolButton::leaveEvent(e); -} - -bool SARibbonToolButton::hitButton(const QPoint& pos) const -{ - if (QToolButton::hitButton(pos)) { - return (!d_ptr->mMenuButtonPressed); - } - return (false); -} - -/** - * @brief 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - * @param e - */ -void SARibbonToolButton::resizeEvent(QResizeEvent* e) -{ - // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - QToolButton::resizeEvent(e); - updateRect(); -} - -/** - * @brief toolbutton的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行 - * @note m_sizeHint的刷新需要注意 - * @return - */ -QSize SARibbonToolButton::sizeHint() const -{ - // if (!d_ptr->mSizeHint.isValid()) { // 22是给与sizehint的最小值,如果小于这个值,重新计算一下 - // QStyleOptionToolButton opt; - // initStyleOption(&opt); - // d_ptr->updateSizeHint(opt); - // } -#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT - qDebug() << "| | |-SARibbonToolButton::sizeHint"; -#endif - QStyleOptionToolButton opt; - initStyleOption(&opt); - d_ptr->updateSizeHint(opt); - return d_ptr->mSizeHint; -} - -void SARibbonToolButton::paintEvent(QPaintEvent* e) -{ - Q_UNUSED(e); - QPainter p(this); - QStyleOptionToolButton opt; - initStyleOption(&opt); - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - // 在菜单弹出消失后,需要通过此方法取消掉鼠标停留 - if (!rect().contains(mapFromGlobal(QCursor::pos()))) { - opt.state &= ~QStyle::State_MouseOver; - } - } - paintButton(p, opt); - paintIcon(p, opt, d_ptr->mDrawIconRect); - paintText(p, opt, d_ptr->mDrawTextRect); - paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect); -} - -/** - * @brief 绘制按钮 - * @param p - * @param opt - */ -void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt) -{ - // QStyle::State_Sunken 代表按钮按下去了 - // QStyle::State_On 代表按钮按checked - // QStyle::State_MouseOver 代表当前鼠标位于按钮上面 - QStyleOption tool = opt; - bool autoRaise = opt.state & QStyle::State_AutoRaise; - // 绘制按钮 - if (autoRaise) { - // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态 - // 先绘制一个鼠标不在按钮上的状态 - if (opt.state & QStyle::State_Sunken) { - tool.state &= ~QStyle::State_MouseOver; - } - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - // 针对MenuButtonPopup的ribbon样式的特殊绘制 - if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) { - if (opt.state & QStyle::State_MouseOver) { // 鼠标在按钮上才进行绘制 - if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) { // 按钮的菜单弹出时不做处理 - if (LargeButton == d_ptr->mButtonType) { // 大按钮模式 - if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 - // 鼠标在文字区,把图标显示为正常(就是鼠标不放上去的状态) - tool.rect = d_ptr->mDrawIconRect; - tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 - tool.state &= ~QStyle::State_MouseOver; - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } else { - // 鼠标在图标区,把文字显示为正常 - if (!tool.state.testFlag(QStyle::State_Sunken)) { - // State_Sunken说明此按钮正在按下,这时候,文本区域不需要绘制,只有在非按下状态才需要绘制 - tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 - tool.state &= ~QStyle::State_MouseOver; - // 文字和Indicator都显示正常 - tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect); - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } - } - } else { // 小按钮模式 - if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 - // 鼠标在文字区,把图标和文字显示为正常 - tool.rect = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect); - tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } else { - // 鼠标在图标区,把文字显示为正常 - tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 - // 文字和Indicator都显示正常 - tool.rect = d_ptr->mDrawIndicatorArrowRect; - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); - } - } - } - } - } - } - // 绘制Focus - // if (opt.state & QStyle::State_HasFocus) { - // QStyleOptionFocusRect fr; - // fr.QStyleOption::operator=(opt); - // fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing); - // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this); - // } -} - -/** - * @brief 绘制图标 - * @param p - * @param opt - */ -void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) -{ - if (!iconDrawRect.isValid()) { - return; - } - - QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); - style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect); -} - -/** - * @brief 绘制文本 - * @param p - * @param opt - */ -void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect) -{ - int alignment = d_ptr->getTextAlignment(); - - if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { - alignment |= Qt::TextHideMnemonic; - } - QString text; - if (isSmallRibbonButton()) { - text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); - } else { - if (!isEnableWordWrap()) { - text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); - } else { - text = opt.text; - } - } - //! 以下内容参考QCommonStyle.cpp - //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const - //! case CC_ToolButton: - QStyle::State bflags = opt.state & ~QStyle::State_Sunken; - if (bflags & QStyle::State_AutoRaise) { - if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { - bflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - if (opt.activeSubControls & QStyle::SC_ToolButton) { - bflags |= QStyle::State_Sunken; - } - } - QStyleOptionToolButton label = opt; - label.state = bflags; - style()->drawItemText(&p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect); -} - -/** - * @brief 绘制Indicator - * @param p - * @param opt - */ -void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect) -{ - if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) { - return; - } - - QStyleOption tool = opt; - tool.rect = indicatorDrawRect; - style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this); - SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect); -} - -void SARibbonToolButton::drawArrow(const QStyle* style, - const QStyleOptionToolButton* toolbutton, - const QRect& rect, - QPainter* painter, - const QWidget* widget) -{ - QStyle::PrimitiveElement pe; - - switch (toolbutton->arrowType) { - case Qt::LeftArrow: - pe = QStyle::PE_IndicatorArrowLeft; - break; - - case Qt::RightArrow: - pe = QStyle::PE_IndicatorArrowRight; - break; - - case Qt::UpArrow: - pe = QStyle::PE_IndicatorArrowUp; - break; - - case Qt::DownArrow: - pe = QStyle::PE_IndicatorArrowDown; - break; - - default: - return; - } - QStyleOption arrowOpt = *toolbutton; - - arrowOpt.rect = rect; - style->drawPrimitive(pe, &arrowOpt, painter, widget); -} - -void SARibbonToolButton::actionEvent(QActionEvent* e) -{ - QToolButton::actionEvent(e); - updateRect(); -} - -/** - * @brief 按钮样式 - * @sa setButtonType - * @return - */ -SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const -{ - return (d_ptr->mButtonType); -} - -/** - * @brief 设置按钮样式 - * @note 设置按钮样式过程会调用setToolButtonStyle,如果要改变toolButtonStyle,如设置为Qt::ToolButtonIconOnly,需要在此函数之后设置 - * @param buttonType - */ -void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType) -{ - d_ptr->mButtonType = buttonType; - // 计算iconrect - // 根据字体计算文字的高度 - - if (LargeButton == buttonType) { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - } else { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - } - - updateRect(); -} - -/** - * @brief 是否是小按钮 - * @return - */ -bool SARibbonToolButton::isSmallRibbonButton() const -{ - return (d_ptr->mButtonType == SmallButton); -} - -/** - * @brief 是否是大按钮 - * @return - */ -bool SARibbonToolButton::isLargeRibbonButton() const -{ - return (d_ptr->mButtonType == LargeButton); -} - -QSize SARibbonToolButton::minimumSizeHint() const -{ - return (sizeHint()); -} - -/** - * @brief 间距是几个重要矩形的间隔 - * @return - */ -int SARibbonToolButton::spacing() const -{ - return d_ptr->mSpacing; -} - -void SARibbonToolButton::updateRect() -{ - QStyleOptionToolButton opt; - initStyleOption(&opt); - d_ptr->updateDrawRect(opt); -} - -/** - * @brief 设置在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 - * @param on - */ -void SARibbonToolButton::setEnableWordWrap(bool on) -{ - SARibbonToolButton::PrivateData::s_enableWordWrap = on; -} - -/** - * @brief 在lite模式下是否允许文字换行 - * @return - */ -bool SARibbonToolButton::isEnableWordWrap() -{ - return SARibbonToolButton::PrivateData::s_enableWordWrap; -} - -bool SARibbonToolButton::event(QEvent* e) -{ - switch (e->type()) { - case QEvent::WindowDeactivate: - d_ptr->mMouseOnSubControl = false; - break; - case QEvent::ActionChanged: - case QEvent::ActionRemoved: - case QEvent::ActionAdded: { - d_ptr->mMouseOnSubControl = false; - updateRect(); - } break; - default: - break; - } - - return (QToolButton::event(e)); -} - -void SARibbonToolButton::changeEvent(QEvent* e) -{ - if (e) { - if (e->type() == QEvent::FontChange) { - // 说明字体改变,需要重新计算和字体相关的信息 - updateRect(); - } - } - QToolButton::changeEvent(e); -} +#include "SARibbonToolButton.h" +#include "SARibbonPannel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @def 定义文字换行时2行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR 2.05 + +/** + * @def 定义文字不换行时单行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR 1.2 + +/** + * @def 定义小按钮的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR 1.4 + +/** + * @def 文本宽度估算时的宽度比高度系数 + * + * 超过此系数的宽度时,开始尝试换行,例如按钮高度为h,如果单行文本的宽度大于h*系数,则触发换行估算 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO 1.4 + +/** + * @def 开启此宏会打印一些常见信息 + */ +#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0 + +#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0 + +#if SARIBBONTOOLBUTTON_DEBUG_DRAW +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + p.setPen(Qt::red); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +#else +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) +#endif +namespace SA +{ + +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt) +{ + debug << "==============" << "\nQStyleOption(" << (QStyleOption)opt << ")" + << "\n QStyleOptionComplex:" + "\n subControls(" + << opt.subControls + << " ) " + "\n activeSubControls(" + << opt.activeSubControls + << "\n QStyleOptionToolButton" + "\n features(" + << opt.features + << ")" + "\n toolButtonStyle(" + << opt.toolButtonStyle << ")"; + + return (debug); +} +} + +//=================================================== +// SARibbonToolButtonProxyStyle +//=================================================== + +class SARibbonToolButtonProxyStyle : public QProxyStyle +{ +public: + void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override + { + if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight + || pe == PE_IndicatorArrowLeft) { + if (opt->rect.width() <= 1 || opt->rect.height() <= 1) + return; + + QRect r = opt->rect; + int size = qMin(r.height(), r.width()); + QPixmap pixmap; + qreal pixelRatio = p->device()->devicePixelRatio(); + int border = qRound(pixelRatio * (size / 4)); + int sqsize = qRound(pixelRatio * (2 * (size / 2))); + QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + QPainter imagePainter(&image); + + QPolygon a; + switch (pe) { + case PE_IndicatorArrowUp: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowDown: + a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowRight: + a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + case PE_IndicatorArrowLeft: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + default: + break; + } + + int bsx = 0; + int bsy = 0; + + if (opt->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget); + } + + QRect bounds = a.boundingRect(); + int sx = sqsize / 2 - bounds.center().x() - 1; + int sy = sqsize / 2 - bounds.center().y() - 1; + imagePainter.translate(sx + bsx, sy + bsy); + imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4)); + imagePainter.setBrush(Qt::NoBrush); + + if (!(opt->state & State_Enabled)) { + imagePainter.translate(1, 1); + imagePainter.setPen(QPen(opt->palette.light().color(), 1.4)); + imagePainter.drawPolyline(a); + imagePainter.translate(-1, -1); + imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4)); + } + + imagePainter.drawPolyline(a); + imagePainter.end(); + pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(pixelRatio); + + int xOffset = r.x() + (r.width() - size) / 2; + int yOffset = r.y() + (r.height() - size) / 2; + p->drawPixmap(xOffset, yOffset, pixmap); + } else { + QProxyStyle::drawPrimitive(pe, opt, p, widget); + } + } +}; + +//=================================================== +// SARibbonToolButton::PrivateData +//=================================================== + +class SARibbonToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton) +public: + PrivateData(SARibbonToolButton* p); + // 根据鼠标位置更新按钮的信息 + void updateStatusByMousePosition(const QPoint& pos); + // 更新绘图相关的尺寸 + void updateDrawRect(const QStyleOptionToolButton& opt); + // 更新SizeHint + void updateSizeHint(const QStyleOptionToolButton& opt); + // 计算涉及到的rect尺寸 + void calcDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 计算小按钮模式下的尺寸 + void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 计算大按钮模式下的尺寸 + void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况) + QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const; + // 判断是否有Indicator + bool hasIndicator(const QStyleOptionToolButton& opt) const; + // 计算sizehint + QSize calcSizeHint(const QStyleOptionToolButton& opt); + QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt); + QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt); + + // 计算文本绘制矩形的高度 + int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const; + // 估算一个最优的文本宽度 + int estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio = SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO, + int maxTrycount = 3); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + // 获取文字的对其方式 + int getTextAlignment() const; + // 确认文字是否确切要换行显示 + bool isTextNeedWrap() const; + // 仅仅对\n进行剔除,和QString::simplified不一样 + static QString simplified(const QString& str); + +public: + bool mMouseOnSubControl { false }; ///< 这个用于标记MenuButtonPopup模式下,鼠标在文本区域 + bool mMenuButtonPressed { false }; ///< 由于Indicator改变,因此hitButton不能用QToolButton的hitButton + bool mWordWrap { false }; ///< 标记是否文字换行 @default false + SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton }; + int mSpacing { 1 }; ///< 按钮和边框的距离 + int mIndicatorLen { 8 }; ///< Indicator的长度 + QRect mDrawIconRect; ///< 记录icon的绘制位置 + QRect mDrawTextRect; ///< 记录text的绘制位置 + QRect mDrawIndicatorArrowRect; ///< 记录IndicatorArrow的绘制位置 + QSize mSizeHint; ///< 保存计算好的sizehint + bool mIsTextNeedWrap { false }; ///< 标记文字是否需要换行显示 +public: + static bool s_enableWordWrap; ///< 在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 +}; + +// 静态参数初始化 +bool SARibbonToolButton::PrivateData::s_enableWordWrap = false; + +SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p) +{ + auto proxy = new SARibbonToolButtonProxyStyle(); + proxy->setParent(p); // take ownership to avoid memleak + p->setStyle(proxy); +} + +/** + * @brief 根据鼠标的位置更新状态,主要用于判断鼠标是否位于subcontrol + * + * 此函数主要应用在action menu模式下 + * @param pos + */ +void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos) +{ + bool isMouseOnSubControl(false); + if (SARibbonToolButton::LargeButton == mButtonType) { + isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos); + } else { + // 小按钮模式就和普通toolbutton一样 + isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos); + } + + if (mMouseOnSubControl != isMouseOnSubControl) { + mMouseOnSubControl = isMouseOnSubControl; + // 从icon变到text过程中刷新一次 + q_ptr->update(); + } +} + +/** + * @brief 更新绘图的几个关键尺寸 + * + * 包括: + * + * - DrawIconRect 绘制图标的矩形区域 + * + * - DrawTextRect 绘制文本的矩形区域 + * + * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域 + * + * @param opt + */ +void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt) +{ + if (!mSizeHint.isValid()) { + updateSizeHint(opt); + } + // 先更新IndicatorLen + mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr); + if (mIndicatorLen < 3) { + if (SARibbonToolButton::LargeButton == mButtonType) { + mIndicatorLen = 8; + } else { + mIndicatorLen = 12; // 小按钮模式下设置为10 + } + } + calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen); +} + +/** + * @brief 更新sizehint + * @param opt + */ +void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt) +{ + mSizeHint = calcSizeHint(opt); +} + +/** + * @brief 计算绘图的几个关键区域 + * @param opt + * @param iconRect 绘制图标的矩形区域 + * @param textRect 绘制文本的矩形区域 + * @param indicatorArrowRect 绘制菜单下箭头的矩形区域 + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + + } else { + calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + } +} + +/** + * @brief 计算小按钮模式下的绘制尺寸 + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, + iconRect.y(), + indicatorLen, + iconRect.height()); + } else { + iconRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 文本区域为空 + textRect = QRect(); + } break; + case Qt::ToolButtonTextOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height()); + } else { + textRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 绘图区域为空 + iconRect = QRect(); + } break; + default: { + bool hasInd = hasIndicator(opt); + // icon Beside和under都是一样的 + QRect buttonRect = q_ptr->rect(); + buttonRect.adjust(spacing, spacing, -spacing, -spacing); + // 先设置IconRect + if (opt.icon.isNull()) { + // 没有图标 + iconRect = QRect(); + } else { + QSize iconSize = adjustIconSize(buttonRect, opt.iconSize); + iconRect = QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height())); + } + // 后设置TextRect + if (opt.text.isEmpty()) { + textRect = QRect(); + } else { + // 分有菜单和没菜单两种情况 + int adjx = iconRect.isValid() ? (iconRect.width() + spacing) + : 0; // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + if (hasInd) { + textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0); + } else { + textRect = buttonRect.adjusted(adjx, 0, 0, 0); // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + } + } + // 最后设置Indicator + if (hasInd) { + if (textRect.isValid()) { + indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, + textRect.y(), + indicatorLen, + textRect.height()); + } else if (iconRect.isValid()) { + indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, + iconRect.y(), + indicatorLen, + iconRect.height()); + } else { + indicatorArrowRect = buttonRect; + } + } else { + indicatorArrowRect = QRect(); + } + } + } +} + +/** + * @brief 计算大按钮模式下的绘制尺寸(普通) + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + //! 3行模式的图标比较大,文字换行情况下,indicator会动态调整 + // 先获取文字矩形的高度 + int textHeight = calcTextDrawRectHeight(opt); + bool hIndicator = hasIndicator(opt); + if (!hIndicator) { + // 没有菜单,把len设置为0 + indicatorLen = 0; + indicatorArrowRect = QRect(); + } + // 这里要判断文字是否要换行显示,换行显示的文字的indicatorArrowRect所处的位置不一样 + // 先布置textRect + if (isEnableWordWrap()) { + // 在换行模式下 + if (isTextNeedWrap()) { + // 如果文字的确换行,indicator放在最右边 + textRect = QRect(spacing, + opt.rect.bottom() - spacing - textHeight, + opt.rect.width() - 2 * spacing - indicatorLen, + textHeight); + if (hIndicator) { + indicatorArrowRect = QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2); + } + } else { + // 如果文字不需要换行,indicator在下板行 + textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing, textHeight); + if (hIndicator) { + int dy = textRect.height() / 2; + dy += (dy - indicatorLen) / 2; + indicatorArrowRect = QRect(textRect.x(), textRect.y() + dy, textRect.width(), indicatorLen); + } + } + } else { + // 文字不换行,indicator放在最右边 + int y = opt.rect.bottom() - spacing - textHeight; + if (hIndicator) { + // 先布置indicator + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight); + textRect = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight); + } else { + textRect = QRect(spacing, y, opt.rect.width() - 2 * spacing, textHeight); + } + } + // 剩下就是icon区域 + iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing); +} + +/** + * @brief 适应iconsize + * @param buttonRect + * @param originIconSize + * @return + */ +QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const +{ + QSize iconSize = originIconSize; + if (iconSize.height() > buttonRect.height()) { + // 说明图标的icon过大,要匹配到buttonRect + iconSize.setHeight(buttonRect.height()); + // 等比例设置宽度 + iconSize.setWidth(originIconSize.width() * iconSize.height() / originIconSize.height()); + } + return iconSize; +} + +/** + * @brief 判断是否有Indicator + * @param opt + * @return + */ +bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const +{ + return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu)); +} + +/** + * @brief 计算sizehint + * + * 此函数非常关键,因为所有尺寸计算都是基于原始的rect来的 + * @param opt + * @return + */ +QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt) +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + return calcLargeButtonSizeHint(opt); + } + return calcSmallButtonSizeHint(opt); +} + +QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0, h = 0; + + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + } break; + case Qt::ToolButtonTextOnly: { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w = textSize.width() + 2 * mSpacing; + h = textSize.height() + 2 * mSpacing; + } break; + default: { + // 先加入icon的尺寸 + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + // 再加入文本的长度 + if (!opt.text.isEmpty()) { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w += mSpacing; + w += textSize.width(); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } else { + // 没有文本的时候也要设置一下高度 + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " "); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } + } + } + if (hasIndicator(opt)) { + // 存在indicator的按钮,宽度尺寸要扩展 + w += mIndicatorLen; + } + if (w < 16) { + w = 16; + } + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(2, 2)); +} + +QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0; + int h = opt.fontMetrics.lineSpacing() * 4.8; // 3*1.6 + int minW = h * 0.75; // 最小宽度,在pannel里面的按钮,最小宽度要和icon适应 + + if (SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(q_ptr->parent())) { + // 对于建立在SARibbonPannel的基础上的大按钮,把高度设置为SARibbonPannel计算的大按钮高度 + h = pannel->largeButtonHeight(); + } + int textHeight = calcTextDrawRectHeight(opt); + // 估算字体的宽度作为宽度 + w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics); + w += (2 * mSpacing); + // 判断是否需要加上indicator + if (isEnableWordWrap() && isTextNeedWrap()) { + w += mIndicatorLen; + } + +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text + << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5 // + << "\n| | | |-textHeight=" << textHeight // + << "\n| | | |-mDrawIconRect=" << mDrawIconRect // + << "\n| | | |-minW=" << minW // + << "\n| | | |-w=" << w // + ; +#endif + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(minW, textHeight)); +} + +/** + * @brief 计算文本高度 + * @param opt + * @return + */ +int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR + opt.fontMetrics.leading(); + } else { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR; + } + } + // 小按钮 + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR; +} + +/** + * @brief 估算一个最优的文字尺寸,在可以换行的情况下会进行换行,且只会换一行 + * @param buttonHeight 按钮的高度 + * @param textDrawRectHeight 文本绘制的高度 + * @param fm QFontMetrics + * @param widthHeightRatio 宽高比,宽度/高度的比值,如果大于这个比值,则会进行尝试换行以获取更低的宽度 + * @param maxTrycount 尝试次数 + * @return + */ +int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio, + int maxTrycount) +{ + QSize textSize; + int space = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2; + int hintMaxWidth = buttonHeight * widthHeightRatio; ///< 建议的宽度 + if (isEnableWordWrap()) { + textSize = fm.size(Qt::TextShowMnemonic, text); + textSize.setWidth(textSize.width() + space); + + if (textSize.height() > fm.lineSpacing() * 1.1) { + //! 说明文字带有换行符,是用户手动换行,这种情况就直接返回字体尺寸,不进行估算 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textSize.width(); + } + + // 这时候需要估算文本的长度 + if (textSize.width() <= hintMaxWidth) { + // 范围合理,直接返回 + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + return textSize.width(); + } + + //! 大于宽高比尝试进行文字换行 + //! 这里先对文本长度逐渐加长估算,一直到和原来长度一致为止 + int trycount = 0; + int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; + // 对于英文字体,直接宽度减半是无法满足完全显示两行的,需要进行预估 + QRect textRect(0, 0, textSize.width(), textDrawRectHeight); + do { + //! 先计算两行文本的紧凑矩形 + //! 从一半开始逐渐递增 + //! 第1次为 w/2 + w/2 * (0/3) + //! 第2次为 w/2 + w/2 * (1/3) + //! 第3次为 w/2 + w/2 * (2/3) + textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount)); + textRect = fm.boundingRect(textRect, alignment, text); + if (textRect.height() <= (fm.lineSpacing() * 2)) { + // 保证在两行 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textRect.width(); + } + ++trycount; +#if SARIBBONTOOLBUTTON_DEBUG_DRAW + if (trycount > 1) { + qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount + << ",textRect=" << textRect; + } +#endif + } while (trycount < 3); + // 到这里说明前面的尝试失败,最终使用原始的长度 + return textSize.width(); + } + + //! 说明是不换行 + + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + // 文字不换行情况下,做simplified处理 + textSize = fm.size(Qt::TextShowMnemonic, simplified(text)); + textSize.setWidth(textSize.width() + space); + if (textSize.width() < hintMaxWidth) { + // 范围合理,直接返回 + return textSize.width(); + } + if (textSize.width() > q_ptr->maximumWidth()) { + // 超出了极限,就返回极限 + return q_ptr->maximumWidth(); + } + return hintMaxWidth; +} + +QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return (QPixmap()); + } + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // 添加高分屏支持 + QSize pxiampSize = iconsize - QSize(2, 2); + return opt.icon.pixmap(pxiampSize, mode, state); +} + +int SARibbonToolButton::PrivateData::getTextAlignment() const +{ + int alignment = Qt::TextShowMnemonic; + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + alignment |= Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter; // 换行的情况下,顶部对齐 + } else { + alignment |= Qt::AlignCenter; + } + } else { + alignment |= Qt::AlignCenter; + } + return alignment; +} + +/** + * @brief 确认文字是否确切要换行显示 + * @return + */ +bool SARibbonToolButton::PrivateData::isTextNeedWrap() const +{ + return mIsTextNeedWrap; +} + +/** + * @brief 仅仅对\n进行剔除 + * @param str + * @return + */ +QString SARibbonToolButton::PrivateData::simplified(const QString& str) +{ + QString res = str; + res.remove('\n'); + return res; +} +//=================================================== +// SARibbonToolButton +//=================================================== + +SARibbonToolButton::SARibbonToolButton(QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setDefaultAction(defaultAction); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::~SARibbonToolButton() +{ +} + +/** + * @brief 鼠标移动事件 + * + * 由于Ribbon的Indicator和正常的Toolbutton不一样,因此无法用QStyleOptionToolButton的activeSubControls的状态 + * + * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态 + * @param e + */ +void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e) +{ + d_ptr->updateStatusByMousePosition(e->pos()); + QToolButton::mouseMoveEvent(e); +} + +/** + * @brief SARibbonToolButton::mousePressEvent + * @param e + */ +void SARibbonToolButton::mousePressEvent(QMouseEvent* e) +{ + if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) { + d_ptr->updateStatusByMousePosition(e->pos()); + if (d_ptr->mMouseOnSubControl) { + d_ptr->mMenuButtonPressed = true; + showMenu(); + // showmenu结束后,在判断当前的鼠标位置是否是在subcontrol + d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos())); + return; + } + } + d_ptr->mMenuButtonPressed = false; + //! 注意这里要用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent + //! QToolButton的mousePressEvent主要是为了弹出菜单,这里弹出菜单的方式是不一样的,因此不能执行QToolButton的mousePressEvent + QAbstractButton::mousePressEvent(e); +} + +void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e) +{ + d_ptr->mMenuButtonPressed = false; + QToolButton::mouseReleaseEvent(e); +} + +void SARibbonToolButton::focusOutEvent(QFocusEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::focusOutEvent(e); +} + +void SARibbonToolButton::leaveEvent(QEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::leaveEvent(e); +} + +bool SARibbonToolButton::hitButton(const QPoint& pos) const +{ + if (QToolButton::hitButton(pos)) { + return (!d_ptr->mMenuButtonPressed); + } + return (false); +} + +/** + * @brief 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + * @param e + */ +void SARibbonToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); + updateRect(); +} + +/** + * @brief toolbutton的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行 + * @note m_sizeHint的刷新需要注意 + * @return + */ +QSize SARibbonToolButton::sizeHint() const +{ + // if (!d_ptr->mSizeHint.isValid()) { // 22是给与sizehint的最小值,如果小于这个值,重新计算一下 + // QStyleOptionToolButton opt; + // initStyleOption(&opt); + // d_ptr->updateSizeHint(opt); + // } +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::sizeHint"; +#endif + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateSizeHint(opt); + return d_ptr->mSizeHint; +} + +void SARibbonToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QPainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + // 在菜单弹出消失后,需要通过此方法取消掉鼠标停留 + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + opt.state &= ~QStyle::State_MouseOver; + } + } + paintButton(p, opt); + paintIcon(p, opt, d_ptr->mDrawIconRect); + paintText(p, opt, d_ptr->mDrawTextRect); + paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt) +{ + // QStyle::State_Sunken 代表按钮按下去了 + // QStyle::State_On 代表按钮按checked + // QStyle::State_MouseOver 代表当前鼠标位于按钮上面 + QStyleOption tool = opt; + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态 + // 先绘制一个鼠标不在按钮上的状态 + if (opt.state & QStyle::State_Sunken) { + tool.state &= ~QStyle::State_MouseOver; + } + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + // 针对MenuButtonPopup的ribbon样式的特殊绘制 + if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) { + if (opt.state & QStyle::State_MouseOver) { // 鼠标在按钮上才进行绘制 + if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) { // 按钮的菜单弹出时不做处理 + if (LargeButton == d_ptr->mButtonType) { // 大按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标显示为正常(就是鼠标不放上去的状态) + tool.rect = d_ptr->mDrawIconRect; + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + if (!tool.state.testFlag(QStyle::State_Sunken)) { + // State_Sunken说明此按钮正在按下,这时候,文本区域不需要绘制,只有在非按下状态才需要绘制 + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect); + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } else { // 小按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标和文字显示为正常 + tool.rect = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect); + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawIndicatorArrowRect; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } + } + } + // 绘制Focus + // if (opt.state & QStyle::State_HasFocus) { + // QStyleOptionFocusRect fr; + // fr.QStyleOption::operator=(opt); + // fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing); + // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this); + // } +} + +/** + * @brief 绘制图标 + * @param p + * @param opt + */ +void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (!iconDrawRect.isValid()) { + return; + } + + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect); +} + +/** + * @brief 绘制文本 + * @param p + * @param opt + */ +void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect) +{ + int alignment = d_ptr->getTextAlignment(); + + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + QString text; + if (isSmallRibbonButton()) { + text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + if (!isEnableWordWrap()) { + text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + text = opt.text; + } + } + //! 以下内容参考QCommonStyle.cpp + //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const + //! case CC_ToolButton: + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + QStyleOptionToolButton label = opt; + label.state = bflags; + style()->drawItemText(&p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect); +} + +/** + * @brief 绘制Indicator + * @param p + * @param opt + */ +void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect) +{ + if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) { + return; + } + + QStyleOption tool = opt; + tool.rect = indicatorDrawRect; + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect); +} + +void SARibbonToolButton::drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget) +{ + QStyle::PrimitiveElement pe; + + switch (toolbutton->arrowType) { + case Qt::LeftArrow: + pe = QStyle::PE_IndicatorArrowLeft; + break; + + case Qt::RightArrow: + pe = QStyle::PE_IndicatorArrowRight; + break; + + case Qt::UpArrow: + pe = QStyle::PE_IndicatorArrowUp; + break; + + case Qt::DownArrow: + pe = QStyle::PE_IndicatorArrowDown; + break; + + default: + return; + } + QStyleOption arrowOpt = *toolbutton; + + arrowOpt.rect = rect; + style->drawPrimitive(pe, &arrowOpt, painter, widget); +} + +void SARibbonToolButton::actionEvent(QActionEvent* e) +{ + QToolButton::actionEvent(e); + updateRect(); +} + +/** + * @brief 按钮样式 + * @sa setButtonType + * @return + */ +SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const +{ + return (d_ptr->mButtonType); +} + +/** + * @brief 设置按钮样式 + * @note 设置按钮样式过程会调用setToolButtonStyle,如果要改变toolButtonStyle,如设置为Qt::ToolButtonIconOnly,需要在此函数之后设置 + * @param buttonType + */ +void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType) +{ + d_ptr->mButtonType = buttonType; + // 计算iconrect + // 根据字体计算文字的高度 + + if (LargeButton == buttonType) { + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + } else { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + } + + updateRect(); +} + +/** + * @brief 是否是小按钮 + * @return + */ +bool SARibbonToolButton::isSmallRibbonButton() const +{ + return (d_ptr->mButtonType == SmallButton); +} + +/** + * @brief 是否是大按钮 + * @return + */ +bool SARibbonToolButton::isLargeRibbonButton() const +{ + return (d_ptr->mButtonType == LargeButton); +} + +QSize SARibbonToolButton::minimumSizeHint() const +{ + return (sizeHint()); +} + +/** + * @brief 间距是几个重要矩形的间隔 + * @return + */ +int SARibbonToolButton::spacing() const +{ + return d_ptr->mSpacing; +} + +void SARibbonToolButton::updateRect() +{ + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateDrawRect(opt); +} + +/** + * @brief 设置在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 + * @param on + */ +void SARibbonToolButton::setEnableWordWrap(bool on) +{ + SARibbonToolButton::PrivateData::s_enableWordWrap = on; +} + +/** + * @brief 在lite模式下是否允许文字换行 + * @return + */ +bool SARibbonToolButton::isEnableWordWrap() +{ + return SARibbonToolButton::PrivateData::s_enableWordWrap; +} + +bool SARibbonToolButton::event(QEvent* e) +{ + switch (e->type()) { + case QEvent::WindowDeactivate: + d_ptr->mMouseOnSubControl = false; + break; + case QEvent::ActionChanged: + case QEvent::ActionRemoved: + case QEvent::ActionAdded: { + d_ptr->mMouseOnSubControl = false; + updateRect(); + } break; + default: + break; + } + + return (QToolButton::event(e)); +} + +void SARibbonToolButton::changeEvent(QEvent* e) +{ + if (e) { + if (e->type() == QEvent::FontChange) { + // 说明字体改变,需要重新计算和字体相关的信息 + updateRect(); + } + } + QToolButton::changeEvent(e); +} diff --git a/src/SARibbonBar/SARibbonToolButton.h b/src/SARibbonBar/SARibbonToolButton.h index 6e49e1be..ae2987c5 100644 --- a/src/SARibbonBar/SARibbonToolButton.h +++ b/src/SARibbonBar/SARibbonToolButton.h @@ -1,92 +1,92 @@ -#ifndef SARIBBONTOOLBUTTON_H -#define SARIBBONTOOLBUTTON_H -#include "SARibbonGlobal.h" -#include -#include -/** - * @brief Ribbon界面适用的toolButton - * - * 相对于普通toolbutton,主要多了两个类型设置,@ref setButtonType 和 @ref setLargeButtonType - * - * @note @sa setIconSize 函数不在起作用,iconsize是根据当前尺寸动态调整的 - */ -class SA_RIBBON_EXPORT SARibbonToolButton : public QToolButton -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonToolButton) -public: - /** - * @brief 按钮样式 - */ - enum RibbonButtonType - { - LargeButton, - SmallButton - }; - Q_ENUM(RibbonButtonType) - -public: - explicit SARibbonToolButton(QWidget* parent = nullptr); - explicit SARibbonToolButton(QAction* defaultAction, QWidget* parent = nullptr); - ~SARibbonToolButton(); - //标记按钮的样式,按钮的样式有不同的渲染方式 - RibbonButtonType buttonType() const; - void setButtonType(const RibbonButtonType& buttonType); - //是否是小按钮 - bool isSmallRibbonButton() const; - //是否是大按钮 - bool isLargeRibbonButton() const; - //最小尺寸提示 - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - - //获取间距 - int spacing() const; - //更新尺寸 - void updateRect(); - - virtual QSize sizeHint() const Q_DECL_OVERRIDE; - -public: - //在lite模式下是否允许文字换行 - static void setEnableWordWrap(bool on); - static bool isEnableWordWrap(); - -protected: - virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - virtual void mouseMoveEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void mousePressEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE; - virtual void focusOutEvent(QFocusEvent* e) Q_DECL_OVERRIDE; - virtual void leaveEvent(QEvent* e) Q_DECL_OVERRIDE; - virtual bool hitButton(const QPoint& pos) const Q_DECL_OVERRIDE; - virtual bool event(QEvent* e) Q_DECL_OVERRIDE; - //事件改变 - 主要为了捕获字体的改变 - virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; - virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; - -protected: - //绘制按钮 - virtual void paintButton(QPainter& p, const QStyleOptionToolButton& opt); - //绘制图标 - virtual void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); - //绘制文本 - virtual void paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect); - //绘制Indicator - virtual void paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect); - -private: - static void drawArrow(const QStyle* style, - const QStyleOptionToolButton* toolbutton, - const QRect& rect, - QPainter* painter, - const QWidget* widget = 0); - -protected: -}; - -namespace SA -{ -QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt); -} -#endif // SARIBBONTOOLBUTTON_H +#ifndef SARIBBONTOOLBUTTON_H +#define SARIBBONTOOLBUTTON_H +#include "SARibbonGlobal.h" +#include +#include +/** + * @brief Ribbon界面适用的toolButton + * + * 相对于普通toolbutton,主要多了两个类型设置,@ref setButtonType 和 @ref setLargeButtonType + * + * @note @sa setIconSize 函数不在起作用,iconsize是根据当前尺寸动态调整的 + */ +class SA_RIBBON_EXPORT SARibbonToolButton : public QToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonToolButton) +public: + /** + * @brief 按钮样式 + */ + enum RibbonButtonType + { + LargeButton, + SmallButton + }; + Q_ENUM(RibbonButtonType) + +public: + explicit SARibbonToolButton(QWidget* parent = nullptr); + explicit SARibbonToolButton(QAction* defaultAction, QWidget* parent = nullptr); + ~SARibbonToolButton(); + //标记按钮的样式,按钮的样式有不同的渲染方式 + RibbonButtonType buttonType() const; + void setButtonType(const RibbonButtonType& buttonType); + //是否是小按钮 + bool isSmallRibbonButton() const; + //是否是大按钮 + bool isLargeRibbonButton() const; + //最小尺寸提示 + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + + //获取间距 + int spacing() const; + //更新尺寸 + void updateRect(); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + +public: + //在lite模式下是否允许文字换行 + static void setEnableWordWrap(bool on); + static bool isEnableWordWrap(); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mousePressEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void focusOutEvent(QFocusEvent* e) Q_DECL_OVERRIDE; + virtual void leaveEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual bool hitButton(const QPoint& pos) const Q_DECL_OVERRIDE; + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + //事件改变 - 主要为了捕获字体的改变 + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + +protected: + //绘制按钮 + virtual void paintButton(QPainter& p, const QStyleOptionToolButton& opt); + //绘制图标 + virtual void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); + //绘制文本 + virtual void paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect); + //绘制Indicator + virtual void paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect); + +private: + static void drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget = 0); + +protected: +}; + +namespace SA +{ +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt); +} +#endif // SARIBBONTOOLBUTTON_H diff --git a/src/SARibbonBar/SARibbonWidget.cpp b/src/SARibbonBar/SARibbonWidget.cpp index ab2b11e8..a67e7881 100644 --- a/src/SARibbonBar/SARibbonWidget.cpp +++ b/src/SARibbonBar/SARibbonWidget.cpp @@ -1,227 +1,227 @@ -#include "SARibbonWidget.h" -#include "SARibbonBar.h" -#include "SARibbonTabBar.h" -#include -#include -#include -#include - -/** - * @brief The SARibbonWidget::PrivateData class - */ -class SARibbonWidget::PrivateData -{ - SA_RIBBON_DECLARE_PUBLIC(SARibbonWidget) -public: - PrivateData(SARibbonWidget* p); - void installFrameless(SARibbonWidget* p); - -public: - SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; -}; - -SARibbonWidget::PrivateData::PrivateData(SARibbonWidget* p) : q_ptr(p) -{ -} - -//=================================================== -// SARibbonWidget -//=================================================== -SARibbonWidget::SARibbonWidget(QWidget* parent) : QWidget(parent), d_ptr(new SARibbonWidget::PrivateData(this)) -{ - // 直接创建SARibbonBar - QVBoxLayout* verticalLayout = new QVBoxLayout(this); - verticalLayout->setSpacing(1); - verticalLayout->setObjectName("verticalLayout"); - verticalLayout->setContentsMargins(0, 0, 0, 0); - SARibbonBar* ribbon = new SARibbonBar(this); - setRibbonBar(ribbon); - connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonWidget::onPrimaryScreenChanged); -} - -SARibbonWidget::~SARibbonWidget() -{ -} - -/** - * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr - * @return - */ -SARibbonBar* SARibbonWidget::ribbonBar() const -{ - QLayout* lay = layout(); - if (lay) { - return qobject_cast< SARibbonBar* >(lay->menuBar()); - } - return nullptr; -} - -/** - * @brief 设置ribbonbar - * @param bar - */ -void SARibbonWidget::setRibbonBar(SARibbonBar* bar) -{ - QLayout* lay = layout(); - if (lay) { - lay->setMenuBar(bar); - } -} - -/** - * @brief SARibbonMainWindow::setRibbonTheme - * - * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - * @code - * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); - * @endcode - * @param theme - */ -void SARibbonWidget::setRibbonTheme(SARibbonTheme theme) -{ - sa_set_ribbon_theme(this, theme); - d_ptr->mCurrentRibbonTheme = theme; - if (SARibbonBar* bar = ribbonBar()) { - auto theme = ribbonTheme(); - // 尺寸修正 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeOffice2016Blue: - case SARibbonTheme::RibbonThemeDark: - case SARibbonTheme::RibbonThemeDark2: { - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2013.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 0px; - //! margin-left: 5px; - //! margin-bottom: 0px; - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - tab->setTabMargin(QMargins(5, 0, 0, 0)); - } break; - case SARibbonTheme::RibbonThemeOffice2021Blue: { - SARibbonTabBar* tab = bar->ribbonTabBar(); - if (!tab) { - break; - } - //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 - //! office2021.qss的margin信息如下设置 - //! margin-top: 0px; - //! margin-right: 5px; - //! margin-left: 5px; - //! margin-bottom: 0px; - tab->setTabMargin(QMargins(5, 0, 5, 0)); - } - default: - break; - } - // 上下文标签颜色设置,以及基线颜色设置 - switch (theme) { - case SARibbonTheme::RibbonThemeWindows7: - case SARibbonTheme::RibbonThemeOffice2013: - case SARibbonTheme::RibbonThemeDark: - bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2016Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 - break; - case SARibbonTheme::RibbonThemeOffice2021Blue: - bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 - break; - default: - break; - } - // 基线颜色设置 - if (SARibbonTheme::RibbonThemeOffice2013 == theme) { - bar->setTabBarBaseLineColor(QColor(186, 201, 219)); - } else { - bar->setTabBarBaseLineColor(QColor()); - } - } -} - -SARibbonTheme SARibbonWidget::ribbonTheme() const -{ - return (d_ptr->mCurrentRibbonTheme); -} - -bool SARibbonWidget::isUseRibbon() const -{ - return (nullptr != ribbonBar()); -} - -/** - @brief 设置窗口 - - @param 窗口指针 - @note 窗口的所有权归SARibbonWidget管理 - @sa widget - @note 原来设置的窗口会被delete - */ -void SARibbonWidget::setWidget(QWidget* w) -{ - QWidget* oldwidget = takeWidget(); - if (oldwidget) { - oldwidget->hide(); - oldwidget->deleteLater(); - } - if (QLayout* lay = layout()) { - lay->addWidget(w); - } -} - -/** - @brief 获取设置的窗口 - - @return 如果没有,返回nullptr - */ -QWidget* SARibbonWidget::widget() const -{ - QLayout* lay = layout(); - if (lay) { - if (lay->count() == 0) { - return nullptr; - } - return lay->itemAt(0)->widget(); - } - return nullptr; -} - -QWidget* SARibbonWidget::takeWidget() -{ - QLayout* lay = layout(); - if (nullptr == lay) { - return nullptr; - } - if (lay->count() == 0) { - return nullptr; - } - auto item = lay->itemAt(0); - if (nullptr == item) { - return nullptr; - } - lay->removeItem(item); - auto w = item->widget(); - delete item; - if (!w) { - return nullptr; - } - return w; -} - -/** - * @brief 主屏幕切换触发的信号 - * @param screen - */ -void SARibbonWidget::onPrimaryScreenChanged(QScreen* screen) -{ - Q_UNUSED(screen); - // 主屏幕切换后,从新计算所有尺寸 - if (SARibbonBar* bar = ribbonBar()) { - qDebug() << "Primary Screen Changed"; - bar->updateRibbonGeometry(); - } -} +#include "SARibbonWidget.h" +#include "SARibbonBar.h" +#include "SARibbonTabBar.h" +#include +#include +#include +#include + +/** + * @brief The SARibbonWidget::PrivateData class + */ +class SARibbonWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonWidget) +public: + PrivateData(SARibbonWidget* p); + void installFrameless(SARibbonWidget* p); + +public: + SARibbonTheme mCurrentRibbonTheme { SARibbonTheme::RibbonThemeOffice2021Blue }; +}; + +SARibbonWidget::PrivateData::PrivateData(SARibbonWidget* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonWidget +//=================================================== +SARibbonWidget::SARibbonWidget(QWidget* parent) : QWidget(parent), d_ptr(new SARibbonWidget::PrivateData(this)) +{ + // 直接创建SARibbonBar + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + verticalLayout->setSpacing(1); + verticalLayout->setObjectName("verticalLayout"); + verticalLayout->setContentsMargins(0, 0, 0, 0); + SARibbonBar* ribbon = new SARibbonBar(this); + setRibbonBar(ribbon); + connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonWidget::onPrimaryScreenChanged); +} + +SARibbonWidget::~SARibbonWidget() +{ +} + +/** + * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr + * @return + */ +SARibbonBar* SARibbonWidget::ribbonBar() const +{ + QLayout* lay = layout(); + if (lay) { + return qobject_cast< SARibbonBar* >(lay->menuBar()); + } + return nullptr; +} + +/** + * @brief 设置ribbonbar + * @param bar + */ +void SARibbonWidget::setRibbonBar(SARibbonBar* bar) +{ + QLayout* lay = layout(); + if (lay) { + lay->setMenuBar(bar); + } +} + +/** + * @brief SARibbonMainWindow::setRibbonTheme + * + * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + * @code + * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + * @endcode + * @param theme + */ +void SARibbonWidget::setRibbonTheme(SARibbonTheme theme) +{ + sa_set_ribbon_theme(this, theme); + d_ptr->mCurrentRibbonTheme = theme; + if (SARibbonBar* bar = ribbonBar()) { + auto theme = ribbonTheme(); + // 尺寸修正 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeOffice2016Blue: + case SARibbonTheme::RibbonThemeDark: + case SARibbonTheme::RibbonThemeDark2: { + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2013.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 0px; + //! margin-left: 5px; + //! margin-bottom: 0px; + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + tab->setTabMargin(QMargins(5, 0, 0, 0)); + } break; + case SARibbonTheme::RibbonThemeOffice2021Blue: { + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2021.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 5px; + //! margin-left: 5px; + //! margin-bottom: 0px; + tab->setTabMargin(QMargins(5, 0, 5, 0)); + } + default: + break; + } + // 上下文标签颜色设置,以及基线颜色设置 + switch (theme) { + case SARibbonTheme::RibbonThemeWindows7: + case SARibbonTheme::RibbonThemeOffice2013: + case SARibbonTheme::RibbonThemeDark: + bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2016Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 + break; + case SARibbonTheme::RibbonThemeOffice2021Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(209, 207, 209)); //< 设置空颜色列表会重置为默认色系 + break; + default: + break; + } + // 基线颜色设置 + if (SARibbonTheme::RibbonThemeOffice2013 == theme) { + bar->setTabBarBaseLineColor(QColor(186, 201, 219)); + } else { + bar->setTabBarBaseLineColor(QColor()); + } + } +} + +SARibbonTheme SARibbonWidget::ribbonTheme() const +{ + return (d_ptr->mCurrentRibbonTheme); +} + +bool SARibbonWidget::isUseRibbon() const +{ + return (nullptr != ribbonBar()); +} + +/** + @brief 设置窗口 + + @param 窗口指针 + @note 窗口的所有权归SARibbonWidget管理 + @sa widget + @note 原来设置的窗口会被delete + */ +void SARibbonWidget::setWidget(QWidget* w) +{ + QWidget* oldwidget = takeWidget(); + if (oldwidget) { + oldwidget->hide(); + oldwidget->deleteLater(); + } + if (QLayout* lay = layout()) { + lay->addWidget(w); + } +} + +/** + @brief 获取设置的窗口 + + @return 如果没有,返回nullptr + */ +QWidget* SARibbonWidget::widget() const +{ + QLayout* lay = layout(); + if (lay) { + if (lay->count() == 0) { + return nullptr; + } + return lay->itemAt(0)->widget(); + } + return nullptr; +} + +QWidget* SARibbonWidget::takeWidget() +{ + QLayout* lay = layout(); + if (nullptr == lay) { + return nullptr; + } + if (lay->count() == 0) { + return nullptr; + } + auto item = lay->itemAt(0); + if (nullptr == item) { + return nullptr; + } + lay->removeItem(item); + auto w = item->widget(); + delete item; + if (!w) { + return nullptr; + } + return w; +} + +/** + * @brief 主屏幕切换触发的信号 + * @param screen + */ +void SARibbonWidget::onPrimaryScreenChanged(QScreen* screen) +{ + Q_UNUSED(screen); + // 主屏幕切换后,从新计算所有尺寸 + if (SARibbonBar* bar = ribbonBar()) { + qDebug() << "Primary Screen Changed"; + bar->updateRibbonGeometry(); + } +} diff --git a/src/SARibbonBar/SARibbonWidget.h b/src/SARibbonBar/SARibbonWidget.h index 2cabef01..d202b67f 100644 --- a/src/SARibbonBar/SARibbonWidget.h +++ b/src/SARibbonBar/SARibbonWidget.h @@ -1,45 +1,45 @@ -#ifndef SARIBBONWIDGET_H -#define SARIBBONWIDGET_H -#include "SARibbonGlobal.h" -#include - -class SARibbonBar; -class QScreen; -/** - * @brief 带SARibbonBar的Widget - * - * @note 注意,SARibbonWidget利用了布局来把ribbonbar放到最顶部,不要给此widget添加布局,否则会导致布局异常, - * 正确的做法是把布局的窗口通过setWidget设置进SARibbonWidget - * - */ -class SA_RIBBON_EXPORT SARibbonWidget : public QWidget -{ - Q_OBJECT - SA_RIBBON_DECLARE_PRIVATE(SARibbonWidget) - friend class SARibbonBar; - Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) - -public: - explicit SARibbonWidget(QWidget* parent = nullptr); - ~SARibbonWidget(); - // 返回SARibbonBar - SARibbonBar* ribbonBar() const; - // 设置ribbonbar - void setRibbonBar(SARibbonBar* bar); - - // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 - // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonTheme::RibbonThemeDark); }); - void setRibbonTheme(SARibbonTheme theme); - SARibbonTheme ribbonTheme() const; - // 判断当前是否使用ribbon模式 - bool isUseRibbon() const; - // 设置窗口 - void setWidget(QWidget* w); - QWidget* widget() const; - // - QWidget* takeWidget(); -private Q_SLOTS: - void onPrimaryScreenChanged(QScreen* screen); -}; - -#endif // SARIBBONWIDGET_H +#ifndef SARIBBONWIDGET_H +#define SARIBBONWIDGET_H +#include "SARibbonGlobal.h" +#include + +class SARibbonBar; +class QScreen; +/** + * @brief 带SARibbonBar的Widget + * + * @note 注意,SARibbonWidget利用了布局来把ribbonbar放到最顶部,不要给此widget添加布局,否则会导致布局异常, + * 正确的做法是把布局的窗口通过setWidget设置进SARibbonWidget + * + */ +class SA_RIBBON_EXPORT SARibbonWidget : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonWidget) + friend class SARibbonBar; + Q_PROPERTY(SARibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) + +public: + explicit SARibbonWidget(QWidget* parent = nullptr); + ~SARibbonWidget(); + // 返回SARibbonBar + SARibbonBar* ribbonBar() const; + // 设置ribbonbar + void setRibbonBar(SARibbonBar* bar); + + // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonTheme::RibbonThemeDark); }); + void setRibbonTheme(SARibbonTheme theme); + SARibbonTheme ribbonTheme() const; + // 判断当前是否使用ribbon模式 + bool isUseRibbon() const; + // 设置窗口 + void setWidget(QWidget* w); + QWidget* widget() const; + // + QWidget* takeWidget(); +private Q_SLOTS: + void onPrimaryScreenChanged(QScreen* screen); +}; + +#endif // SARIBBONWIDGET_H diff --git a/src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp b/src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp index 206af5d6..c68c1591 100644 --- a/src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp +++ b/src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp @@ -1,504 +1,504 @@ -#include "SAColorGridWidget.h" -#include "SAColorToolButton.h" -#include -#include -#include - -class SAColorGridWidget::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget) -public: - PrivateData(SAColorGridWidget* p); - // 获取ColorToolButton - SAColorToolButton* getColorToolButtonAt(int index); - SAColorToolButton* getColorToolButtonAt(int r, int c); - SAColorToolButton* getCheckedButton() const; - void updateGridColor(bool isRemoveSpacer = false); - void updateGridColorSize(); - void updateGridColorCheckable(); - void iterationColorBtns(SAColorGridWidget::FunColorBtn fn); - void removeAt(int r, int c); - void setColorAt(const QColor& clr, int r, int c); - bool isSpacer(int r, int c) const; - -public: - QList< QColor > mColors; - QGridLayout* mGridLayout { nullptr }; - QButtonGroup* mButtonGroup { nullptr }; - QSize mIconSize { 16, 16 }; - int mColumnCount { 8 }; ///< 列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - bool mColorCheckable; ///< 设置颜色是否是checkable - bool mHorizontalSpacerToRight { false }; ///< 最右边是否有弹簧 -}; - -SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p) -{ - mGridLayout = new QGridLayout(p); - p->setLayout(mGridLayout); - mGridLayout->setSpacing(0); - mGridLayout->setContentsMargins(1, 1, 1, 1); - mButtonGroup = new QButtonGroup(p); - mButtonGroup->setExclusive(true); - p->setMinimumHeight(mIconSize.height()); - p->setMinimumWidth(mIconSize.width()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index) -{ - QLayoutItem* item = mGridLayout->itemAt(index); - if (nullptr == item) { - return nullptr; - } - return qobject_cast< SAColorToolButton* >(item->widget()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (nullptr == item) { - return nullptr; - } - return qobject_cast< SAColorToolButton* >(item->widget()); -} - -SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const -{ - return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton()); -} - -/** - * @brief 根据mColors更新布局 - */ -void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer) -{ - int row = 1; - int col = mColumnCount; - if (col <= 0) { - col = mColors.size(); - } else { - row = std::ceil(mColors.size() / (float)col); - } - int index = 0; - for (int r = 0; r < row; ++r) { - for (int c = 0; c < col; ++c) { - if (index < mColors.size()) { - setColorAt(mColors[ index ], r, c); - ++index; - } else { - removeAt(r, c); - } - } - } - // 清除多余单元格 - int nowGridRow = mGridLayout->rowCount(); - int nowGridCol = mGridLayout->columnCount(); - if (nowGridRow > row) { - // 多余的清除 - for (int r = row; r < nowGridRow; ++r) { - for (int c = 0; c < nowGridCol; ++c) { - removeAt(r, c); - } - } - } - if (nowGridCol > col) { - // 多余的列清除 - for (int r = 0; r < row; ++r) { - for (int c = col; c < nowGridCol; ++c) { - if (isRemoveSpacer) { - removeAt(r, c); - } else { - if (!isSpacer(r, c)) { - removeAt(r, c); - } - } - } - } - } -} - -/** - * @brief 更新colorsize - */ -void SAColorGridWidget::PrivateData::updateGridColorSize() -{ - QSize s = mIconSize; - iterationColorBtns([ s ](SAColorToolButton* btn) { - if (btn) { - btn->setIconSize(s); - } - }); -} - -void SAColorGridWidget::PrivateData::updateGridColorCheckable() -{ - bool v = mColorCheckable; - iterationColorBtns([ v ](SAColorToolButton* btn) { - if (btn) { - btn->setCheckable(v); - } - }); -} - -/** - * @brief 遍历所有的button - * @param fn - */ -void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn) -{ - int cnt = mGridLayout->count(); - for (int i = 0; i < cnt; ++i) { - SAColorToolButton* tl = getColorToolButtonAt(i); - fn(tl); - } -} - -/** - * @brief 删除网格窗口 - * @param r - * @param c - */ -void SAColorGridWidget::PrivateData::removeAt(int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - QWidget* w = item->widget(); - mGridLayout->removeItem(item); - delete item; - if (w) { - w->deleteLater(); - } - } -} - -void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c) -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget()); - if (tl) { - tl->setColor(clr); - } - } else { - SAColorToolButton* tl = new SAColorToolButton(SAColorToolButton::NoColorMenu, q_ptr); - tl->setToolButtonStyle(Qt::ToolButtonIconOnly); - tl->setIconSize(mIconSize); - tl->setMargins(QMargins(4, 4, 4, 4)); - tl->setColor(clr); - tl->setCheckable(mColorCheckable); - tl->setAutoRaise(true); - mButtonGroup->addButton(tl, r + c); - mGridLayout->addWidget(tl, r, c); - } -} - -bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const -{ - QLayoutItem* item = mGridLayout->itemAtPosition(r, c); - if (item) { - if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) { - return true; - } - } - return false; -} - -//============================================================== -// SAColorGridWidget -//============================================================== - -SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this)) -{ - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked), - this, - &SAColorGridWidget::onButtonClicked); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed), - this, - &SAColorGridWidget::onButtonPressed); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased), - this, - &SAColorGridWidget::onButtonReleased); - connect(d_ptr->mButtonGroup, - QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled), - this, - &SAColorGridWidget::onButtonToggled); -} - -SAColorGridWidget::~SAColorGridWidget() -{ -} - -/** - * @brief 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - * @param c - */ -void SAColorGridWidget::setColumnCount(int c) -{ - d_ptr->mColumnCount = c; - d_ptr->updateGridColor(true); - if (d_ptr->mHorizontalSpacerToRight) { - setHorizontalSpacerToRight(); - } - updateGeometry(); -} - -/** - * @brief 设置颜色列表 - * @param c - */ -void SAColorGridWidget::setColorList(const QList< QColor >& cls) -{ - d_ptr->mColors = cls; - d_ptr->updateGridColor(); - updateGeometry(); -} - -/** - * @brief 获取颜色列表 - * @return - */ -QList< QColor > SAColorGridWidget::getColorList() const -{ - return d_ptr->mColors; -} - -/** - * @brief 获取间隔 - * @return - */ -int SAColorGridWidget::spacing() const -{ - return d_ptr->mGridLayout->spacing(); -} - -/** - * @brief 设置间隔 - * @param v - */ -void SAColorGridWidget::setSpacing(int v) -{ - d_ptr->mGridLayout->setSpacing(v); -} - -/** - * @brief 获取颜色的数量 - * @return - */ -int SAColorGridWidget::colorCount() const -{ - return d_ptr->mColors.size(); -} - -/** - * @brief 设置图标 size - * @return - */ -void SAColorGridWidget::setColorIconSize(const QSize& s) -{ - d_ptr->mIconSize = s; - setMinimumHeight(s.height()); - setMinimumWidth(s.width()); - d_ptr->updateGridColorSize(); -} - -/** - * @brief 获取图标 size - * @return - */ -QSize SAColorGridWidget::colorIconSize() const -{ - return d_ptr->mIconSize; -} - -/** - * @brief 设置颜色是否是checkable - * - * checkable的颜色按钮是可checked的 - * @param on - */ -void SAColorGridWidget::setColorCheckable(bool on) -{ - d_ptr->mColorCheckable = on; - d_ptr->updateGridColorCheckable(); -} - -/** - * @brief 颜色是否是checkable - * @return - */ -bool SAColorGridWidget::isColorCheckable() const -{ - return d_ptr->mColorCheckable; -} - -/** - * @brief 获取当前选中的颜色 - * @return - */ -QColor SAColorGridWidget::currentCheckedColor() const -{ - QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton(); - if (nullptr == btn) { - return QColor(); - } - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (nullptr == t) { - return QColor(); - } - return t->color(); -} - -/** - * @brief 获取index对应的colorbutton - * @param index - * @return 如果没有返回nullptr - */ -SAColorToolButton* SAColorGridWidget::colorButton(int index) const -{ - return d_ptr->getColorToolButtonAt(index); -} - -/** - * @brief 等同GridLayout的VerticalSpacing属性 - * @param v - */ -void SAColorGridWidget::setVerticalSpacing(int v) -{ - d_ptr->mGridLayout->setVerticalSpacing(v); -} -/** - * @brief 等同GridLayout的VerticalSpacing属性 - * @return - */ -int SAColorGridWidget::verticalSpacing() const -{ - return d_ptr->mGridLayout->verticalSpacing(); -} -/** - * @brief 等同GridLayout的HorizontalSpacing属性 - * @param v - */ -void SAColorGridWidget::setHorizontalSpacing(int v) -{ - d_ptr->mGridLayout->setHorizontalSpacing(v); -} -/** - * @brief 等同GridLayout的HorizontalSpacing属性 - * @return - */ -int SAColorGridWidget::horizontalSpacing() const -{ - return d_ptr->mGridLayout->horizontalSpacing(); -} - -/** - * @brief 清除选中状态,这时没有颜色是选中的 - */ -void SAColorGridWidget::clearCheckedState() -{ - if (d_ptr->mButtonGroup->exclusive()) { - SAColorToolButton* btn = d_ptr->getCheckedButton(); - if (btn) { - d_ptr->mButtonGroup->setExclusive(false); - btn->setChecked(false); - d_ptr->mButtonGroup->setExclusive(true); - } - } else { - d_ptr->iterationColorBtns([](SAColorToolButton* btn) { - if (btn->isChecked()) { - btn->setChecked(false); - } - }); - } -} - -void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn) -{ - d_ptr->iterationColorBtns(fn); -} - -void SAColorGridWidget::setRowMinimumHeight(int row, int minSize) -{ - d_ptr->mGridLayout->setRowMinimumHeight(row, minSize); -} - -void SAColorGridWidget::setHorizontalSpacerToRight(bool on) -{ - if (on) { - QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1); - } else { - d_ptr->removeAt(0, d_ptr->mColumnCount); - } - d_ptr->mHorizontalSpacerToRight = on; -} - -void SAColorGridWidget::onButtonClicked(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorClicked(t->color()); - } -} - -void SAColorGridWidget::onButtonPressed(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorPressed(t->color()); - } -} - -void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorToggled(t->color(), on); - } -} - -QSize SAColorGridWidget::sizeHint() const -{ - return d_ptr->mGridLayout->sizeHint(); - // int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing(); - // int h = d_ptr->mIconSize.height(); - // if (d_ptr->mColumnCount > 0) { - // h *= d_ptr->mColumnCount; - // if (!d_ptr->mColors.empty()) { - // int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount)); - // if (r > 0) { - // w *= r; - // } - // } - // } - // return QSize(w, h); -} - -void SAColorGridWidget::onButtonReleased(QAbstractButton* btn) -{ - SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); - if (t) { - Q_EMIT colorReleased(t->color()); - } -} - -namespace SA -{ - -QList< QColor > getStandardColorList() -{ - static QList< QColor > s_standardColorList({ QColor(192, 0, 0), - QColor(255, 0, 0), - QColor(255, 192, 0), - QColor(255, 255, 0), - QColor(146, 208, 80), - QColor(0, 176, 80), - QColor(0, 176, 240), - QColor(0, 112, 192), - QColor(0, 32, 96), - QColor(112, 48, 160) }); - return s_standardColorList; -} -} +#include "SAColorGridWidget.h" +#include "SAColorToolButton.h" +#include +#include +#include + +class SAColorGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget) +public: + PrivateData(SAColorGridWidget* p); + // 获取ColorToolButton + SAColorToolButton* getColorToolButtonAt(int index); + SAColorToolButton* getColorToolButtonAt(int r, int c); + SAColorToolButton* getCheckedButton() const; + void updateGridColor(bool isRemoveSpacer = false); + void updateGridColorSize(); + void updateGridColorCheckable(); + void iterationColorBtns(SAColorGridWidget::FunColorBtn fn); + void removeAt(int r, int c); + void setColorAt(const QColor& clr, int r, int c); + bool isSpacer(int r, int c) const; + +public: + QList< QColor > mColors; + QGridLayout* mGridLayout { nullptr }; + QButtonGroup* mButtonGroup { nullptr }; + QSize mIconSize { 16, 16 }; + int mColumnCount { 8 }; ///< 列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + bool mColorCheckable; ///< 设置颜色是否是checkable + bool mHorizontalSpacerToRight { false }; ///< 最右边是否有弹簧 +}; + +SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p) +{ + mGridLayout = new QGridLayout(p); + p->setLayout(mGridLayout); + mGridLayout->setSpacing(0); + mGridLayout->setContentsMargins(1, 1, 1, 1); + mButtonGroup = new QButtonGroup(p); + mButtonGroup->setExclusive(true); + p->setMinimumHeight(mIconSize.height()); + p->setMinimumWidth(mIconSize.width()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index) +{ + QLayoutItem* item = mGridLayout->itemAt(index); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const +{ + return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton()); +} + +/** + * @brief 根据mColors更新布局 + */ +void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer) +{ + int row = 1; + int col = mColumnCount; + if (col <= 0) { + col = mColors.size(); + } else { + row = std::ceil(mColors.size() / (float)col); + } + int index = 0; + for (int r = 0; r < row; ++r) { + for (int c = 0; c < col; ++c) { + if (index < mColors.size()) { + setColorAt(mColors[ index ], r, c); + ++index; + } else { + removeAt(r, c); + } + } + } + // 清除多余单元格 + int nowGridRow = mGridLayout->rowCount(); + int nowGridCol = mGridLayout->columnCount(); + if (nowGridRow > row) { + // 多余的清除 + for (int r = row; r < nowGridRow; ++r) { + for (int c = 0; c < nowGridCol; ++c) { + removeAt(r, c); + } + } + } + if (nowGridCol > col) { + // 多余的列清除 + for (int r = 0; r < row; ++r) { + for (int c = col; c < nowGridCol; ++c) { + if (isRemoveSpacer) { + removeAt(r, c); + } else { + if (!isSpacer(r, c)) { + removeAt(r, c); + } + } + } + } + } +} + +/** + * @brief 更新colorsize + */ +void SAColorGridWidget::PrivateData::updateGridColorSize() +{ + QSize s = mIconSize; + iterationColorBtns([ s ](SAColorToolButton* btn) { + if (btn) { + btn->setIconSize(s); + } + }); +} + +void SAColorGridWidget::PrivateData::updateGridColorCheckable() +{ + bool v = mColorCheckable; + iterationColorBtns([ v ](SAColorToolButton* btn) { + if (btn) { + btn->setCheckable(v); + } + }); +} + +/** + * @brief 遍历所有的button + * @param fn + */ +void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn) +{ + int cnt = mGridLayout->count(); + for (int i = 0; i < cnt; ++i) { + SAColorToolButton* tl = getColorToolButtonAt(i); + fn(tl); + } +} + +/** + * @brief 删除网格窗口 + * @param r + * @param c + */ +void SAColorGridWidget::PrivateData::removeAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + QWidget* w = item->widget(); + mGridLayout->removeItem(item); + delete item; + if (w) { + w->deleteLater(); + } + } +} + +void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget()); + if (tl) { + tl->setColor(clr); + } + } else { + SAColorToolButton* tl = new SAColorToolButton(SAColorToolButton::NoColorMenu, q_ptr); + tl->setToolButtonStyle(Qt::ToolButtonIconOnly); + tl->setIconSize(mIconSize); + tl->setMargins(QMargins(4, 4, 4, 4)); + tl->setColor(clr); + tl->setCheckable(mColorCheckable); + tl->setAutoRaise(true); + mButtonGroup->addButton(tl, r + c); + mGridLayout->addWidget(tl, r, c); + } +} + +bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) { + return true; + } + } + return false; +} + +//============================================================== +// SAColorGridWidget +//============================================================== + +SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this)) +{ + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked), + this, + &SAColorGridWidget::onButtonClicked); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed), + this, + &SAColorGridWidget::onButtonPressed); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased), + this, + &SAColorGridWidget::onButtonReleased); + connect(d_ptr->mButtonGroup, + QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled), + this, + &SAColorGridWidget::onButtonToggled); +} + +SAColorGridWidget::~SAColorGridWidget() +{ +} + +/** + * @brief 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + * @param c + */ +void SAColorGridWidget::setColumnCount(int c) +{ + d_ptr->mColumnCount = c; + d_ptr->updateGridColor(true); + if (d_ptr->mHorizontalSpacerToRight) { + setHorizontalSpacerToRight(); + } + updateGeometry(); +} + +/** + * @brief 设置颜色列表 + * @param c + */ +void SAColorGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mColors = cls; + d_ptr->updateGridColor(); + updateGeometry(); +} + +/** + * @brief 获取颜色列表 + * @return + */ +QList< QColor > SAColorGridWidget::getColorList() const +{ + return d_ptr->mColors; +} + +/** + * @brief 获取间隔 + * @return + */ +int SAColorGridWidget::spacing() const +{ + return d_ptr->mGridLayout->spacing(); +} + +/** + * @brief 设置间隔 + * @param v + */ +void SAColorGridWidget::setSpacing(int v) +{ + d_ptr->mGridLayout->setSpacing(v); +} + +/** + * @brief 获取颜色的数量 + * @return + */ +int SAColorGridWidget::colorCount() const +{ + return d_ptr->mColors.size(); +} + +/** + * @brief 设置图标 size + * @return + */ +void SAColorGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mIconSize = s; + setMinimumHeight(s.height()); + setMinimumWidth(s.width()); + d_ptr->updateGridColorSize(); +} + +/** + * @brief 获取图标 size + * @return + */ +QSize SAColorGridWidget::colorIconSize() const +{ + return d_ptr->mIconSize; +} + +/** + * @brief 设置颜色是否是checkable + * + * checkable的颜色按钮是可checked的 + * @param on + */ +void SAColorGridWidget::setColorCheckable(bool on) +{ + d_ptr->mColorCheckable = on; + d_ptr->updateGridColorCheckable(); +} + +/** + * @brief 颜色是否是checkable + * @return + */ +bool SAColorGridWidget::isColorCheckable() const +{ + return d_ptr->mColorCheckable; +} + +/** + * @brief 获取当前选中的颜色 + * @return + */ +QColor SAColorGridWidget::currentCheckedColor() const +{ + QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton(); + if (nullptr == btn) { + return QColor(); + } + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (nullptr == t) { + return QColor(); + } + return t->color(); +} + +/** + * @brief 获取index对应的colorbutton + * @param index + * @return 如果没有返回nullptr + */ +SAColorToolButton* SAColorGridWidget::colorButton(int index) const +{ + return d_ptr->getColorToolButtonAt(index); +} + +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @param v + */ +void SAColorGridWidget::setVerticalSpacing(int v) +{ + d_ptr->mGridLayout->setVerticalSpacing(v); +} +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @return + */ +int SAColorGridWidget::verticalSpacing() const +{ + return d_ptr->mGridLayout->verticalSpacing(); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @param v + */ +void SAColorGridWidget::setHorizontalSpacing(int v) +{ + d_ptr->mGridLayout->setHorizontalSpacing(v); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @return + */ +int SAColorGridWidget::horizontalSpacing() const +{ + return d_ptr->mGridLayout->horizontalSpacing(); +} + +/** + * @brief 清除选中状态,这时没有颜色是选中的 + */ +void SAColorGridWidget::clearCheckedState() +{ + if (d_ptr->mButtonGroup->exclusive()) { + SAColorToolButton* btn = d_ptr->getCheckedButton(); + if (btn) { + d_ptr->mButtonGroup->setExclusive(false); + btn->setChecked(false); + d_ptr->mButtonGroup->setExclusive(true); + } + } else { + d_ptr->iterationColorBtns([](SAColorToolButton* btn) { + if (btn->isChecked()) { + btn->setChecked(false); + } + }); + } +} + +void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn) +{ + d_ptr->iterationColorBtns(fn); +} + +void SAColorGridWidget::setRowMinimumHeight(int row, int minSize) +{ + d_ptr->mGridLayout->setRowMinimumHeight(row, minSize); +} + +void SAColorGridWidget::setHorizontalSpacerToRight(bool on) +{ + if (on) { + QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1); + } else { + d_ptr->removeAt(0, d_ptr->mColumnCount); + } + d_ptr->mHorizontalSpacerToRight = on; +} + +void SAColorGridWidget::onButtonClicked(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorClicked(t->color()); + } +} + +void SAColorGridWidget::onButtonPressed(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorPressed(t->color()); + } +} + +void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorToggled(t->color(), on); + } +} + +QSize SAColorGridWidget::sizeHint() const +{ + return d_ptr->mGridLayout->sizeHint(); + // int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing(); + // int h = d_ptr->mIconSize.height(); + // if (d_ptr->mColumnCount > 0) { + // h *= d_ptr->mColumnCount; + // if (!d_ptr->mColors.empty()) { + // int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount)); + // if (r > 0) { + // w *= r; + // } + // } + // } + // return QSize(w, h); +} + +void SAColorGridWidget::onButtonReleased(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + Q_EMIT colorReleased(t->color()); + } +} + +namespace SA +{ + +QList< QColor > getStandardColorList() +{ + static QList< QColor > s_standardColorList({ QColor(192, 0, 0), + QColor(255, 0, 0), + QColor(255, 192, 0), + QColor(255, 255, 0), + QColor(146, 208, 80), + QColor(0, 176, 80), + QColor(0, 176, 240), + QColor(0, 112, 192), + QColor(0, 32, 96), + QColor(112, 48, 160) }); + return s_standardColorList; +} +} diff --git a/src/SARibbonBar/colorWidgets/SAColorGridWidget.h b/src/SARibbonBar/colorWidgets/SAColorGridWidget.h index 041a1fb7..f8cd89ab 100644 --- a/src/SARibbonBar/colorWidgets/SAColorGridWidget.h +++ b/src/SARibbonBar/colorWidgets/SAColorGridWidget.h @@ -1,93 +1,93 @@ -#ifndef SACOLORGRIDWIDGET_H -#define SACOLORGRIDWIDGET_H -#include -#include -#include "SAColorWidgetsGlobal.h" -class QAbstractButton; -class SAColorToolButton; -/** - * @brief 一个grid的Color布局 - * - * 一个形如下面的颜色grid: - * - * □□□□□□□□□ - * - * □□□□□□□□□ - * - * □□□□□□□□□ - */ -class SA_COLOR_WIDGETS_API SAColorGridWidget : public QWidget -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorGridWidget) - Q_PROPERTY(int spacing READ spacing WRITE setSpacing) -public: - using FunColorBtn = std::function< void(SAColorToolButton*) >; - -public: - SAColorGridWidget(QWidget* par = nullptr); - ~SAColorGridWidget(); - // 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 - void setColumnCount(int c); - int columnCount() const; - // 设置当前的颜色列表 - void setColorList(const QList< QColor >& cls); - QList< QColor > getColorList() const; - // 间隔 - int spacing() const; - void setSpacing(int v); - // 获取颜色的数量 - int colorCount() const; - // 图标的尺寸 - void setColorIconSize(const QSize& s); - QSize colorIconSize() const; - // 设置颜色是否是checkable - void setColorCheckable(bool on = true); - bool isColorCheckable() const; - // 获取当前选中的颜色 - QColor currentCheckedColor() const; - // 获取index对于的colorbutton - SAColorToolButton* colorButton(int index) const; - // 垂直间距 - void setVerticalSpacing(int v); - int verticalSpacing() const; - // 水平间距 - void setHorizontalSpacing(int v); - int horizontalSpacing() const; - // 清除当前选中状态,这时没有颜色是选中的 - void clearCheckedState(); - // 对所有的colorbtn就行遍历处理,可以通过此函数进行tooltip设置等操作 - void iterationColorBtns(FunColorBtn fn); - // 设置行最小高度 - void setRowMinimumHeight(int row, int minSize); - // 让颜色块左对齐 - void setHorizontalSpacerToRight(bool on = true); -private Q_SLOTS: - void onButtonClicked(QAbstractButton* btn); - void onButtonPressed(QAbstractButton* btn); - void onButtonReleased(QAbstractButton* btn); - void onButtonToggled(QAbstractButton* btn, bool on); - -Q_SIGNALS: - /** - * @brief 对于check模式,check的颜色触发的信号 - * @param c - * @param on - */ - void colorClicked(const QColor& c); - void colorPressed(const QColor& c); - void colorReleased(const QColor& c); - void colorToggled(const QColor& c, bool on); - -public: - virtual QSize sizeHint() const Q_DECL_OVERRIDE; -}; -namespace SA -{ -/** - * @brief 获取标准色列表(一共10种颜色) - * @return - */ -SA_COLOR_WIDGETS_API QList< QColor > getStandardColorList(); -} -#endif // SACOLORGRIDWIDGET_H +#ifndef SACOLORGRIDWIDGET_H +#define SACOLORGRIDWIDGET_H +#include +#include +#include "SAColorWidgetsGlobal.h" +class QAbstractButton; +class SAColorToolButton; +/** + * @brief 一个grid的Color布局 + * + * 一个形如下面的颜色grid: + * + * □□□□□□□□□ + * + * □□□□□□□□□ + * + * □□□□□□□□□ + */ +class SA_COLOR_WIDGETS_API SAColorGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorGridWidget) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) +public: + using FunColorBtn = std::function< void(SAColorToolButton*) >; + +public: + SAColorGridWidget(QWidget* par = nullptr); + ~SAColorGridWidget(); + // 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + void setColumnCount(int c); + int columnCount() const; + // 设置当前的颜色列表 + void setColorList(const QList< QColor >& cls); + QList< QColor > getColorList() const; + // 间隔 + int spacing() const; + void setSpacing(int v); + // 获取颜色的数量 + int colorCount() const; + // 图标的尺寸 + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; + // 获取当前选中的颜色 + QColor currentCheckedColor() const; + // 获取index对于的colorbutton + SAColorToolButton* colorButton(int index) const; + // 垂直间距 + void setVerticalSpacing(int v); + int verticalSpacing() const; + // 水平间距 + void setHorizontalSpacing(int v); + int horizontalSpacing() const; + // 清除当前选中状态,这时没有颜色是选中的 + void clearCheckedState(); + // 对所有的colorbtn就行遍历处理,可以通过此函数进行tooltip设置等操作 + void iterationColorBtns(FunColorBtn fn); + // 设置行最小高度 + void setRowMinimumHeight(int row, int minSize); + // 让颜色块左对齐 + void setHorizontalSpacerToRight(bool on = true); +private Q_SLOTS: + void onButtonClicked(QAbstractButton* btn); + void onButtonPressed(QAbstractButton* btn); + void onButtonReleased(QAbstractButton* btn); + void onButtonToggled(QAbstractButton* btn, bool on); + +Q_SIGNALS: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); + void colorPressed(const QColor& c); + void colorReleased(const QColor& c); + void colorToggled(const QColor& c, bool on); + +public: + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +}; +namespace SA +{ +/** + * @brief 获取标准色列表(一共10种颜色) + * @return + */ +SA_COLOR_WIDGETS_API QList< QColor > getStandardColorList(); +} +#endif // SACOLORGRIDWIDGET_H diff --git a/src/SARibbonBar/colorWidgets/SAColorMenu.cpp b/src/SARibbonBar/colorWidgets/SAColorMenu.cpp index 03f9495d..1af0d4b2 100644 --- a/src/SARibbonBar/colorWidgets/SAColorMenu.cpp +++ b/src/SARibbonBar/colorWidgets/SAColorMenu.cpp @@ -1,260 +1,260 @@ -#include "SAColorMenu.h" -#include -#include -#include -#include -#include -#include -#include "SAColorGridWidget.h" -#include "SAColorPaletteGridWidget.h" -#include "SAColorToolButton.h" -class SAColorMenu::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu) -public: - PrivateData(SAColorMenu* p); - QColor getColorByDialog(); - void recordCustomColor(const QColor& c); - QWidgetAction* addWidget(QWidget* w); - //创建一个无颜色的icon - QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32)); - -public: - QLabel* mTitleLabel { nullptr }; ///< 主题颜色标题 - QWidgetAction* mTitleLabelAction { nullptr }; - SAColorPaletteGridWidget* mThemeColorsWidget { nullptr }; ///< 主题颜色 - QWidgetAction* mThemeColorPaletteAction { nullptr }; ///< ThemeColorsPalette对应的action - QAction* mCustomColorAction { nullptr }; ///< 自定义颜色action - SAColorGridWidget* mCustomColorsWidget { nullptr }; ///< 自定义颜色记录 - QWidgetAction* mCustomColorsWidgetAction { nullptr }; ///< 自定义颜色窗口对应的action - QAction* mNoneColorAction { nullptr }; ///< 无颜色action - QList< QColor > mCustomColors; - int mMaxCustomColorSize { 10 }; ///< 记录最多的自定义颜色数量 - QScopedPointer< QColorDialog > mColorDlg { nullptr }; ///<颜色对话框 -}; - -SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p) -{ -} - -QColor SAColorMenu::PrivateData::getColorByDialog() -{ - if (nullptr == mColorDlg) { - mColorDlg.reset(new QColorDialog()); - } - if (QDialog::Accepted == mColorDlg->exec()) { - return mColorDlg->currentColor(); - } - return QColor(); -} - -void SAColorMenu::PrivateData::recordCustomColor(const QColor& c) -{ - if (mCustomColors.size() < mMaxCustomColorSize) { - mCustomColors.push_back(c); - } else { - //超过数量,就左移动 - for (int i = 1; i < mCustomColors.size(); ++i) { - mCustomColors[ i - 1 ] = mCustomColors[ i ]; - } - mCustomColors.back() = c; - } -} - -QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w) -{ - QWidgetAction* wa = new QWidgetAction(q_ptr); - wa->setDefaultWidget(w); - q_ptr->addAction(wa); - return wa; -} - -QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize) -{ - QPixmap pixmap(baseSize); - pixmap.fill(Qt::transparent); - QPainter p(&pixmap); - SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1)); - return QIcon(pixmap); -} -//=================================================== -// SAColorMenu -//=================================================== - -SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this)) -{ - init(SA::getStandardColorList()); -} - -SAColorMenu::SAColorMenu(const QString& title, QWidget* parent) - : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this)) -{ - init(SA::getStandardColorList()); -} - -SAColorMenu::~SAColorMenu() -{ -} - -/** - * @brief 快速绑定ColorToolButton - * @param btn - */ -void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn) -{ - if (!btn) { - return; - } - if (btn->menu() != this) { - btn->setMenu(this); - } - connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor); -} - -/** - * @brief ThemeColorsPalette对应的action - * @return - */ -QWidgetAction* SAColorMenu::themeColorsPaletteAction() const -{ - return d_ptr->mThemeColorPaletteAction; -} - -/** - * @brief CustomColorsWidget对应的action - * @return - */ -QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const -{ - return d_ptr->mCustomColorsWidgetAction; -} - -/** - * @brief 自定义颜色action - * @return - */ -QAction* SAColorMenu::customColorAction() const -{ - return d_ptr->mCustomColorAction; -} - -/** - * @brief 获取ThemeColorsPalette - * @return - */ -SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const -{ - return d_ptr->mThemeColorsWidget; -} - -/** - * @brief 获取自定义颜色grid - * @return - */ -SAColorGridWidget* SAColorMenu::customColorsWidget() const -{ - return d_ptr->mCustomColorsWidget; -} - -/** - * @brief 建立没有颜色的action,可以选择无颜色 - * - * 无颜色选中会发射selectedColor(QColor()) - * @param on - */ -void SAColorMenu::enableNoneColorAction(bool on) -{ - //无颜色默认是在自定义颜色的上方 - if (on) { - if (d_ptr->mNoneColorAction) { - if (actions().contains(d_ptr->mNoneColorAction)) { - //已经包含了NoneColorAction,退出 - return; - } else { - insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); - } - } else { - QIcon ic = d_ptr->createNoneColorIcon(); - d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this); - connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered); - insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); - } - } else { - removeAction(d_ptr->mNoneColorAction); - // remove后暂时不删除action - } -} - -/** - * @brief 获取None Color Action - * - * @note 注意,enableNoneColorAction(true),之后才不是nullptr - * @return 如果没有建立NoneColorAction,会返回nullptr - */ -QAction* SAColorMenu::noneColorAction() const -{ - return d_ptr->mNoneColorAction; -} - -/** - * @brief 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 - * @param c - */ -void SAColorMenu::emitSelectedColor(const QColor& c) -{ - Q_EMIT selectedColor(c); - hide(); -} - -void SAColorMenu::init(const QList< QColor >& themeCls) -{ - d_ptr->mTitleLabel = new QLabel(this); - d_ptr->mTitleLabel->setText(tr("Theme Colors")); - d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel); - - d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this); - d_ptr->mThemeColorsWidget->setColorCheckable(false); - d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget); - - d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this); // cn:自定义颜色 - addAction(d_ptr->mCustomColorAction); - - QSize clrSize = d_ptr->mThemeColorsWidget->colorIconSize(); - d_ptr->mCustomColorsWidget = new SAColorGridWidget(this); - d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height()); - d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight(); - d_ptr->mCustomColorsWidget->setColorIconSize(clrSize); - d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize); - d_ptr->mCustomColorsWidget->setColorCheckable(false); - d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget); - - connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered); - connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); - connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); -} - -/** - * @brief 自定义颜色 - * @param on - */ -void SAColorMenu::onCustomColorActionTriggered(bool on) -{ - Q_UNUSED(on); - QColor c = d_ptr->getColorByDialog(); - if (c.isValid()) { - d_ptr->recordCustomColor(c); - d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors); - updateGeometry(); - emitSelectedColor(c); - } -} - -/** - * @brief 无颜色 - * @param on - */ -void SAColorMenu::onNoneColorActionTriggered(bool on) -{ - Q_UNUSED(on); - emitSelectedColor(QColor()); -} +#include "SAColorMenu.h" +#include +#include +#include +#include +#include +#include +#include "SAColorGridWidget.h" +#include "SAColorPaletteGridWidget.h" +#include "SAColorToolButton.h" +class SAColorMenu::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu) +public: + PrivateData(SAColorMenu* p); + QColor getColorByDialog(); + void recordCustomColor(const QColor& c); + QWidgetAction* addWidget(QWidget* w); + //创建一个无颜色的icon + QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32)); + +public: + QLabel* mTitleLabel { nullptr }; ///< 主题颜色标题 + QWidgetAction* mTitleLabelAction { nullptr }; + SAColorPaletteGridWidget* mThemeColorsWidget { nullptr }; ///< 主题颜色 + QWidgetAction* mThemeColorPaletteAction { nullptr }; ///< ThemeColorsPalette对应的action + QAction* mCustomColorAction { nullptr }; ///< 自定义颜色action + SAColorGridWidget* mCustomColorsWidget { nullptr }; ///< 自定义颜色记录 + QWidgetAction* mCustomColorsWidgetAction { nullptr }; ///< 自定义颜色窗口对应的action + QAction* mNoneColorAction { nullptr }; ///< 无颜色action + QList< QColor > mCustomColors; + int mMaxCustomColorSize { 10 }; ///< 记录最多的自定义颜色数量 + QScopedPointer< QColorDialog > mColorDlg { nullptr }; ///<颜色对话框 +}; + +SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p) +{ +} + +QColor SAColorMenu::PrivateData::getColorByDialog() +{ + if (nullptr == mColorDlg) { + mColorDlg.reset(new QColorDialog()); + } + if (QDialog::Accepted == mColorDlg->exec()) { + return mColorDlg->currentColor(); + } + return QColor(); +} + +void SAColorMenu::PrivateData::recordCustomColor(const QColor& c) +{ + if (mCustomColors.size() < mMaxCustomColorSize) { + mCustomColors.push_back(c); + } else { + //超过数量,就左移动 + for (int i = 1; i < mCustomColors.size(); ++i) { + mCustomColors[ i - 1 ] = mCustomColors[ i ]; + } + mCustomColors.back() = c; + } +} + +QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w) +{ + QWidgetAction* wa = new QWidgetAction(q_ptr); + wa->setDefaultWidget(w); + q_ptr->addAction(wa); + return wa; +} + +QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize) +{ + QPixmap pixmap(baseSize); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1)); + return QIcon(pixmap); +} +//=================================================== +// SAColorMenu +//=================================================== + +SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::SAColorMenu(const QString& title, QWidget* parent) + : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::~SAColorMenu() +{ +} + +/** + * @brief 快速绑定ColorToolButton + * @param btn + */ +void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn) +{ + if (!btn) { + return; + } + if (btn->menu() != this) { + btn->setMenu(this); + } + connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor); +} + +/** + * @brief ThemeColorsPalette对应的action + * @return + */ +QWidgetAction* SAColorMenu::themeColorsPaletteAction() const +{ + return d_ptr->mThemeColorPaletteAction; +} + +/** + * @brief CustomColorsWidget对应的action + * @return + */ +QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const +{ + return d_ptr->mCustomColorsWidgetAction; +} + +/** + * @brief 自定义颜色action + * @return + */ +QAction* SAColorMenu::customColorAction() const +{ + return d_ptr->mCustomColorAction; +} + +/** + * @brief 获取ThemeColorsPalette + * @return + */ +SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const +{ + return d_ptr->mThemeColorsWidget; +} + +/** + * @brief 获取自定义颜色grid + * @return + */ +SAColorGridWidget* SAColorMenu::customColorsWidget() const +{ + return d_ptr->mCustomColorsWidget; +} + +/** + * @brief 建立没有颜色的action,可以选择无颜色 + * + * 无颜色选中会发射selectedColor(QColor()) + * @param on + */ +void SAColorMenu::enableNoneColorAction(bool on) +{ + //无颜色默认是在自定义颜色的上方 + if (on) { + if (d_ptr->mNoneColorAction) { + if (actions().contains(d_ptr->mNoneColorAction)) { + //已经包含了NoneColorAction,退出 + return; + } else { + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + QIcon ic = d_ptr->createNoneColorIcon(); + d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this); + connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered); + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + removeAction(d_ptr->mNoneColorAction); + // remove后暂时不删除action + } +} + +/** + * @brief 获取None Color Action + * + * @note 注意,enableNoneColorAction(true),之后才不是nullptr + * @return 如果没有建立NoneColorAction,会返回nullptr + */ +QAction* SAColorMenu::noneColorAction() const +{ + return d_ptr->mNoneColorAction; +} + +/** + * @brief 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + * @param c + */ +void SAColorMenu::emitSelectedColor(const QColor& c) +{ + Q_EMIT selectedColor(c); + hide(); +} + +void SAColorMenu::init(const QList< QColor >& themeCls) +{ + d_ptr->mTitleLabel = new QLabel(this); + d_ptr->mTitleLabel->setText(tr("Theme Colors")); + d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel); + + d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this); + d_ptr->mThemeColorsWidget->setColorCheckable(false); + d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget); + + d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this); // cn:自定义颜色 + addAction(d_ptr->mCustomColorAction); + + QSize clrSize = d_ptr->mThemeColorsWidget->colorIconSize(); + d_ptr->mCustomColorsWidget = new SAColorGridWidget(this); + d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height()); + d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight(); + d_ptr->mCustomColorsWidget->setColorIconSize(clrSize); + d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize); + d_ptr->mCustomColorsWidget->setColorCheckable(false); + d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget); + + connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered); + connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); + connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); +} + +/** + * @brief 自定义颜色 + * @param on + */ +void SAColorMenu::onCustomColorActionTriggered(bool on) +{ + Q_UNUSED(on); + QColor c = d_ptr->getColorByDialog(); + if (c.isValid()) { + d_ptr->recordCustomColor(c); + d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors); + updateGeometry(); + emitSelectedColor(c); + } +} + +/** + * @brief 无颜色 + * @param on + */ +void SAColorMenu::onNoneColorActionTriggered(bool on) +{ + Q_UNUSED(on); + emitSelectedColor(QColor()); +} diff --git a/src/SARibbonBar/colorWidgets/SAColorMenu.h b/src/SARibbonBar/colorWidgets/SAColorMenu.h index 7101d867..b90d2abc 100644 --- a/src/SARibbonBar/colorWidgets/SAColorMenu.h +++ b/src/SARibbonBar/colorWidgets/SAColorMenu.h @@ -1,53 +1,53 @@ -#ifndef SACOLORMENU_H -#define SACOLORMENU_H -#include -#include "SAColorWidgetsGlobal.h" -class QWidgetAction; -class SAColorPaletteGridWidget; -class SAColorGridWidget; -class SAColorToolButton; -/** - * @brief 标准颜色菜单 - */ -class SA_COLOR_WIDGETS_API SAColorMenu : public QMenu -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorMenu) -public: - explicit SAColorMenu(QWidget* parent = nullptr); - explicit SAColorMenu(const QString& title, QWidget* parent = nullptr); - ~SAColorMenu(); - // 快速绑定colorbtn - void bindToColorToolButton(SAColorToolButton* btn); - // ThemeColorsPalette对应的action - QWidgetAction* themeColorsPaletteAction() const; - // CustomColorsWidget对应的action - QWidgetAction* getCustomColorsWidgetAction() const; - // 自定义颜色action - QAction* customColorAction() const; - // 获取ThemeColorsPalette - SAColorPaletteGridWidget* colorPaletteGridWidget() const; - // 获取自定义颜色grid - SAColorGridWidget* customColorsWidget() const; - // 构建无颜色action,默认无颜色action是没有的 - void enableNoneColorAction(bool on = true); - // 获取None Color Action,注意,enableNoneColorAction(true),之后才不是nullptr - QAction* noneColorAction() const; -public Q_SLOTS: - // 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 - void emitSelectedColor(const QColor& c); -Q_SIGNALS: - /** - * @brief 选择了颜色触发的信号 - * @param c - */ - void selectedColor(const QColor& c); -private Q_SLOTS: - void onCustomColorActionTriggered(bool on); - void onNoneColorActionTriggered(bool on); - -private: - void init(const QList< QColor >& themeCls); -}; - -#endif // SACOLORMENU_H +#ifndef SACOLORMENU_H +#define SACOLORMENU_H +#include +#include "SAColorWidgetsGlobal.h" +class QWidgetAction; +class SAColorPaletteGridWidget; +class SAColorGridWidget; +class SAColorToolButton; +/** + * @brief 标准颜色菜单 + */ +class SA_COLOR_WIDGETS_API SAColorMenu : public QMenu +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorMenu) +public: + explicit SAColorMenu(QWidget* parent = nullptr); + explicit SAColorMenu(const QString& title, QWidget* parent = nullptr); + ~SAColorMenu(); + // 快速绑定colorbtn + void bindToColorToolButton(SAColorToolButton* btn); + // ThemeColorsPalette对应的action + QWidgetAction* themeColorsPaletteAction() const; + // CustomColorsWidget对应的action + QWidgetAction* getCustomColorsWidgetAction() const; + // 自定义颜色action + QAction* customColorAction() const; + // 获取ThemeColorsPalette + SAColorPaletteGridWidget* colorPaletteGridWidget() const; + // 获取自定义颜色grid + SAColorGridWidget* customColorsWidget() const; + // 构建无颜色action,默认无颜色action是没有的 + void enableNoneColorAction(bool on = true); + // 获取None Color Action,注意,enableNoneColorAction(true),之后才不是nullptr + QAction* noneColorAction() const; +public Q_SLOTS: + // 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + void emitSelectedColor(const QColor& c); +Q_SIGNALS: + /** + * @brief 选择了颜色触发的信号 + * @param c + */ + void selectedColor(const QColor& c); +private Q_SLOTS: + void onCustomColorActionTriggered(bool on); + void onNoneColorActionTriggered(bool on); + +private: + void init(const QList< QColor >& themeCls); +}; + +#endif // SACOLORMENU_H diff --git a/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp b/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp index 056ef8b1..de7ebc77 100644 --- a/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp +++ b/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp @@ -1,171 +1,171 @@ -#include "SAColorPaletteGridWidget.h" -// Qt -#include -#include -#include -#include -// SA -#include "SAColorGridWidget.h" -class SAColorPaletteGridWidget::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget) -public: - PrivateData(SAColorPaletteGridWidget* p); - //生成color palette - QList< QColor > makeColorPalette(const QList< QColor >& clrList) const; - -public: - QList< int > mFactor { 180, 160, 140, 75, 50 }; ///< palette的比例因子,将调用QColor的lighter函数执行 - QVBoxLayout* mLayout { nullptr }; ///< 垂直布局 - SAColorGridWidget* mMainColorList { nullptr }; ///< 这个用于显示标准颜色 - SAColorGridWidget* mPaletteColorGrid { nullptr }; ///< 这个用于生成3行亮色,2行暗色的palette -}; - -SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p) -{ - mLayout = new QVBoxLayout(p); - p->setLayout(mLayout); - mMainColorList = new SAColorGridWidget(p); - mPaletteColorGrid = new SAColorGridWidget(p); - mLayout->addWidget(mMainColorList); - mLayout->addWidget(mPaletteColorGrid); - mLayout->setContentsMargins(1, 1, 1, 1); - mLayout->setSpacing(8); - QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - mMainColorList->setSizePolicy(sizePolicy); - mPaletteColorGrid->setSizePolicy(sizePolicy); - mMainColorList->setColumnCount(0); - mPaletteColorGrid->setVerticalSpacing(0); -} - -QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const -{ - QList< QColor > res; - for (int f : qAsConst(mFactor)) { - for (const QColor& c : qAsConst(clrList)) { - res.append(c.lighter(f)); - } - } - return res; -} -//============================================================== -// name -//============================================================== - -/** - * @brief 使用默认的标准颜色生成一个color palette - * @param par - */ -SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this)) -{ - init(); - setColorList(SA::getStandardColorList()); -} - -/** - * @brief 根据指定的颜色生成一个color palette - * @param cls - * @param par - */ -SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par) - : QWidget(par), d_ptr(new PrivateData(this)) -{ - init(); - setColorList(cls); -} - -SAColorPaletteGridWidget::~SAColorPaletteGridWidget() -{ -} -void SAColorPaletteGridWidget::init() -{ - connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked); - connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked); - QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - setSizePolicy(sizePolicy); - setColorIconSize(QSize(10, 10)); -} - -/** - * @brief 设置颜色系列,颜色系列的个数决定了主色卡的数量,palette色卡会根据factor进行生成 - * @param cls - */ -void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls) -{ - d_ptr->mMainColorList->setColumnCount(0); - d_ptr->mMainColorList->setColorList(cls); - d_ptr->mPaletteColorGrid->setColumnCount(cls.size()); - d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls)); -} - -/** - * @brief 获取颜色系列 - * @return - */ -QList< QColor > SAColorPaletteGridWidget::colorList() const -{ - return d_ptr->mMainColorList->getColorList(); -} - -/** - * @brief 设置颜色深浅比例factor,factor决定了palette的行数,factor有5个,就有5行 - * - * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行,每个系数会对标准颜色执行QColor::lighter操作 - * @param factor - */ -void SAColorPaletteGridWidget::setFactor(const QList< int >& factor) -{ - d_ptr->mFactor = factor; - d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList())); -} - -/** - * @brief 获取色卡 - * @return - */ -QList< int > SAColorPaletteGridWidget::factor() const -{ - return d_ptr->mFactor; -} - -/** - * @brief 设置颜色块的尺寸,默认为10*10 - * @param s - */ -void SAColorPaletteGridWidget::setColorIconSize(const QSize& s) -{ - d_ptr->mMainColorList->setColorIconSize(s); - d_ptr->mPaletteColorGrid->setColorIconSize(s); -} - -QSize SAColorPaletteGridWidget::colorIconSize() const -{ - return d_ptr->mMainColorList->colorIconSize(); -} - -/** - * @brief 设置颜色块是否能被checked - * @param on - */ -void SAColorPaletteGridWidget::setColorCheckable(bool on) -{ - d_ptr->mMainColorList->setColorCheckable(on); - d_ptr->mPaletteColorGrid->setColorCheckable(on); -} - -bool SAColorPaletteGridWidget::isColorCheckable() const -{ - return d_ptr->mMainColorList->isColorCheckable(); -} - -void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c) -{ - d_ptr->mPaletteColorGrid->clearCheckedState(); - Q_EMIT colorClicked(c); -} - -void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c) -{ - d_ptr->mMainColorList->clearCheckedState(); - Q_EMIT colorClicked(c); -} +#include "SAColorPaletteGridWidget.h" +// Qt +#include +#include +#include +#include +// SA +#include "SAColorGridWidget.h" +class SAColorPaletteGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget) +public: + PrivateData(SAColorPaletteGridWidget* p); + //生成color palette + QList< QColor > makeColorPalette(const QList< QColor >& clrList) const; + +public: + QList< int > mFactor { 180, 160, 140, 75, 50 }; ///< palette的比例因子,将调用QColor的lighter函数执行 + QVBoxLayout* mLayout { nullptr }; ///< 垂直布局 + SAColorGridWidget* mMainColorList { nullptr }; ///< 这个用于显示标准颜色 + SAColorGridWidget* mPaletteColorGrid { nullptr }; ///< 这个用于生成3行亮色,2行暗色的palette +}; + +SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p) +{ + mLayout = new QVBoxLayout(p); + p->setLayout(mLayout); + mMainColorList = new SAColorGridWidget(p); + mPaletteColorGrid = new SAColorGridWidget(p); + mLayout->addWidget(mMainColorList); + mLayout->addWidget(mPaletteColorGrid); + mLayout->setContentsMargins(1, 1, 1, 1); + mLayout->setSpacing(8); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + mMainColorList->setSizePolicy(sizePolicy); + mPaletteColorGrid->setSizePolicy(sizePolicy); + mMainColorList->setColumnCount(0); + mPaletteColorGrid->setVerticalSpacing(0); +} + +QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const +{ + QList< QColor > res; + for (int f : qAsConst(mFactor)) { + for (const QColor& c : qAsConst(clrList)) { + res.append(c.lighter(f)); + } + } + return res; +} +//============================================================== +// name +//============================================================== + +/** + * @brief 使用默认的标准颜色生成一个color palette + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(SA::getStandardColorList()); +} + +/** + * @brief 根据指定的颜色生成一个color palette + * @param cls + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par) + : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(cls); +} + +SAColorPaletteGridWidget::~SAColorPaletteGridWidget() +{ +} +void SAColorPaletteGridWidget::init() +{ + connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked); + connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(sizePolicy); + setColorIconSize(QSize(10, 10)); +} + +/** + * @brief 设置颜色系列,颜色系列的个数决定了主色卡的数量,palette色卡会根据factor进行生成 + * @param cls + */ +void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mMainColorList->setColumnCount(0); + d_ptr->mMainColorList->setColorList(cls); + d_ptr->mPaletteColorGrid->setColumnCount(cls.size()); + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls)); +} + +/** + * @brief 获取颜色系列 + * @return + */ +QList< QColor > SAColorPaletteGridWidget::colorList() const +{ + return d_ptr->mMainColorList->getColorList(); +} + +/** + * @brief 设置颜色深浅比例factor,factor决定了palette的行数,factor有5个,就有5行 + * + * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行,每个系数会对标准颜色执行QColor::lighter操作 + * @param factor + */ +void SAColorPaletteGridWidget::setFactor(const QList< int >& factor) +{ + d_ptr->mFactor = factor; + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList())); +} + +/** + * @brief 获取色卡 + * @return + */ +QList< int > SAColorPaletteGridWidget::factor() const +{ + return d_ptr->mFactor; +} + +/** + * @brief 设置颜色块的尺寸,默认为10*10 + * @param s + */ +void SAColorPaletteGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mMainColorList->setColorIconSize(s); + d_ptr->mPaletteColorGrid->setColorIconSize(s); +} + +QSize SAColorPaletteGridWidget::colorIconSize() const +{ + return d_ptr->mMainColorList->colorIconSize(); +} + +/** + * @brief 设置颜色块是否能被checked + * @param on + */ +void SAColorPaletteGridWidget::setColorCheckable(bool on) +{ + d_ptr->mMainColorList->setColorCheckable(on); + d_ptr->mPaletteColorGrid->setColorCheckable(on); +} + +bool SAColorPaletteGridWidget::isColorCheckable() const +{ + return d_ptr->mMainColorList->isColorCheckable(); +} + +void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c) +{ + d_ptr->mPaletteColorGrid->clearCheckedState(); + Q_EMIT colorClicked(c); +} + +void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c) +{ + d_ptr->mMainColorList->clearCheckedState(); + Q_EMIT colorClicked(c); +} diff --git a/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h b/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h index 3fb9d193..6e359d8a 100644 --- a/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h +++ b/src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h @@ -1,45 +1,45 @@ -#ifndef SACOLORPALETTEGRIDWIDGET_H -#define SACOLORPALETTEGRIDWIDGET_H -#include -#include "SAColorWidgetsGlobal.h" -class QMenu; -class SAColorToolButton; -/** - * @brief 类似office的颜色选择窗口,有一排标准色,下面有一个颜色板,有3行浅色,有2行深色 - */ -class SA_COLOR_WIDGETS_API SAColorPaletteGridWidget : public QWidget -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorPaletteGridWidget) -public: - SAColorPaletteGridWidget(QWidget* par = nullptr); - SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par = nullptr); - ~SAColorPaletteGridWidget(); - // 设置窗口维护的colorList - void setColorList(const QList< QColor >& cls); - QList< QColor > colorList() const; - // 设置颜色深浅比例factor,默认为{ 180, 160, 140, 75, 50 } - void setFactor(const QList< int >& factor); - QList< int > factor() const; - // 设置iconsize - void setColorIconSize(const QSize& s); - QSize colorIconSize() const; - // 设置颜色是否是checkable - void setColorCheckable(bool on = true); - bool isColorCheckable() const; -private Q_SLOTS: - void onMainColorClicked(const QColor& c); - void onPaletteColorClicked(const QColor& c); - -private: - void init(); -Q_SIGNALS: - /** - * @brief 对于check模式,check的颜色触发的信号 - * @param c - * @param on - */ - void colorClicked(const QColor& c); -}; - -#endif // SACOLORPALETTEGRIDWIDGET_H +#ifndef SACOLORPALETTEGRIDWIDGET_H +#define SACOLORPALETTEGRIDWIDGET_H +#include +#include "SAColorWidgetsGlobal.h" +class QMenu; +class SAColorToolButton; +/** + * @brief 类似office的颜色选择窗口,有一排标准色,下面有一个颜色板,有3行浅色,有2行深色 + */ +class SA_COLOR_WIDGETS_API SAColorPaletteGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorPaletteGridWidget) +public: + SAColorPaletteGridWidget(QWidget* par = nullptr); + SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par = nullptr); + ~SAColorPaletteGridWidget(); + // 设置窗口维护的colorList + void setColorList(const QList< QColor >& cls); + QList< QColor > colorList() const; + // 设置颜色深浅比例factor,默认为{ 180, 160, 140, 75, 50 } + void setFactor(const QList< int >& factor); + QList< int > factor() const; + // 设置iconsize + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; +private Q_SLOTS: + void onMainColorClicked(const QColor& c); + void onPaletteColorClicked(const QColor& c); + +private: + void init(); +Q_SIGNALS: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); +}; + +#endif // SACOLORPALETTEGRIDWIDGET_H diff --git a/src/SARibbonBar/colorWidgets/SAColorToolButton.cpp b/src/SARibbonBar/colorWidgets/SAColorToolButton.cpp index 4b803d3a..2d4b4fdd 100644 --- a/src/SARibbonBar/colorWidgets/SAColorToolButton.cpp +++ b/src/SARibbonBar/colorWidgets/SAColorToolButton.cpp @@ -1,631 +1,631 @@ -#include "SAColorToolButton.h" -#include -#include -#include -#include -#include -#include -#include -#include "SAColorMenu.h" - -#define SAColorToolButton_DEBUG_PRINT 0 -class SAColorToolButton::PrivateData -{ - SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton) -public: - PrivateData(SAColorToolButton* p); - void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect); - QRect getButtonRect(const QStyleOptionToolButton& opt); - QRect getIndicatorRect(const QStyleOptionToolButton& opt); - QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt); - QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt); - void removeMenu(); - -public: - QColor mColor { Qt::white }; - int mSpacing { 2 }; ///< 间隔 - QMargins mMargins { 3, 3, 3, 3 }; ///< box - ColorToolButtonStyle mColorButtonStyle { WithColorMenu }; ///< 样式 - static int s_indicatorArrorWidth; ///< 菜单宽度 -}; - -int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8; - -SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p) -{ -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - // 确定文本区域 - textRect = QRect(); - QRect buttonRect = getButtonRect(opt); - if (opt.icon.isNull()) { - colorRect = buttonRect; - iconRect = QRect(); - } else { - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - int colorHeight = tmpSize.height() / 4; - int totalHeight = colorHeight + tmpSize.height() + mSpacing; - if (totalHeight > buttonRect.height()) { - // 过高,这时要通过高度反推tmpSize和colorHeight - colorHeight = (buttonRect.height() - mSpacing) / 5; - tmpSize.setHeight(colorHeight * 4); - if (opt.iconSize.height() > 0) { - tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height()); // 等比例 - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - } - // 有icon,颜色位于图标下面 - int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 - int x = (buttonRect.width() - tmpSize.width()) / 2; - iconRect = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height()); - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - } -} - -/** - * @brief 纯文本情况,左边有5像素显示颜色 - * @param opt - * @param iconRect - * @param textRect - * @param colorRect 左边有5像素显示颜色 - */ -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - iconRect = QRect(); - QSize colorSize = opt.iconSize; - if (colorSize.isNull()) { - colorSize = QSize(16, 16); - } - colorSize = buttonRect.size().boundedTo(colorSize); - colorRect = QRect(buttonRect.left(), - buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, - colorSize.width(), - colorSize.height()); - textRect = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0); -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - if (opt.icon.isNull()) { - QSize colorSize = opt.iconSize; - if (colorSize.isNull()) { - colorSize = QSize(16, 16); - } - // 说明没有icon - // 这时所有都是color - // iconRect=Null - colorSize = buttonRect.size().boundedTo(colorSize); - iconRect = QRect(); - colorRect = QRect(buttonRect.left(), - buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, - colorSize.width(), - colorSize.height()); - textRect = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0); - } else { // 有图标 - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - tmpSize = buttonRect.size().boundedTo(tmpSize); - int colorHeight = tmpSize.height() / 4; - int totalHeight = colorHeight + tmpSize.height() + mSpacing; - if (totalHeight > buttonRect.height()) { - // 过高,这时要通过高度反推tmpSize和colorHeight - colorHeight = (buttonRect.height() - mSpacing) / 5; - tmpSize.setHeight(colorHeight * 4); - tmpSize.setWidth(tmpSize.height()); // 对于异形也设置为正方行 - } - // 有icon,颜色位于图标下面 - int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 - iconRect = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height()); - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - textRect = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0); - } -} - -void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, - QRect& iconRect, - QRect& textRect, - QRect& colorRect) -{ - QRect buttonRect = getButtonRect(opt); - QSize tmpSize = opt.iconSize; - if (tmpSize.isNull()) { - tmpSize = QSize(16, 16); - } - // 获取字体高度 - int textHeight = opt.fontMetrics.height(); - tmpSize = buttonRect.size().boundedTo(tmpSize); - if (opt.icon.isNull()) { - int totalHeight = textHeight + opt.iconSize.height() + mSpacing; - if (totalHeight < buttonRect.height()) { - // 足够高 - colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + (buttonRect.height() - totalHeight) / 2, - tmpSize.width(), - opt.iconSize.height()); - } else { - // 空间不足 - colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + mSpacing, - tmpSize.width(), - opt.iconSize.height()); - } - iconRect = QRect(); - textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); - - } else { - // 有图标 - int colorHeight = tmpSize.height() / 4; - int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing; - if (totalHeight < buttonRect.height()) { - // 高度空间足够 - // 先布置icon - iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + (buttonRect.height() - totalHeight) / 2, - tmpSize.width(), - opt.iconSize.height()); - - } else { - // 空间不足 - iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, - buttonRect.top() + mSpacing, - tmpSize.width(), - opt.iconSize.height()); - } - colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); - textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); - } -} - -QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect) -{ - if (opt.icon.isNull()) { - return (QPixmap()); - } - // 有图标 - QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off; - QIcon::Mode mode; - if (!(opt.state & QStyle::State_Enabled)) { - mode = QIcon::Disabled; - } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { - mode = QIcon::Active; - } else { - mode = QIcon::Normal; - } - // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state)); - return (opt.icon.pixmap(iconRect.size(), mode, state)); -} - -/** - * @brief 获取按钮的矩形区域 - * @param opt - * @return - */ -QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt) -{ - QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr); - return btnRect.marginsRemoved(mMargins); -} - -/** - * @brief 获取ToolButtonMenu的区域 - * @note 注意,如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数,返回的是QRect() - * @param opt - * @return - */ -QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt) -{ - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr); - } - return QRect(); -} - -/** - * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 - * @param opt - * @return - */ -QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt) -{ - QStyle::State bflags = opt.state & ~QStyle::State_Sunken; - if (bflags & QStyle::State_AutoRaise) { - if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { - bflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - if (opt.activeSubControls & QStyle::SC_ToolButton) { - bflags |= QStyle::State_Sunken; - } - } - return bflags; -} - -/** - * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 - * @param opt - * @return - */ -QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt) -{ - QStyle::State mflags = opt.state & ~QStyle::State_Sunken; - if (mflags & QStyle::State_AutoRaise) { - if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) { - mflags &= ~QStyle::State_Raised; - } - } - if (opt.state & QStyle::State_Sunken) { - mflags |= QStyle::State_Sunken; - } - return mflags; -} - -void SAColorToolButton::PrivateData::removeMenu() -{ - QMenu* m = q_ptr->menu(); - if (m) { - q_ptr->setMenu(nullptr); - m->deleteLater(); - } -} - -//============================================================== -// SAColorToolButton -//============================================================== - -SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this)) -{ - setColorToolButtonStyle(WithColorMenu); - connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); -} - -SAColorToolButton::SAColorToolButton(ColorToolButtonStyle style, QWidget* parent) - : QToolButton(parent), d_ptr(new PrivateData(this)) -{ - setColorToolButtonStyle(style); - connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); -} - -SAColorToolButton::~SAColorToolButton() -{ -} - -/** - * @brief 获取颜色 - * @return - */ -QColor SAColorToolButton::color() const -{ - return d_ptr->mColor; -} - -/** - * @brief 设置Margins - * @param mg - */ -void SAColorToolButton::setMargins(const QMargins& mg) -{ - d_ptr->mMargins = mg; - repaint(); -} - -QMargins SAColorToolButton::margins() const -{ - return d_ptr->mMargins; -} - -/** - * @brief 绘制无颜色表示 - * @param p - * @param colorRect 绘制的区域 - */ -void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect) -{ - p->save(); - QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); - p->setPen(pen); - p->setRenderHint(QPainter::SmoothPixmapTransform, true); - p->setRenderHint(QPainter::Antialiasing, true); - int ss = colorRect.width() / 3; - p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); - pen.setColor(Qt::black); - p->setPen(pen); - p->drawRect(colorRect); - p->restore(); -} - -/** - * @brief 设置颜色按钮的样式 - * @param s - */ -void SAColorToolButton::setColorToolButtonStyle(ColorToolButtonStyle s) -{ - if (NoColorMenu == s) { - d_ptr->removeMenu(); - } else { - createColorMenu(); - } - d_ptr->mColorButtonStyle = s; -} - -/** - * @brief 颜色按钮的样式 - * @return - */ -SAColorToolButton::ColorToolButtonStyle SAColorToolButton::colorToolButtonStyle() const -{ - return d_ptr->mColorButtonStyle; -} - -/** - * @brief 获取colorMenu - * - * @note 注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr - * @return SAColorMenu指针,如果没有,返回nullptr - */ -SAColorMenu* SAColorToolButton::colorMenu() const -{ - return qobject_cast< SAColorMenu* >(menu()); -} - -/** - * @brief 建立标准的颜色菜单 - * @return - */ -SAColorMenu* SAColorToolButton::createColorMenu() -{ - setPopupMode(QToolButton::InstantPopup); - SAColorMenu* m = new SAColorMenu(this); - m->enableNoneColorAction(true); - QAction* customColor = m->customColorAction(); - if (customColor) { - customColor->setIcon(QIcon(":/image/resource/define-color.svg")); - } - connect(m, &SAColorMenu::selectedColor, this, &SAColorToolButton::setColor); - setMenu(m); - return m; -} - -/** - * @brief 设置颜色 - * @note 会发射@sa colorChanged 信号 - * @param c - */ -void SAColorToolButton::setColor(const QColor& c) -{ - if (d_ptr->mColor != c) { - d_ptr->mColor = c; - repaint(); - Q_EMIT colorChanged(c); - } -} - -/** - * @brief 计算各个基本组件的位置 - * @param opt - * @param iconRect 图标区域 - * @param textRect 文本区域 - * @param colorRect 颜色区域 - */ -void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) -{ - switch (opt.toolButtonStyle) { - case Qt::ToolButtonTextOnly: - d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonTextBesideIcon: - d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonTextUnderIcon: - d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect); - break; - case Qt::ToolButtonFollowStyle: - case Qt::ToolButtonIconOnly: - d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); - break; - default: - d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); - break; - } -} - -void SAColorToolButton::paintEvent(QPaintEvent* e) -{ - Q_UNUSED(e); - QStylePainter p(this); - - QStyleOptionToolButton opt; - - initStyleOption(&opt); - QRect iconRect, colorRect, textRect; - calcRect(opt, iconRect, textRect, colorRect); - paintButton(&p, opt); - // 绘制图标 - paintIcon(&p, iconRect, opt); - // 绘制文字 - paintText(&p, textRect, opt); - // 绘制颜色 - paintColor(&p, colorRect, d_ptr->mColor, opt); -} - -void SAColorToolButton::resizeEvent(QResizeEvent* e) -{ - // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 - QToolButton::resizeEvent(e); -} - -/** - * @brief sizeHint重载 - * @return - */ -QSize SAColorToolButton::sizeHint() const -{ - ensurePolished(); - QStyleOptionToolButton opt; - initStyleOption(&opt); - int w = 0, h = 0; - if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) { - // 和文本无关 - w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) { - QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); - textSize.setHeight(textSize.height() + 4); - QSize iconSize = opt.iconSize; - if (!opt.icon.isNull()) { - // 有图标,要有iconsize高度的1/4给颜色 - iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); - } - w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) { - QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); - textSize.setHeight(textSize.height() + 4); - QSize iconSize = opt.iconSize; - if (!opt.icon.isNull()) { - // 有图标,要有iconsize高度的1/4给颜色 - iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); - } - w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right(); - h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); - } - opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height - if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { - w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); - } - //! Qt6.4 取消了QApplication::globalStrut - return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2)); -} - -void SAColorToolButton::onButtonClicked(bool checked) -{ - Q_EMIT colorClicked(color(), checked); -} - -/** - * @brief 绘制按钮 - * @param p - * @param opt - */ -void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt) -{ - bool autoRaise = opt.state & QStyle::State_AutoRaise; - // 绘制按钮 - if (autoRaise) { - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this); - } else { - style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this); - } - if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { - QStyleOption tool = opt; - tool.state = d_ptr->getButtonMenuStyleState(opt); - tool.rect = d_ptr->getIndicatorRect(opt); - // if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { - // style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this); - // } - style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this); - } - - // 绘制focus - if (opt.state & QStyle::State_HasFocus) { - QStyleOptionFocusRect fr; - fr.QStyleOption::operator=(opt); - fr.rect.adjust(3, 3, -3, -3); - if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { - fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0); - } - style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this); - } -} - -/** - * @brief 绘制icon - * @param p - * @param iconRect - * @param opt icon信息从QStyleOptionToolButton获取 - */ -void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt) -{ - if (!iconRect.isNull()) { - QPixmap pm = d_ptr->createIconPixmap(opt, iconRect); - style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm); - } -} - -/** - * @brief 绘制文本 - * @param p - * @param textRect - * @param opt - */ -void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt) -{ - if (opt.text.isEmpty()) { - return; - } - if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) { - return; - } - p->save(); - p->setFont(opt.font); - int alignment = Qt::TextShowMnemonic; - // 快捷键的下划线 - if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { - alignment |= Qt::TextHideMnemonic; - } - alignment |= Qt::AlignHCenter | Qt::AlignVCenter; - style()->drawItemText(p, - QStyle::visualRect(opt.direction, opt.rect, textRect), - alignment, - opt.palette, - opt.state & QStyle::State_Enabled, - opt.text, - QPalette::ButtonText); - p->restore(); -} - -/** - * @brief 绘制color - * @param p - * @param iconRect - * @param opt - */ -void SAColorToolButton::paintColor(QStylePainter* p, - const QRect& colorRect, - const QColor& color, - const QStyleOptionToolButton& opt) -{ - Q_UNUSED(opt); - if (colorRect.isNull()) { - return; - } - // 绘制颜色 - if (color.isValid()) { - p->fillRect(colorRect, color); - } else { - paintNoneColor(p, colorRect); - } -} +#include "SAColorToolButton.h" +#include +#include +#include +#include +#include +#include +#include +#include "SAColorMenu.h" + +#define SAColorToolButton_DEBUG_PRINT 0 +class SAColorToolButton::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton) +public: + PrivateData(SAColorToolButton* p); + void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect); + QRect getButtonRect(const QStyleOptionToolButton& opt); + QRect getIndicatorRect(const QStyleOptionToolButton& opt); + QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt); + QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt); + void removeMenu(); + +public: + QColor mColor { Qt::white }; + int mSpacing { 2 }; ///< 间隔 + QMargins mMargins { 3, 3, 3, 3 }; ///< box + ColorToolButtonStyle mColorButtonStyle { WithColorMenu }; ///< 样式 + static int s_indicatorArrorWidth; ///< 菜单宽度 +}; + +int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8; + +SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p) +{ +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + // 确定文本区域 + textRect = QRect(); + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + colorRect = buttonRect; + iconRect = QRect(); + } else { + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + if (opt.iconSize.height() > 0) { + tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height()); // 等比例 + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + int x = (buttonRect.width() - tmpSize.width()) / 2; + iconRect = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + } +} + +/** + * @brief 纯文本情况,左边有5像素显示颜色 + * @param opt + * @param iconRect + * @param textRect + * @param colorRect 左边有5像素显示颜色 + */ +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + iconRect = QRect(); + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + colorSize = buttonRect.size().boundedTo(colorSize); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0); +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + // 说明没有icon + // 这时所有都是color + // iconRect=Null + colorSize = buttonRect.size().boundedTo(colorSize); + iconRect = QRect(); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0); + } else { // 有图标 + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + tmpSize.setWidth(tmpSize.height()); // 对于异形也设置为正方行 + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + iconRect = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0); + } +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + // 获取字体高度 + int textHeight = opt.fontMetrics.height(); + tmpSize = buttonRect.size().boundedTo(tmpSize); + if (opt.icon.isNull()) { + int totalHeight = textHeight + opt.iconSize.height() + mSpacing; + if (totalHeight < buttonRect.height()) { + // 足够高 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + } else { + // 空间不足 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + iconRect = QRect(); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + + } else { + // 有图标 + int colorHeight = tmpSize.height() / 4; + int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing; + if (totalHeight < buttonRect.height()) { + // 高度空间足够 + // 先布置icon + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + + } else { + // 空间不足 + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + } +} + +QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect) +{ + if (opt.icon.isNull()) { + return (QPixmap()); + } + // 有图标 + QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state)); + return (opt.icon.pixmap(iconRect.size(), mode, state)); +} + +/** + * @brief 获取按钮的矩形区域 + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt) +{ + QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr); + return btnRect.marginsRemoved(mMargins); +} + +/** + * @brief 获取ToolButtonMenu的区域 + * @note 注意,如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数,返回的是QRect() + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt) +{ + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr); + } + return QRect(); +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + return bflags; +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State mflags = opt.state & ~QStyle::State_Sunken; + if (mflags & QStyle::State_AutoRaise) { + if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) { + mflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + mflags |= QStyle::State_Sunken; + } + return mflags; +} + +void SAColorToolButton::PrivateData::removeMenu() +{ + QMenu* m = q_ptr->menu(); + if (m) { + q_ptr->setMenu(nullptr); + m->deleteLater(); + } +} + +//============================================================== +// SAColorToolButton +//============================================================== + +SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this)) +{ + setColorToolButtonStyle(WithColorMenu); + connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); +} + +SAColorToolButton::SAColorToolButton(ColorToolButtonStyle style, QWidget* parent) + : QToolButton(parent), d_ptr(new PrivateData(this)) +{ + setColorToolButtonStyle(style); + connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); +} + +SAColorToolButton::~SAColorToolButton() +{ +} + +/** + * @brief 获取颜色 + * @return + */ +QColor SAColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置Margins + * @param mg + */ +void SAColorToolButton::setMargins(const QMargins& mg) +{ + d_ptr->mMargins = mg; + repaint(); +} + +QMargins SAColorToolButton::margins() const +{ + return d_ptr->mMargins; +} + +/** + * @brief 绘制无颜色表示 + * @param p + * @param colorRect 绘制的区域 + */ +void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect) +{ + p->save(); + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + p->setPen(pen); + p->setRenderHint(QPainter::SmoothPixmapTransform, true); + p->setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + p->setPen(pen); + p->drawRect(colorRect); + p->restore(); +} + +/** + * @brief 设置颜色按钮的样式 + * @param s + */ +void SAColorToolButton::setColorToolButtonStyle(ColorToolButtonStyle s) +{ + if (NoColorMenu == s) { + d_ptr->removeMenu(); + } else { + createColorMenu(); + } + d_ptr->mColorButtonStyle = s; +} + +/** + * @brief 颜色按钮的样式 + * @return + */ +SAColorToolButton::ColorToolButtonStyle SAColorToolButton::colorToolButtonStyle() const +{ + return d_ptr->mColorButtonStyle; +} + +/** + * @brief 获取colorMenu + * + * @note 注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr + * @return SAColorMenu指针,如果没有,返回nullptr + */ +SAColorMenu* SAColorToolButton::colorMenu() const +{ + return qobject_cast< SAColorMenu* >(menu()); +} + +/** + * @brief 建立标准的颜色菜单 + * @return + */ +SAColorMenu* SAColorToolButton::createColorMenu() +{ + setPopupMode(QToolButton::InstantPopup); + SAColorMenu* m = new SAColorMenu(this); + m->enableNoneColorAction(true); + QAction* customColor = m->customColorAction(); + if (customColor) { + customColor->setIcon(QIcon(":/image/resource/define-color.svg")); + } + connect(m, &SAColorMenu::selectedColor, this, &SAColorToolButton::setColor); + setMenu(m); + return m; +} + +/** + * @brief 设置颜色 + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SAColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + repaint(); + Q_EMIT colorChanged(c); + } +} + +/** + * @brief 计算各个基本组件的位置 + * @param opt + * @param iconRect 图标区域 + * @param textRect 文本区域 + * @param colorRect 颜色区域 + */ +void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonTextOnly: + d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextBesideIcon: + d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextUnderIcon: + d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonFollowStyle: + case Qt::ToolButtonIconOnly: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + default: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + } +} + +void SAColorToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QStylePainter p(this); + + QStyleOptionToolButton opt; + + initStyleOption(&opt); + QRect iconRect, colorRect, textRect; + calcRect(opt, iconRect, textRect, colorRect); + paintButton(&p, opt); + // 绘制图标 + paintIcon(&p, iconRect, opt); + // 绘制文字 + paintText(&p, textRect, opt); + // 绘制颜色 + paintColor(&p, colorRect, d_ptr->mColor, opt); +} + +void SAColorToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); +} + +/** + * @brief sizeHint重载 + * @return + */ +QSize SAColorToolButton::sizeHint() const +{ + ensurePolished(); + QStyleOptionToolButton opt; + initStyleOption(&opt); + int w = 0, h = 0; + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) { + // 和文本无关 + w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } + opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); + } + //! Qt6.4 取消了QApplication::globalStrut + return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2)); +} + +void SAColorToolButton::onButtonClicked(bool checked) +{ + Q_EMIT colorClicked(color(), checked); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt) +{ + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this); + } + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + QStyleOption tool = opt; + tool.state = d_ptr->getButtonMenuStyleState(opt); + tool.rect = d_ptr->getIndicatorRect(opt); + // if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { + // style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this); + // } + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this); + } + + // 绘制focus + if (opt.state & QStyle::State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(opt); + fr.rect.adjust(3, 3, -3, -3); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0); + } + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this); + } +} + +/** + * @brief 绘制icon + * @param p + * @param iconRect + * @param opt icon信息从QStyleOptionToolButton获取 + */ +void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt) +{ + if (!iconRect.isNull()) { + QPixmap pm = d_ptr->createIconPixmap(opt, iconRect); + style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm); + } +} + +/** + * @brief 绘制文本 + * @param p + * @param textRect + * @param opt + */ +void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt) +{ + if (opt.text.isEmpty()) { + return; + } + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) { + return; + } + p->save(); + p->setFont(opt.font); + int alignment = Qt::TextShowMnemonic; + // 快捷键的下划线 + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + alignment |= Qt::AlignHCenter | Qt::AlignVCenter; + style()->drawItemText(p, + QStyle::visualRect(opt.direction, opt.rect, textRect), + alignment, + opt.palette, + opt.state & QStyle::State_Enabled, + opt.text, + QPalette::ButtonText); + p->restore(); +} + +/** + * @brief 绘制color + * @param p + * @param iconRect + * @param opt + */ +void SAColorToolButton::paintColor(QStylePainter* p, + const QRect& colorRect, + const QColor& color, + const QStyleOptionToolButton& opt) +{ + Q_UNUSED(opt); + if (colorRect.isNull()) { + return; + } + // 绘制颜色 + if (color.isValid()) { + p->fillRect(colorRect, color); + } else { + paintNoneColor(p, colorRect); + } +} diff --git a/src/SARibbonBar/colorWidgets/SAColorToolButton.h b/src/SARibbonBar/colorWidgets/SAColorToolButton.h index a3ee4b6d..a3eff478 100644 --- a/src/SARibbonBar/colorWidgets/SAColorToolButton.h +++ b/src/SARibbonBar/colorWidgets/SAColorToolButton.h @@ -1,105 +1,105 @@ -#ifndef SACOLORTOOLBUTTON_H -#define SACOLORTOOLBUTTON_H -#include -#include "SAColorWidgetsGlobal.h" -class QPaintEvent; -class QResizeEvent; -class QPainter; -class QStylePainter; -class SAColorMenu; -/** - * @brief 这是一个只显示颜色的toolbutton - * - * - * 在ToolButtonIconOnly模式下,如果没有setIcon,则颜色占用所有区域,如下图所示 - * - * ┌─────┐ - * │color│ - * └─────┘ - * - * 如果在ToolButtonIconOnly模式下有图标,图标在上面显示,下面显示颜色,如下图所示 - * - * ┌─────┐ - * │icon │ - * │color│ - * └─────┘ - * - * 在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下,setIconSize 可以指定颜色的大小, - * 但只在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下有效 - * - * 如果没有设置图标,也就是setIcon(QIcon()),iconSize作为颜色块的大小 - * - * ┌─────────┐ - * │┌─┐ │ - * │└─┘ │ - * └─────────┘ - * - * 如果有图标,颜色条会在图标下方,为图标高度的1/4 为图标宽度一致,如若超过控件的大小,会自动缩小体积 - * - */ -class SA_COLOR_WIDGETS_API SAColorToolButton : public QToolButton -{ - Q_OBJECT - SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorToolButton) -public: - /** - * @brief 颜色按钮的内置样式 - * @param parent - * @return - */ - enum ColorToolButtonStyle - { - WithColorMenu, ///< 默认会构建一个SAColorMenu - NoColorMenu ///< 没有ColorMenu - }; - -public: - explicit SAColorToolButton(QWidget* parent = nullptr); - explicit SAColorToolButton(ColorToolButtonStyle style, QWidget* parent = nullptr); - ~SAColorToolButton(); - // 获取颜色 - QColor color() const; - // 设置Margins - void setMargins(const QMargins& mg); - QMargins margins() const; - // 绘制无颜色 - static void paintNoneColor(QPainter* p, const QRect& colorRect); - // 设置颜色按钮的样式 - void setColorToolButtonStyle(ColorToolButtonStyle s); - ColorToolButtonStyle colorToolButtonStyle() const; - // 获取colorMenu,注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr - SAColorMenu* colorMenu() const; - // 建立标准的颜色菜单 - SAColorMenu* createColorMenu(); -public Q_SLOTS: - // 设置颜色,会发射colorChanged信号 - void setColor(const QColor& c); - -protected: - // 获取关键的三个rect位置 - virtual void calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); - virtual void paintButton(QStylePainter* p, const QStyleOptionToolButton& opt); - virtual void paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt); - virtual void paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt); - virtual void paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt); - -protected: - virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; - virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; - virtual QSize sizeHint() const Q_DECL_OVERRIDE; -private Q_SLOTS: - void onButtonClicked(bool checked = false); -Q_SIGNALS: - /** - * @brief 颜色被点击的响应 - * @param color - */ - void colorClicked(const QColor& color, bool checked = false); - /** - * @brief 颜色改变信号 - * @param color - */ - void colorChanged(const QColor& color); -}; - -#endif // SACOLORTOOLBUTTON_H +#ifndef SACOLORTOOLBUTTON_H +#define SACOLORTOOLBUTTON_H +#include +#include "SAColorWidgetsGlobal.h" +class QPaintEvent; +class QResizeEvent; +class QPainter; +class QStylePainter; +class SAColorMenu; +/** + * @brief 这是一个只显示颜色的toolbutton + * + * + * 在ToolButtonIconOnly模式下,如果没有setIcon,则颜色占用所有区域,如下图所示 + * + * ┌─────┐ + * │color│ + * └─────┘ + * + * 如果在ToolButtonIconOnly模式下有图标,图标在上面显示,下面显示颜色,如下图所示 + * + * ┌─────┐ + * │icon │ + * │color│ + * └─────┘ + * + * 在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下,setIconSize 可以指定颜色的大小, + * 但只在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下有效 + * + * 如果没有设置图标,也就是setIcon(QIcon()),iconSize作为颜色块的大小 + * + * ┌─────────┐ + * │┌─┐ │ + * │└─┘ │ + * └─────────┘ + * + * 如果有图标,颜色条会在图标下方,为图标高度的1/4 为图标宽度一致,如若超过控件的大小,会自动缩小体积 + * + */ +class SA_COLOR_WIDGETS_API SAColorToolButton : public QToolButton +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorToolButton) +public: + /** + * @brief 颜色按钮的内置样式 + * @param parent + * @return + */ + enum ColorToolButtonStyle + { + WithColorMenu, ///< 默认会构建一个SAColorMenu + NoColorMenu ///< 没有ColorMenu + }; + +public: + explicit SAColorToolButton(QWidget* parent = nullptr); + explicit SAColorToolButton(ColorToolButtonStyle style, QWidget* parent = nullptr); + ~SAColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置Margins + void setMargins(const QMargins& mg); + QMargins margins() const; + // 绘制无颜色 + static void paintNoneColor(QPainter* p, const QRect& colorRect); + // 设置颜色按钮的样式 + void setColorToolButtonStyle(ColorToolButtonStyle s); + ColorToolButtonStyle colorToolButtonStyle() const; + // 获取colorMenu,注意,这个函数很有可能会返回nullptr,如果ColorToolButtonStyle设置为NoColorMenu或者自己设置了菜单,此函数返回nullptr + SAColorMenu* colorMenu() const; + // 建立标准的颜色菜单 + SAColorMenu* createColorMenu(); +public Q_SLOTS: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); + +protected: + // 获取关键的三个rect位置 + virtual void calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + virtual void paintButton(QStylePainter* p, const QStyleOptionToolButton& opt); + virtual void paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt); + virtual void paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt); + virtual void paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +private Q_SLOTS: + void onButtonClicked(bool checked = false); +Q_SIGNALS: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); +}; + +#endif // SACOLORTOOLBUTTON_H diff --git a/src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h b/src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h index 59fc62e7..1884a3c2 100644 --- a/src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h +++ b/src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h @@ -1,58 +1,58 @@ -#ifndef SACOLORWIDGETSGLOBAL_H -#define SACOLORWIDGETSGLOBAL_H -#include - -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_MAJ -#define SA_COLOR_WIDGETS_VERSION_MAJ 0 -#endif -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_MIN -#define SA_COLOR_WIDGETS_VERSION_MIN 1 -#endif -/** - * @def color-widgets的数字版本 MAJ.MIN.PAT - */ -#ifndef SA_COLOR_WIDGETS_VERSION_PAT -#define SA_COLOR_WIDGETS_VERSION_PAT 0 -#endif - -/** - * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 - */ -#ifndef SA_COLOR_WIDGETS_DECLARE_PRIVATE -#define SA_COLOR_WIDGETS_DECLARE_PRIVATE(classname) \ - class PrivateData; \ - friend class classname::PrivateData; \ - QScopedPointer< PrivateData > d_ptr; -#endif -/** - * @def 模仿Q_DECLARE_PUBLIC - */ -#ifndef SA_COLOR_WIDGETS_DECLARE_PUBLIC -#define SA_COLOR_WIDGETS_DECLARE_PUBLIC(classname) \ - friend class classname; \ - classname* q_ptr { nullptr }; -#endif - -#ifndef SA_COLOR_WIDGETS_NO_DLL -#if defined(SA_COLOR_WIDGETS_MAKE_LIB) // 定义此宏将构建library -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API Q_DECL_EXPORT -#endif -#else -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API Q_DECL_IMPORT -#endif -#endif -#else -#ifndef SA_COLOR_WIDGETS_API -#define SA_COLOR_WIDGETS_API -#endif -#endif - -#endif // SACOLORWIDGETSGLOBAL_H +#ifndef SACOLORWIDGETSGLOBAL_H +#define SACOLORWIDGETSGLOBAL_H +#include + +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MAJ +#define SA_COLOR_WIDGETS_VERSION_MAJ 0 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MIN +#define SA_COLOR_WIDGETS_VERSION_MIN 1 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_PAT +#define SA_COLOR_WIDGETS_VERSION_PAT 0 +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PRIVATE +#define SA_COLOR_WIDGETS_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + QScopedPointer< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PUBLIC +#define SA_COLOR_WIDGETS_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; +#endif + +#ifndef SA_COLOR_WIDGETS_NO_DLL +#if defined(SA_COLOR_WIDGETS_MAKE_LIB) // 定义此宏将构建library +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_EXPORT +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_IMPORT +#endif +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API +#endif +#endif + +#endif // SACOLORWIDGETSGLOBAL_H diff --git a/src/SARibbonBar/colorWidgets/tst/Widget.cpp b/src/SARibbonBar/colorWidgets/tst/Widget.cpp index cd1b83b7..fce19f07 100644 --- a/src/SARibbonBar/colorWidgets/tst/Widget.cpp +++ b/src/SARibbonBar/colorWidgets/tst/Widget.cpp @@ -1,134 +1,134 @@ -#include "Widget.h" -#include "ui_Widget.h" -#include -#include -#include -#include -#include -#include - -Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) -{ - ui->setupUi(this); - setColor(Qt::blue); - - QMenu* menu = new QMenu(this); - menu->addAction("1"); - menu->addAction("2"); - menu->addAction("3"); - menu->addAction("4"); - menu->addAction("5"); - - ui->toolButtonIconOnlyHaveIconMenuPopupMode->setMenu(menu); - ui->toolButtonIconOnlyHaveIconMenuPopupMode2->setMenu(menu); - ui->toolButtonIconOnlyHaveIconInstantPop->setMenu(menu); - ui->toolButtonIconOnlyHaveIconInstantPop2->setMenu(menu); - ui->toolButtonIconOnlyHaveIconDelayedPop->setMenu(menu); - ui->toolButtonIconOnlyHaveIconDelayedPop2->setMenu(menu); - - ui->toolButtonIconOnlyTextHaveIconMenuPopupMode->setMenu(menu); - ui->toolButtonIconOnlyTextHaveIconMenuPopupMode_1->setMenu(menu); - ui->toolButtonIconOnlyTextHaveIconDelayedPop->setMenu(menu); - ui->toolButtonIconOnlyTextHaveIconDelayedPop_1->setMenu(menu); - ui->toolButtonIconOnlyTextHaveIconInstantPop->setMenu(menu); - ui->toolButtonIconOnlyTextHaveIconInstantPop_1->setMenu(menu); - - // - QList< QColor > clrs = SA::getStandardColorList(); - ui->colorGrid1->setColumnCount(0); - ui->colorGrid1->setColorList(clrs); - - QList< QColor > clrs2; - for (int i = 0; i < 30; ++i) { - clrs2.append(makeRandColor()); - } - ui->colorGrid2->setColumnCount(5); - ui->colorGrid2->setColorList(clrs2); - - connect(ui->colorGrid1, &SAColorGridWidget::colorClicked, this, &Widget::onGridColorWidgetClicked); - connect(ui->colorGrid2, &SAColorGridWidget::colorClicked, this, &Widget::onGridColorWidgetClicked); -} - -Widget::~Widget() -{ - delete ui; -} - -QColor Widget::makeRandColor() const -{ - static std::random_device s_rd; - static std::mt19937 s_gen(s_rd()); - static std::uniform_int_distribution<> s_dis(0, 255); - return QColor(s_dis(s_gen), s_dis(s_gen), s_dis(s_gen)); -} - -void Widget::setColor(const QColor& clr) -{ - ui->toolButtonIconOnlyNoIcon->setColor(clr); - ui->toolButtonIconOnlyNoIcon2->setColor(clr); - ui->toolButtonIconOnlyHaveIcon->setColor(clr); - ui->toolButtonIconOnlyHaveIcon2->setColor(clr); - ui->toolButtonIconOnlyHaveIconSmall->setColor(clr); - ui->toolButtonIconOnlyHaveIconSmall2->setColor(clr); - ui->toolButtonIconOnlyHaveIconLarge->setColor(clr); - ui->toolButtonIconOnlyHaveIconLarge2->setColor(clr); - ui->toolButtonIconOnlyHaveIconMenuPopupMode->setColor(clr); - ui->toolButtonIconOnlyHaveIconMenuPopupMode2->setColor(clr); - ui->toolButtonIconOnlyHaveIconInstantPop->setColor(clr); - ui->toolButtonIconOnlyHaveIconInstantPop2->setColor(clr); - ui->toolButtonIconOnlyHaveIconDelayedPop->setColor(clr); - ui->toolButtonIconOnlyHaveIconDelayedPop2->setColor(clr); - - // Text Only - ui->toolButtonIconOnlyText->setColor(clr); - ui->toolButtonIconOnlyText_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIcon->setColor(clr); - ui->toolButtonIconOnlyTextHaveIcon_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconSmall->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconSmall_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconLarge->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconLarge_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconMenuPopupMode->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconMenuPopupMode_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconDelayedPop->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconDelayedPop_1->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconInstantPop->setColor(clr); - ui->toolButtonIconOnlyTextHaveIconInstantPop_1->setColor(clr); - - // Text beside - ui->toolButtonIconTextBeside->setColor(clr); - ui->toolButtonIconTextBeside1->setColor(clr); - ui->toolButtonTextBesideHaveIcon->setColor(clr); - ui->toolButtonTextBesideHaveIcon1->setColor(clr); - ui->toolButtonTextBesideHaveIconSmall->setColor(clr); - ui->toolButtonTextBesideHaveIconSmall1->setColor(clr); - ui->toolButtonTextBesideHaveIconLarge->setColor(clr); - ui->toolButtonTextBesideHaveIconLarge1->setColor(clr); - ui->toolButtonTextBesideHaveIconInstantPop->setColor(clr); - ui->toolButtonTextBesideHaveIconInstantPop1->setColor(clr); - ui->toolButtonTextBesideHaveIconDelayedPop->setColor(clr); - ui->toolButtonTextBesideHaveIconDelayedPop1->setColor(clr); - ui->toolButtonTextBesideMenuPopupMode->setColor(clr); - ui->toolButtonTextBesideMenuPopupMode1->setColor(clr); - - // TextUnderIcon - ui->toolButtonTextUnderIcon->setColor(clr); - ui->toolButtonTextUnderIcon1->setColor(clr); - ui->toolButtonTextUnderIconHaveIcon->setColor(clr); - ui->toolButtonTextUnderIconHaveIcon1->setColor(clr); - ui->toolButtonTextUnderIconHaveIconSmall->setColor(clr); - ui->toolButtonTextUnderIconHaveIconSmall1->setColor(clr); - ui->toolButtonTextUnderIconHaveIconLarge->setColor(clr); - ui->toolButtonTextUnderIconHaveIconLarge1->setColor(clr); - ui->toolButtonTextUnderIconMenuPopupMode->setColor(clr); - ui->toolButtonTextUnderIconMenuPopupMode1->setColor(clr); - ui->toolButtonTextUnderIconInstantPop->setColor(clr); - ui->toolButtonTextUnderIconInstantPop1->setColor(clr); - ui->toolButtonTextUnderIconDelayedPop->setColor(clr); - ui->toolButtonTextUnderIconDelayedPop1->setColor(clr); -} - -void Widget::onGridColorWidgetClicked(const QColor& c) -{ - setColor(c); -} +#include "Widget.h" +#include "ui_Widget.h" +#include +#include +#include +#include +#include +#include + +Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) +{ + ui->setupUi(this); + setColor(Qt::blue); + + QMenu* menu = new QMenu(this); + menu->addAction("1"); + menu->addAction("2"); + menu->addAction("3"); + menu->addAction("4"); + menu->addAction("5"); + + ui->toolButtonIconOnlyHaveIconMenuPopupMode->setMenu(menu); + ui->toolButtonIconOnlyHaveIconMenuPopupMode2->setMenu(menu); + ui->toolButtonIconOnlyHaveIconInstantPop->setMenu(menu); + ui->toolButtonIconOnlyHaveIconInstantPop2->setMenu(menu); + ui->toolButtonIconOnlyHaveIconDelayedPop->setMenu(menu); + ui->toolButtonIconOnlyHaveIconDelayedPop2->setMenu(menu); + + ui->toolButtonIconOnlyTextHaveIconMenuPopupMode->setMenu(menu); + ui->toolButtonIconOnlyTextHaveIconMenuPopupMode_1->setMenu(menu); + ui->toolButtonIconOnlyTextHaveIconDelayedPop->setMenu(menu); + ui->toolButtonIconOnlyTextHaveIconDelayedPop_1->setMenu(menu); + ui->toolButtonIconOnlyTextHaveIconInstantPop->setMenu(menu); + ui->toolButtonIconOnlyTextHaveIconInstantPop_1->setMenu(menu); + + // + QList< QColor > clrs = SA::getStandardColorList(); + ui->colorGrid1->setColumnCount(0); + ui->colorGrid1->setColorList(clrs); + + QList< QColor > clrs2; + for (int i = 0; i < 30; ++i) { + clrs2.append(makeRandColor()); + } + ui->colorGrid2->setColumnCount(5); + ui->colorGrid2->setColorList(clrs2); + + connect(ui->colorGrid1, &SAColorGridWidget::colorClicked, this, &Widget::onGridColorWidgetClicked); + connect(ui->colorGrid2, &SAColorGridWidget::colorClicked, this, &Widget::onGridColorWidgetClicked); +} + +Widget::~Widget() +{ + delete ui; +} + +QColor Widget::makeRandColor() const +{ + static std::random_device s_rd; + static std::mt19937 s_gen(s_rd()); + static std::uniform_int_distribution<> s_dis(0, 255); + return QColor(s_dis(s_gen), s_dis(s_gen), s_dis(s_gen)); +} + +void Widget::setColor(const QColor& clr) +{ + ui->toolButtonIconOnlyNoIcon->setColor(clr); + ui->toolButtonIconOnlyNoIcon2->setColor(clr); + ui->toolButtonIconOnlyHaveIcon->setColor(clr); + ui->toolButtonIconOnlyHaveIcon2->setColor(clr); + ui->toolButtonIconOnlyHaveIconSmall->setColor(clr); + ui->toolButtonIconOnlyHaveIconSmall2->setColor(clr); + ui->toolButtonIconOnlyHaveIconLarge->setColor(clr); + ui->toolButtonIconOnlyHaveIconLarge2->setColor(clr); + ui->toolButtonIconOnlyHaveIconMenuPopupMode->setColor(clr); + ui->toolButtonIconOnlyHaveIconMenuPopupMode2->setColor(clr); + ui->toolButtonIconOnlyHaveIconInstantPop->setColor(clr); + ui->toolButtonIconOnlyHaveIconInstantPop2->setColor(clr); + ui->toolButtonIconOnlyHaveIconDelayedPop->setColor(clr); + ui->toolButtonIconOnlyHaveIconDelayedPop2->setColor(clr); + + // Text Only + ui->toolButtonIconOnlyText->setColor(clr); + ui->toolButtonIconOnlyText_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIcon->setColor(clr); + ui->toolButtonIconOnlyTextHaveIcon_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconSmall->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconSmall_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconLarge->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconLarge_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconMenuPopupMode->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconMenuPopupMode_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconDelayedPop->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconDelayedPop_1->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconInstantPop->setColor(clr); + ui->toolButtonIconOnlyTextHaveIconInstantPop_1->setColor(clr); + + // Text beside + ui->toolButtonIconTextBeside->setColor(clr); + ui->toolButtonIconTextBeside1->setColor(clr); + ui->toolButtonTextBesideHaveIcon->setColor(clr); + ui->toolButtonTextBesideHaveIcon1->setColor(clr); + ui->toolButtonTextBesideHaveIconSmall->setColor(clr); + ui->toolButtonTextBesideHaveIconSmall1->setColor(clr); + ui->toolButtonTextBesideHaveIconLarge->setColor(clr); + ui->toolButtonTextBesideHaveIconLarge1->setColor(clr); + ui->toolButtonTextBesideHaveIconInstantPop->setColor(clr); + ui->toolButtonTextBesideHaveIconInstantPop1->setColor(clr); + ui->toolButtonTextBesideHaveIconDelayedPop->setColor(clr); + ui->toolButtonTextBesideHaveIconDelayedPop1->setColor(clr); + ui->toolButtonTextBesideMenuPopupMode->setColor(clr); + ui->toolButtonTextBesideMenuPopupMode1->setColor(clr); + + // TextUnderIcon + ui->toolButtonTextUnderIcon->setColor(clr); + ui->toolButtonTextUnderIcon1->setColor(clr); + ui->toolButtonTextUnderIconHaveIcon->setColor(clr); + ui->toolButtonTextUnderIconHaveIcon1->setColor(clr); + ui->toolButtonTextUnderIconHaveIconSmall->setColor(clr); + ui->toolButtonTextUnderIconHaveIconSmall1->setColor(clr); + ui->toolButtonTextUnderIconHaveIconLarge->setColor(clr); + ui->toolButtonTextUnderIconHaveIconLarge1->setColor(clr); + ui->toolButtonTextUnderIconMenuPopupMode->setColor(clr); + ui->toolButtonTextUnderIconMenuPopupMode1->setColor(clr); + ui->toolButtonTextUnderIconInstantPop->setColor(clr); + ui->toolButtonTextUnderIconInstantPop1->setColor(clr); + ui->toolButtonTextUnderIconDelayedPop->setColor(clr); + ui->toolButtonTextUnderIconDelayedPop1->setColor(clr); +} + +void Widget::onGridColorWidgetClicked(const QColor& c) +{ + setColor(c); +} diff --git a/src/SARibbonBar/colorWidgets/tst/Widget.h b/src/SARibbonBar/colorWidgets/tst/Widget.h index 7aff74ac..695c9ec5 100644 --- a/src/SARibbonBar/colorWidgets/tst/Widget.h +++ b/src/SARibbonBar/colorWidgets/tst/Widget.h @@ -1,29 +1,29 @@ -#ifndef WIDGET_H -#define WIDGET_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui -{ -class Widget; -} -QT_END_NAMESPACE - -class Widget : public QWidget -{ - Q_OBJECT - -public: - Widget(QWidget* parent = nullptr); - ~Widget(); - QColor makeRandColor() const; - // - void setColor(const QColor& clr); -private Q_SLOTS: - void onGridColorWidgetClicked(const QColor& c); - -private: - Ui::Widget* ui; -}; -#endif // WIDGET_H +#ifndef WIDGET_H +#define WIDGET_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui +{ +class Widget; +} +QT_END_NAMESPACE + +class Widget : public QWidget +{ + Q_OBJECT + +public: + Widget(QWidget* parent = nullptr); + ~Widget(); + QColor makeRandColor() const; + // + void setColor(const QColor& clr); +private Q_SLOTS: + void onGridColorWidgetClicked(const QColor& c); + +private: + Ui::Widget* ui; +}; +#endif // WIDGET_H diff --git a/src/SARibbonBar/colorWidgets/tst/main.cpp b/src/SARibbonBar/colorWidgets/tst/main.cpp index cec7bb68..a8f1b0bf 100644 --- a/src/SARibbonBar/colorWidgets/tst/main.cpp +++ b/src/SARibbonBar/colorWidgets/tst/main.cpp @@ -1,11 +1,11 @@ -#include "Widget.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - Widget w; - w.show(); - return a.exec(); -} +#include "Widget.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Widget w; + w.show(); + return a.exec(); +} diff --git a/tools/amalgamate/SARibbonAmalgamTemplate.cpp b/tools/amalgamate/SARibbonAmalgamTemplate.cpp index 391bc4a8..2fd29b11 100644 --- a/tools/amalgamate/SARibbonAmalgamTemplate.cpp +++ b/tools/amalgamate/SARibbonAmalgamTemplate.cpp @@ -1,69 +1,69 @@ -//定义此宏,将SA_RIBBON_EXPORT定义为空 -#ifndef SA_RIBBON_BAR_NO_EXPORT -#define SA_RIBBON_BAR_NO_EXPORT -#endif -//定义此宏,将SA_COLOR_WIDGETS_API定义为空 -#ifndef SA_COLOR_WIDGETS_NO_DLL -#define SA_COLOR_WIDGETS_NO_DLL -#endif - -/*@remap "SARibbonAmalgamTemplatePublicHeaders.h" "SARibbon.h" */ -#include "SARibbonAmalgamTemplateHeaderGlue.h" - -// disable warnings about unsafe standard library calls -#ifdef _MSC_VER -#pragma push_macro ("_CRT_SECURE_NO_WARNINGS") -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif -#pragma warning (push) -#pragma warning (disable: 4996) // deprecated POSIX names -#endif - -#include "../../src/SARibbonBar/colorWidgets/SAColorMenu.cpp" -#include "../../src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp" -#include "../../src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp" -#include "../../src/SARibbonBar/colorWidgets/SAColorToolButton.cpp" -//sa ribbon -#include "../../src/SARibbonBar/SAFramelessHelper.cpp" -#include "../../src/SARibbonBar/SARibbonApplicationButton.cpp" -#include "../../src/SARibbonBar/SARibbonSystemButtonBar.cpp" -#include "../../src/SARibbonBar/SARibbonToolButton.cpp" -#include "../../src/SARibbonBar/SARibbonColorToolButton.cpp" -#include "../../src/SARibbonBar/SARibbonLineWidgetContainer.cpp" -#include "../../src/SARibbonBar/SARibbonActionsManager.cpp" -#include "../../src/SARibbonBar/SARibbonLineEdit.cpp" -#include "../../src/SARibbonBar/SARibbonCheckBox.cpp" -#include "../../src/SARibbonBar/SARibbonComboBox.cpp" -#include "../../src/SARibbonBar/SARibbonButtonGroupWidget.cpp" -#include "../../src/SARibbonBar/SARibbonStackedWidget.cpp" -#include "../../src/SARibbonBar/SARibbonSeparatorWidget.cpp" -#include "../../src/SARibbonBar/SARibbonCtrlContainer.cpp" -#include "../../src/SARibbonBar/SARibbonQuickAccessBar.cpp" -#include "../../src/SARibbonBar/SARibbonTabBar.cpp" -#include "../../src/SARibbonBar/SARibbonControlButton.cpp" -#include "../../src/SARibbonBar/SARibbonMenu.cpp" - -#include "../../src/SARibbonBar/SARibbonPannelOptionButton.cpp" -#include "../../src/SARibbonBar/SARibbonPannelItem.cpp" -#include "../../src/SARibbonBar/SARibbonPannelLayout.cpp" -#include "../../src/SARibbonBar/SARibbonPannel.cpp" -#include "../../src/SARibbonBar/SARibbonCategory.cpp" -#include "../../src/SARibbonBar/SARibbonCategoryLayout.cpp" -#include "../../src/SARibbonBar/SARibbonContextCategory.cpp" -#include "../../src/SARibbonBar/SARibbonGalleryItem.cpp" -#include "../../src/SARibbonBar/SARibbonGalleryGroup.cpp" -#include "../../src/SARibbonBar/SARibbonGallery.cpp" -#include "../../src/SARibbonBar/SARibbonBar.cpp" -#include "../../src/SARibbonBar/SARibbonElementFactory.cpp" -#include "../../src/SARibbonBar/SARibbonElementManager.cpp" -#include "../../src/SARibbonBar/SARibbonCustomizeData.cpp" -#include "../../src/SARibbonBar/SARibbonCustomizeWidget.cpp" -#include "../../src/SARibbonBar/SARibbonCustomizeDialog.cpp" -#include "../../src/SARibbonBar/SARibbonMainWindow.cpp" -#include "../../src/SARibbonBar/SARibbonWidget.cpp" -#ifdef _MSC_VER -#pragma warning (pop) -#pragma pop_macro ("_CRT_SECURE_NO_WARNINGS") -#endif - +//定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif +//定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +/*@remap "SARibbonAmalgamTemplatePublicHeaders.h" "SARibbon.h" */ +#include "SARibbonAmalgamTemplateHeaderGlue.h" + +// disable warnings about unsafe standard library calls +#ifdef _MSC_VER +#pragma push_macro ("_CRT_SECURE_NO_WARNINGS") +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#pragma warning (push) +#pragma warning (disable: 4996) // deprecated POSIX names +#endif + +#include "../../src/SARibbonBar/colorWidgets/SAColorMenu.cpp" +#include "../../src/SARibbonBar/colorWidgets/SAColorGridWidget.cpp" +#include "../../src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.cpp" +#include "../../src/SARibbonBar/colorWidgets/SAColorToolButton.cpp" +//sa ribbon +#include "../../src/SARibbonBar/SAFramelessHelper.cpp" +#include "../../src/SARibbonBar/SARibbonApplicationButton.cpp" +#include "../../src/SARibbonBar/SARibbonSystemButtonBar.cpp" +#include "../../src/SARibbonBar/SARibbonToolButton.cpp" +#include "../../src/SARibbonBar/SARibbonColorToolButton.cpp" +#include "../../src/SARibbonBar/SARibbonLineWidgetContainer.cpp" +#include "../../src/SARibbonBar/SARibbonActionsManager.cpp" +#include "../../src/SARibbonBar/SARibbonLineEdit.cpp" +#include "../../src/SARibbonBar/SARibbonCheckBox.cpp" +#include "../../src/SARibbonBar/SARibbonComboBox.cpp" +#include "../../src/SARibbonBar/SARibbonButtonGroupWidget.cpp" +#include "../../src/SARibbonBar/SARibbonStackedWidget.cpp" +#include "../../src/SARibbonBar/SARibbonSeparatorWidget.cpp" +#include "../../src/SARibbonBar/SARibbonCtrlContainer.cpp" +#include "../../src/SARibbonBar/SARibbonQuickAccessBar.cpp" +#include "../../src/SARibbonBar/SARibbonTabBar.cpp" +#include "../../src/SARibbonBar/SARibbonControlButton.cpp" +#include "../../src/SARibbonBar/SARibbonMenu.cpp" + +#include "../../src/SARibbonBar/SARibbonPannelOptionButton.cpp" +#include "../../src/SARibbonBar/SARibbonPannelItem.cpp" +#include "../../src/SARibbonBar/SARibbonPannelLayout.cpp" +#include "../../src/SARibbonBar/SARibbonPannel.cpp" +#include "../../src/SARibbonBar/SARibbonCategory.cpp" +#include "../../src/SARibbonBar/SARibbonCategoryLayout.cpp" +#include "../../src/SARibbonBar/SARibbonContextCategory.cpp" +#include "../../src/SARibbonBar/SARibbonGalleryItem.cpp" +#include "../../src/SARibbonBar/SARibbonGalleryGroup.cpp" +#include "../../src/SARibbonBar/SARibbonGallery.cpp" +#include "../../src/SARibbonBar/SARibbonBar.cpp" +#include "../../src/SARibbonBar/SARibbonElementFactory.cpp" +#include "../../src/SARibbonBar/SARibbonElementManager.cpp" +#include "../../src/SARibbonBar/SARibbonCustomizeData.cpp" +#include "../../src/SARibbonBar/SARibbonCustomizeWidget.cpp" +#include "../../src/SARibbonBar/SARibbonCustomizeDialog.cpp" +#include "../../src/SARibbonBar/SARibbonMainWindow.cpp" +#include "../../src/SARibbonBar/SARibbonWidget.cpp" +#ifdef _MSC_VER +#pragma warning (pop) +#pragma pop_macro ("_CRT_SECURE_NO_WARNINGS") +#endif + diff --git a/tools/amalgamate/SARibbonAmalgamTemplate.h b/tools/amalgamate/SARibbonAmalgamTemplate.h index 1523ee38..ede49923 100644 --- a/tools/amalgamate/SARibbonAmalgamTemplate.h +++ b/tools/amalgamate/SARibbonAmalgamTemplate.h @@ -1,14 +1,14 @@ -#ifndef SA_RIBBON_H -#define SA_RIBBON_H -//定义此宏,将SA_RIBBON_EXPORT定义为空 -#ifndef SA_RIBBON_BAR_NO_EXPORT -#define SA_RIBBON_BAR_NO_EXPORT -#endif -//定义此宏,将SA_COLOR_WIDGETS_API定义为空 -#ifndef SA_COLOR_WIDGETS_NO_DLL -#define SA_COLOR_WIDGETS_NO_DLL -#endif - -#include "SARibbonAmalgamTemplatePublicHeaders.h" - +#ifndef SA_RIBBON_H +#define SA_RIBBON_H +//定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif +//定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +#include "SARibbonAmalgamTemplatePublicHeaders.h" + #endif \ No newline at end of file diff --git a/tools/amalgamate/SARibbonAmalgamTemplateHeaderGlue.h b/tools/amalgamate/SARibbonAmalgamTemplateHeaderGlue.h index 22568bbb..5b85ad39 100644 --- a/tools/amalgamate/SARibbonAmalgamTemplateHeaderGlue.h +++ b/tools/amalgamate/SARibbonAmalgamTemplateHeaderGlue.h @@ -1,2 +1,2 @@ -// This file provides an extra level of indirection for the @remap in the template -#include "SARibbonAmalgamTemplatePublicHeaders.h" +// This file provides an extra level of indirection for the @remap in the template +#include "SARibbonAmalgamTemplatePublicHeaders.h" diff --git a/tools/amalgamate/SARibbonAmalgamTemplatePublicHeaders.h b/tools/amalgamate/SARibbonAmalgamTemplatePublicHeaders.h index fb7092f3..6736075e 100644 --- a/tools/amalgamate/SARibbonAmalgamTemplatePublicHeaders.h +++ b/tools/amalgamate/SARibbonAmalgamTemplatePublicHeaders.h @@ -1,47 +1,47 @@ -//Global -#include "../../src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h" -#include "../../src/SARibbonBar/SARibbonBarVersionInfo.h" -#include "../../src/SARibbonBar/SARibbonGlobal.h" -//color widget -#include "../../src/SARibbonBar/colorWidgets/SAColorMenu.h" -#include "../../src/SARibbonBar/colorWidgets/SAColorGridWidget.h" -#include "../../src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h" -#include "../../src/SARibbonBar/colorWidgets/SAColorToolButton.h" -//sa ribbon -#include "../../src/SARibbonBar/SAFramelessHelper.h" -#include "../../src/SARibbonBar/SARibbonApplicationButton.h" -#include "../../src/SARibbonBar/SARibbonSystemButtonBar.h" -#include "../../src/SARibbonBar/SARibbonToolButton.h" -#include "../../src/SARibbonBar/SARibbonColorToolButton.h" -#include "../../src/SARibbonBar/SARibbonLineWidgetContainer.h" -#include "../../src/SARibbonBar/SARibbonActionsManager.h" -#include "../../src/SARibbonBar/SARibbonLineEdit.h" -#include "../../src/SARibbonBar/SARibbonCheckBox.h" -#include "../../src/SARibbonBar/SARibbonComboBox.h" -#include "../../src/SARibbonBar/SARibbonButtonGroupWidget.h" -#include "../../src/SARibbonBar/SARibbonStackedWidget.h" -#include "../../src/SARibbonBar/SARibbonSeparatorWidget.h" -#include "../../src/SARibbonBar/SARibbonCtrlContainer.h" -#include "../../src/SARibbonBar/SARibbonQuickAccessBar.h" -#include "../../src/SARibbonBar/SARibbonTabBar.h" -#include "../../src/SARibbonBar/SARibbonControlButton.h" -#include "../../src/SARibbonBar/SARibbonMenu.h" - -#include "../../src/SARibbonBar/SARibbonPannelOptionButton.h" -#include "../../src/SARibbonBar/SARibbonPannelItem.h" -#include "../../src/SARibbonBar/SARibbonPannelLayout.h" -#include "../../src/SARibbonBar/SARibbonPannel.h" -#include "../../src/SARibbonBar/SARibbonCategory.h" -#include "../../src/SARibbonBar/SARibbonCategoryLayout.h" -#include "../../src/SARibbonBar/SARibbonContextCategory.h" -#include "../../src/SARibbonBar/SARibbonGalleryItem.h" -#include "../../src/SARibbonBar/SARibbonGalleryGroup.h" -#include "../../src/SARibbonBar/SARibbonGallery.h" -#include "../../src/SARibbonBar/SARibbonBar.h" -#include "../../src/SARibbonBar/SARibbonElementFactory.h" -#include "../../src/SARibbonBar/SARibbonElementManager.h" -#include "../../src/SARibbonBar/SARibbonCustomizeData.h" -#include "../../src/SARibbonBar/SARibbonCustomizeWidget.h" -#include "../../src/SARibbonBar/SARibbonCustomizeDialog.h" -#include "../../src/SARibbonBar/SARibbonMainWindow.h" -#include "../../src/SARibbonBar/SARibbonWidget.h" +//Global +#include "../../src/SARibbonBar/colorWidgets/SAColorWidgetsGlobal.h" +#include "../../src/SARibbonBar/SARibbonBarVersionInfo.h" +#include "../../src/SARibbonBar/SARibbonGlobal.h" +//color widget +#include "../../src/SARibbonBar/colorWidgets/SAColorMenu.h" +#include "../../src/SARibbonBar/colorWidgets/SAColorGridWidget.h" +#include "../../src/SARibbonBar/colorWidgets/SAColorPaletteGridWidget.h" +#include "../../src/SARibbonBar/colorWidgets/SAColorToolButton.h" +//sa ribbon +#include "../../src/SARibbonBar/SAFramelessHelper.h" +#include "../../src/SARibbonBar/SARibbonApplicationButton.h" +#include "../../src/SARibbonBar/SARibbonSystemButtonBar.h" +#include "../../src/SARibbonBar/SARibbonToolButton.h" +#include "../../src/SARibbonBar/SARibbonColorToolButton.h" +#include "../../src/SARibbonBar/SARibbonLineWidgetContainer.h" +#include "../../src/SARibbonBar/SARibbonActionsManager.h" +#include "../../src/SARibbonBar/SARibbonLineEdit.h" +#include "../../src/SARibbonBar/SARibbonCheckBox.h" +#include "../../src/SARibbonBar/SARibbonComboBox.h" +#include "../../src/SARibbonBar/SARibbonButtonGroupWidget.h" +#include "../../src/SARibbonBar/SARibbonStackedWidget.h" +#include "../../src/SARibbonBar/SARibbonSeparatorWidget.h" +#include "../../src/SARibbonBar/SARibbonCtrlContainer.h" +#include "../../src/SARibbonBar/SARibbonQuickAccessBar.h" +#include "../../src/SARibbonBar/SARibbonTabBar.h" +#include "../../src/SARibbonBar/SARibbonControlButton.h" +#include "../../src/SARibbonBar/SARibbonMenu.h" + +#include "../../src/SARibbonBar/SARibbonPannelOptionButton.h" +#include "../../src/SARibbonBar/SARibbonPannelItem.h" +#include "../../src/SARibbonBar/SARibbonPannelLayout.h" +#include "../../src/SARibbonBar/SARibbonPannel.h" +#include "../../src/SARibbonBar/SARibbonCategory.h" +#include "../../src/SARibbonBar/SARibbonCategoryLayout.h" +#include "../../src/SARibbonBar/SARibbonContextCategory.h" +#include "../../src/SARibbonBar/SARibbonGalleryItem.h" +#include "../../src/SARibbonBar/SARibbonGalleryGroup.h" +#include "../../src/SARibbonBar/SARibbonGallery.h" +#include "../../src/SARibbonBar/SARibbonBar.h" +#include "../../src/SARibbonBar/SARibbonElementFactory.h" +#include "../../src/SARibbonBar/SARibbonElementManager.h" +#include "../../src/SARibbonBar/SARibbonCustomizeData.h" +#include "../../src/SARibbonBar/SARibbonCustomizeWidget.h" +#include "../../src/SARibbonBar/SARibbonCustomizeDialog.h" +#include "../../src/SARibbonBar/SARibbonMainWindow.h" +#include "../../src/SARibbonBar/SARibbonWidget.h"