QtCanpool 3.3.0
载入中...
搜索中...
未找到
ribbonbar.cpp
浏览该文件的文档.
1
5#include "ribbonbar.h"
6#include "ribbonbar_p.h"
8#include "ribbonbuttongroup.h"
9#include "ribboncontrols.h"
10#include "ribbonmanager.h"
11#include "ribbontabbar.h"
13
14#include <QAction>
15#include <QApplication>
16#include <QDebug>
17#include <QHoverEvent>
18#include <QLinearGradient>
19#include <QPainter>
20#include <QResizeEvent>
21#include <QSet>
22#include <QStyleOptionMenuItem>
23#include <QTimer>
24#include <QVariant>
25#include <QEventLoop>
26#include <QMouseEvent>
27#include "qtcompat/qtcompat.h"
28#include <QEvent>
29#include <QLayout>
30
32
34const int tabBarMinimumWidth = 100;
35const int dragAreaMinimumWidth = 20;
36
37#define HELP_DRAW_RECT(p, rect) \
38 do { \
39 p.save(); \
40 QPen _pen(Qt::DashDotDotLine); \
41 _pen.setColor(QColor(219, 26, 59)); \
42 p.setPen(_pen); \
43 p.setBrush(QBrush()); \
44 p.drawRect(rect); \
45 p.restore(); \
46 } while (0)
47
48/* RibbonStackedWidget */
49RibbonStackedWidget::RibbonStackedWidget(QWidget *parent)
50 : QStackedWidget(parent)
51 , m_isPopup(false)
52{
53}
54
58
60{
61 return m_isPopup;
62}
63
65{
66 if (m_isPopup == popup) {
67 return;
68 }
69 m_isPopup = popup;
70 if (popup) {
71 setWindowFlags(Qt::Popup);
72 } else {
73 setWindowFlags(Qt::Widget);
74 }
75}
76
77bool RibbonStackedWidget::event(QEvent *event)
78{
79 if (m_isPopup) {
80 switch (event->type()) {
81 case QEvent::Show:
82 Q_EMIT aboutToShow();
83 break;
84 case QEvent::Hide:
85 Q_EMIT aboutToHide();
86 break;
87 default:
88 break;
89 }
90 }
91 return QStackedWidget::event(event);
92}
93
101{
102 QWidget *w = widget(from);
103
104 removeWidget(w);
105 insertWidget(to, w);
106}
107
108/* RibbonQuickAccessBarContainer */
110 : RibbonCtrlContainer(parent)
111{
112 m_quickAccessBar = new RibbonQuickAccessBar(this);
113 // use QLayout::SetDefaultConstraint instread of QLayout::SetFixedSize.
114 // Otherwise, the size cannot be set using setGeometry
115 m_quickAccessBar->layout()->setSizeConstraint(QLayout::SetDefaultConstraint);
116 m_quickAccessBar->setObjectName(QStringLiteral("qx_QuickAccessBar"));
117 setWidget(m_quickAccessBar);
118 setTitleVisible(false);
119}
120
124
126{
127 return m_quickAccessBar;
128}
129
131 : m_applicationButton(Q_NULLPTR)
132 , m_tabBar(Q_NULLPTR)
133 , m_stack(Q_NULLPTR)
134 , m_iconRightBorderPosition(1)
135 , m_minimumPageButton(Q_NULLPTR)
136 , m_topLeftButtonGroup(Q_NULLPTR)
137 , m_topRightButtonGroup(Q_NULLPTR)
138 , m_bottomLeftButtonGroup(Q_NULLPTR)
139 , m_bottomRightButtonGroup(Q_NULLPTR)
140 , m_windowButtonGroup(Q_NULLPTR)
141 , m_quickAccessBarPosition(RibbonBar::QABRightPosition)
142 , m_tabBarPosition(RibbonBar::TBLeftPosition)
143 , m_ribbonStyle(RibbonBar::OfficeStyle)
144 , m_lastShowStyle(RibbonBar::OfficeStyle)
145 , m_pageContextColorListIndex(-1)
146 , m_titleAligment(Qt::AlignCenter)
147 , m_minimized(true)
148 , m_titleVisible(true)
149 , m_pageContextCoverTab(false)
150 , m_layoutRequest(false)
151 , m_blockResize(false)
152{
153 m_pageContextColorList << QColor(201, 89, 156) // 玫红
154 << QColor(242, 203, 29) // 黄
155 << QColor(255, 157, 0) // 橙
156 << QColor(14, 81, 167) // 蓝
157 << QColor(228, 0, 69) // 红
158 << QColor(67, 148, 0) // 绿
159 ;
160}
161
163{
164 Q_Q(RibbonBar);
166 m_applicationButton->setObjectName(QStringLiteral("qx_RibbonApplicationButton"));
167 connect(m_applicationButton, &QAbstractButton::clicked, q, &RibbonBar::applicationButtonClicked);
168 m_tabBar = new RibbonTabBar(q);
169 m_tabBar->setObjectName(QStringLiteral("qx_RibbonTabBar"));
170 m_tabBar->setDrawBase(false);
171 connect(m_tabBar, &QTabBar::currentChanged, this, &RibbonBarPrivate::onCurrentRibbonTabChanged);
172 connect(m_tabBar, &QTabBar::tabBarClicked, this, &RibbonBarPrivate::onCurrentRibbonTabClicked);
173 connect(m_tabBar, &QTabBar::tabBarDoubleClicked, this, &RibbonBarPrivate::onCurrentRibbonTabDoubleClicked);
174 connect(m_tabBar, &QTabBar::tabMoved, this, &RibbonBarPrivate::onTabMoved);
175
176 m_tabBarBaseLine = new QWidget(q);
177 m_tabBarBaseLine->setObjectName(QStringLiteral("qx_RibbonTabBarBaseLine"));
178 m_tabBarBaseLine->setFixedHeight(tabBarBaseLineHeight);
179 m_tabBarBaseLine->setVisible(false);
180
182 m_tabBar->setObjectName(QStringLiteral("qx_RibbonStackedWidget"));
184 m_stack->installEventFilter(q);
185 setMinimizedFlag(false);
186
188 m_quickAccessBar->setObjectName(QStringLiteral("qx_RibbonQuickAccessBar"));
189 m_quickAccessBar->setIcon(q->windowIcon());
190}
191
193{
194 Q_Q(RibbonBar);
196 delete m_applicationButton;
197 }
198 if (btn) {
199 if (btn->parent() != q) {
200 btn->setParent(q);
201 }
202 btn->move(0, q->titleBarHeight());
203 connect(btn, &QAbstractButton::clicked, q, &RibbonBar::applicationButtonClicked);
204
205 if (btn->objectName().isEmpty()) {
206 btn->setObjectName(QStringLiteral("qx_RibbonApplicationButton"));
207 }
208 btn->setVisible(true);
209 }
211}
212
214{
215 for (int i = 0; i < m_currentShowingPageContextList.size(); ++i) {
216 if (m_currentShowingPageContextList[i] == pageContext) { // operator is overridden
217 return true;
218 }
219 }
220 return false;
221}
222
224{
225 Q_Q(RibbonBar);
226 if (m_minimized == flag) {
227 return;
228 }
229 m_minimized = flag;
230
231 if (flag) {
232 m_stack->setPopup(true);
233 m_stack->setFocusPolicy(Qt::NoFocus);
234 m_stack->clearFocus();
235 m_tabBar->setFocus();
236 m_stack->hide();
237 m_blockResize = true;
238 q->setFixedHeight(m_tabBar->geometry().bottom());
239 m_blockResize = false;
240 } else {
241 m_stack->setPopup(false);
242 m_stack->setFocus();
243 m_stack->show();
244 }
245}
246
248{
249 Q_Q(RibbonBar);
251 QAction *action = m_minimumPageButton->defaultAction();
252 if (action) {
253 action->setIcon(q->style()->standardIcon(m_minimized ? QStyle::SP_TitleBarUnshadeButton
254 : QStyle::SP_TitleBarShadeButton,
256 }
257 }
258}
259
272
274{
275 int tabcount = m_tabBar->count();
276
277 for (int i = 0; i < tabcount; ++i) {
278 QVariant var = m_tabBar->tabData(i);
279 if (var.isValid()) {
280 RibbonTabData data = var.value<RibbonTabData>();
281 data.index = i;
282 m_tabBar->setTabData(i, QVariant::fromValue(data));
283 }
284 }
285 // 刷新完tabdata信息也要接着刷新ContextPage信息
287}
288
293{
294 const int c = m_tabBar->count();
295
297 cd.tabPageIndex.clear();
298 for (int i = 0; i < cd.pageContext->pageCount(); ++i) {
299 RibbonPage *page = cd.pageContext->page(i);
300 for (int t = 0; t < c; ++t) {
301 QVariant v = m_tabBar->tabData(t);
302 if (v.isValid()) {
303 RibbonTabData data = v.value<RibbonTabData>();
304 if (data.page == page) {
305 cd.tabPageIndex.append(t);
306 }
307 } else {
308 cd.tabPageIndex.append(-1);
309 }
310 }
311 }
312 }
313}
314
316{
317 Q_Q(RibbonBar);
318 // 根据样式调整 RibbonPage 的布局形式
319 QList<RibbonPage *> pages = q->pages();
320 for (RibbonPage *page : std::as_const(pages)) {
322 }
323 // 阻止因高度变化而会触发resizeEvent,进而执行resizeXX
324 m_blockResize = true;
326 m_blockResize = false;
327}
328
330{
331 Q_Q(RibbonBar);
332 // 根据样式调整bar的高度
333 if (m_minimized) {
334 // 处于最小模式下时,bar 的高度为 tabbar 的 bottom
335 // 最小模式时,bar 的高度在 setMinimizedFlag 中已调整
336 // q->setFixedHeight(m_tabBar->geometry().bottom());
337 } else {
338 q->setFixedHeight(mainBarHeight());
339 }
340}
341
343{
344 Q_Q(RibbonBar);
346
348 p.save();
349 QList<PageContextManagerData> pageContextDataList = m_currentShowingPageContextList;
350
351 QPoint pageContextPos(q->width(), -1); // x表示左边界,y表示右边界
352 QMargins border = q->contentsMargins();
353 for (int i = 0; i < pageContextDataList.size(); ++i) {
354 QRect contextTitleRect;
355 QList<int> indexs = pageContextDataList[i].tabPageIndex;
356 QColor clr = pageContextDataList[i].pageContext->contextColor();
357 if (!indexs.isEmpty()) {
358 contextTitleRect = m_tabBar->tabRect(indexs.first());
359 QRect endRect = m_tabBar->tabRect(indexs.last());
360 contextTitleRect.setRight(endRect.right());
361 // contextTitleRect是相对于m_tabBar返回的矩形框,不是RibbonBar中的真实位置,所以需要移动到m_tabBar的起点坐标处
362 contextTitleRect.translate(m_tabBar->x(), m_tabBar->y());
363 contextTitleRect.setHeight(m_tabBar->height() - 1); // 减1像素,避免tabbar基线覆盖
364 contextTitleRect -= m_tabBar->tabMargin() / 2;
365 contextTitleRect.setTop(border.top()); // 把区域顶部扩展到窗口顶部
366 QRect tabBarRect(m_tabBar->pos(), m_tabBar->size());
367 if (tabBarRect.intersects(contextTitleRect)) {
368 QRect visibleRect = tabBarRect.intersected(contextTitleRect);
369 visibleRect.setTop(contextTitleRect.top());
370 paintPageContextTab(p, pageContextDataList[i].pageContext->contextTitle(), visibleRect, clr);
371 }
372 // 更新上下文标签的范围,用于控制标题栏的显示。如果pageContextPos.y()小于0,说明没有显示上下文标签
373 if (contextTitleRect.left() < pageContextPos.x()) {
374 pageContextPos.setX(contextTitleRect.left());
375 }
376 if (contextTitleRect.right() > pageContextPos.y()) {
377 pageContextPos.setY(contextTitleRect.right());
378 }
379 }
380#ifdef QX_DRAW_CONTEXT_PAGE_BORDER
381 if (indexs.contains(m_tabBar->currentIndex())) {
382 QPen pen;
383 pen.setColor(clr);
384 pen.setWidth(1);
385 p.setPen(pen);
386 p.setBrush(Qt::NoBrush);
387 p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
388 p.drawRect(m_stack->geometry());
389 }
390#endif
391 }
392 p.restore();
393
395 QWidget *parWindow = q->parentWidget();
396 if (parWindow && m_titleVisible) {
397 QRect titleRegion;
398 int x = border.right();
399 // |border|m_quickAccessBar|m_topLeftButtonGroup|~~~
400 if (m_topLeftButtonGroup && m_topLeftButtonGroup->isVisible()) {
401 x = m_topLeftButtonGroup->geometry().right() + 1;
402 } else if (m_quickAccessBar && m_quickAccessBar->isVisible()) {
403 x = m_quickAccessBar->geometry().right() + 1;
404 }
405 if (pageContextPos.y() < 0) {
406 // ~~~~|m_topRightButtonGroup|m_windowButtonGroup|m_iconRightBorderPosition|border|
407 int w = q->width() - m_iconRightBorderPosition - border.right() - x;
408 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
409 w -= m_windowButtonGroup->width();
410 }
411 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
412 w -= m_topRightButtonGroup->width();
413 }
414 titleRegion.setRect(x, border.top(), w, q->titleBarHeight());
415 } else {
416 // ~~~|~~~~~ ~~~~~~|PageContext|~~~~~ ~~~~|
417 int leftwidth = pageContextPos.x() - m_iconRightBorderPosition - x;
418 int rightwidth = q->width() - pageContextPos.y();
419 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
420 rightwidth -= m_windowButtonGroup->width();
421 }
422 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
423 rightwidth -= m_topRightButtonGroup->width();
424 }
425 if (rightwidth > leftwidth) {
426 titleRegion.setRect(pageContextPos.y(), border.top(), rightwidth, q->titleBarHeight());
427 } else {
428 titleRegion.setRect(m_iconRightBorderPosition + x, border.top(), leftwidth, q->titleBarHeight());
429 }
430 }
431#ifdef QX_RIBBON_DEBUG_HELP_DRAW
432 p.save();
433 p.setBrush(QColor(255, 0, 0, 120));
434 p.drawRect(titleRegion);
435 p.restore();
436#endif
437 paintWindowTitle(p, parWindow->windowTitle(), titleRegion);
438 }
439}
440
442{
443 Q_Q(RibbonBar);
445
447 p.save();
448 QList<PageContextManagerData> pageContextDataList = m_currentShowingPageContextList;
449
450 QMargins border = q->contentsMargins();
451 for (int i = 0; i < pageContextDataList.size(); ++i) {
452 QRect contextTitleRect;
453 QList<int> indexs = pageContextDataList[i].tabPageIndex;
454 QColor clr = pageContextDataList[i].pageContext->contextColor();
455 if (!indexs.isEmpty()) {
456 contextTitleRect = m_tabBar->tabRect(indexs.first());
457 QRect endRect = m_tabBar->tabRect(indexs.last());
458 contextTitleRect.setRight(endRect.right());
459 contextTitleRect.translate(m_tabBar->x(), m_tabBar->y());
460 contextTitleRect.setHeight(m_tabBar->height() - 1);
461 contextTitleRect -= m_tabBar->tabMargin() / 2;
462 contextTitleRect.setTop(border.top()); // 把区域顶部扩展到窗口顶部
463 QRect tabBarRect(m_tabBar->pos(), m_tabBar->size());
464 if (tabBarRect.intersects(contextTitleRect)) {
465 QRect visibleRect = tabBarRect.intersected(contextTitleRect);
466 paintPageContextTab(p, QString(), visibleRect, clr);
467 }
468 }
469#ifdef QX_DRAW_CONTEXT_PAGE_BORDER
470 if (indexs.contains(m_tabBar->currentIndex())) {
471 QPen pen;
472 pen.setColor(clr);
473 pen.setWidth(1);
474 p.setPen(pen);
475 p.setBrush(Qt::NoBrush);
476 p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
477 p.drawRect(m_stack->geometry());
478 }
479#endif
480 }
481 p.restore();
482
484 QWidget *parWindow = q->parentWidget();
485 if (parWindow && m_titleVisible) {
486 // |m_tabBar|~~~ ~~~|m_windowButtonGroup|
487 int start = m_tabBar->x() + m_tabBar->width();
488 int width = q->width() - start;
489 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
490 width -= m_windowButtonGroup->width();
491 }
492 // ~~~|m_quickAccessBar|bottomLeftBG|topRightBG|bottomRightBG|m_windowButtonGroup|
494 m_quickAccessBar->isVisible()) {
495 width = m_quickAccessBar->x() - start;
496 } else if (m_bottomLeftButtonGroup && m_bottomLeftButtonGroup->isVisible()) {
497 width = m_bottomLeftButtonGroup->x() - start;
498 } else if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
499 width = m_topRightButtonGroup->x() - start;
500 } else if (m_bottomRightButtonGroup && m_bottomRightButtonGroup->isVisible()) {
501 width = m_bottomRightButtonGroup->x() - start;
502 }
503 if (width > 20) {
504 QRect titleRegion(start, border.top(), width, q->titleBarHeight());
505#ifdef QX_RIBBON_DEBUG_HELP_DRAW
506 p.save();
507 p.setBrush(QColor(255, 0, 0, 120));
508 p.drawRect(titleRegion);
509 p.restore();
510#endif
511 paintWindowTitle(p, parWindow->windowTitle(), titleRegion);
512 }
513 }
514}
515
517{
518 Q_Q(RibbonBar);
519 // 在tabbar下绘制一条线
520 painter.save();
521 QPen pen(m_tabBarBaseLine->palette().window().color());
522 pen.setWidth(tabBarBaseLineHeight);
523 pen.setStyle(Qt::SolidLine);
524 painter.setPen(pen);
525 QMargins border = q->contentsMargins();
526 const int lineY = m_tabBar->geometry().bottom();
527 painter.drawLine(QPoint(border.left(), lineY), QPoint(q->width() - border.right() - 1, lineY));
528 painter.restore();
529}
530
531void RibbonBarPrivate::paintWindowTitle(QPainter &painter, const QString &title, const QRect &titleRegion)
532{
533 Q_Q(RibbonBar);
534 painter.save();
535 painter.setPen(q->palette().color(QPalette::WindowText));
536 painter.drawText(titleRegion, m_titleAligment, title);
537 painter.restore();
538}
539
540void RibbonBarPrivate::paintPageContextTab(QPainter &painter, const QString &title, QRect contextRect,
541 const QColor &color)
542{
543 Q_Q(RibbonBar);
544 // 首先有5像素的实体粗线位于顶部
545 QMargins border = q->contentsMargins();
546 painter.save();
547 painter.setPen(Qt::NoPen);
548 painter.setBrush(color);
549 painter.drawRect(QRect(contextRect.x(), border.top(), contextRect.width(), 5));
550
551 // 剩下把颜色变亮90%
552 QColor gColor = color.lighter(190);
553
554 // 减去之前的5像素
555 contextRect -= QMargins(0, 5, 0, 0);
557 // 设置底部位置,不覆盖 tab
558 contextRect.setBottom(m_tabBar->geometry().top());
559 }
560 painter.fillRect(contextRect, gColor);
561
562 // 只有在office模式下才需要绘制标题
563 if (q->isOfficeStyle()) {
564 if (!title.isEmpty()) {
565 contextRect.setBottom(m_tabBar->geometry().top());
566 painter.setPen(color);
567 painter.drawText(contextRect, Qt::AlignCenter, title);
568 }
569 }
570 painter.restore();
571
573 painter.save();
574 // 绘制 tab 边框
575 painter.setPen(gColor);
576 painter.setBrush(Qt::NoBrush);
577 QRect tabRect(contextRect.bottomLeft(), QPoint(contextRect.bottomRight().x(), m_tabBar->geometry().bottom()));
578 painter.drawLine(tabRect.topLeft(), tabRect.bottomLeft());
579 painter.drawLine(tabRect.topRight(), tabRect.bottomRight());
580 painter.restore();
581 }
582}
583
585{
586 Q_Q(RibbonBar);
588
589 QMargins border = q->contentsMargins();
590 int x = border.left();
591 int y = border.top();
592
593 const int validTitleBarHeight = q->titleBarHeight();
594 const int tabH = q->tabBarHeight();
595 const int otherH = tabH - tabBarBaseLineHeight;
596
597 // row-1
599 // |cornerL|m_quickAccessBar|m_topLeftButtonGroup|~~~~~
600 QWidget *cornerL = q->cornerWidget(Qt::TopLeftCorner);
601 if (cornerL && cornerL->isVisible()) {
602 QSize cornerSize = cornerL->sizeHint();
603 if (cornerSize.height() < validTitleBarHeight) {
604 int detal = (validTitleBarHeight - cornerSize.height()) / 2;
605 cornerL->setGeometry(x, y + detal, cornerSize.width(), cornerSize.height());
606 } else {
607 cornerL->setGeometry(x, y, cornerSize.width(), validTitleBarHeight);
608 }
609 x = cornerL->geometry().right() + 5;
610 }
611 if (m_quickAccessBar && m_quickAccessBar->isVisible()) {
612 QSize quickAccessBarSize = m_quickAccessBar->sizeHint();
613 m_quickAccessBar->setGeometry(x, y, quickAccessBarSize.width(), validTitleBarHeight);
614 x += quickAccessBarSize.width() + 5;
615 }
616 if (m_topLeftButtonGroup && m_topLeftButtonGroup->isVisible()) {
617 QSize wSize = m_topLeftButtonGroup->sizeHint();
618 m_topLeftButtonGroup->setGeometry(x, y, wSize.width(), validTitleBarHeight);
619 }
620 // ~~~~~~~~|m_topRightButtonGroup|m_windowButtonGroup|
621 int endX = q->width() - border.right();
622 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
623 QSize wSize = m_windowButtonGroup->sizeHint();
624 endX -= wSize.width();
625 m_windowButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
626 }
627 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
628 QSize wSize = m_topRightButtonGroup->sizeHint();
629 endX -= wSize.width();
630 m_topRightButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
631 }
632
633 // row-2
634 x = border.left();
635 y += validTitleBarHeight;
636 // |m_applicationButton|~~~~~ m_tabBar ~~~~~|m_bottomLeftButtonGroup|m_bottomRightButtonGroup|cornerW|
637 if (m_applicationButton && m_applicationButton->isVisible()) {
638 m_applicationButton->setGeometry(x, y, m_applicationButton->size().width(), otherH);
639 x = m_applicationButton->geometry().right();
640 }
641 endX = q->width() - border.right();
642 QWidget *cornerW = q->cornerWidget(Qt::TopRightCorner);
643 if (cornerW && cornerW->isVisible()) {
644 QSize cornerSize = cornerW->sizeHint();
645 endX -= cornerSize.width();
646 if (cornerSize.height() < tabH) {
647 int detal = (tabH - cornerSize.height()) / 2;
648 cornerW->setGeometry(endX, y + detal, cornerSize.width(), cornerSize.height());
649 } else {
650 cornerW->setGeometry(endX, y, cornerSize.width(), otherH);
651 }
652 }
654 QSize wSize = m_bottomRightButtonGroup->sizeHint();
655 endX -= wSize.width();
656 m_bottomRightButtonGroup->setGeometry(endX, y, wSize.width(), otherH);
657 }
659 QSize wSize = m_bottomLeftButtonGroup->sizeHint();
660 endX -= wSize.width();
661 m_bottomLeftButtonGroup->setGeometry(endX, y, wSize.width(), otherH);
662 }
663 int allowedTabBarWidth = endX - x;
664 int minTabBarWidth = calcMinTabBarWidth();
666 m_tabBar->setGeometry(x, y, qMin(minTabBarWidth, allowedTabBarWidth), tabH);
667 } else { // RibbonBar::TBCenterPosition
668 if (minTabBarWidth < allowedTabBarWidth) {
669 // |~~~~~~~~~~~~~~~~~~ allowedTabBarWidth ~~~~~~~~~~~~~~|
670 // |~~ xOffset ~~|~~~~ mintabBarWidth ~~~~|~~ xOffset ~~|
671 int xOffset = (allowedTabBarWidth - minTabBarWidth) / 2;
672 m_tabBar->setGeometry(x + xOffset, y, minTabBarWidth, tabH);
673 } else {
674 m_tabBar->setGeometry(x, y, allowedTabBarWidth, tabH);
675 }
676 }
677
679}
680
682{
683 Q_Q(RibbonBar);
685
686 QMargins border = q->contentsMargins();
687 int x = border.left();
688 int y = border.top();
689
690 const int validTitleBarHeight = q->titleBarHeight();
691
692 // ~~~~|m_bottomLeftButtonGroup|m_topRightButtonGroup|m_bottomRightButtonGroup|cornerR|m_windowButtonGroup|
693 int endX = q->width() - border.right();
694 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
695 QSize wSize = m_windowButtonGroup->sizeHint();
696 endX -= wSize.width();
697 m_windowButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
698 }
699 QWidget *cornerR = q->cornerWidget(Qt::TopRightCorner);
700 if (cornerR && cornerR->isVisible()) {
701 QSize cornerSize = cornerR->sizeHint();
702 endX -= cornerSize.width();
703 if (cornerSize.height() < validTitleBarHeight) {
704 int detal = (validTitleBarHeight - cornerSize.height()) / 2;
705 cornerR->setGeometry(endX, y + detal, cornerSize.width(), cornerSize.height());
706 } else {
707 cornerR->setGeometry(endX, y, cornerSize.width(), validTitleBarHeight);
708 }
709 }
711 QSize wSize = m_bottomRightButtonGroup->sizeHint();
712 endX -= wSize.width();
713 m_bottomRightButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
714 }
715 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
716 QSize wSize = m_topRightButtonGroup->sizeHint();
717 endX -= wSize.width();
718 m_topRightButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
719 }
721 QSize wSize = m_bottomLeftButtonGroup->sizeHint();
722 endX -= wSize.width();
723 m_bottomLeftButtonGroup->setGeometry(endX, y, wSize.width(), validTitleBarHeight);
724 }
725 // ~~~~|m_quickAccessBar|m_bottomLeftButtonGroup|~~~~~
727 QSize quickAccessBarSize = m_quickAccessBar->sizeHint();
728 endX -= quickAccessBarSize.width();
729 m_quickAccessBar->setGeometry(endX, y, quickAccessBarSize.width(), validTitleBarHeight);
730 }
731 // ~~~~|cornerL|<m_quickAccessBar>|m_bottomLeftButtonGroup|~~~~~
732 QWidget *cornerL = q->cornerWidget(Qt::TopLeftCorner);
733 if (cornerL && cornerL->isVisible()) {
734 QSize cornerSize = cornerL->sizeHint();
735 endX -= cornerSize.width();
736 if (cornerSize.height() < validTitleBarHeight) {
737 int detal = (validTitleBarHeight - cornerSize.height()) / 2;
738 cornerL->setGeometry(endX, y + detal, cornerSize.width(), cornerSize.height());
739 } else {
740 cornerL->setGeometry(endX, y, cornerSize.width(), validTitleBarHeight);
741 }
742 }
743
744 // tab 的y值需要重新计算
745 int tabH = q->tabBarHeight();
746 if (tabH > validTitleBarHeight) {
747 // 这种直接把tabH设置为validTitleBarHeight
748 tabH = validTitleBarHeight;
749 }
750 // 如果tabH较小,则下移,让tab底部和title的底部对齐
751 y += validTitleBarHeight - tabH;
752
753 // |m_applicationButton|<m_quickAccessBar>|m_topLeftButtonGroup|m_tabBar|~~~~~~
754 if (m_applicationButton && m_applicationButton->isVisible()) {
755 m_applicationButton->setGeometry(x, y, m_applicationButton->size().width(), tabH);
756 x = m_applicationButton->geometry().right() + 2;
757 }
759 QSize quickAccessBarSize = m_quickAccessBar->sizeHint();
760 m_quickAccessBar->setGeometry(x, border.top(), quickAccessBarSize.width(), validTitleBarHeight);
761 x = m_quickAccessBar->geometry().right() + 2;
762 }
763 if (m_topLeftButtonGroup && m_topLeftButtonGroup->isVisible()) {
764 QSize wSize = m_topLeftButtonGroup->sizeHint();
765 m_topLeftButtonGroup->setGeometry(x, y, wSize.width(), tabH);
766 x = m_topLeftButtonGroup->geometry().right() + 2;
767 }
768 int allowedTabBarWidth = endX - x - dragAreaMinimumWidth;
769 // 20200831
770 // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整
771 // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出
772 // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow
773 int minTabBarWidth = calcMinTabBarWidth();
775 m_tabBar->setGeometry(x, y, qMin(minTabBarWidth, allowedTabBarWidth), tabH);
776 } else { // RibbonBar::TBCenterPosition
777 if (minTabBarWidth < allowedTabBarWidth) {
778 // |~~~~~~~~~~~~~~~~~~ allowedTabBarWidth ~~~~~~~~~~~~~~|
779 // |~~ xOffset ~~|~~~~ mintabBarWidth ~~~~|~~ xOffset ~~|
780 int xOffset = (allowedTabBarWidth - minTabBarWidth) / 2;
781 m_tabBar->setGeometry(x + xOffset, y, minTabBarWidth, tabH);
782 } else {
783 m_tabBar->setGeometry(x, y, allowedTabBarWidth, tabH);
784 }
785 }
786
788}
789
791{
792 Q_Q(RibbonBar);
793 QMargins border = q->contentsMargins();
794 if (m_stack->isPopup()) {
795 QPoint absPosition = q->mapToGlobal(QPoint(border.left(), m_tabBar->geometry().bottom() + 1));
796 m_stack->setGeometry(absPosition.x(), absPosition.y(), q->width() - border.left() - border.right(),
797 mainBarHeight() - m_tabBar->geometry().bottom() - border.bottom() - 1);
798 } else {
799 m_stack->setGeometry(border.left(), m_tabBar->geometry().bottom() + 1,
800 q->width() - border.left() - border.right(),
801 mainBarHeight() - m_tabBar->geometry().bottom() - border.bottom() - 1);
802 }
803}
804
806{
807 if (isOfficeStyle()) {
809 } else {
811 }
812}
813
815{
816 Q_Q(const RibbonBar);
817 QMargins border = q->contentsMargins();
818
819 int w1 = 0;
820 int w2 = 0;
821
822 // row-1
824 // |cornerL|m_quickAccessBar|m_topLeftButtonGroup|~~~~~~~~~|m_topRightButtonGroup|m_windowButtonGroup|
825 QWidget *cornerL = q->cornerWidget(Qt::TopLeftCorner);
826 if (cornerL && cornerL->isVisible()) {
827 w1 += cornerL->sizeHint().width() + 5;
828 }
829 if (m_quickAccessBar && m_quickAccessBar->isVisible()) {
830 w1 += m_quickAccessBar->sizeHint().width() + 5;
831 }
832 if (m_topLeftButtonGroup && m_topLeftButtonGroup->isVisible()) {
833 w1 += m_topLeftButtonGroup->sizeHint().width();
834 }
835 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
836 w1 += m_windowButtonGroup->sizeHint().width();
837 }
838 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
839 w1 += m_topRightButtonGroup->sizeHint().width();
840 }
842
843 // row-2
844 // |m_applicationButton|~~~~~ m_tabBar ~~~~~|m_bottomLeftButtonGroup|m_bottomRightButtonGroup|cornerW|
845 if (m_applicationButton && m_applicationButton->isVisible()) {
846 w2 += m_applicationButton->size().width();
847 }
848 QWidget *cornerW = q->cornerWidget(Qt::TopRightCorner);
849 if (cornerW && cornerW->isVisible()) {
850 w2 += cornerW->sizeHint().width();
851 }
853 w2 += m_bottomRightButtonGroup->sizeHint().width();
854 }
856 w2 += m_bottomLeftButtonGroup->sizeHint().width();
857 }
858 w2 += tabBarMinimumWidth;
859
860 return qMax(w1, w2) + border.left() + border.right();
861}
862
864{
865 Q_Q(const RibbonBar);
866 QMargins border = q->contentsMargins();
867
868 int w = 0;
869
870 // ~~~~|m_bottomLeftButtonGroup|m_topRightButtonGroup|m_bottomRightButtonGroup|cornerR|m_windowButtonGroup|
871 if (m_windowButtonGroup && m_windowButtonGroup->isVisible()) {
872 w += m_windowButtonGroup->sizeHint().width();
873 }
874 QWidget *cornerR = q->cornerWidget(Qt::TopRightCorner);
875 if (cornerR && cornerR->isVisible()) {
876 w += cornerR->sizeHint().width();
877 }
879 w += m_bottomRightButtonGroup->sizeHint().width();
880 }
881 if (m_topRightButtonGroup && m_topRightButtonGroup->isVisible()) {
882 w += m_topRightButtonGroup->sizeHint().width();
883 }
885 w += m_bottomLeftButtonGroup->sizeHint().width();
886 }
887 // ~~~~|m_quickAccessBar|m_bottomLeftButtonGroup|~~~~~
888 if (m_quickAccessBar && m_quickAccessBar->isVisible()) {
889 w += m_quickAccessBar->sizeHint().width();
890 }
891 // ~~~~|cornerL|<m_quickAccessBar>|m_bottomLeftButtonGroup|~~~~~
892 QWidget *cornerL = q->cornerWidget(Qt::TopLeftCorner);
893 if (cornerL && cornerL->isVisible()) {
894 w += cornerL->sizeHint().width();
895 }
896 // |m_applicationButton|<m_quickAccessBar>|m_topLeftButtonGroup|m_tabBar|~~~~~~
897 if (m_applicationButton && m_applicationButton->isVisible()) {
898 w += m_applicationButton->size().width();
899 }
900 if (m_topLeftButtonGroup && m_topLeftButtonGroup->isVisible()) {
901 w += m_topLeftButtonGroup->sizeHint().width();
902 }
904
905 return w + tabBarMinimumWidth + border.left() + border.right();
906}
907
912{
913 const QMargins &mg = m_tabBar->tabMargin();
914 int mintabBarWidth = 0;
915
916 int cnt = m_tabBar->count();
917 ++cnt; // add sentry tab
918
919 for (int i = 0; i < cnt; ++i) {
920 mintabBarWidth += m_tabBar->tabRect(i).width();
921 }
922 mintabBarWidth += (mg.left() + mg.right());
923 return mintabBarWidth;
924}
925
933{
934 return RibbonElementStyleOpt.ribbonBarHeight(m_ribbonStyle);
935}
936
945{
946 if (page == Q_NULLPTR) {
947 return -1;
948 }
949 const int size = m_tabBar->count();
950
951 for (int i = 0; i < size; ++i) {
952 QVariant v = m_tabBar->tabData(i);
953 if (v.isValid()) {
954 RibbonTabData innerValue = v.value<RibbonTabData>();
955 if (innerValue.page == page) {
956 return i;
957 }
958 }
959 }
960 return -1;
961}
962
964{
965 Q_Q(RibbonBar);
966 RibbonButtonGroup *buttonGroup = new RibbonButtonGroup(q);
967 buttonGroup->setObjectName(QStringLiteral("qx_RibbonButtonGroup"));
968 buttonGroup->setFrameShape(QFrame::NoFrame);
969 return buttonGroup;
970}
971
973{
974 Q_Q(RibbonBar);
975 Q_UNUSED(title);
976 q->update();
977}
978
980{
981 if (m_quickAccessBar) {
983 }
984}
985
987{
988 RibbonPage *w = qobject_cast<RibbonPage *>(sender());
989
990 if (Q_NULLPTR == w) {
991 return;
992 }
993 for (int i = 0; i < m_tabBar->count(); ++i) {
994 // 鉴于tab不会很多,不考虑效率问题
995 QVariant var = m_tabBar->tabData(i);
996 if (var.isValid()) {
997 RibbonTabData data = var.value<RibbonTabData>();
998 if (w == data.page) {
999 m_tabBar->setTabText(i, title);
1000 }
1001 }
1002 }
1003}
1004
1006{
1007 Q_ASSERT_X(page != Q_NULLPTR, "onPageContextAdded", "add Q_NULLPTR page");
1008 m_stack->addWidget(page); // 这里stackedWidget用append,其他地方都应该使用insert
1009}
1010
1012{
1013 Q_Q(RibbonBar);
1014 QVariant var = m_tabBar->tabData(index);
1015 RibbonPage *page = Q_NULLPTR;
1016
1017 if (var.isValid()) {
1018 RibbonTabData data = var.value<RibbonTabData>();
1019 page = data.page;
1020 }
1021 if (page) {
1022 if (m_stack->currentWidget() != page) {
1023 m_stack->setCurrentWidget(page);
1024 }
1025 if (m_minimized) {
1026 m_tabBar->clearFocus();
1027 if (!m_stack->isVisible() && m_stack->isPopup()) {
1028 // 在StackedWindget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
1029#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
1030 QHoverEvent ehl(QEvent::HoverLeave, m_tabBar->mapToGlobal(QCursor::pos()),
1031 m_tabBar->mapToGlobal(QCursor::pos()));
1032#else
1033 QHoverEvent ehl(QEvent::HoverLeave, m_tabBar->mapToParent(QCursor::pos()),
1034 m_tabBar->mapToGlobal(QCursor::pos()), m_tabBar->mapToGlobal(QCursor::pos()));
1035#endif
1036 QApplication::sendEvent(m_tabBar, &ehl);
1038 m_stack->setFocus();
1039 m_stack->show();
1040 // 在最小模式下,每次显示完StackedWindget后把tab的
1041 // 的index设置为-1,这样每次点击都会触发onCurrentRibbonTabChanged
1042 }
1043 }
1044 }
1045 Q_EMIT q->currentRibbonTabChanged(index);
1046}
1047
1057{
1058 if (index != m_tabBar->currentIndex()) {
1059 // 点击的标签不一致通过changed槽去处理
1060 return;
1061 }
1062 if (index == -1) {
1063 return;
1064 }
1065 if (index == m_tabBar->count()) {
1066 return; // sentry tab
1067 }
1068 if (m_minimized) {
1069 if (!m_stack->isVisible() && m_stack->isPopup()) {
1070 // 在StackedWindget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
1071#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
1072 QHoverEvent ehl(QEvent::HoverLeave, m_tabBar->mapToGlobal(QCursor::pos()),
1073 m_tabBar->mapToGlobal(QCursor::pos()));
1074#else
1075 QHoverEvent ehl(QEvent::HoverLeave, m_tabBar->mapToParent(QCursor::pos()),
1076 m_tabBar->mapToGlobal(QCursor::pos()), m_tabBar->mapToGlobal(QCursor::pos()));
1077#endif
1078 QApplication::sendEvent(m_tabBar, &ehl);
1079 // 弹出前都调整一下位置,避免移动后位置异常
1081 m_stack->setFocus();
1082 m_stack->show();
1083 }
1084 }
1085}
1086
1088{
1089 Q_Q(RibbonBar);
1090 if (index == -1) {
1091 return;
1092 }
1093 if (index == m_tabBar->count()) {
1094 return; // sentry tab
1095 }
1096 q->setMinimized(!m_minimized);
1097}
1098
1099void RibbonBarPrivate::onTabMoved(int from, int to)
1100{
1101 m_stack->moveWidget(from, to);
1102}
1103
1108
1113
1114/* RibbonBar */
1115RibbonBar::RibbonBar(QWidget *parent)
1116 : QMenuBar(parent)
1117{
1119 Q_D(RibbonBar);
1120 d->init();
1121 if (parent) {
1122 connect(parent, &QWidget::windowTitleChanged, d, &RibbonBarPrivate::onWindowTitleChanged);
1123 connect(parent, &QWidget::windowIconChanged, d, &RibbonBarPrivate::onWindowIconChanged);
1124 }
1126}
1127
1132
1134{
1135 Q_D(RibbonBar);
1136 return d->m_applicationButton;
1137}
1138
1148void RibbonBar::setApplicationButton(QAbstractButton *btn)
1149{
1150 Q_D(RibbonBar);
1151 d->setApplicationButton(btn);
1152 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1153}
1154
1155RibbonPage *RibbonBar::addPage(const QString &title)
1156{
1157 return insertPage(-1, title);
1158}
1159
1161{
1162 insertPage(-1, page);
1163}
1164
1165RibbonPage *RibbonBar::insertPage(int index, const QString &title)
1166{
1167 RibbonPage *page = new RibbonPage(this);
1168 page->setObjectName(title);
1169 page->setPageName(title);
1170 insertPage(index, page);
1171 return page;
1172}
1173
1174void RibbonBar::insertPage(int index, RibbonPage *page)
1175{
1176 Q_D(RibbonBar);
1177 if (Q_NULLPTR == page) {
1178 return;
1179 }
1181 page->setRibbonBar(this);
1182
1183 int i = d->m_tabBar->insertTab(index, page->windowTitle());
1184 RibbonTabData tabdata;
1185 tabdata.page = page;
1186 tabdata.index = i;
1187 d->m_tabBar->setTabData(i, QVariant::fromValue(tabdata));
1188 if (index != -1) {
1189 d->updateTabData();
1190 }
1191
1192 d->m_stack->insertWidget(i, page);
1193
1194 connect(page, &QWidget::windowTitleChanged, d, &RibbonBarPrivate::onPageWindowTitleChanged);
1195 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1196}
1197
1209{
1210 Q_D(const RibbonBar);
1211 QVariant var = d->m_tabBar->tabData(index);
1212
1213 if (var.isValid()) {
1214 RibbonTabData data = var.value<RibbonTabData>();
1215 return data.page;
1216 }
1217 return Q_NULLPTR;
1218}
1219
1227RibbonPage *RibbonBar::pageByName(const QString &title) const
1228{
1229 Q_D(const RibbonBar);
1230 int c = d->m_stack->count();
1231
1232 for (int i = 0; i < c; ++i) {
1233 RibbonPage *w = qobject_cast<RibbonPage *>(d->m_stack->widget(i));
1234 if (w && w->windowTitle() == title) {
1235 return w;
1236 }
1237 }
1238 return Q_NULLPTR;
1239}
1240
1247RibbonPage *RibbonBar::pageByObjectName(const QString &objname) const
1248{
1249 Q_D(const RibbonBar);
1250 int c = d->m_stack->count();
1251
1252 for (int i = 0; i < c; ++i) {
1253 RibbonPage *w = qobject_cast<RibbonPage *>(d->m_stack->widget(i));
1254 if (w && w->objectName() == objname) {
1255 return (w);
1256 }
1257 }
1258 return Q_NULLPTR;
1259}
1260
1266QList<RibbonPage *> RibbonBar::pages(bool getAll) const
1267{
1268 Q_D(const RibbonBar);
1269 int c = d->m_stack->count();
1270 QList<RibbonPage *> res;
1271
1272 for (int i = 0; i < c; ++i) {
1273 RibbonPage *w = qobject_cast<RibbonPage *>(d->m_stack->widget(i));
1274 if (w) {
1275 if (!getAll && w->isPageContext()) {
1276 // 不是getall且是上下文时跳过
1277 continue;
1278 }
1279 res.append(w);
1280 }
1281 }
1282 return res;
1283}
1284
1291{
1292 int tabindex = pageIndex(page);
1293
1294 return (tabindex >= 0);
1295}
1296
1302{
1303 Q_D(RibbonBar);
1304 for (auto i = d->m_hidedPage.begin(); i != d->m_hidedPage.end(); ++i) {
1305 if (i->page == page) { // 说明要显示
1306 int index = d->m_tabBar->insertTab(i->index, i->page->windowTitle());
1307 i->index = index;
1308 d->m_tabBar->setTabData(index, QVariant::fromValue(*i));
1309 d->m_hidedPage.erase(i); // 移除
1310 d->updateTabData(); // 更新index信息
1311 break;
1312 }
1313 }
1314 raisePage(page);
1315}
1316
1322{
1323 Q_D(RibbonBar);
1324 int tabcount = d->m_tabBar->count();
1325
1326 for (int i = 0; i < tabcount; ++i) {
1327 QVariant var = d->m_tabBar->tabData(i);
1328 if (var.isValid()) {
1329 RibbonTabData p = var.value<RibbonTabData>();
1330 if (p.page == page) {
1331 d->m_hidedPage.append(p);
1332 d->m_tabBar->removeTab(i); // 仅仅把tab移除
1333 // 注意Page隐藏后,contex的位置就会发生变化,需要更新
1334 d->updateTabData();
1335 return;
1336 }
1337 }
1338 }
1339}
1340
1346{
1347 Q_D(RibbonBar);
1348 int index = d->m_stack->indexOf(page);
1349
1350 if (index >= 0) {
1351 setCurrentIndex(index);
1352 }
1353}
1354
1363{
1364 Q_D(RibbonBar);
1365 int index = d->tabIndex(page);
1366 bool isupdate = false;
1367 if (index >= 0) {
1368 d->m_tabBar->removeTab(index);
1369 isupdate = true;
1370 }
1371 d->m_stack->removeWidget(page);
1372 // 同时验证这个page是否是contexpage里的
1373 for (RibbonPageContext *c : std::as_const(d->m_pageContextList)) {
1374 c->takePage(page);
1375 }
1376 // 这时要刷新所有tabdata的index信息
1377 if (isupdate) {
1378 d->updateTabData();
1379 }
1380 // 移除完后需要重绘
1381 repaint();
1382 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1383}
1384
1386{
1387 for (RibbonPage *page : pages(false)) {
1389 delete page;
1390 }
1391}
1392
1393void RibbonBar::movePage(int from, int to)
1394{
1395 Q_D(RibbonBar);
1396 d->m_tabBar->moveTab(from, to);
1397 // 这时要刷新所有tabdata的index信息
1398 d->updateTabData();
1399 // 这里会触发tabMoved信号,在tabMoved信号中调整stacked里窗口的位置
1400}
1401
1403{
1404 Q_D(const RibbonBar);
1405 return d->tabIndex(page);
1406}
1407
1422RibbonPageContext *RibbonBar::addPageContext(const QString &title, const QColor &color)
1423{
1424 Q_D(RibbonBar);
1425 RibbonPageContext *context = new RibbonPageContext(this);
1426
1427 context->setObjectName(title);
1428 context->setContextTitle(title);
1429 context->setContextColor(color.isValid() ? color : d->getPageContextColor());
1430 addPageContext(context);
1431 return context;
1432}
1433
1435{
1436 Q_D(RibbonBar);
1437 if (Q_NULLPTR == context) {
1438 return;
1439 }
1441 // remove并没有绑定,主要是remove后在stacked里也不会显示,remove且delete的话,stacked里也会删除
1443 d->resizeInWpsLiteStyle();
1444 }
1445 d->m_pageContextList.append(context);
1446}
1447
1449{
1450 Q_D(RibbonBar);
1451 if (isPageContextVisible(context)) {
1452 return;
1453 }
1454 PageContextManagerData pageContextData;
1455
1456 pageContextData.pageContext = context;
1457 int cnt = context->pageCount();
1458 for (int i = 0; i < context->pageCount(); ++i) {
1459 RibbonPage *page = context->page(i);
1460 // 此句如果模式重复设置不会进行多余操作
1462 // 切换模式后会改变高度,上下文标签显示时要保证显示出来
1463 // page->setFixedHeight(pageHeight());
1464 int index = d->m_tabBar->addTab(page->windowTitle());
1465 pageContextData.tabPageIndex.append(index);
1466
1467 RibbonTabData tabdata;
1468 tabdata.page = page;
1469 tabdata.index = index;
1470 d->m_tabBar->setTabData(index, QVariant::fromValue(tabdata));
1471 if (cnt > 1) {
1472 // 如果连续添加多页,每添加一页就立即更新一下尺寸,如果依赖最后的postEvent会短暂出现导航箭头
1473 QRect r = d->m_tabBar->geometry();
1474 r.setWidth(d->calcMinTabBarWidth());
1475 d->m_tabBar->setGeometry(r);
1476 }
1477 }
1478 d->m_currentShowingPageContextList.append(pageContextData);
1479 // 由于上下文都是在最后追加,不需要调用updateTabData();
1480 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1481}
1482
1484{
1485 Q_D(RibbonBar);
1486 bool needResize = false;
1487
1488 for (int i = 0; i < d->m_currentShowingPageContextList.size(); ++i) {
1489 if (d->m_currentShowingPageContextList[i].pageContext == context) {
1490 const QList<int> &indexs = d->m_currentShowingPageContextList[i].tabPageIndex;
1491 for (int j = indexs.size() - 1; j >= 0; --j) {
1492 d->m_tabBar->removeTab(indexs[j]);
1493 }
1494 // 注意,删除ContextPage后,tab的序号就会改变,这时,这个tab后面的都要调整它的序号
1495 needResize = true;
1496 d->m_currentShowingPageContextList.removeAt(i);
1497 break;
1498 }
1499 }
1500 if (needResize) {
1501 d->updateTabData();
1502 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1503 }
1504}
1505
1507{
1508 Q_D(RibbonBar);
1509 return d->isPageContextVisible(context);
1510}
1511
1513{
1514 if (Q_NULLPTR == context) {
1515 return;
1516 }
1517 if (visible) {
1518 showPageContext(context);
1519 } else {
1520 hidePageContext(context);
1521 }
1522}
1523
1525{
1526 Q_D(RibbonBar);
1527 return d->m_pageContextCoverTab;
1528}
1529
1531{
1532 Q_D(RibbonBar);
1533 d->m_pageContextCoverTab = cover;
1534}
1535
1536QList<RibbonPageContext *> RibbonBar::pageContextList() const
1537{
1538 Q_D(const RibbonBar);
1539 return d->m_pageContextList;
1540}
1541
1547{
1548 Q_D(RibbonBar);
1549 if (Q_NULLPTR == context) {
1550 return;
1551 }
1553 if (isPageContextVisible(context)) {
1554 hidePageContext(context);
1555 }
1557 d->m_pageContextList.removeAll(context);
1558
1559 QList<RibbonPage *> res = context->pageList();
1560 for (RibbonPage *page : std::as_const(res)) {
1561 page->hide();
1562 page->deleteLater();
1563 }
1564 context->deleteLater();
1565 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1566}
1567
1569{
1570 setMinimized(true);
1571}
1572
1574{
1575 Q_D(const RibbonBar);
1576 return d->m_minimized;
1577}
1578
1580{
1581 Q_D(RibbonBar);
1582 d->setMinimizedFlag(flag);
1583 d->updateMinimumButtonIcon();
1584 QResizeEvent resizeEvent(size(), size());
1585 QApplication::sendEvent(this, &resizeEvent);
1586 Q_EMIT minimizationChanged(flag);
1587}
1588
1590{
1591 Q_D(const RibbonBar);
1592 return (Q_NULLPTR != d->m_minimumPageButton);
1593}
1594
1596{
1597 Q_D(RibbonBar);
1598 if (isShow) {
1599 cornerButtonGroup(Qt::BottomRightCorner); // create m_bottomRightButtonGroup
1600 if (Q_NULLPTR == d->m_minimumPageButton) {
1601 d->m_minimumPageButton = RibbonElementFactory->createHideGroupButton(this);
1602 d->m_minimumPageButton->ensurePolished(); // 载入样式图标
1603 QAction *action = new QAction(d->m_minimumPageButton);
1604 connect(action, &QAction::triggered, this, [=]() {
1605 this->setMinimized(!isMinimized());
1606 });
1607 d->m_minimumPageButton->setDefaultAction(action);
1608 d->m_bottomRightButtonGroup->addWidget(d->m_minimumPageButton);
1609 update();
1610 }
1611 d->updateMinimumButtonIcon();
1612 } else {
1613 if (Q_NULLPTR != d->m_minimumPageButton) {
1614 d->m_minimumPageButton->hide();
1615 d->m_minimumPageButton->deleteLater();
1616 d->m_minimumPageButton = Q_NULLPTR;
1617 }
1618 }
1619 QResizeEvent resizeEvent(size(), size());
1620 QApplication::sendEvent(this, &resizeEvent);
1621}
1622
1624{
1625 return RibbonElementStyleOpt.tabBarHeight();
1626}
1627
1629{
1630 return RibbonElementStyleOpt.titleBarHeight();
1631}
1632
1634{
1635 Q_D(RibbonBar);
1636 RibbonButtonGroup *buttonGroup = Q_NULLPTR;
1637
1638 switch (corner) {
1639 case Qt::TopLeftCorner:
1640 if (Q_NULLPTR == d->m_topLeftButtonGroup) {
1641 d->m_topLeftButtonGroup = d->createButtonGroup();
1642 }
1643 buttonGroup = d->m_topLeftButtonGroup;
1644 break;
1645 case Qt::TopRightCorner:
1646 if (Q_NULLPTR == d->m_topRightButtonGroup) {
1647 d->m_topRightButtonGroup = d->createButtonGroup();
1648 }
1649 buttonGroup = d->m_topRightButtonGroup;
1650 break;
1651 case Qt::BottomLeftCorner:
1652 if (Q_NULLPTR == d->m_bottomLeftButtonGroup) {
1653 d->m_bottomLeftButtonGroup = d->createButtonGroup();
1654 }
1655 buttonGroup = d->m_bottomLeftButtonGroup;
1656 break;
1657 case Qt::BottomRightCorner:
1658 if (Q_NULLPTR == d->m_bottomRightButtonGroup) {
1659 d->m_bottomRightButtonGroup = d->createButtonGroup();
1660 }
1661 buttonGroup = d->m_bottomRightButtonGroup;
1662 break;
1663 default:
1664 break;
1665 }
1666 if (buttonGroup) {
1667 buttonGroup->show();
1668 }
1669 return buttonGroup;
1670}
1671
1673{
1674 Q_D(const RibbonBar);
1675 return d->m_quickAccessBar->quickAccessBar();
1676}
1677
1679{
1680 Q_D(RibbonBar);
1681 if (d->m_quickAccessBarPosition == position) {
1682 return;
1683 }
1684 d->m_quickAccessBarPosition = position;
1685 if (!d->isOfficeStyle()) {
1686 resizeRibbon();
1687 }
1688}
1689
1691{
1692 Q_D(const RibbonBar);
1693 return d->m_quickAccessBarPosition;
1694}
1695
1697{
1698 Q_D(RibbonBar);
1699 if (d->m_tabBarPosition == position) {
1700 return;
1701 }
1702 d->m_tabBarPosition = position;
1703 resizeRibbon();
1704}
1705
1707{
1708 Q_D(const RibbonBar);
1709 return d->m_tabBarPosition;
1710}
1711
1713{
1714 Q_D(const RibbonBar);
1715 return d->m_ribbonStyle;
1716}
1717
1725{
1726 Q_D(RibbonBar);
1727 d->m_ribbonStyle = v;
1728 d->m_lastShowStyle = v;
1729 d->m_quickAccessBar->setIconVisible(isOfficeStyle() && d->m_titleVisible);
1730 d->updateRibbonElementGeometry();
1731
1732 /*
1733 QSize oldSize = size();
1734 QSize newSize(oldSize.width(), d->mainBarHeight());
1735 QResizeEvent es(newSize, oldSize);
1736 QApplication::sendEvent(this, &es);
1737 */
1738 // sendEvent会导致RibbonQuickAccessBar在样式切换后无法更新尺寸,改为用postEvent
1739 // resizeRibbon();
1740
1741 // FIXME: 使用 resizeRibbon 中的 postEvent,会存在延迟。当采用 WPS 样式且处于最小模式时,从 WPS 样式切换到
1742 // OFFICE 样式,会出现 m_tabBar 的尺寸未及时更新就设置了 setFixedHeight(d->m_tabBar->geometry().bottom())
1743 // 从而导致高度仍为 WPS 样式时的高度,切换到 OFFICE 样式后,m_tabBar 所在的水平区域内容无法正常显示
1744 // WORKAROUND: 此处直接调用 d->resizeRibbon,保证立即更新 m_tabBar 尺寸
1745 d->resizeRibbon();
1746 update();
1747
1748 if (isMinimized()) {
1749 // 处于最小模式下时,bar的高度为tabbar的bottom,这个调整必须在resize event之后
1750 d->m_blockResize = true;
1751 setFixedHeight(d->m_tabBar->geometry().bottom());
1752 d->m_blockResize = false;
1753 }
1754 Q_EMIT ribbonStyleChanged(d->m_ribbonStyle);
1755}
1756
1758{
1759 Q_D(RibbonBar);
1760 return d->m_tabBar->currentIndex();
1761}
1762
1764{
1765 Q_D(RibbonBar);
1766 d->m_tabBar->setCurrentIndex(index);
1767}
1768
1770{
1771 Q_D(const RibbonBar);
1772 return d->isOfficeStyle();
1773}
1774
1776{
1777 Q_D(const RibbonBar);
1778 return d->isTwoRowStyle();
1779}
1780
1782{
1783 Q_D(RibbonBar);
1784 d->m_windowButtonGroup = widget;
1785}
1786
1793{
1794 Q_D(RibbonBar);
1795 d->updateRibbonBarHeight();
1796 QList<RibbonPage *> pages = this->pages();
1797 for (RibbonPage *page : std::as_const(pages)) {
1799 }
1800}
1801
1803{
1804 Q_D(RibbonBar);
1805 d->updateMinimumButtonIcon();
1806}
1807
1809{
1810 // FIXME: 此处使用resizeEvent中的d->resizeRibbon()和update()来代替postEvent会出现如下问题:
1811 // WPS模式下,无边框和有边框之间切换后,
1812 // m_applicationButton.isVisible()突然返回false,导致d->resizeRibbon()中计算尺寸异常
1813 QApplication::postEvent(this, new QResizeEvent(size(), size()));
1814}
1815
1817{
1818 Q_D(const RibbonBar);
1819 return d->m_titleAligment;
1820}
1821
1823{
1824 Q_D(RibbonBar);
1825 d->m_titleAligment = al;
1826}
1827
1829{
1830 Q_D(RibbonBar);
1831 d->m_titleVisible = visible;
1832 d->m_quickAccessBar->setIconVisible(isOfficeStyle() && visible);
1833}
1834
1836{
1837 Q_D(const RibbonBar);
1838 QSize sz = QMenuBar::sizeHint();
1839 int w = 0;
1840 if (isOfficeStyle()) {
1841 w = d->minimumWidthInOfficeStyle();
1842 } else {
1843 w = d->minimumWidthInWpsLiteStyle();
1844 }
1845 sz.setWidth(w);
1846 return sz;
1847}
1848
1850{
1851 return sizeHint();
1852}
1853
1854bool RibbonBar::event(QEvent *event)
1855{
1856 Q_D(RibbonBar);
1857 bool res = QMenuBar::event(event);
1858
1859 switch (event->type()) {
1860 // QGuiApplication::fontChanged() is deprecated since Qt 6; the same
1861 // notification arrives as an event on every widget instead.
1862 case QEvent::ApplicationFontChange: {
1863 d->onFontChanged();
1864 } break;
1865 case QEvent::LayoutRequest: {
1866 // FIXME: 引入LayoutRequest后,如果处理不当,会增加resize次数,需要梳理触发LayoutRequest的条件,
1867 // 同时,需要梳理哪些postEvent或sendEvent可以由LayoutRequest代替。
1868 // 目前,当cornerWidget为QMdiArea时,根据有效事件来设置m_layoutRequest
1869 if (d->m_layoutRequest) {
1870 d->resizeRibbon();
1871 d->m_layoutRequest = false;
1872 }
1873 } break;
1874 default:
1875 break;
1876 }
1877
1878 return res;
1879}
1880
1881bool RibbonBar::eventFilter(QObject *obj, QEvent *e)
1882{
1883 Q_D(RibbonBar);
1884 if (obj) {
1885 // 调整多文档时在窗口模式下的按钮更新
1886 if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) {
1887 // 对于QMdiArea,最大化时会存在ShowToParent事件,之后有效点击按钮(logo、最大/小、关闭按钮)会存在MouseButtonRelease事件
1888 if ((QEvent::ShowToParent == e->type()) || (QEvent::MouseButtonRelease == e->type())) {
1889 d->m_layoutRequest = true;
1890 }
1891 } else if (obj == d->m_stack && d->m_stack->isPopup()) {
1892 // 在 stack 是 popup 模式时,点击 stack 区域外的时候,如果是在 ribbonTabBar 上点击, 那么忽略掉这次点击
1893 // 把点击下发到 ribbonTabBar, 这样可以避免在点击 ribbonTabBar 后 stack 隐藏又显示,出现闪烁
1894 if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) {
1895 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
1896#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1897 QPoint pos = mouseEvent->pos();
1898 QPoint globalPos = QtCanpoolCompat::globalMousePos(mouseEvent);
1899#else
1900 QPoint pos = mouseEvent->position().toPoint();
1901 QPoint globalPos = mouseEvent->globalPosition().toPoint();
1902#endif
1903 if (!d->m_stack->rect().contains(pos)) {
1904 QWidget *clickedWidget = QApplication::widgetAt(globalPos);
1905 if (clickedWidget == d->m_tabBar) {
1906 const QPoint targetPoint = clickedWidget->mapFromGlobal(globalPos);
1907 QMouseEvent *evPress =
1908 new QMouseEvent(mouseEvent->type(), targetPoint, globalPos, mouseEvent->button(),
1909 mouseEvent->buttons(), mouseEvent->modifiers());
1910 QApplication::postEvent(clickedWidget, evPress);
1911 return true;
1912 }
1913 }
1914 }
1915 }
1916 }
1917 return QMenuBar::eventFilter(obj, e);
1918}
1919
1920void RibbonBar::paintEvent(QPaintEvent *e)
1921{
1922 Q_D(RibbonBar);
1923 Q_UNUSED(e);
1924 QPainter p(this);
1925 if (isOfficeStyle()) {
1926 d->paintInOfficeStyle(p);
1927 } else {
1928 d->paintInWpsLiteStyle(p);
1929 }
1930#ifdef QX_RIBBON_DEBUG_HELP_DRAW
1931 HELP_DRAW_RECT(p, d->m_quickAccessBar->geometry());
1932 HELP_DRAW_RECT(p, d->m_tabBar->geometry());
1933 HELP_DRAW_RECT(p, d->m_stack->geometry());
1934#endif
1935}
1936
1937void RibbonBar::resizeEvent(QResizeEvent *e)
1938{
1939 Q_D(RibbonBar);
1940 Q_UNUSED(e);
1941 if (d->m_blockResize) {
1942 return;
1943 }
1944 d->resizeRibbon();
1945 update();
1946}
1947
RibbonPageContext * pageContext
Definition ribbonbar_p.h:56
Qt::Alignment m_titleAligment
void onPageWindowTitleChanged(const QString &title)
RibbonBar::TabBarPosition m_tabBarPosition
void onCurrentRibbonTabClicked(int index)
ribbon tab bar单击
void onCurrentRibbonTabChanged(int index)
RibbonQuickAccessBarContainer * m_quickAccessBar
void paintBackground(QPainter &painter)
QList< PageContextManagerData > m_currentShowingPageContextList
RibbonButtonGroup * m_bottomLeftButtonGroup
RibbonControlButton * m_minimumPageButton
隐藏面板按钮
void onTabMoved(int from, int to)
int calcMinTabBarWidth() const
根据情况重置tabbar的宽度,主要针对wps模式
void paintWindowTitle(QPainter &painter, const QString &title, const QRect &titleRegion)
bool isPageContextVisible(RibbonPageContext *pageContext)
void paintInOfficeStyle(QPainter &p)
void setMinimizedFlag(bool flag)
RibbonButtonGroup * m_topLeftButtonGroup
void paintInWpsLiteStyle(QPainter &p)
RibbonBar::RibbonStyle m_ribbonStyle
RibbonStackedWidget * m_stack
void setApplicationButton(QAbstractButton *btn)
int mainBarHeight() const
mainBarHeight 的计算高度
RibbonButtonGroup * createButtonGroup()
void onWindowTitleChanged(const QString &title)
int minimumWidthInWpsLiteStyle() const
RibbonBar::QuickAccessBarPosition m_quickAccessBarPosition
void updatePageContextManagerData()
刷新所有ContextPageManagerData,这个在单独一个Page删除时调用
RibbonButtonGroup * m_bottomRightButtonGroup
在 tabbar 右边的按钮群
void onPageContextAdded(RibbonPage *page)
int m_iconRightBorderPosition
标题栏x值的最小值,在有图标和快捷启动按钮,此值都需要变化
void onWindowIconChanged(const QIcon &icon)
void onCurrentRibbonTabDoubleClicked(int index)
QList< QColor > m_pageContextColorList
QAbstractButton * m_applicationButton
int tabIndex(RibbonPage *page) const
根据RibbonPage*指针查找tabbar的index
void paintPageContextTab(QPainter &painter, const QString &title, QRect contextRect, const QColor &color)
RibbonButtonGroup * m_topRightButtonGroup
int minimumWidthInOfficeStyle() const
RibbonBar 继承 QMenuBar, 在 RibbonWindow 中直接替换了原来的 QMenuBar
Definition ribbonbar.h:99
bool isMinimized() const
void setPageContextVisible(RibbonPageContext *context, bool visible)
int titleBarHeight() const
RibbonPage * page(int index) const
通过索引找到page,如果超过索引范围,会返回Q_NULLPTR
void setWindowButtonGroup(QWidget *widget)
RibbonPage * insertPage(int index, const QString &title)
void minimizationChanged(bool minimized)
int tabBarHeight() const
void setWindowTitleVisible(bool visible)
RibbonStyle currentRibbonStyle() const
void movePage(int from, int to)
void showMinimumButton(bool isShow=true)
void paintEvent(QPaintEvent *e) override
int pageIndex(RibbonPage *page) const
QList< RibbonPageContext * > pageContextList() const
void setTabBarPosition(TabBarPosition position)
void setWindowTitleAligment(Qt::Alignment al)
void setApplicationButton(QAbstractButton *btn)
设置applicationButton,如果想隐藏,可以传入Q_NULLPTR
RibbonPageContext * addPageContext(const QString &title, const QColor &color=QColor())
添加上下文标签
void setPageContextCoverTab(bool cover)
QList< RibbonPage * > pages(bool getAll=true) const
获取当前显示的所有的RibbonPage,包含未显示的RibbonPageContext的RibbonPage也一并返回
TabBarPosition tabBarPosition() const
bool haveShowMinimumButton() const
bool isOfficeStyle() const
bool eventFilter(QObject *obj, QEvent *e) override
void resizeEvent(QResizeEvent *e) override
QSize minimumSizeHint() const override
QAbstractButton * applicationButton()
bool isPageVisible(RibbonPage *page) const
判断这个page是否在显示状态,也就是tabbar有这个page
RibbonPage * pageByName(const QString &title) const
通过名字查找Page
void showPage(RibbonPage *page)
显示被隐藏的page
Qt::Alignment windowTitleAligment() const
QSize sizeHint() const override
RibbonButtonGroup * cornerButtonGroup(Qt::Corner corner=Qt::BottomRightCorner)
void removePage(RibbonPage *page)
移除RibbonPage
void applicationButtonClicked()
bool isPageContextVisible(RibbonPageContext *context)
void raisePage(RibbonPage *page)
确保标签显示出来,tab并切换到对应页
RibbonBar(QWidget *parent=Q_NULLPTR)
void setMinimized(bool flag)
RibbonPage * pageByObjectName(const QString &objname) const
通过ObjectName查找Page
void destroyPageContext(RibbonPageContext *context)
销毁上下文标签,上下文标签的RibbonPage也会随之销毁
void setQuickAccessBarPosition(QuickAccessBarPosition position)
void updateRibbonGeometry()
更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局
void setCurrentIndex(int index)
void showPageContext(RibbonPageContext *context)
void setRibbonStyle(RibbonStyle v)
设置ribbonbar的风格,此函数会重新设置所有元素,包括button的布局方式, 尤其是从三行变到两行的过程,重设的内容较多
void hidePage(RibbonPage *page)
隐藏page,并不会删除或者取走,只是隐藏
void ribbonStyleChanged(RibbonStyle nowStyle)
RibbonPage * addPage(const QString &title)
RibbonQuickAccessBar * quickAccessBar() const
bool event(QEvent *event) override
QuickAccessBarPosition quickAccessBarPosition() const
bool isTwoRowStyle() const
void hidePageContext(RibbonPageContext *context)
QSize sizeHint() const override
@ ThreeRowMode
三行布局模式,office就是三行布局模式,group能布置3行小toolbutton,默认模式
Definition ribbongroup.h:36
@ TwoRowMode
两行布局模式,wps的后续布局模式就是两行布局模式,group能布置2行小toolbutton
Definition ribbongroup.h:37
管理上下文标签的类
RibbonPage * page(int index)
QList< RibbonPage * > pageList() const
void pageAdded(RibbonPage *page)
void setContextColor(const QColor color)
void setContextTitle(const QString &title)
设置上下文标签的标题,标题仅在 office 模式下显示 RibbonBar::RibbonStyle
一项 ribbon tab 页
Definition ribbonpage.h:30
bool isPageContext() const
如果是ContextPage,此函数返回true
void updateItemGeometry()
刷新page的布局,适用于改变ribbon的模式之后调用
void setPageName(const QString &title)
void setGroupLayoutMode(RibbonGroup::GroupLayoutMode m)
RibbonQuickAccessBar * quickAccessBar() const
RibbonQuickAccessBarContainer(QWidget *parent=Q_NULLPTR)
void moveWidget(int from, int to)
类似 tabbar 的 moveTab 函数,交换两个窗口的 index
virtual bool event(QEvent *event)
Definition ribbonbar.cpp:77
void setCurrentIndex(int index)
const QMargins & tabMargin() const
QPoint globalMousePos(const QMouseEvent *event)
Definition qtcompat.h:26
const int dragAreaMinimumWidth
Definition ribbonbar.cpp:35
const int tabBarBaseLineHeight
Definition ribbonbar.cpp:33
const int tabBarMinimumWidth
Definition ribbonbar.cpp:34
#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
#define HELP_DRAW_RECT(p, rect)
Definition ribbonbar.cpp:37
#define RibbonElementStyleOpt
#define RibbonElementFactory