QtCanpool 3.3.0
载入中...
搜索中...
未找到
ribbongroup.cpp
浏览该文件的文档.
1
5#include "ribbongroup.h"
6#include "ribbongroup_p.h"
7#include "ribbongallery.h"
8#include "ribbongrouplayout.h"
9#include "ribboncontrols.h"
10#include "ribbonbutton.h"
11#include "ribbonmanager.h"
12
13#include <QAction>
14#include <QApplication>
15#include <QDebug>
16#include <QFontMetrics>
17#include <QGridLayout>
18#include <QIcon>
19#include <QMenu>
20#include <QPainter>
21#include <QResizeEvent>
22#include <QWidgetAction>
23
25
26#ifndef QX_RIBBON_PROP_ROW_PROPORTION
27#define QX_RIBBON_PROP_ROW_PROPORTION "_qx_RibbonGroupRowProportion"
28#endif
29
30#define HELP_DRAW_RECT(p, rect) \
31 do { \
32 p.save(); \
33 QPen _pen(Qt::DashLine); \
34 _pen.setColor(Qt::blue); \
35 p.setPen(_pen); \
36 p.setBrush(QBrush()); \
37 p.drawRect(rect); \
38 p.restore(); \
39 } while (0)
40
41RibbonGroupOptionButton::RibbonGroupOptionButton(QWidget *parent)
42 : QToolButton(parent)
43{
44 setAutoRaise(true);
45 setCheckable(false);
46 setToolButtonStyle(Qt::ToolButtonIconOnly);
47 setFixedSize(16, 16);
48 setIconSize(QSize(10, 10));
49 setIcon(QIcon(":/image/res/group_option.png"));
50}
51
53{
54 connect(this, &RibbonGroupOptionButton::clicked, action, &QAction::trigger);
55}
56
59
61 : m_optionActionButton(Q_NULLPTR)
62 , m_groupLayoutMode(RibbonGroup::ThreeRowMode)
63 , m_isCanCustomize(true)
64{
65}
66
68{
69 Q_Q(RibbonGroup);
71 m_layout->setSpacing(2);
72 m_layout->setContentsMargins(2, 2, 2, 2);
73}
74
76{
77 QWidget *w = m_layout->lastWidget();
78
79 return qobject_cast<RibbonButton *>(w);
80}
81
86{
87 Q_Q(RibbonGroup);
88 QList<RibbonButton *> btns = q->ribbonButtons();
89
90 for (RibbonButton *b : std::as_const(btns)) {
91 if ((Q_NULLPTR == b) || (RibbonButton::LargeButton != b->buttonType())) {
92 continue;
93 }
95 if (RibbonButton::Normal != b->largeButtonType()) {
96 b->setLargeButtonType(RibbonButton::Normal);
97 }
98 } else {
99 if (RibbonButton::Lite != b->largeButtonType()) {
100 b->setLargeButtonType(RibbonButton::Lite);
101 }
102 }
103 }
104}
105
110const QList<RibbonGroupItem *> &RibbonGroupPrivate::ribbonGroupItems() const
111{
112 return m_layout->ribbonGroupItems();
113}
114
115/* RibbonGroup */
117 : QWidget(parent)
118{
120 Q_D(RibbonGroup);
121 d->init();
123}
124
125RibbonGroup::RibbonGroup(const QString &name, QWidget *parent)
126 : RibbonGroup(parent)
127{
129}
130
135
137{
138 return windowTitle();
139}
140
146void RibbonGroup::setGroupName(const QString &title)
147{
148 setWindowTitle(title);
149 update();
150}
151
152QString RibbonGroup::title() const
153{
154 return groupName();
155}
156
157void RibbonGroup::setTitle(const QString &title)
158{
160}
161
168{
169 bool isok = false;
170 int r = action->property(QX_RIBBON_PROP_ROW_PROPORTION).toInt(&isok);
171
172 if (isok) {
173 return static_cast<RibbonGroup::RowProportion>(r);
174 }
175 return RibbonGroup::Large;
176}
177
184{
185 if (action == Q_NULLPTR) {
186 return;
187 }
188 action->setProperty(QX_RIBBON_PROP_ROW_PROPORTION, int(rp));
189}
190
197{
198 Q_D(RibbonGroup);
199 if (action == Q_NULLPTR) {
200 return;
201 }
203
204 RibbonGroupLayout *lay = d->m_layout;
205 if (lay) {
206 RibbonGroupItem *it = lay->groupItem(action);
207 if (it) {
208 it->rowProportion = rp;
209 lay->invalidate();
210 }
211 }
212}
213
221{
222 Q_D(RibbonGroup);
223 if (action == Q_NULLPTR) {
224 return Q_NULLPTR;
225 }
227 QWidget::addAction(action);
228
229 return d->lastAddedButton();
230}
231
239{
240 return addAction(action, RibbonGroup::Large);
241}
242
251{
252 return addAction(action, RibbonGroup::Medium);
253}
254
261{
262 return addAction(action, RibbonGroup::Small);
263}
264
271void RibbonGroup::addAction(QAction *act, QToolButton::ToolButtonPopupMode popMode, RibbonGroup::RowProportion rp)
272{
273 Q_D(RibbonGroup);
274 if (act == Q_NULLPTR) {
275 return;
276 }
278 QWidget::addAction(act);
279 RibbonButton *btn = d->lastAddedButton();
280 if (btn) {
281 btn->setPopupMode(popMode);
282 }
283}
284
299QAction *RibbonGroup::addAction(const QString &text, const QIcon &icon, QToolButton::ToolButtonPopupMode popMode,
301{
302 QAction *action = new QAction(icon, text, this);
303 addAction(action, popMode, rp);
304 return action;
305}
306
314RibbonButton *RibbonGroup::addMenu(QMenu *menu, RibbonGroup::RowProportion rp, QToolButton::ToolButtonPopupMode popMode)
315{
316 Q_D(RibbonGroup);
317 if (menu == Q_NULLPTR) {
318 return Q_NULLPTR;
319 }
320 QAction *action = menu->menuAction();
321
322 addAction(action, rp);
323 RibbonButton *btn = d->lastAddedButton();
324 if (btn) {
325 btn->setPopupMode(popMode);
326 }
327 return btn;
328}
329
330RibbonButton *RibbonGroup::addLargeMenu(QMenu *menu, QToolButton::ToolButtonPopupMode popMode)
331{
332 return addMenu(menu, RibbonGroup::Large, popMode);
333}
334
335RibbonButton *RibbonGroup::addSmallMenu(QMenu *menu, QToolButton::ToolButtonPopupMode popMode)
336{
337 return addMenu(menu, RibbonGroup::Small, popMode);
338}
339
348{
349 RibbonButton *btn = addAction(action, rp);
350 if (Q_NULLPTR == btn) {
351 return Q_NULLPTR;
352 }
353 btn->setMenu(menu);
354 btn->setPopupMode(QToolButton::MenuButtonPopup);
355 return btn;
356}
357
364RibbonButton *RibbonGroup::addLargeActionMenu(QAction *action, QMenu *menu)
365{
366 return addActionMenu(action, menu, RibbonGroup::Large);
367}
368
378{
379 QWidgetAction *action = new QWidgetAction(this);
380
381 // The lifecycle of w is taken over by action
382 action->setDefaultWidget(w);
383 w->setAttribute(Qt::WA_Hover);
385 QWidget::addAction(action);
386 return action;
387}
388
394QAction *RibbonGroup::addLargeWidget(QWidget *w)
395{
396 return addWidget(w, RibbonGroup::Large);
397}
398
405{
407}
408
414QAction *RibbonGroup::addSmallWidget(QWidget *w)
415{
416 return addWidget(w, RibbonGroup::Small);
417}
418
425{
426 RibbonGallery *gallery = new RibbonGallery(this);
427
429
430 setExpanding();
431 return gallery;
432}
433
439QAction *RibbonGroup::addSeparator(int top, int bottom)
440{
441 Q_D(RibbonGroup);
442 QAction *action = new QAction(this);
443
444 action->setSeparator(true);
446 QWidget::addAction(action);
447 QWidget *w = d->m_layout->lastWidget();
448 RibbonSeparator *sep = qobject_cast<RibbonSeparator *>(w);
449 if (sep) {
450 sep->setTopBottomMargins(top, bottom);
451 }
452 return action;
453}
454
456{
457 return ribbonButtons().count();
458}
459
461{
462 int cnt = ribbonButtonCount();
463 if (index < 0 || index >= cnt) {
464 return nullptr;
465 }
466 return ribbonButtons().at(index);
467}
468
475{
476 RibbonGroupLayout *lay = qobject_cast<RibbonGroupLayout *>(layout());
477
478 if (lay) {
479 int index = lay->indexOf(action);
480 if (index == -1) {
481 return Q_NULLPTR;
482 }
483 QLayoutItem *item = lay->itemAt(index);
484 if (item) {
485 return qobject_cast<RibbonButton *>(item->widget());
486 }
487 }
488 return Q_NULLPTR;
489}
490
491QList<RibbonButton *> RibbonGroup::ribbonButtons() const
492{
493 const QObjectList &objs = children();
494 QList<RibbonButton *> res;
495
496 for (QObject *o : objs) {
497 RibbonButton *b = qobject_cast<RibbonButton *>(o);
498 if (b) {
499 res.append(b);
500 }
501 }
502 return res;
503}
504
506{
507 Q_D(const RibbonGroup);
508 return (d->m_optionActionButton != Q_NULLPTR);
509}
510
517void RibbonGroup::addOptionAction(QAction *action)
518{
519 Q_D(RibbonGroup);
520 if (Q_NULLPTR == action) {
521 if (d->m_optionActionButton) {
522 delete d->m_optionActionButton;
523 d->m_optionActionButton = Q_NULLPTR;
524 }
525 return;
526 }
527 if (Q_NULLPTR == d->m_optionActionButton) {
528 d->m_optionActionButton = new RibbonGroupOptionButton(this);
529 }
530 d->m_optionActionButton->setFixedSize(optionActionButtonSize());
531 d->m_optionActionButton->setIconSize(optionActionButtonSize() - QSize(-2, -2));
532 d->m_optionActionButton->connectAction(action);
533 updateGeometry(); // 通知layout进行重新布局
534 repaint();
535}
536
542int RibbonGroup::actionIndex(QAction *act) const
543{
544 Q_D(const RibbonGroup);
545 return d->m_layout->indexOf(act);
546}
547
553void RibbonGroup::moveAction(int from, int to)
554{
555 Q_D(RibbonGroup);
556 d->m_layout->move(from, to);
557 updateGeometry(); // 通知layout进行重新布局
558}
559
561{
562 Q_D(const RibbonGroup);
563 return d->m_groupLayoutMode;
564}
565
567{
568 Q_D(RibbonGroup);
569 if (d->m_groupLayoutMode == mode) {
570 return;
571 }
572 d->m_groupLayoutMode = mode;
574}
575
577{
578 Q_D(RibbonGroup);
579 // reset layout
580 layout()->setSpacing(isTwoRow() ? 4 : 2);
581 updateGeometry();
582
583 d->resetLargeToolButtonStyle();
584}
585
587{
588 Q_D(const RibbonGroup);
589 return (TwoRowMode == d->m_groupLayoutMode);
590}
591
593{
594 return (isTwoRow() ? QSize(12, 12) : QSize(16, 16));
595}
596
598{
599 Q_D(const RibbonGroup);
600 QSize laySize = layout()->sizeHint();
601 int maxWidth = laySize.width() + 2;
602
603 if (titleVisible()) {
604 QFontMetrics fm = fontMetrics();
605 QSize titleSize = fm.size(Qt::TextShowMnemonic, groupName());
606 if (d->m_optionActionButton) {
607 // optionActionButton的宽度需要预留
608 titleSize.setWidth(titleSize.width() + d->m_optionActionButton->width() + 4);
609 }
610 maxWidth = qMax(maxWidth, titleSize.width());
611 }
612 return QSize(maxWidth, laySize.height());
613}
614
616{
617 return layout()->minimumSize();
618}
619
625{
626 QSizePolicy sp = sizePolicy();
627
628 return (sp.horizontalPolicy() == QSizePolicy::Expanding);
629}
630
635void RibbonGroup::setExpanding(bool expanding)
636{
637 setSizePolicy(expanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed);
638}
639
641{
642 Q_D(const RibbonGroup);
643 return d->m_isCanCustomize;
644}
645
647{
648 Q_D(RibbonGroup);
649 d->m_isCanCustomize = b;
650}
651
657{
658 return RibbonGroupLayout::calcLargeHeight(rect(), this);
659}
660
662{
663 return (titleVisible() ? groupTitleHeight() : 0);
664}
665
670
672{
673 if (RibbonGroupPrivate::s_titleVisible != visible) {
675 RibbonElementStyleOpt.recalc();
676 }
677}
678
689
703
704void RibbonGroup::paintEvent(QPaintEvent *event)
705{
706 Q_D(RibbonGroup);
707 QPainter p(this);
708
709#ifdef QX_RIBBON_DEBUG_HELP_DRAW
710 HELP_DRAW_RECT(p, rect());
711#endif
712 if (titleVisible()) {
713 const int th = titleHeight();
714 QFont f = font();
715 p.setFont(f);
716 QRect r;
717 if (d->m_optionActionButton) {
718 r = QRect(1, height() - th, width() - d->m_optionActionButton->width() - 4, th);
719 } else {
720 r = QRect(1, height() - th, width(), th);
721 }
722 p.drawText(r, Qt::AlignCenter, groupName());
723#ifdef QX_RIBBON_DEBUG_HELP_DRAW
724 HELP_DRAW_RECT(p, r);
725#endif
726 }
727
728 QWidget::paintEvent(event);
729}
730
731void RibbonGroup::resizeEvent(QResizeEvent *event)
732{
733 Q_D(RibbonGroup);
735 if (d->m_optionActionButton) {
736 if (titleVisible()) {
737 d->m_optionActionButton->move(width() - d->m_optionActionButton->width() - 2,
738 height() - titleHeight() +
739 (titleHeight() - d->m_optionActionButton->height()) / 2);
740 } else {
741 d->m_optionActionButton->move(width() - d->m_optionActionButton->width(),
742 height() - d->m_optionActionButton->height());
743 }
744 }
747 return QWidget::resizeEvent(event);
748}
749
766void RibbonGroup::actionEvent(QActionEvent *event)
767{
768 Q_D(RibbonGroup);
769 QAction *action = event->action();
770
771 switch (event->type()) {
772 case QEvent::ActionAdded: {
773 RibbonGroupLayout *lay = d->m_layout;
774 int index = layout()->count();
775 if (event->before()) {
776 // 说明是插入
777 index = lay->indexOf(action);
778 if (-1 == index) {
779 index = layout()->count(); // 找不到的时候就插入到最后
780 }
781 }
782 lay->insertAction(index, action, getActionRowProportionProperty(action));
783 // 由于group的尺寸发生变化,需要让page也调整
784 if (parentWidget()) {
785 QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest));
786 }
787 } break;
788
789 case QEvent::ActionChanged: {
790 // 让布局重新绘制
791 layout()->invalidate();
792 // 由于group的尺寸发生变化,需要让page也调整
793 if (parentWidget()) {
794 QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest));
795 }
796 } break;
797
798 case QEvent::ActionRemoved: {
799 RibbonGroupLayout *lay = d->m_layout;
800 action->disconnect(this);
801 int index = lay->indexOf(action);
802 if (index != -1) {
803 QLayoutItem *item = lay->takeAt(index);
804 delete item;
805 }
806 // 由于group的尺寸发生变化,需要让page也调整
807 if (parentWidget()) {
808 QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest));
809 }
810 } break;
811
812 default:
813 break;
814 }
815}
816
817/* RibbonSeparator */
818RibbonSeparator::RibbonSeparator(int height, QWidget *parent)
819 : RibbonSeparator(parent)
820{
821 setFixedHeight(height);
822}
823
825 : QWidget(parent)
826 , m_topMargin(4)
827 , m_bottomMargin(4)
828{
829 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
830 setFixedWidth(6);
831}
832
834{
835 return QSize(6, height());
836}
837
839{
840 m_topMargin = top;
841 m_bottomMargin = bottom;
842}
843
844void RibbonSeparator::paintEvent(QPaintEvent *event)
845{
846 Q_UNUSED(event);
847 QPainter painter(this);
848
849 painter.setPen(palette().window().color().darker(114));
850 int x1 = rect().center().x();
851
852 painter.drawLine(QPoint(x1, rect().top() + m_topMargin), QPoint(x1, rect().bottom() - m_bottomMargin));
853}
854
Ribbon 界面适用的 toolButton
@ Lite
icon占大按钮的60,文字占40,且文字不换行,对应于wps模式的菜单按钮,下箭头在文字旁边
@ Normal
icon占大按钮的一半区域, 箭头在按钮最下方,文字可以换一行,对应于office的菜单按钮,下箭头在按钮最下方(默认)
是对 group 所有子窗口的抽象,参考 qt 的 toolbar
RibbonGroup::RowProportion rowProportion
行的占比,ribbon中有large,medium和small三种占比,见 RibbonGroup::RowProportion
针对 RibbonGroup 的布局
QLayoutItem * takeAt(int index) override
virtual int indexOf(QAction *action) const
通过action查找索引,用于actionEvent添加action用
const QList< RibbonGroupItem * > & ribbonGroupItems() const
RibbonGroupItem * groupItem(QAction *action) const
QWidget * lastWidget() const
获取最后生成的窗口
static int calcLargeHeight(const QRect &setrect, const RibbonGroup *group)
计算大图标的高度
void insertAction(int index, QAction *act, RibbonGroup::RowProportion rp=RibbonGroup::Auto)
RibbonGroup主要通过此函数来添加action
QLayoutItem * itemAt(int index) const override
Group右下角的操作按钮
void connectAction(QAction *action)
void resetLargeToolButtonStyle()
重置大按钮的类型
static int s_groupTitleHeight
group标题栏的全局高度,默认为21
RibbonGroup::GroupLayoutMode m_groupLayoutMode
group的布局模式,默认为3行模式ThreeRowMode
const QList< RibbonGroupItem * > & ribbonGroupItems() const
获取布局item
RibbonGroupLayout * m_layout
RibbonButton * lastAddedButton()
group 用于对 ribbon 页窗口中的控件进行分组
Definition ribbongroup.h:29
bool hasOptionAction() const
QAction * addSmallWidget(QWidget *w)
添加窗口,占用ribbon的一行
QAction * addSeparator(int top=6, int bottom=6)
添加分割线
void setGroupLayoutMode(GroupLayoutMode mode)
virtual void actionEvent(QActionEvent *event) override
处理action的事件
void addOptionAction(QAction *action)
添加操作action,如果要去除,传入Q_NULLPTR指针即可,RibbonGroup不会对QAction的所有权进行管理
static bool titleVisible()
static int groupTitleHeight()
定义所有的 group 的标题栏高度,有别于
void setActionRowProportion(QAction *action, RibbonGroup::RowProportion rp)
设置action的行行为,行属性决定了ribbon group的显示方式
RibbonButton * addSmallMenu(QMenu *menu, QToolButton::ToolButtonPopupMode popMode=QToolButton::InstantPopup)
QString groupName() const
static void setActionRowProportionProperty(QAction *action, RibbonGroup::RowProportion rp)
把action的行属性设置进action中,action自身携带了行属性
virtual QSize minimumSizeHint() const override
RibbonButton * ribbonButtonByIndex(int index) const
static void setGroupTitleHeight(int h)
设置 group 的全局高度,此函数是个全局的影响
QList< RibbonButton * > ribbonButtons() const
QAction * addMediumWidget(QWidget *w)
添加窗口,占用ribbon的一行
RibbonButton * addAction(QAction *action, RibbonGroup::RowProportion rp)
添加action
virtual void resizeEvent(QResizeEvent *event) override
void moveAction(int from, int to)
移动action
RibbonButton * ribbonButtonForAction(QAction *action) const
从group中把action对应的button提取出来,如果action没有对应的button,就返回Q_NULLPTR
void setTitle(const QString &title)
virtual QSize sizeHint() const override
void setExpanding(bool expanding=true)
把group设置为扩展模式,此时会撑大水平区域
RibbonButton * addSmallAction(QAction *action)
添加小图标
RibbonGroup(QWidget *parent=Q_NULLPTR)
bool isCanCustomize() const
bool isExpanding() const
判断此group是否为(水平)扩展模式
static RibbonGroup::RowProportion getActionRowProportionProperty(QAction *action)
获取action的行属性
int largeHeight() const
获取大图标的高度
RibbonButton * addMenu(QMenu *menu, RibbonGroup::RowProportion rp, QToolButton::ToolButtonPopupMode popMode=QToolButton::InstantPopup)
添加一个普通菜单
int ribbonButtonCount() const
RibbonButton * addActionMenu(QAction *action, QMenu *menu, RibbonGroup::RowProportion rp)
添加一个ActionMenu
GroupLayoutMode groupLayoutMode() const
QAction * addLargeWidget(QWidget *w)
添加窗口,占用所有行
RibbonGallery * addGallery()
RibbonGroup::addGallery
QAction * addWidget(QWidget *w, RibbonGroup::RowProportion rp)
添加窗口
void setCanCustomize(bool b)
RibbonButton * addLargeAction(QAction *action)
添加大图标
RibbonButton * addLargeActionMenu(QAction *action, QMenu *menu)
添加action menu,action menu是一个特殊的menu,即可点击触发action,也可弹出菜单
int actionIndex(QAction *act) const
action对应的布局index,此操作一般用于移动,其他意义不大
@ ThreeRowMode
三行布局模式,office就是三行布局模式,group能布置3行小toolbutton,默认模式
Definition ribbongroup.h:36
@ TwoRowMode
两行布局模式,wps的后续布局模式就是两行布局模式,group能布置2行小toolbutton
Definition ribbongroup.h:37
void setGroupName(const QString &title)
设置group的标题
QString title() const
static void setTitleVisible(bool visible)
@ Medium
中占比,在 RibbonGroup::groupLayoutMode 为 RibbonGroup::ThreeRowMode 时才会起作用, 且要同一列里两个都是 Medium 时,会在三行中占据两行
Definition ribbongroup.h:42
@ Small
小占比,占RibbonGroup的一行,Medium在不满足条件时也会变为Small,但不会变为Large
Definition ribbongroup.h:44
@ Large
大占比,一个widget的高度会充满整个group
Definition ribbongroup.h:41
RibbonButton * addLargeMenu(QMenu *menu, QToolButton::ToolButtonPopupMode popMode=QToolButton::InstantPopup)
RibbonButton * addMediumAction(QAction *action)
在三栏模式下,强制加为2栏action
virtual void paintEvent(QPaintEvent *event) override
virtual QSize optionActionButtonSize() const
void paintEvent(QPaintEvent *event) override
RibbonSeparator(int height, QWidget *parent=Q_NULLPTR)
virtual QSize sizeHint() const override
void setTopBottomMargins(int top, int bottom)
#define QX_INIT_PRIVATE(Class)
#define Q_NULLPTR
Copyleft (C) 2025 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QX_FINI_PRIVATE()
#define QX_RIBBON_END_NAMESPACE
#define QX_RIBBON_BEGIN_NAMESPACE
const char * name
Definition qxtheme.cpp:25
QColor text
QColor window
#define HELP_DRAW_RECT(p, rect)
Definition ribbonbar.cpp:37
#define QX_RIBBON_PROP_ROW_PROPORTION
#define RibbonElementStyleOpt