QtCanpool 3.3.0
载入中...
搜索中...
未找到
ribbonbutton.cpp
浏览该文件的文档.
1
5#include "ribbonbutton.h"
6#include "ribbonbutton_p.h"
7#include "ribbongroup.h"
8
9#include <QCursor>
10#include <QAction>
11#include <QDebug>
12#include <QMouseEvent>
13#include <QPaintEvent>
14#include <QStyleOption>
15#include <QStyleOptionFocusRect>
16#include <QStyleOptionToolButton>
17#include <QStylePainter>
18
20
21#define QX_INDICATOR_ARROW_WIDTH 8
22#define QX_WIDTH_HEIGHT_RATIO 1.2
23
27#define DebugRibbonButton_TextDrawPrint 0
28
29// #define QX_RIBBON_DEBUG_HELP_DRAW
30
31#ifdef QX_RIBBON_DEBUG_HELP_DRAW
32#define HELP_DRAW_RECT(p, rect) \
33 do { \
34 p.save(); \
35 p.setPen(Qt::red); \
36 p.setBrush(QBrush()); \
37 p.drawRect(rect); \
38 p.restore(); \
39 } while (0)
40
41QDebug operator<<(QDebug debug, const QStyleOptionToolButton &opt)
42{
43 debug << "=============="
44 << "\nQStyleOption(" << (QStyleOption)opt << ")"
45 << "\n QStyleOptionComplex:"
46 "\n subControls("
47 << opt.subControls
48 << " ) "
49 "\n activeSubControls("
50 << opt.activeSubControls
51 << "\n QStyleOptionToolButton"
52 "\n features("
53 << opt.features
54 << ")"
55 "\n toolButtonStyle("
56 << opt.toolButtonStyle << ")";
57
58 return (debug);
59}
60
61#endif
62
63bool RibbonButtonPrivate::s_isToolButtonTextShift = false;
64
65RibbonButtonPrivate::RibbonButtonPrivate()
66 : m_buttonType(RibbonButton::LargeButton)
67 , m_largeButtonType(RibbonButton::Normal)
68 , m_mouseOnSubControl(false)
69 , m_menuButtonPressed(false)
70 , m_isWordWrap(false)
71 , m_spacing(2)
72{
73}
74
78
80{
81 Q_Q(const RibbonButton);
82 return q->style();
83}
84
95void RibbonButtonPrivate::recalcSizeHint(QStyleOptionToolButton &opt, QSize s)
96{
97 Q_Q(RibbonButton);
98 // QToolButton的sizeHint已经考虑了菜单箭头的位置
99 // 从源码看,QToolButton的sizeHint是不会考虑换行的
101 // 计算最佳大小
102 if (RibbonGroup *group = qobject_cast<RibbonGroup *>(q->parent())) {
103 // 对于建立在RibbonGroup的基础上的大按钮,把高度设置为RibbonGroup计算的大按钮高度
104 s.setHeight(group->largeHeight());
105 }
107 // 普通模式下才涉及到文字换行
108 if (s.width() > s.height() * QX_WIDTH_HEIGHT_RATIO) {
109 // 说明是一个长方形按钮,如果宽度大于高度,就看看换行是否能满足
110 QFontMetrics fm = q->fontMetrics();
111 QRect textRange(0, 0, s.width(), s.height());
112
114 {
115 const int maxTryCount = 3;
116 int tryCount = 0;
117 int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; // 文本对齐方式
118 do {
119 // 先计算两行文本的紧凑矩形,从一半开始逐渐递增
120 textRange.setWidth(s.width() / 2 + (s.width() / 2) * (float(tryCount) / maxTryCount));
121 textRange = fm.boundingRect(textRange, alignment, opt.text);
122 // Linux GNOME平台上,fm.lineSpacing()为15,textRange.height()为35
123 // Windows 10平台上,fm.lineSpacing()为16,textRange.height()为32
124#ifdef Q_OS_WINDOWS
125 if (textRange.height() <= (fm.lineSpacing() * 2)) {
126#else
127 if (textRange.height() <= (fm.lineSpacing() * 2.5)) {
128#endif
129 // 保证在两行
130 m_isWordWrap = (textRange.height() > fm.lineSpacing());
131 break;
132 }
133 ++tryCount;
134 } while (tryCount < maxTryCount);
135 }
136 // 左右留2像素
137 s.setWidth(textRange.width() + m_spacing * 2);
139 if ((opt.features & QStyleOptionToolButton::MenuButtonPopup) ||
140 (opt.features & QStyleOptionToolButton::HasMenu)) {
141 if (m_isWordWrap) {
142 s.rwidth() += QX_INDICATOR_ARROW_WIDTH + m_spacing;
143 }
144 }
145 } else {
146 m_isWordWrap = opt.text.contains('\n');
147 if ((opt.features & QStyleOptionToolButton::MenuButtonPopup) ||
148 (opt.features & QStyleOptionToolButton::HasMenu)) {
149 if (m_isWordWrap) {
150 s.rwidth() += QX_INDICATOR_ARROW_WIDTH + m_spacing;
151 }
152 }
153 }
154 } else {
155 // 否则就是lite模式,只允许1行,有菜单就偏移
156 if (s.width() > s.height() * 2) {
157 // 过于宽的按钮,把文字用...来替代
158 s.rwidth() = s.height() * 2;
159 }
160 if ((opt.features & QStyleOptionToolButton::MenuButtonPopup) ||
161 (opt.features & QStyleOptionToolButton::HasMenu)) {
162 s.rwidth() += QX_INDICATOR_ARROW_WIDTH + m_spacing;
163 }
164 }
166 if (s.width() < s.height()) {
167 // 短文本的图标相对于长文本的图标可能会比较小,此处根据高度扩展短文本的按钮宽度
168 int textHeight = q->fontMetrics().lineSpacing();
169 if (m_isWordWrap && !opt.text.contains('\n')) {
170 textHeight <<= 1;
171 }
172 int iconHeight = s.height() - textHeight - m_spacing;
173 if (iconHeight > s.width()) {
174 s.rwidth() = iconHeight;
175 }
176 }
177 }
178 } else {
179 // InstantPopup在qtoolbutton不会添加控件来放下箭头,这里处理的和MenuButtonPopup一致
180 // 在仅有图标的小模式显示时,预留一个下拉箭头位置
181#ifdef Q_OS_WINDOWS
182 if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) {
183#else
184 if (1) { // Tested on ubuntu 22.04
185#endif
186 if (opt.features & QStyleOptionToolButton::MenuButtonPopup ||
187 opt.features & QStyleOptionToolButton::HasMenu) {
188 // 如果有菜单
189 // s.rwidth() += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
190 s.rwidth() += QX_INDICATOR_ARROW_WIDTH + m_spacing;
191 }
192 }
193 }
194 m_sizeHint = s;
195}
196
205void RibbonButtonPrivate::drawIconAndLabel(QStyleOptionToolButton &opt, QPainter &p, const QWidget *w)
206{
208 // 绘制图标和文字
209 QRect textRect = adjustedTextRect(opt, w);
210 bool isArrow = opt.features & QStyleOptionToolButton::Arrow;
211 if (((!isArrow && opt.icon.isNull()) && !opt.text.isEmpty()) ||
212 (opt.toolButtonStyle == Qt::ToolButtonTextOnly)) {
213 // 没有箭头 且 没图标 有文字 || 只有文字模式
214 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextWordWrap; // 纯文本下,居中对齐,换行
215 if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, w)) {
216 alignment |= Qt::TextHideMnemonic;
217 }
218 p.setFont(opt.font);
219 style()->drawItemText(&p, textRect, alignment, opt.palette, opt.state & QStyle::State_Enabled, opt.text,
220 QPalette::ButtonText);
221 } else {
222 // 非纯文本
223 if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
224 // 文本加图标情况
225 p.setFont(opt.font);
226 int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; // 换行
227 if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, w)) {
228 alignment |= Qt::TextHideMnemonic;
229 }
230 // 文字在icon下
231 // 先绘制图标
232 if (!isArrow) {
233#ifdef QX_RIBBON_DEBUG_HELP_DRAW
235#endif
236 QPixmap pm = createIconPixmap(opt, m_iconRect.size());
237 style()->drawItemPixmap(&p, m_iconRect, Qt::AlignCenter, pm);
238 } else {
239 drawArrow(style(), &opt, m_iconRect, &p, w);
240 }
241#ifdef QX_RIBBON_DEBUG_HELP_DRAW
242 HELP_DRAW_RECT(p, textRect);
243#endif
245 alignment |= Qt::AlignCenter;
246 } else {
247 alignment |= Qt::AlignHCenter | Qt::AlignTop; // 文字是顶部对齐
248 }
249
250 QString text = opt.text;
251 // 再绘制文本,对于Normal模式下的Largebutton,如果有菜单,且m_isWordWrap是true,箭头将在文本旁边
253 // lite 模式,文字不换行, 显示的内容需要进行省略处理
254 text.remove('\n');
255 text = w->fontMetrics().elidedText(text, Qt::ElideRight, textRect.width(), Qt::TextShowMnemonic);
256 }
257 style()->drawItemText(&p, QStyle::visualRect(opt.direction, opt.rect, textRect), alignment, opt.palette,
258 opt.state & QStyle::State_Enabled, text, QPalette::ButtonText);
259 } else {
260 // 只有图标情况
261 if (isArrow) {
262 drawArrow(style(), &opt, opt.rect, &p, w);
263 } else {
264 QPixmap pm = createIconPixmap(opt, m_iconRect.size());
265 style()->drawItemPixmap(&p, m_iconRect, Qt::AlignCenter, pm);
266 }
267 }
268 // 绘制sub control 的下拉箭头
269 if (opt.features & QStyleOptionToolButton::MenuButtonPopup ||
270 opt.features & QStyleOptionToolButton::HasMenu) {
271 opt.rect = calcIndicatorArrowDownRect(opt);
272#ifdef QX_RIBBON_DEBUG_HELP_DRAW
273 HELP_DRAW_RECT(p, opt.rect);
274#endif
275 style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &opt, &p, w);
276 }
277 }
278 } else {
279 // 小图标
280 if (!opt.icon.isNull() && opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
281 QPixmap pm = createIconPixmap(opt, m_iconRect.size());
282
283 if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
284 p.save();
285 p.setFont(opt.font);
286
287 QRect pr = m_iconRect; // 图标区域
288 QRect tr = opt.rect.adjusted(pr.width() + 2 * m_spacing, 0, -1, 0); // 文本区域
289
290#ifdef QX_RIBBON_DEBUG_HELP_DRAW
291 HELP_DRAW_RECT(p, pr);
292 HELP_DRAW_RECT(p, tr);
293#endif
294
295 int alignment = Qt::TextShowMnemonic;
296 // 快捷键的下划线
297 if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, w)) {
298 alignment |= Qt::TextHideMnemonic;
299 }
300 if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
301 // ribbonbutton在小图标下,不支持ToolButtonTextUnderIcon
302 } else {
303 style()->drawItemPixmap(&p, QStyle::visualRect(opt.direction, opt.rect, pr), Qt::AlignCenter, pm);
304 alignment |= Qt::AlignLeft | Qt::AlignVCenter;
305 }
306 style()->drawItemText(&p, QStyle::visualRect(opt.direction, opt.rect, tr), alignment, opt.palette,
307 opt.state & QStyle::State_Enabled, opt.text, QPalette::ButtonText);
308 p.restore();
309 } else {
310 style()->drawItemPixmap(&p, m_iconRect, Qt::AlignCenter, pm);
311 }
312 } else { // 只有文字
313 int alignment = Qt::TextShowMnemonic;
314 // 快捷键的下划线
315 if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, w)) {
316 alignment |= Qt::TextHideMnemonic;
317 }
318 style()->drawItemText(&p, QStyle::visualRect(opt.direction, opt.rect, opt.rect.adjusted(2, 1, -2, -1)),
319 alignment, opt.palette, opt.state & QStyle::State_Enabled, opt.text,
320 QPalette::ButtonText);
321 }
322
323 // 绘制sub control 的下拉箭头
324 if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) {
325 QStyleOptionToolButton tool = opt;
326 tool.rect = calcIndicatorArrowDownRect(tool);
327#ifdef QX_RIBBON_DEBUG_HELP_DRAW
328 HELP_DRAW_RECT(p, tool.rect);
329#endif
330 style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, w);
331 }
332 }
333}
334
335void RibbonButtonPrivate::drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton, const QRect &rect,
336 QPainter *painter, const QWidget *widget)
337{
338 QStyle::PrimitiveElement pe;
339
340 switch (toolbutton->arrowType) {
341 case Qt::LeftArrow:
342 pe = QStyle::PE_IndicatorArrowLeft;
343 break;
344 case Qt::RightArrow:
345 pe = QStyle::PE_IndicatorArrowRight;
346 break;
347 case Qt::UpArrow:
348 pe = QStyle::PE_IndicatorArrowUp;
349 break;
350 case Qt::DownArrow:
351 pe = QStyle::PE_IndicatorArrowDown;
352 break;
353 default:
354 return;
355 }
356
357 QStyleOption arrowOpt = *toolbutton;
358 arrowOpt.rect = rect;
359 style->drawPrimitive(pe, &arrowOpt, painter, widget);
360}
361
366void RibbonButtonPrivate::calcIconAndTextRect(const QStyleOptionToolButton &opt)
367{
368 Q_Q(RibbonButton);
370 m_iconRect = opt.rect.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing);
371 if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
372 // sizehint已经保证宽度能显示两行
373 // icon 的区域就是文字以外的区域,如果m_isEnableWordWrap=true,就保证有两行文本的空间
374 QFontMetrics fm = q->fontMetrics();
375 int maxheight = 30;
377 // 减去2行文本,减去上下边距,减去icon和text的分隔距离
378 maxheight = opt.rect.height() - (fm.lineSpacing() * 2) - 3 * m_spacing;
379 } else {
380 maxheight = opt.rect.height() - (fm.lineSpacing() * 1.2) - 3 * m_spacing;
381 }
382 m_iconRect.setHeight(maxheight);
383
384 // 没有菜单,高度是矩形底部减去icon底部再减去2*Space
385 m_textRect.setRect(m_spacing, m_iconRect.bottom() + m_spacing, m_iconRect.width(),
386 opt.rect.bottom() - m_iconRect.bottom() - 2 * m_spacing);
387 // 保证能显示两行文本
388 // 下面确定文字区域
389 if ((opt.features & QStyleOptionToolButton::MenuButtonPopup) ||
390 (opt.features & QStyleOptionToolButton::HasMenu)) {
391 // 有菜单且换行,宽度偏移ARROW_WIDTH
392 if (m_isWordWrap) {
393 m_textRect.adjust(0, 0, -(QX_INDICATOR_ARROW_WIDTH + m_spacing), 0);
395 // 在lite模式下,不允许换行的时候,也需要偏移下三角
396 m_textRect.adjust(0, 0, -(QX_INDICATOR_ARROW_WIDTH + m_spacing), 0);
397 }
398 }
399 } else {
400 m_textRect = QRect();
401 }
402 } else {
403 // 小按钮
404 if (opt.toolButtonStyle == Qt::ToolButtonIconOnly) {
405 // InstantPopup在qtoolbutton不会添加控件来放下箭头,这里处理的和MenuButtonPopup一致
406 // 在仅有图标的小模式显示时,预留一个下拉箭头位置
407 m_iconRect = opt.rect.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing);
408 if (opt.features & QStyleOptionToolButton::MenuButtonPopup ||
409 opt.features & QStyleOptionToolButton::HasMenu) {
410 m_iconRect.adjust(0, 0, -(QX_INDICATOR_ARROW_WIDTH + m_spacing), 0);
411 }
412 // 确定文本区域
413 m_textRect = QRect();
414 } else {
415 m_iconRect = QRect(m_spacing, m_spacing, qMax(opt.rect.height(), opt.iconSize.width()) - 2 * m_spacing,
416 opt.rect.height() - 2 * m_spacing);
417 m_textRect.setRect(m_iconRect.right() + m_spacing, m_spacing,
418 opt.rect.right() - m_iconRect.right() - 2 * m_spacing,
419 opt.rect.height() - 2 * m_spacing);
420 // 确定文本区域
421 if ((opt.features & QStyleOptionToolButton::MenuButtonPopup) ||
422 (opt.features & QStyleOptionToolButton::HasMenu)) {
423 // 有菜单,宽度偏移ARROW_WIDTH
424 m_textRect.adjust(0, 0, -(QX_INDICATOR_ARROW_WIDTH + m_spacing), 0);
425 }
426 }
427 }
428
429 // 纯文本的文字位置
430 bool isArrow = opt.features & QStyleOptionToolButton::Arrow;
431 if ((Qt::ToolButtonTextOnly == opt.toolButtonStyle) || (opt.icon.isNull() && !isArrow)) {
432 m_textRect = opt.rect.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing);
433 }
434}
435
442QRect RibbonButtonPrivate::adjustedTextRect(const QStyleOptionToolButton &opt, const QWidget *w)
443{
444 QRect rect = m_textRect;
445
446 // 这两个偏移量是有些按钮在点击时文字会出现一个类似抖动的效果,但据观察office2013的ribbon是不会动的,因此把这个效果取消
448 int shiftX = 0;
449 int shiftY = 0;
450
451 if (opt.state & (QStyle::State_Sunken | QStyle::State_On)) {
452 shiftX = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, w);
453 shiftY = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, w);
454 }
455
456 rect.translate(shiftX, shiftY);
457 }
458 return rect;
459}
460
469QRect RibbonButtonPrivate::calcIndicatorArrowDownRect(const QStyleOptionToolButton &opt)
470{
471 // 预留 QX_INDICATOR_ARROW_WIDTH px绘制箭头
472 QRect rect = opt.rect;
474 // Lite下,箭头的大小通过rect的宽度控制(见calcIconAndTextRect中的m_textRect),
475 // 否则通过rect的高度控制(见下文的QX_INDICATOR_ARROW_WIDTH)
477 // 首先判断是否为lite且不允许换行
478 // yh + QX_INDICATOR_ARROW_WIDTH + yh = m_textRect.height()
479 // yh = (m_textRect.height() - QX_INDICATOR_ARROW_WIDTH) / 2
480 // y = m_textRect.top() + yh
481 int y = m_textRect.top() + (m_textRect.height() - QX_INDICATOR_ARROW_WIDTH) / 2;
483 } else if (m_isWordWrap) {
484 // 如果允许换行,那么就在文本第二行最后端,菜单的下拉箭头位于第二行文本的矩形区域
485 rect.setRect(m_textRect.right() + m_spacing, m_textRect.top() + m_textRect.height() / 2,
487 } else {
488 // 都不是的情况下就第二行文本位置 |y坐标
489 // m_textRect.height() / 2 + yh + QX_INDICATOR_ARROW_WIDTH + yh = m_textRect.height()
490 // yh = m_textRect.height() / 4 - QX_INDICATOR_ARROW_WIDTH / 2
491 // y = m_textRect.bottom() - yh - QX_INDICATOR_ARROW_WIDTH
492 int y = m_textRect.bottom() - m_textRect.height() / 4 - QX_INDICATOR_ARROW_WIDTH / 2;
493 rect.setRect(m_textRect.left(), y, m_textRect.width(), QX_INDICATOR_ARROW_WIDTH);
494 }
495 } else {
497 rect.height() - 2 * m_spacing);
498 }
499 m_indicatorArrowRect = rect; // 记录
500 return rect;
501}
502
503QPixmap RibbonButtonPrivate::createIconPixmap(const QStyleOptionToolButton &opt, const QSize &iconsize)
504{
505 Q_Q(RibbonButton);
506 if (!opt.icon.isNull()) { // 有图标
507 QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off;
508 QIcon::Mode mode;
509 if (!(opt.state & QStyle::State_Enabled)) {
510 mode = QIcon::Disabled;
511 } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) {
512 mode = QIcon::Active;
513 } else {
514 mode = QIcon::Normal;
515 }
516#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
517 // return (opt.icon.pixmap(q->window()->windowHandle(), opt.rect.size().boundedTo(iconsize), mode, state));
518 return opt.icon.pixmap(q->window()->windowHandle(), iconsize, mode, state);
519#else
520 return opt.icon.pixmap(iconsize, q->window()->devicePixelRatio(), mode, state);
521#endif
522 }
523 return QPixmap();
524}
525
527{
528 Q_Q(RibbonButton);
529 bool isMouseOnSubControl(false);
531 isMouseOnSubControl = m_textRect.united(m_indicatorArrowRect).contains(pos);
532 } else {
533 // 小按钮模式就和普通toolbutton一样
534 isMouseOnSubControl = m_indicatorArrowRect.contains(pos);
535 }
536 if (m_mouseOnSubControl != isMouseOnSubControl) {
537 m_mouseOnSubControl = isMouseOnSubControl;
538 // 从icon变到text过程中刷新一次
539 q->update();
540 }
541}
542
543void RibbonButtonPrivate::drawButton(QStyleOptionToolButton &opt, QPainter &p, const QWidget *w)
544{
545 if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) {
546 if (!w->rect().contains(w->mapFromGlobal(QCursor::pos()))) {
547 opt.state &= ~QStyle::State_MouseOver;
548 }
549 }
550 bool autoRaise = opt.state & QStyle::State_AutoRaise;
551 QStyle::State bflags = opt.state & ~QStyle::State_Sunken;
552
553 if (autoRaise) {
554 if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) {
555 bflags &= ~QStyle::State_Raised;
556 }
557 }
558 if (opt.state & QStyle::State_Sunken) {
559 if (opt.activeSubControls & QStyle::SC_ToolButton) {
560 bflags |= QStyle::State_Sunken;
561 } else if (opt.activeSubControls & QStyle::SC_ToolButtonMenu) {
562 bflags |= QStyle::State_MouseOver;
563 }
564 }
565 // 绘制背景
566 QStyleOption tool(0);
567 tool.palette = opt.palette;
568 QStyle::PrimitiveElement pe = autoRaise ? QStyle::PE_PanelButtonTool : QStyle::PE_PanelButtonBevel;
569 if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) {
570 tool.rect = opt.rect;
571 tool.state = bflags;
572 if (opt.activeSubControls & QStyle::SC_ToolButtonMenu) {
573 // 菜单激活,整个按钮都绘制为选中
574 style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, w);
575 } else {
576 // 菜单没有激活,这时候要把图标域或者文字域绘制为正常模式
577 style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, w);
578 if (tool.state & QStyle::State_MouseOver) {
580 if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) { // LargeButton
581 tool.rect = m_iconRect; // 鼠标在文字区,把图标显示为正常
582 } else {
583 tool.rect = m_iconRect.united(m_textRect); // 鼠标在箭头区,把图标和文字显示为正常
584 }
585 } else {
586 if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) { // LargeButton
587 tool.rect = m_textRect.united(m_indicatorArrowRect); // 鼠标在图标区,把文字显示为正常
588 } else {
589 tool.rect = m_indicatorArrowRect; // 鼠标在非箭头区,把箭头区显示为正常
590 }
591 }
592 tool.state = QStyle::State_Raised;
593 style()->drawPrimitive(pe, &tool, &p, w);
594 }
595 }
596 } else if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::HasMenu)) {
597 tool.rect = opt.rect;
598 tool.state = bflags;
599 style()->drawPrimitive(pe, &tool, &p, w);
600 } else if (opt.subControls & QStyle::SC_ToolButton) {
601 tool.rect = opt.rect;
602 tool.state = bflags;
603 if (opt.state & QStyle::State_Sunken) {
604 tool.state &= ~QStyle::State_MouseOver;
605 }
606 style()->drawPrimitive(pe, &tool, &p, w);
607 }
608 drawIconAndLabel(opt, p, w);
609}
610
612 : QToolButton(parent)
613{
615 setAutoRaise(true);
617 setMouseTracking(true);
618}
619
620RibbonButton::RibbonButton(QAction *defaultAction, QWidget *parent)
621 : RibbonButton(parent)
622{
623 setDefaultAction(defaultAction);
624}
625
627 : QToolButton(parent)
628{
630 setAutoRaise(true);
632 setMouseTracking(true);
633}
634
639
640void RibbonButton::paintEvent(QPaintEvent *event)
641{
642 Q_D(RibbonButton);
643 Q_UNUSED(event);
644 QPainter p(this);
645 QStyleOptionToolButton opt;
646 initStyleOption(&opt);
647 d->drawButton(opt, p, this);
648}
649
651{
652 Q_D(RibbonButton);
653 d->updateButtonState(e->pos());
654 QToolButton::mouseMoveEvent(e);
655}
656
658{
659 Q_D(RibbonButton);
660 if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) {
661 d->updateButtonState(e->pos());
662 if (d->m_mouseOnSubControl) {
663 d->m_menuButtonPressed = true;
664 showMenu();
665 // showMenu结束后,再判断当前的鼠标位置是否是在SubControl
666 d->updateButtonState(mapFromGlobal(QCursor::pos()));
667 return;
668 }
669 }
670 d->m_menuButtonPressed = false;
671 // 此处用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent
672 // 对于带菜单的QToolButton,QToolButton的mousePressEvent默认是点击按钮右侧弹出菜单,
673 // 弹出菜单的点击位置在上面进行了定制处理,所以不需要再用QToolButton的mousePressEvent进行处理
674 QAbstractButton::mousePressEvent(e);
675}
676
678{
679 Q_D(RibbonButton);
680 QToolButton::mouseReleaseEvent(e);
681 d->m_menuButtonPressed = false;
682}
683
684void RibbonButton::focusOutEvent(QFocusEvent *e)
685{
686 Q_D(RibbonButton);
687 QToolButton::focusOutEvent(e);
688 d->m_mouseOnSubControl = false;
689}
690
692{
693 Q_D(RibbonButton);
694 d->m_mouseOnSubControl = false;
695 QToolButton::leaveEvent(e);
696}
697
698bool RibbonButton::hitButton(const QPoint &pos) const
699{
700 Q_D(const RibbonButton);
701 if (QAbstractButton::hitButton(pos)) {
702 return !d->m_menuButtonPressed;
703 }
704 return false;
705}
706
711void RibbonButton::resizeEvent(QResizeEvent *e)
712{
713 Q_D(RibbonButton);
714#if DebugRibbonButton_TextDrawPrint
715 qDebug() << "RibbonButton::resizeEvent, text=" << text() << " obj=" << objectName() << " size=" << e->size();
716#endif
717 QToolButton::resizeEvent(e);
718 QStyleOptionToolButton opt;
719 initStyleOption(&opt);
720 d->calcIconAndTextRect(opt);
721}
722
729{
730 QSize sz = QToolButton::sizeHint();
731 QStyleOptionToolButton opt;
732 initStyleOption(&opt);
733 d_ptr->recalcSizeHint(opt, sz);
734 return d_ptr->m_sizeHint;
735}
736
738{
739 Q_D(RibbonButton);
740 if (e) {
741 if (e->type() == QEvent::FontChange) {
742 // todo 说明字体改变,需要重新计算和字体相关的信息
743 QStyleOptionToolButton opt;
744 initStyleOption(&opt);
745 d->calcIconAndTextRect(opt);
746 }
747 }
748 QToolButton::changeEvent(e);
749}
750
751void RibbonButton::actionEvent(QActionEvent *e)
752{
753 QToolButton::actionEvent(e);
754}
755
757{
758 Q_D(const RibbonButton);
759 return d->m_buttonType;
760}
761
769{
770 Q_D(RibbonButton);
771 d->m_buttonType = type;
772 if (LargeButton == type) {
773 setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
774 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
775 } else {
776 setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
777 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
778 }
779}
780
782{
783 return sizeHint();
784}
785
787{
788 Q_D(RibbonButton);
789 d->m_largeButtonType = type;
790}
791
793{
794 Q_D(const RibbonButton);
795 return d->m_largeButtonType;
796}
797
806
811
812bool RibbonButton::event(QEvent *e)
813{
814 Q_D(RibbonButton);
815 switch (e->type()) {
816 case QEvent::WindowDeactivate:
817 case QEvent::ActionChanged:
818 case QEvent::ActionRemoved:
819 case QEvent::ActionAdded:
820 d->m_mouseOnSubControl = false;
821 break;
822 default:
823 break;
824 }
825
826 return QToolButton::event(e);
827}
828
QRect m_iconRect
记录icon的绘制位置
void calcIconAndTextRect(const QStyleOptionToolButton &opt)
根据设定计算图标和文本的绘制区域
QRect adjustedTextRect(const QStyleOptionToolButton &opt, const QWidget *w)
根据设定计算文本显示区域
void updateButtonState(const QPoint &pos)
QRect calcIndicatorArrowDownRect(const QStyleOptionToolButton &opt)
sub control 的下拉箭头的位置
void recalcSizeHint(QStyleOptionToolButton &opt, QSize s)
重新计算sizehint,sizehint函数在第一次计算完成后会记录结果,如果文字字体或者内容不变是不需要进行重复计算
QRect m_indicatorArrowRect
记录IndicatorArrow的绘制位置
virtual QPixmap createIconPixmap(const QStyleOptionToolButton &opt, const QSize &iconsize)
static bool s_isToolButtonTextShift
配置RibbonButton文字在点击时是否会轻微改变位置而达到一种类似跳动的效果
void drawIconAndLabel(QStyleOptionToolButton &opt, QPainter &p, const QWidget *w)
RibbonButton::drawIconAndLabel
void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton, const QRect &rect, QPainter *painter, const QWidget *widget=Q_NULLPTR)
QSize m_sizeHint
保存计算好的sizehint
int m_spacing
按钮和边框的距离
QRect m_textRect
记录text的绘制位置
RibbonButton::LargeButtonType m_largeButtonType
void drawButton(QStyleOptionToolButton &opt, QPainter &p, const QWidget *w)
bool m_isWordWrap
标记是否文字换行,默认 false
RibbonButton::RibbonButtonType m_buttonType
bool m_mouseOnSubControl
这个用于标记MenuButtonPopup模式下,鼠标在文本区域
Ribbon 界面适用的 toolButton
virtual void changeEvent(QEvent *e) override
virtual void resizeEvent(QResizeEvent *e) override
在 resizeevent 计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率
virtual bool event(QEvent *e) override
static void setToolButtonTextShift(bool on)
设置按钮点击时文字会闪烁一下
LargeButtonType largeButtonType() const
virtual void mouseMoveEvent(QMouseEvent *e) override
virtual void leaveEvent(QEvent *e) override
RibbonButton(QWidget *parent=Q_NULLPTR)
virtual void mousePressEvent(QMouseEvent *e) override
void setLargeButtonType(LargeButtonType type)
RibbonButtonType buttonType() const
virtual void paintEvent(QPaintEvent *event) override
virtual QSize sizeHint() const override
toolbutton 的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行
void setButtonType(RibbonButtonType type)
设置按钮样式
virtual bool hitButton(const QPoint &pos) const override
static bool isToolButtonTextShift()
virtual void actionEvent(QActionEvent *e) override
virtual QSize minimumSizeHint() const override
virtual void focusOutEvent(QFocusEvent *e) override
@ Lite
icon占大按钮的60,文字占40,且文字不换行,对应于wps模式的菜单按钮,下箭头在文字旁边
@ Normal
icon占大按钮的一半区域, 箭头在按钮最下方,文字可以换一行,对应于office的菜单按钮,下箭头在按钮最下方(默认)
virtual void mouseReleaseEvent(QMouseEvent *e) override
group 用于对 ribbon 页窗口中的控件进行分组
Definition ribbongroup.h:29
#define QX_INIT_PRIVATE(Class)
#define QX_SET_PRIVATE(Dptr)
#define QX_FINI_PRIVATE()
#define QX_RIBBON_END_NAMESPACE
#define QX_RIBBON_BEGIN_NAMESPACE
QColor text
#define HELP_DRAW_RECT(p, rect)
Definition ribbonbar.cpp:37
#define QX_INDICATOR_ARROW_WIDTH
#define QX_WIDTH_HEIGHT_RATIO