QtCanpool 3.3.0
载入中...
搜索中...
未找到
ribbongrouplayout.cpp
浏览该文件的文档.
1
5#include "ribbongrouplayout.h"
6#include "ribbongroup.h"
7#include "ribbongroup_p.h"
8#include "ribbonmanager.h"
9
10#include <QQueue>
11#include <QWidgetAction>
12
14
15#define RibbonGroupLayout_DEBUG_PRINT 0
16#define HELP_DRAW_RECT(p, rect) \
17 do { \
18 p.save(); \
19 QPen _pen(Qt::DashLine); \
20 _pen.setColor(Qt::blue); \
21 p.setPen(_pen); \
22 p.setBrush(QBrush()); \
23 p.drawRect(rect); \
24 p.restore(); \
25 } while (0)
26
27RibbonGroupItem::RibbonGroupItem(QWidget *widget)
28 : QWidgetItem(widget)
29 , action(Q_NULLPTR)
30 , customWidget(false)
31{
32}
33
35{
36 return (action == Q_NULLPTR || !action->isVisible());
37}
38
39class RibbonGroupLayoutPrivate
40{
42public:
43 RibbonGroupLayoutPrivate();
44
45 // 根据列数,计算窗口的宽度,以及最大宽度
46 void columnWidthInfo(int colindex, int &width, int &maximum) const;
47
48 // 重新计算扩展尺寸,此函数必须在updateGeomArray函数之后调用
49 void recalcExpandGeomArray(const QRect &setrect);
50
51 RibbonGroupItem *createItem(QAction *action, RibbonGroup::RowProportion rp = RibbonGroup::Auto);
52 void updateGeomArray(const QRect &setrect);
53 void layoutActions();
54public:
55 QList<RibbonGroupItem *> m_items;
56 QSize m_sizeHint;
57 static QMargins s_contentsMargins;
58 int m_columnCount;
59 int m_largeHeight;
60 bool m_expandFlag;
61 bool m_dirty;
62};
63
64QMargins RibbonGroupLayoutPrivate::s_contentsMargins = QMargins(1, 1, 1, 1);
65
66RibbonGroupLayoutPrivate::RibbonGroupLayoutPrivate()
67 : m_columnCount(0)
68 , m_expandFlag(false)
69 , m_dirty(true)
70{
71}
72
79void RibbonGroupLayoutPrivate::columnWidthInfo(int colIndex, int &width, int &maximum) const
80{
81 width = -1;
82 maximum = -1;
83 for (RibbonGroupItem *item : std::as_const(m_items)) {
84 if (!item->isEmpty() && (item->columnIndex == colIndex)) {
85 width = qMax(width, item->willGeometry.width());
86 maximum = qMax(maximum, item->widget()->maximumWidth());
87 }
88 }
89}
90
91void RibbonGroupLayoutPrivate::recalcExpandGeomArray(const QRect &setrect)
92{
93 // 计算能扩展的尺寸
94 int expandwidth = setrect.width() - m_sizeHint.width();
95
96 if (expandwidth <= 0) {
97 return;
98 }
99 // 列扩展信息
100 struct _columnExpandInfo {
101 int oldColumnWidth = 0;
102 int columnMaximumWidth = -1;
103 int columnExpandedWidth = 0;
104 QList<RibbonGroupItem *> expandItems;
105 };
106 // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展
107 QMap<int, _columnExpandInfo> columnExpandInfo;
108
109 for (RibbonGroupItem *item : std::as_const(m_items)) {
110 if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) {
111 // 只获取可见的
112 QMap<int, _columnExpandInfo>::iterator i = columnExpandInfo.find(item->columnIndex);
113 if (i == columnExpandInfo.end()) {
114 i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo());
115 }
116 i.value().expandItems.append(item);
117 }
118 }
119 if (columnExpandInfo.size() <= 0) {
120 // 没有需要扩展的就退出
121 return;
122 }
123 // 获取完可扩展的列和控件后,计算对应的列的尺寸
124 // 计算能扩展的尺寸
125 int oneColCanExpandWidth = expandwidth / columnExpandInfo.size();
126
127 for (QMap<int, _columnExpandInfo>::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) {
128 int &oldColumnWidth = i.value().oldColumnWidth;
129 int &columnMaximumWidth = i.value().columnMaximumWidth;
130 columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth);
131 if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) {
132 // 如果小于0说明没有这个列,这种属于异常,删除继续
133 // oldColumnWidth > columnMaximumWidth也是异常
134 i = columnExpandInfo.erase(i);
135 continue;
136 }
137 // 开始调整
138 int colWidth = oneColCanExpandWidth + oldColumnWidth; // 先扩展了
139 if (colWidth >= columnMaximumWidth) {
140 i.value().columnExpandedWidth = columnMaximumWidth;
141 } else {
142 i.value().columnExpandedWidth = colWidth;
143 }
144 ++i;
145 }
146 // 重新调整尺寸
147 // 由于会涉及其他列的变更,因此需要所有都遍历一下
148 for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) {
149 int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth;
150 for (RibbonGroupItem *item : this->m_items) {
151 if (item->isEmpty() || (item->columnIndex < i.key())) {
152 // 之前的列不用管
153 continue;
154 }
155 if (item->columnIndex == i.key()) {
156 // 此列的扩展
157 if (i.value().expandItems.contains(item)) {
158 // 此列需要扩展的item才扩展尺寸
159 item->willGeometry.setWidth(i.value().columnExpandedWidth);
160 } else {
161 // 此列不扩展的模块保持原来的尺寸
162 continue;
163 }
164 } else {
165 // 后面的移动
166 item->willGeometry.moveLeft(item->willGeometry.x() + moveXLen);
167 }
168 }
169 }
170}
171
184RibbonGroupItem *RibbonGroupLayoutPrivate::createItem(QAction *action, RibbonGroup::RowProportion rp)
185{
186 Q_Q(RibbonGroupLayout);
187 bool customWidget = false;
188 QWidget *widget = Q_NULLPTR;
189 RibbonGroup *group = qobject_cast<RibbonGroup *>(q->parentWidget());
190 if (!group) {
191 return Q_NULLPTR;
192 }
193 if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(action)) {
194 widget = widgetAction->requestWidget(group);
195 if (widget != Q_NULLPTR) {
196 widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
197 // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如RibbonSeparator和RibbonButton
198 customWidget = true;
199 }
200 } else if (action->isSeparator()) {
201 RibbonSeparator *sep = new RibbonSeparator(group);
202 widget = sep;
203 }
204 // 不是widget,自动生成 RibbonButton
205 if (!widget) {
207 ((rp == RibbonGroup::Large) ? RibbonButton::LargeButton : RibbonButton::SmallButton);
208
209 RibbonButton *button = new RibbonButton(group);
210 button->setAutoRaise(true);
211 button->setFocusPolicy(Qt::NoFocus);
212 button->setButtonType(buttonType);
213 if (RibbonButton::LargeButton == buttonType) {
214 // 根据group的模式设置button样式
215 button->setLargeButtonType(group->isTwoRow() ? RibbonButton::Lite : RibbonButton::Normal);
216 }
217 button->setDefaultAction(action);
218
219 QObject::connect(button, &RibbonButton::triggered, group, &RibbonGroup::actionTriggered);
220 widget = button;
221 }
222 // 这时总会有widget
223 widget->hide();
224 RibbonGroupItem *result = new RibbonGroupItem(widget);
225
226 result->rowProportion = rp;
227 result->customWidget = customWidget;
228 result->action = action;
229 return result;
230}
231
235void RibbonGroupLayoutPrivate::updateGeomArray(const QRect &setrect)
236{
237 Q_Q(RibbonGroupLayout);
238 RibbonGroup *group = qobject_cast<RibbonGroup *>(q->parentWidget());
239 if (!group) {
240 return;
241 }
242#if RibbonGroupLayout_DEBUG_PRINT
243 qDebug() << "RibbonGroupLayoutPrivate::updateGeomArray(" << setrect << ")";
244#endif
245 int height = setrect.height();
246 const QMargins &margin = q->groupContentsMargins();
247 const int spacing = q->spacing();
248 int x = margin.left();
249
250 // 获取group的布局模式,3行或者2行
251 const short rowCount = (group->groupLayoutMode() == RibbonGroup::ThreeRowMode) ? 3 : 2;
252 // largeHeight是对应large占比的高度,group->titleHeight()在两行模式返回0
253 const int largeHeight = q->calcLargeHeight(setrect, group);
254 m_largeHeight = largeHeight;
255 // 计算smallHeight的高度
256 const int smallHeight = (largeHeight - (rowCount - 1) * spacing) / rowCount;
257
258 // Medium行的y位置
259 // 三行模式:假设两个 Medium 的上中下间隔高度为 mediumSpacing,每个 Medium 高度为 smallHeight,
260 // 则 largeHeight = mediumSpacing + smallHeight + mediumSpacing + smallHeight + mediumSpacing
261 // = 3 * mediumSpacing + 2 * smallHeight
262 // 即 mediumSpacing = (largeHeight - 2 * smallHeight) / 3
263 const int mediumSpacing = (largeHeight - 2 * smallHeight) / 3;
264 const int yMediumRow0 = (2 == rowCount) ? margin.top() : (margin.top() + mediumSpacing);
265 const int yMediumRow1 =
266 (2 == rowCount) ? (yMediumRow0 + smallHeight + spacing) : (yMediumRow0 + smallHeight + mediumSpacing);
267 // Small行的y位置
268 const int ySmallRow0 = margin.top();
269 const int ySmallRow1 = ySmallRow0 + smallHeight + spacing;
270 const int ySmallRow2 = ySmallRow1 + smallHeight + spacing;
271
272 // 两行模式:
273 // yMediumRow0 == ySmallRow0
274 // yMediumRow1 == ySmallRow1
275
276 // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行,
277 // item->rowIndex主要用于RibbonGroupItem::Medium
278 short row = 0;
279 int column = 0;
280 // 记录每列最大的宽度
281 int columnMaxWidth = 0;
282 // 记录总宽度
283 int totalWidth = 0;
284
285 int itemCount = m_items.count();
286
287#if RibbonGroupLayout_DEBUG_PRINT
288 qDebug() << "\r\n\r\n============================================="
289 << "\r\RibbonGroupLayoutPrivate::updateGeomArray()"
290 << " setrect:" << setrect << "\r\ngroup name:" << group->windowTitle()
291 << "\r\n largeHeight:" << largeHeight << "\r\n smallHeight:" << smallHeight
292 << "\r\n rowCount:" << rowCount;
293#endif
294
295 // 本列第一、二行占比
297 RibbonGroupItem *lastGeomItem = Q_NULLPTR; // 记录最后一个设置位置的item
298 for (int i = 0; i < itemCount; ++i) {
299 RibbonGroupItem *item = m_items.at(i);
300 if (item->isEmpty()) {
301 // 如果是hide就直接跳过
302#if RibbonGroupLayout_DEBUG_PRINT
303 qDebug() << item->widget()->metaObject()->className() << "is hide"
304 << " row:" << row << " col:" << column;
305#endif
306 item->rowIndex = -1;
307 item->columnIndex = -1;
308 continue;
309 }
310
311 QSize hint = item->sizeHint();
312 Qt::Orientations exp = item->expandingDirections();
313 if (item->widget()) {
314 // 有窗口是水平扩展,则标记为扩展
315 if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) {
316 m_expandFlag = true;
317 }
318 }
319 RibbonGroup::RowProportion rp = item->rowProportion;
320 if (RibbonGroup::Auto == rp) {
321 // 定义行占比,如果是垂直扩展,就定义为Large占比,否则就是small占比
322 if (exp & Qt::Vertical) {
324 } else {
326 }
327 }
328 // 开始根据占比和layoutmode来布局
329 switch (rp) {
330 case RibbonGroup::Large: {
331 // !!在Large,如果不是处于新列的第一行,就需要进行换列处理
332 // 把large一直设置在下一列的开始
333 if (row != 0) {
334 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
335 x += (columnMaxWidth + spacing);
336 ++column;
337 row = 0;
338 columnMaxWidth = 0;
339 }
340 item->rowIndex = 0;
341 item->columnIndex = column;
342 item->willGeometry = QRect(x, margin.top(), hint.width(), largeHeight);
343 columnMaxWidth = hint.width();
344 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
345 x += (columnMaxWidth + spacing);
346 ++column;
347 row = 0;
348 columnMaxWidth = 0;
349 } break;
350
351 case RibbonGroup::Medium: {
352 // 2行模式下Medium和small等价
353 if (2 == rowCount) {
354 if (0 == row) {
355 item->rowIndex = 0;
356 item->columnIndex = column;
357 item->willGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
358 thisColumnRP0 = RibbonGroup::Medium;
359 columnMaxWidth = hint.width();
360 // 下个row为1,x不变
361 row = 1;
362 } else {
363 item->rowIndex = 1;
364 item->columnIndex = column;
365 item->willGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
366 // 和上个进行比较得到最长宽度
367 columnMaxWidth = qMax(columnMaxWidth, hint.width());
368 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
369 x += (columnMaxWidth + spacing);
370 ++column;
371 row = 0;
372 columnMaxWidth = 0;
373 }
374 } else {
375 // 3行模式
376 if (0 == row) {
377 item->rowIndex = 0;
378 item->columnIndex = column;
379 item->willGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
380 thisColumnRP0 = RibbonGroup::Medium;
381 columnMaxWidth = hint.width();
382 // 下个row为1,x不变
383 row = 1;
384 } else if (1 == row) {
385 item->rowIndex = 1;
386 item->columnIndex = column;
387 item->willGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
388 columnMaxWidth = qMax(columnMaxWidth, hint.width());
389 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
390 x += (columnMaxWidth + spacing);
391 ++column;
392 row = 0;
393 columnMaxWidth = 0;
394 } else {
395 // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium,这时需要先换列
396 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
397 x += (columnMaxWidth + spacing);
398 ++column;
399 row = 0;
400 columnMaxWidth = 0;
401 // 换列后此时等价于0 == row
402 item->rowIndex = 0;
403 item->columnIndex = column;
404 item->willGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
405 thisColumnRP0 = RibbonGroup::Medium;
406 columnMaxWidth = hint.width();
407 row = 1;
408 }
409 }
410 } break;
411
412 case RibbonGroup::Small: {
413 if (0 == row) {
414 // 第一行
415 item->rowIndex = 0;
416 item->columnIndex = column;
417 item->willGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight);
418 thisColumnRP0 = RibbonGroup::Small;
419 columnMaxWidth = hint.width();
420 // 下个row为1,x不变
421 row = 1;
422 } else if (1 == row) {
423 // 第二行
424 item->rowIndex = 1;
425 item->columnIndex = column;
426 // 两行模式, ySmallRow1 == yMediumRow1,所以不需要关心第一行是 Medium 还是 Small
427 item->willGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight);
428 if ((3 == rowCount) && (RibbonGroup::Medium == thisColumnRP0)) {
429 // 三行模式,并且第一行是Medium
430 item->willGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
431 }
432 // 和上个进行比较得到最长宽度
433 columnMaxWidth = qMax(columnMaxWidth, hint.width());
434 // 这里要看两行还是三行,确定是否要换列
435 if (2 == rowCount) {
436 // 两行模式,换列
437 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
438 x += (columnMaxWidth + spacing);
439 ++column;
440 row = 0;
441 columnMaxWidth = 0;
442 } else {
443 // 三行模式,继续增加行数,x不变
444 row = 2;
445 }
446 if ((3 == rowCount) && (RibbonGroup::Medium == thisColumnRP0)) {
447 // 三行模式,并且第一行是Medium,按两个Medium计算,换列
448 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
449 x += (columnMaxWidth + spacing);
450 ++column;
451 row = 0;
452 columnMaxWidth = 0;
453 }
454 } else {
455 // 第三行
456 item->rowIndex = 2;
457 item->columnIndex = column;
458 item->willGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight);
459 columnMaxWidth = qMax(columnMaxWidth, hint.width());
460 // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零
461 x += (columnMaxWidth + spacing);
462 ++column;
463 row = 0;
464 columnMaxWidth = 0;
465 }
466 } break;
467
468 default:
469 // 不可能出现
470 break;
471 }
472 lastGeomItem = item;
473#if RibbonGroupLayout_DEBUG_PRINT
474 qDebug() << item->widget()->metaObject()->className() << " rp:" << rp << " row:" << item->rowIndex
475 << " col:" << item->columnIndex << " new row:" << row << " new column:" << column
476 << " willGeometry:" << item->willGeometry << " sizeHint:" << hint << " x:" << x;
477#endif
478 }
479 // 最后一个元素,更新列数
480 // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断,导致异常
481 if (lastGeomItem) { // 最后一个元素,更新totalWidth
482 if (lastGeomItem->columnIndex != column) {
483 // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引
484 m_columnCount = column;
485 // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth
486 totalWidth = x + margin.right();
487 } else {
488 // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1
489 m_columnCount = column + 1;
490 // 由于最后一个元素未触发换列,需要计算totalWidth
491 totalWidth = x + columnMaxWidth + spacing + margin.right();
492 }
493 }
494 // 在有optionButton情况下,不显示标题时,需要调整totalWidth
495 if (group->hasOptionAction()) {
496 if (!group->titleVisible()) {
497 totalWidth += group->optionActionButtonSize().width();
498 }
499 }
500 // 在设置完所有窗口后,再设置扩展属性的窗口
501 if (totalWidth < setrect.width()) {
502 // 说明可以设置扩展属性的窗口
503 recalcExpandGeomArray(setrect);
504 }
505 m_sizeHint = QSize(totalWidth, height);
506}
507
511void RibbonGroupLayoutPrivate::layoutActions()
512{
513 Q_Q(RibbonGroupLayout);
514 if (m_dirty) {
515 updateGeomArray(q->geometry());
516 }
517 QList<QWidget *> showWidgets, hideWidgets;
518
519#if RibbonGroupLayout_DEBUG_PRINT
520 qDebug() << "\r\n\r\n =============================================="
521 "\r\n RibbonGroupLayoutPrivate::layoutActions"
522 << " \r\n name:" << q->parentWidget()->windowTitle() << " sizehint:" << q->sizeHint();
523#endif
524 for (RibbonGroupItem *item : std::as_const(m_items)) {
525 if (item->isEmpty()) {
526 hideWidgets << item->widget();
527 } else {
528 item->setGeometry(item->willGeometry);
529 // item->widget()->setFixedSize(item->willGeometry.size());
530 // item->widget()->move(item->willGeometry.topLeft());
531 showWidgets << item->widget();
532#if RibbonGroupLayout_DEBUG_PRINT
533 qDebug() << "[" << item->rowIndex << "," << item->columnIndex << "]"
534 << " -> " << item->willGeometry << ":" << item->widget()->metaObject()->className();
535#endif
536 }
537 }
538
539 // 不在上面那里进行show和hide因为这会触发RibbonGroupLayout的重绘,导致循环绘制,非常影响效率
540 for (QWidget *w : std::as_const(showWidgets)) {
541 w->show();
542 }
543 for (QWidget *w : std::as_const(hideWidgets)) {
544 w->hide();
545 }
546}
547
549 : QLayout(p)
550{
552 setSpacing(1);
553}
554
556{
558 while (!d->m_items.isEmpty()) {
559 RibbonGroupItem *item = d->m_items.takeFirst();
560 if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(item->action)) {
561 if (item->customWidget) {
562 widgetAction->releaseWidget(item->widget());
563 }
564 }
565 delete item;
566 }
568}
569
575int RibbonGroupLayout::indexOf(QAction *action) const
576{
577 Q_D(const RibbonGroupLayout);
578 for (int i = 0; i < d->m_items.count(); ++i) {
579 if (d->m_items.at(i)->action == action) {
580 return i;
581 }
582 }
583 return -1;
584}
585
586void RibbonGroupLayout::addItem(QLayoutItem *item)
587{
588 Q_UNUSED(item);
589 qWarning("RibbonGroupLayout::addItem(): please use insertAction() instead");
590 return;
591}
592
600{
602 if (index < 0 || index > d->m_items.count()) {
603 index = d->m_items.count();
604 }
605 RibbonGroupItem *item = d->createItem(act, rp);
606
607 if (item) {
608 d->m_items.insert(index, item);
609 // 标记需要重新计算尺寸
610 invalidate();
611 }
612}
613
614QLayoutItem *RibbonGroupLayout::itemAt(int index) const
615{
616 Q_D(const RibbonGroupLayout);
617 if ((index < 0) || (index >= d->m_items.count())) {
618 return Q_NULLPTR;
619 }
620 return d->m_items.at(index);
621}
622
623QLayoutItem *RibbonGroupLayout::takeAt(int index)
624{
626 if ((index < 0) || (index >= d->m_items.count())) {
627 return Q_NULLPTR;
628 }
629 RibbonGroupItem *item = d->m_items.takeAt(index);
630
631 QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(item->action);
632
633 if ((widgetAction != Q_NULLPTR) && item->customWidget) {
634 widgetAction->releaseWidget(item->widget());
635 } else {
636 // destroy the RibbonButton/RibbonSeparator
637 item->widget()->hide();
638 item->widget()->deleteLater();
639 }
640
641 invalidate();
642 return item;
643}
644
646{
647 Q_D(const RibbonGroupLayout);
648 return d->m_items.count();
649}
650
652{
653 Q_D(const RibbonGroupLayout);
654 return d->m_items.isEmpty();
655}
656
658{
660 d->m_dirty = true;
661 QLayout::invalidate();
662}
663
665{
666 return Qt::Horizontal;
667}
668
670{
671 Q_D(const RibbonGroupLayout);
672 return d->m_sizeHint;
673}
674
676{
677 Q_D(const RibbonGroupLayout);
678 return d->m_sizeHint;
679}
680
682{
683 Q_D(const RibbonGroupLayout);
684 int index = indexOf(action);
685
686 if (index >= 0) {
687 return d->m_items[index];
688 }
689 return Q_NULLPTR;
690}
691
693{
694 Q_D(const RibbonGroupLayout);
695 if (d->m_items.isEmpty()) {
696 return Q_NULLPTR;
697 }
698 return d->m_items.last();
699}
700
706{
707 RibbonGroupItem *item = lastItem();
708
709 if (item) {
710 return item->widget();
711 }
712 return Q_NULLPTR;
713}
714
721void RibbonGroupLayout::move(int from, int to)
722{
724 int c = count();
725 if (c <= 0) {
726 return;
727 }
728 if (from == to) {
729 return;
730 }
731 if (from < 0 || from >= c || to < 0 || to >= c) {
732 return;
733 }
734 d->m_items.move(from, to);
735 invalidate();
736}
737
743{
744 Q_D(const RibbonGroupLayout);
745 return d->m_dirty;
746}
747
754int RibbonGroupLayout::calcLargeHeight(const QRect &setrect, const RibbonGroup *group)
755{
756 const QMargins &m = groupContentsMargins();
757 return setrect.height() - m.top() - m.bottom() - group->titleHeight();
758}
759
769{
770 return RibbonGroupLayoutPrivate::s_contentsMargins;
771}
772
781{
782 if (RibbonGroupLayoutPrivate::s_contentsMargins != m) {
783 RibbonGroupLayoutPrivate::s_contentsMargins = m;
784 RibbonElementStyleOpt.recalc();
785 }
786}
787
788const QList<RibbonGroupItem *> &RibbonGroupLayout::ribbonGroupItems() const
789{
790 Q_D(const RibbonGroupLayout);
791 return d->m_items;
792}
793
794void RibbonGroupLayout::setGeometry(const QRect &rect)
795{
797 d->m_dirty = false;
798 d->updateGeomArray(rect);
799 QLayout::setGeometry(rect);
800 d->layoutActions();
801}
802
@ Lite
icon占大按钮的60,文字占40,且文字不换行,对应于wps模式的菜单按钮,下箭头在文字旁边
是对 group 所有子窗口的抽象,参考 qt 的 toolbar
QAction * action
记录action,参考QToolBarLayoutItem
bool customWidget
对于没有窗口的action,实际也会有一个RibbonButton,在销毁时要delete掉
bool isEmpty() const override
针对 RibbonGroup 的布局
QLayoutItem * takeAt(int index) override
QSize minimumSize() const override
virtual int indexOf(QAction *action) const
通过action查找索引,用于actionEvent添加action用
const QList< RibbonGroupItem * > & ribbonGroupItems() const
static const QMargins & groupContentsMargins()
全局的contentsMargins
RibbonGroupItem * groupItem(QAction *action) const
void move(int from, int to)
移动两个item
QSize sizeHint() const override
static void setGroupContentsMargins(const QMargins &m)
全局的contentsMargins
RibbonGroupItem * lastItem() const
void setGeometry(const QRect &rect) override
RibbonGroupLayout(QWidget *p=Q_NULLPTR)
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
Qt::Orientations expandingDirections() const override
void addItem(QLayoutItem *item) override
bool isDirty() const
判断是否需要重新布局
QLayoutItem * itemAt(int index) const override
group 用于对 ribbon 页窗口中的控件进行分组
Definition ribbongroup.h:29
void actionTriggered(QAction *action)
@ ThreeRowMode
三行布局模式,office就是三行布局模式,group能布置3行小toolbutton,默认模式
Definition ribbongroup.h:36
@ Medium
中占比,在 RibbonGroup::groupLayoutMode 为 RibbonGroup::ThreeRowMode 时才会起作用, 且要同一列里两个都是 Medium 时,会在三行中占据两行
Definition ribbongroup.h:42
@ Small
小占比,占RibbonGroup的一行,Medium在不满足条件时也会变为Small,但不会变为Large
Definition ribbongroup.h:44
@ Auto
未定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small
Definition ribbongroup.h:40
@ Large
大占比,一个widget的高度会充满整个group
Definition ribbongroup.h:41
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(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
QColor button
#define RibbonElementStyleOpt