QtCanpool 3.3.0
载入中...
搜索中...
未找到
ribbonpage.cpp
浏览该文件的文档.
1
5#include "ribbonpage.h"
6#include "ribbonpage_p.h"
7#include "ribbongroup_p.h"
8
9#include <QApplication>
10#include <QDebug>
11#include <QHBoxLayout>
12#include <QLinearGradient>
13#include <QList>
14#include <QMap>
15#include <QPainter>
16#include <QResizeEvent>
17
19
20RibbonPageScrollButton::RibbonPageScrollButton(Qt::ArrowType arr, QWidget *p)
21 : QToolButton(p)
22{
23 setArrowType(arr);
24}
25
27 : m_ribbonBar(Q_NULLPTR)
28 , m_groupLayoutMode(RibbonGroup::ThreeRowMode)
29 , m_leftScrollBtn(Q_NULLPTR)
30 , m_rightScrollBtn(Q_NULLPTR)
31 , m_sizeHint(50, 50)
32 , m_contentsMargins(1, 1, 1, 1)
33 , m_isRightScrollBtnShow(false)
34 , m_isLeftScrollBtnShow(false)
35 , m_isPageContext(false)
36 , m_isCanCustomize(true)
37{
38}
39
43
45{
46 Q_Q(RibbonPage);
47 m_leftScrollBtn = new RibbonPageScrollButton(Qt::LeftArrow, q);
48 m_rightScrollBtn = new RibbonPageScrollButton(Qt::RightArrow, q);
49 m_leftScrollBtn->setVisible(false);
50 m_rightScrollBtn->setVisible(false);
51 connect(m_leftScrollBtn, &QToolButton::clicked, this, &RibbonPagePrivate::onLeftScrollButtonClicked);
52 connect(m_rightScrollBtn, &QToolButton::clicked, this, &RibbonPagePrivate::onRightScrollButtonClicked);
53}
54
56{
57 return insertGroup(m_itemList.size(), title);
58}
59
60RibbonGroup *RibbonPagePrivate::insertGroup(int index, const QString &title)
61{
62 Q_Q(RibbonPage);
63 RibbonGroup *group = new RibbonGroup(q);
64
65 group->setWindowTitle(title);
66 group->setObjectName(title);
67 insertGroup(index, group);
68 return group;
69}
70
72{
73 insertGroup(m_itemList.size(), group);
74}
75
77{
78 Q_Q(RibbonPage);
79 if (Q_NULLPTR == group) {
80 return;
81 }
82 if (group->parentWidget() != q) {
83 group->setParent(q);
84 }
86 if (index < 0 || index > m_itemList.size()) {
87 index = m_itemList.size();
88 }
89 RibbonPageItem item;
90
91 item.m_group = group;
92 item.m_separator = new RibbonSeparator(q);
93 m_itemList.insert(index, item);
95 group->setVisible(true);
96}
97
99{
100 RibbonPageItem item;
101
102 for (int i = 0; i < m_itemList.size(); ++i) {
103 if (group == m_itemList[i].m_group) {
104 item = m_itemList.takeAt(i);
105 break;
106 }
107 }
108 if (item.isNull()) {
109 return false;
110 }
111 if (item.m_separator) {
112 item.m_separator->hide();
113 item.m_separator->deleteLater();
114 }
116 return true;
117}
118
120{
121 if (takeGroup(group)) {
122 group->hide();
123 group->deleteLater();
124 return true;
125 }
126 return false;
127}
128
129QList<RibbonGroup *> RibbonPagePrivate::groupList() const
130{
131 QList<RibbonGroup *> res;
132
133 for (const RibbonPageItem &i : m_itemList) {
134 if (!i.isNull()) {
135 res.append(i.m_group);
136 }
137 }
138 return res;
139}
140
152{
153 if (m_groupLayoutMode == m) {
154 return;
155 }
157 QList<RibbonGroup *> groups = groupList();
158
159 for (RibbonGroup *group : std::as_const(groups)) {
160 group->setGroupLayoutMode(m);
161 }
163}
164
166{
167 int total = 0;
168 QMargins m = m_contentsMargins;
169
170 if (!m.isNull()) {
171 total += (m.left() + m.right());
172 }
173
174 for (const RibbonPageItem &item : std::as_const(m_itemList)) {
175 if (item.isEmpty()) {
176 continue;
177 }
178 total += item.m_group->sizeHint().width();
179 if (item.m_separator) {
180 total += item.m_separator->sizeHint().width();
181 }
182 }
183 return total;
184}
185
187{
188 Q_Q(const RibbonPage);
189 QSize s = q->size();
190 QMargins m = m_contentsMargins;
191
192 if (!m.isNull()) {
193 s.rheight() -= (m.top() + m.bottom());
194 s.rwidth() -= (m.right() + m.left());
195 }
196 return s;
197}
198
200{
201 Q_Q(RibbonPage);
202 RibbonPage *page = q;
203 QSize pageSize = pageContentSize();
204 int pageWidth = pageSize.width();
205
206 int y = 0;
207 int x = 0;
208
209 if (!m_contentsMargins.isNull()) {
210 y = m_contentsMargins.top();
211 x = m_contentsMargins.left();
212 }
213
214 int totalWidth = totalSizeHintWidth();
215 // 记录可以扩展的数量
216 int canExpandingCount = 0;
217 // 扩展的宽度
218 int expandWidth = 0;
219
220#ifdef QX_RIBBON_DEBUG_HELP_DRAW
221 qDebug() << "\r\n\r\n============================================="
222 << "\r\nRibbonPagePrivate::updateItemGeometry"
223 << "\r\npage name:" << page->windowTitle() << "\r\n first totalWidth:" << totalWidth << "\r\n y:" << y
224 << "\r\nexpandWidth:" << expandWidth;
225#endif
226 // 如果 totalWidth < pageWidth, m_XBase 可以设置为 0
227 if (totalWidth <= pageWidth) {
228 // 这个是避免一开始 totalWidth > pageWidth,通过滚动按钮调整了 m_XBase,随之调整了窗体尺寸,
229 // 调整后 totalWidth < pageWidth 导致 page 在原来位置无法显示,必须这里把 m_XBase 设置为 0
230 m_XBase = 0;
231 for (const RibbonPageItem &item : m_itemList) {
232 if (!item.isEmpty()) {
233 if (item.m_group->isExpanding()) {
234 // group可扩展
235 ++canExpandingCount;
236 }
237 }
238 }
239 // 计算可扩展的宽度
240 if (canExpandingCount > 0) {
241 expandWidth = (pageWidth - totalWidth) / canExpandingCount;
242 } else {
243 expandWidth = 0;
244 }
245 }
246 totalWidth = 0; // total重新计算
247 x += m_XBase;
248
249 // 先按照sizeHint设置所有的尺寸
250 for (RibbonPageItem &item : m_itemList) {
251 if (item.isEmpty()) {
252 // 如果是hide就直接跳过
253 if (item.m_separator) {
254 // group hide分割线也要hide
255 item.m_separator->hide();
256 }
257 item.m_groupWillGeometry = QRect(0, 0, 0, 0);
258 item.m_separatorWillGeometry = QRect(0, 0, 0, 0);
259 continue;
260 }
261 RibbonGroup *group = item.m_group;
262 if (Q_NULLPTR == group) {
263 qDebug() << "unknow widget in RibbonPageLayout";
264 continue;
265 }
266 QSize groupSize = group->sizeHint();
267 QSize separatorSize(0, 0);
268 if (item.m_separator) {
269 separatorSize = item.m_separator->sizeHint();
270 }
271 if (group->isExpanding()) {
272 // 可扩展,就把group扩展到最大
273 groupSize.setWidth(groupSize.width() + expandWidth);
274 }
275 int w = groupSize.width();
276 item.m_groupWillGeometry = QRect(x, y, w, pageSize.height());
277 x += w;
278 totalWidth += w;
279 w = separatorSize.width();
280 item.m_separatorWillGeometry = QRect(x, y, w, pageSize.height());
281 x += w;
282 totalWidth += w;
283 }
284 m_totalWidth = totalWidth;
285 // 判断滚动按钮是否显示
286 if (totalWidth > pageWidth) {
287 // 超过总长度,需要显示滚动按钮
288 if (m_XBase == 0) {
289 // 已经移动到最左,需要可以向右移动
291 m_isLeftScrollBtnShow = false;
292 } else if (m_XBase <= (pageWidth - totalWidth)) {
293 // 已经移动到最右,需要可以向左移动
296 } else {
297 // 移动到中间两边都可以动
300 }
301 } else {
302 // 说明 totalWidth 小于 pageWidth
304 m_isLeftScrollBtnShow = false;
305 }
306 QWidget *par = page->parentWidget();
307 int parentHeight = (Q_NULLPTR == par) ? pageSize.height() : par->height();
308 int parentWidth = (Q_NULLPTR == par) ? totalWidth : par->width();
309 m_sizeHint = QSize(parentWidth, parentHeight);
310 doItemLayout();
311}
312
314{
315 Q_Q(RibbonPage);
316 RibbonPage *page = q;
317
318 // 两个滚动按钮的位置永远不变
319 m_leftScrollBtn->setGeometry(0, 0, 12, page->height());
320 m_rightScrollBtn->setGeometry(page->width() - 12, 0, 12, page->height());
321 QList<QWidget *> showWidgets, hideWidgets;
322
323 for (const RibbonPageItem &item : m_itemList) {
324 if (item.isNull()) {
325 continue;
326 }
327 if (item.isEmpty()) {
328 hideWidgets << item.m_group;
329 if (item.m_separator) {
330 hideWidgets << item.m_separator;
331 }
332 } else {
333 // item.m_group->setFixedSize(item.m_groupWillGeometry.size());
334 // item.m_group->move(item.m_groupWillGeometry.topLeft());
335 item.m_group->setGeometry(item.m_groupWillGeometry);
336 showWidgets << item.m_group;
337 if (item.m_separator) {
338 item.m_separator->setGeometry(item.m_separatorWillGeometry);
339 showWidgets << item.m_separator;
340 }
341#ifdef QX_RIBBON_DEBUG_HELP_DRAW
342 qDebug() << "RibbonPagePrivate::doItemLayout() =";
343 qDebug() << "\r\n group:" << item.m_group->windowTitle()
344 << "\r\n group geo:" << item.m_groupWillGeometry
345 << "\r\n sep geo:" << item.m_separatorWillGeometry;
346#endif
347 }
348 }
349
353 m_rightScrollBtn->raise();
354 }
356 m_leftScrollBtn->raise();
357 }
358 // 不在上面那里进行show和hide因为这会触发RibbonGroupLayout的重绘,导致循环绘制,非常影响效率
359 for (QWidget *w : std::as_const(showWidgets)) {
360 w->show();
361 }
362 for (QWidget *w : std::as_const(hideWidgets)) {
363 w->hide();
364 }
365}
366
367void RibbonPagePrivate::doWheelEvent(QWheelEvent *event)
368{
369 int pageWidth = pageContentSize().width();
370 int totalWidth = m_totalWidth;
371
372 if (totalWidth > pageWidth) {
373 // 这个时候滚动有效
374 QPoint numPixels = event->pixelDelta();
375 QPoint numDegrees = event->angleDelta() / 8;
376
377 // Linux GNOME 平台上测试,event->pixelDelta() 和 event->angleDelta() 取值都为 (0, +/-120)
378 // Windows 10 平台上测试,event->pixelDelta() 和 event->angleDelta() 取值分别为 (0, 0) 和 (0, +/-120)
379 int scrollpix = 0;
380 if (!numPixels.isNull()) {
381 // Linux GNOME 平台上,一次 scrollpix 为 30
382 scrollpix = numPixels.y() / 4;
383 } else if (!numDegrees.isNull()) {
384 // Windows 10 平台上, 一次 scrollpix 为 15
385 scrollpix = numDegrees.y();
386 }
387 if (scrollpix > 0) { // 当滚轮向上滑,RibbonPage向左走
388 int tmp = m_XBase - scrollpix;
389 if (tmp < (pageWidth - totalWidth)) {
390 tmp = pageWidth - totalWidth;
391 }
392 m_XBase = tmp;
393 } else { // 当滚轮向下滑,RibbonPage向右走
394 int tmp = m_XBase - scrollpix; // 此时numDegrees为负数
395 if (tmp > 0) {
396 tmp = 0;
397 }
398 m_XBase = tmp;
399 }
401 } else {
402 // 这时候无需处理事件,把滚动事件上发让父级也能接收
403 event->ignore();
404 m_XBase = 0;
405 }
406}
407
409{
410 int pageWidth = pageContentSize().width();
411 int totalWidth = m_totalWidth;
412
413 if (totalWidth > pageWidth) {
414 // 点击左侧,相当于向右滚动一页
415 //
416 // |---- pageWidth ----|
417 // |-------- totalWidth --------|
418 // `-> m_xBase (max pos is 0)
419 //
420 int tmp = m_XBase + pageWidth;
421 if (tmp > 0) {
422 tmp = 0;
423 }
424 m_XBase = tmp;
425 } else {
426 m_XBase = 0;
427 }
429}
430
432{
433 int pageWidth = pageContentSize().width();
434 int totalWidth = m_totalWidth;
435
436 if (totalWidth > pageWidth) {
437 // 点击右侧,相当于向左滚动一页
438 //
439 // |---- pageWidth ----|
440 // |-------- totalWidth --------|
441 // `-> m_xBase (min pos is pageWidth - totalWidth)
442 //
443 int tmp = m_XBase - pageWidth;
444 if (tmp < (pageWidth - totalWidth)) {
445 tmp = pageWidth - totalWidth; // m_XBase 最小值
446 }
447 m_XBase = tmp;
448 } else {
449 m_XBase = 0;
450 }
452}
453
455 : QWidget(parent)
456{
458 Q_D(RibbonPage);
459 d->init();
460 setAutoFillBackground(true);
461 setBackgroundBrush(Qt::white);
462}
463
464RibbonPage::RibbonPage(const QString &name, QWidget *parent)
465 : RibbonPage(parent)
466{
468}
469
474
475QString RibbonPage::pageName() const
476{
477 return windowTitle();
478}
479
480void RibbonPage::setPageName(const QString &title)
481{
482 setWindowTitle(title);
483}
484
485QString RibbonPage::title() const
486{
487 return pageName();
488}
489
491{
492 Q_D(const RibbonPage);
493 return d->m_groupLayoutMode;
494}
495
497{
498 Q_D(RibbonPage);
499 d->setGroupLayoutMode(m);
500}
501
510RibbonGroup *RibbonPage::addGroup(const QString &title)
511{
512 Q_D(RibbonPage);
513 return d->addGroup(title);
514}
515
521{
522 Q_D(RibbonPage);
523 d->addGroup(group);
524}
525
532RibbonGroup *RibbonPage::insertGroup(int index, const QString &title)
533{
534 Q_D(RibbonPage);
535 return d->insertGroup(index, title);
536}
537
544{
545 Q_D(const RibbonPage);
546 return d->m_itemList.value(index).m_group;
547}
548
554RibbonGroup *RibbonPage::groupByName(const QString &title) const
555{
556 Q_D(const RibbonPage);
557 for (const RibbonPageItem &i : d->m_itemList) {
558 if (i.m_group) {
559 if (i.m_group->windowTitle() == title) {
560 return i.m_group;
561 }
562 }
563 }
564 return Q_NULLPTR;
565}
566
572RibbonGroup *RibbonPage::groupByObjectName(const QString &objname) const
573{
574 Q_D(const RibbonPage);
575 for (const RibbonPageItem &i : d->m_itemList) {
576 if (i.m_group) {
577 if (i.m_group->objectName() == objname) {
578 return i.m_group;
579 }
580 }
581 }
582 return Q_NULLPTR;
583}
584
591{
592 Q_D(const RibbonPage);
593 int c = groupCount();
594
595 for (int i = 0; i < c; ++i) {
596 if (d->m_itemList[i].m_group == group) {
597 return i;
598 }
599 }
600 return -1;
601}
602
608void RibbonPage::moveGroup(int from, int to)
609{
610 Q_D(RibbonPage);
611 if (from == to) {
612 return;
613 }
614 int c = groupCount();
615 if (c <= 0) {
616 return;
617 }
618 if (from < 0 || from >= c || to < 0 || to >= c) {
619 return;
620 }
621 // This is the same as insert(to, takeAt(from))
622 d->m_itemList.move(from, to);
623 d->updateItemGeometry();
624}
625
627{
628 Q_D(RibbonPage);
629 return d->takeGroup(group);
630}
631
648{
649 Q_D(RibbonPage);
650 return d->removeGroup(group);
651}
652
659{
660 RibbonGroup *grp = group(index);
661
662 if (Q_NULLPTR == grp) {
663 return false;
664 }
665 return removeGroup(grp);
666}
667
669{
670 for (RibbonGroup *group : groupList()) {
671 if (group->parent() == this) {
673 }
674 }
675}
676
677QList<RibbonGroup *> RibbonPage::groupList() const
678{
679 Q_D(const RibbonPage);
680 return d->groupList();
681}
682
683QList<RibbonGroup *> RibbonPage::groups() const
684{
685 return groupList();
686}
687
688void RibbonPage::setBackgroundBrush(const QBrush &brush)
689{
690 QPalette p = palette();
691 p.setBrush(QPalette::Window, brush);
692 setPalette(p);
693}
694
696{
697 Q_D(const RibbonPage);
698 return d->m_sizeHint;
699}
700
706{
707 Q_D(const RibbonPage);
708 return d->m_isPageContext;
709}
710
712{
713 Q_D(const RibbonPage);
714 return d->m_itemList.size();
715}
716
718{
719 Q_D(const RibbonPage);
720 return d->m_isCanCustomize;
721}
722
724{
725 Q_D(RibbonPage);
726 d->m_isCanCustomize = b;
727}
728
730{
731 Q_D(const RibbonPage);
732 return d->m_ribbonBar;
733}
734
739{
740 Q_D(RibbonPage);
741 QList<RibbonGroup *> groups = groupList();
742 for (RibbonGroup *group : std::as_const(groups)) {
744 }
745 d->updateItemGeometry();
746}
747
748void RibbonPage::setTitle(const QString &title)
749{
751}
752
757void RibbonPage::markIsPageContext(bool isPageContext)
758{
759 Q_D(RibbonPage);
760 d->m_isPageContext = isPageContext;
761}
762
763bool RibbonPage::event(QEvent *e)
764{
765 Q_D(RibbonPage);
766 switch (e->type()) {
767 case QEvent::LayoutRequest: {
768 d->updateItemGeometry();
769 } break;
770
771 default:
772 break;
773 }
774 return QWidget::event(e);
775}
776
777void RibbonPage::resizeEvent(QResizeEvent *e)
778{
779 Q_D(RibbonPage);
780 QWidget::resizeEvent(e);
781 d->updateItemGeometry();
782}
783
784void RibbonPage::wheelEvent(QWheelEvent *event)
785{
786 Q_D(RibbonPage);
787 d->doWheelEvent(event);
788}
789
790void RibbonPage::setRibbonBar(RibbonBar *bar)
791{
792 Q_D(RibbonPage);
793 d->m_ribbonBar = bar;
794}
795
RibbonBar 继承 QMenuBar, 在 RibbonWindow 中直接替换了原来的 QMenuBar
Definition ribbonbar.h:99
group 用于对 ribbon 页窗口中的控件进行分组
Definition ribbongroup.h:29
void setGroupLayoutMode(GroupLayoutMode mode)
virtual QSize sizeHint() const override
bool isExpanding() const
判断此group是否为(水平)扩展模式
RibbonSeparator * m_separator
QList< RibbonGroup * > groupList() const
RibbonGroup::GroupLayoutMode m_groupLayoutMode
RibbonGroup * insertGroup(int index, const QString &title)
void doWheelEvent(QWheelEvent *event)
QList< RibbonPageItem > m_itemList
RibbonGroup * addGroup(const QString &title)
RibbonPageScrollButton * m_leftScrollBtn
bool takeGroup(RibbonGroup *group)
RibbonPageScrollButton * m_rightScrollBtn
void setGroupLayoutMode(RibbonGroup::GroupLayoutMode m)
设置group的模式
bool removeGroup(RibbonGroup *group)
一项 ribbon tab 页
Definition ribbonpage.h:30
bool removeGroup(RibbonGroup *group)
移除Group,Page会直接回收RibbonGroup内存
void markIsPageContext(bool isPageContext=true)
标记这个是上下文标签
RibbonBar * ribbonBar() const
bool takeGroup(RibbonGroup *group)
QSize sizeHint() const override
bool isPageContext() const
如果是ContextPage,此函数返回true
RibbonGroup * group(int index) const
通过索引找到group,如果超过索引范围,会返回Q_NULLPTR
void moveGroup(int from, int to)
移动一个Group从from index到to index
void updateItemGeometry()
刷新page的布局,适用于改变ribbon的模式之后调用
RibbonGroup * insertGroup(int index, const QString &title)
新建一个group,并插入到index位置
RibbonGroup::GroupLayoutMode groupLayoutMode() const
QString title() const
void setBackgroundBrush(const QBrush &brush)
RibbonGroup * groupByName(const QString &title) const
通过名字查找group
int groupIndex(RibbonGroup *group) const
查找group对应的索引
QList< RibbonGroup * > groups() const
RibbonGroup * groupByObjectName(const QString &objname) const
通过ObjectName查找group
QString pageName() const
void resizeEvent(QResizeEvent *e) override
QList< RibbonGroup * > groupList() const
bool isCanCustomize() const
void setPageName(const QString &title)
void setTitle(const QString &title)
void setGroupLayoutMode(RibbonGroup::GroupLayoutMode m)
bool event(QEvent *e) override
RibbonGroup * addGroup(const QString &title)
添加group
RibbonPage(QWidget *parent=Q_NULLPTR)
void setCanCustomize(bool b)
void wheelEvent(QWheelEvent *event) override
#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