VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UITabBar.cpp@ 82781

Last change on this file since 82781 was 76606, checked in by vboxsync, 5 years ago

FE/Qt: Cleaning out old precompiled header experiment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.7 KB
Line 
1/* $Id: UITabBar.cpp 76606 2019-01-02 05:40:39Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UITabBar class implementation.
4 */
5
6/*
7 * Copyright (C) 2017-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QAction>
20#include <QApplication>
21#include <QDrag>
22#include <QDragEnterEvent>
23#include <QDragMoveEvent>
24#include <QDropEvent>
25#include <QEvent>
26#include <QHBoxLayout>
27#include <QLabel>
28#include <QMimeData>
29#include <QMouseEvent>
30#include <QStyleOption>
31#include <QPainter>
32#ifdef VBOX_WS_MAC
33# include <QStackedLayout>
34#endif
35#include <QStyle>
36#include <QToolButton>
37
38/* GUI includes: */
39#include "QIWithRetranslateUI.h"
40#include "UIIconPool.h"
41#include "UITabBar.h"
42
43/* Other VBox includes: */
44#include "iprt/assert.h"
45
46/* Forward declarations: */
47class QApplication;
48class QDrag;
49class QEvent;
50class QHBoxLayout;
51class QLabel;
52class QMimeData;
53class QMouseEvent;
54#ifdef VBOX_WS_MAC
55class QStackedLayout;
56#endif
57class QStyle;
58class QStyleOption;
59class QToolButton;
60
61
62/** Our own skinnable implementation of tabs for tab-bar. */
63class UITabBarItem : public QIWithRetranslateUI<QWidget>
64{
65 Q_OBJECT;
66
67signals:
68
69 /** Notifies about item was clicked. */
70 void sigClicked(UITabBarItem *pItem);
71
72 /** Notifies about item close button was clicked. */
73 void sigCloseClicked(UITabBarItem *pItem);
74
75 /** Notifies about drag-object destruction. */
76 void sigDragObjectDestroy();
77
78public:
79
80 /** Position styles. */
81 enum PositionStyle { PositionStyle_Left, PositionStyle_Middle, PositionStyle_Right, PositionStyle_Single };
82
83 /** Holds the mime-type for the D&D system. */
84 static const QString MimeType;
85
86 /** Creates tab-bar item on the basis of passed @a uuid and @a pAction. */
87 UITabBarItem(const QUuid &uuid, const QAction *pAction);
88
89 /** Returns item ID. */
90 const QUuid uuid() const { return m_uuid; }
91
92 /** Defines the item @a enmPositionStyle. */
93 void setPositionStyle(PositionStyle enmPositionStyle);
94
95 /** Marks item @a fCurrent. */
96 void setCurrent(bool fCurrent);
97
98protected:
99
100 /** Handles any Qt @a pEvent. */
101 virtual bool event(QEvent *pEvent) /* override */;
102
103 /** Handles translation event. */
104 virtual void retranslateUi() /* override */;
105
106 /** Handles paint @a pEvent. */
107 virtual void paintEvent(QPaintEvent *pEvent) /* override */;
108
109 /** Handles mouse-press @a pEvent. */
110 virtual void mousePressEvent(QMouseEvent *pEvent) /* override */;
111 /** Handles mouse-release @a pEvent. */
112 virtual void mouseReleaseEvent(QMouseEvent *pEvent) /* override */;
113 /** Handles mouse-move @a pEvent. */
114 virtual void mouseMoveEvent(QMouseEvent *pEvent) /* override */;
115 /** Handles mouse-enter @a pEvent. */
116 virtual void enterEvent(QEvent *pEvent) /* override */;
117 /** Handles mouse-leave @a pEvent. */
118 virtual void leaveEvent(QEvent *pEvent) /* override */;
119
120private slots:
121
122 /** Handles close button click. */
123 void sltCloseClicked() { emit sigCloseClicked(this); }
124
125private:
126
127 /** Prepares all. */
128 void prepare();
129 /** Update pixmap. */
130 void updatePixmap();
131
132 /** Holds the item ID. */
133 const QUuid m_uuid;
134 /** Holds the item action reference. */
135 const QAction *m_pAction;
136
137 /** Holds the item position style. */
138 PositionStyle m_enmPosition;
139
140 /** Holds whether the item is current. */
141 bool m_fCurrent;
142 /** Holds whether the item is hovered. */
143 bool m_fHovered;
144
145 /** Holds the main layout instance. */
146 QHBoxLayout *m_pLayout;
147#ifdef VBOX_WS_MAC
148 /** Holds the stacked layout instance. */
149 QStackedLayout *m_pLayoutStacked;
150#endif
151
152 /** Holds the icon label instance. */
153 QLabel *m_pLabelIcon;
154 /** Holds the name label instance. */
155 QLabel *m_pLabelName;
156 /** Holds the close button instance. */
157 QToolButton *m_pButtonClose;
158
159 /** Holds the last mouse-press position. */
160 QPoint m_mousePressPosition;
161};
162
163
164/*********************************************************************************************************************************
165* Class UITabBarItem implementation. *
166*********************************************************************************************************************************/
167
168/* static */
169const QString UITabBarItem::MimeType = QString("application/virtualbox;value=TabID");
170
171UITabBarItem::UITabBarItem(const QUuid &uuid, const QAction *pAction)
172 : m_uuid(uuid)
173 , m_pAction(pAction)
174 , m_enmPosition(PositionStyle_Single)
175 , m_fCurrent(false)
176 , m_fHovered(false)
177 , m_pLayout(0)
178#ifdef VBOX_WS_MAC
179 , m_pLayoutStacked(0)
180#endif
181 , m_pLabelIcon(0)
182 , m_pLabelName(0)
183 , m_pButtonClose(0)
184{
185 /* Prepare: */
186 prepare();
187}
188
189void UITabBarItem::setPositionStyle(PositionStyle enmPosition)
190{
191 /* Remember the position: */
192 m_enmPosition = enmPosition;
193
194 /* And call for repaint: */
195 update();
196}
197
198void UITabBarItem::setCurrent(bool fCurrent)
199{
200 /* Remember the state: */
201 m_fCurrent = fCurrent;
202
203#ifdef VBOX_WS_MAC
204 /* Adjust name color: */
205 QPalette pal = qApp->palette();
206 if (m_fCurrent)
207 pal.setColor(QPalette::ButtonText, pal.color(QPalette::BrightText));
208 m_pLabelName->setPalette(pal);
209#endif
210
211 /* And call for repaint: */
212 update();
213}
214
215bool UITabBarItem::event(QEvent *pEvent)
216{
217 /* Handle know event types: */
218 switch (pEvent->type())
219 {
220 case QEvent::Show:
221 case QEvent::ScreenChangeInternal:
222 {
223 /* Update pixmap: */
224 updatePixmap();
225 break;
226 }
227 default:
228 break;
229 }
230
231 /* Call to base-class: */
232 return QIWithRetranslateUI<QWidget>::event(pEvent);
233}
234
235void UITabBarItem::retranslateUi()
236{
237 /* Translate label: */
238 m_pLabelName->setText(m_pAction->text().remove('&'));
239}
240
241void UITabBarItem::paintEvent(QPaintEvent * /* pEvent */)
242{
243#ifdef VBOX_WS_MAC
244
245 /* Prepare painter: */
246 QPainter painter(this);
247
248 /* Prepare palette colors: */
249 const QPalette pal = palette();
250 const QColor color0 = m_fCurrent
251 ? pal.color(QPalette::Shadow).darker(110)
252 : pal.color(QPalette::Window).lighter(105);
253 const QColor color1 = pal.color(QPalette::Window);
254 const QColor color2 = color0.darker(120);
255 const QColor color3 = color0.darker(130);
256
257 /* Invent pixel metric: */
258 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
259
260 /* Top-left corner: */
261 QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
262 {
263 grad1.setColorAt(0, color0);
264 grad1.setColorAt(.8, color0);
265 grad1.setColorAt(.81, color2);
266 grad1.setColorAt(1, color1);
267 }
268 /* Top-right corner: */
269 QRadialGradient grad2(QPointF(width() - iMetric, iMetric), iMetric);
270 {
271 grad2.setColorAt(0, color0);
272 grad2.setColorAt(.8, color0);
273 grad2.setColorAt(.81, color2);
274 grad2.setColorAt(1, color1);
275 }
276 /* Bottom-left corner: */
277 QRadialGradient grad3(QPointF(iMetric, height() - iMetric), iMetric);
278 {
279 grad3.setColorAt(0, color0);
280 grad3.setColorAt(.8, color0);
281 grad3.setColorAt(.81, color3);
282 grad3.setColorAt(1, color1);
283 }
284 /* Botom-right corner: */
285 QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
286 {
287 grad4.setColorAt(0, color0);
288 grad4.setColorAt(.8, color0);
289 grad4.setColorAt(.81, color3);
290 grad4.setColorAt(1, color1);
291 }
292
293 /* Top line: */
294 QLinearGradient grad5(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
295 {
296 grad5.setColorAt(0, color1);
297 grad5.setColorAt(.19, color2);
298 grad5.setColorAt(.2, color0);
299 grad5.setColorAt(1, color0);
300 }
301 /* Bottom line: */
302 QLinearGradient grad6(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
303 {
304 grad6.setColorAt(0, color1);
305 grad6.setColorAt(.19, color3);
306 grad6.setColorAt(.2, color0);
307 grad6.setColorAt(1, color0);
308 }
309 /* Left line: */
310 QLinearGradient grad7(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
311 {
312 grad7.setColorAt(0, color1);
313 grad7.setColorAt(.19, color2);
314 grad7.setColorAt(.2, color0);
315 grad7.setColorAt(1, color0);
316 }
317 /* Right line: */
318 QLinearGradient grad8(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
319 {
320 grad8.setColorAt(0, color1);
321 grad8.setColorAt(.19, color2);
322 grad8.setColorAt(.2, color0);
323 grad8.setColorAt(1, color0);
324 }
325
326 /* Paint: */
327 painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
328
329 if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
330 {
331 painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
332 painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad3);
333 }
334 if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
335 {
336 painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad2);
337 painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
338 }
339
340 int iX = 0;
341 int iYL = 0;
342 int iYR = 0;
343 int iWid = width();
344 int iHeiL = height();
345 int iHeiR = height();
346 if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
347 {
348 iX = iMetric;
349 iYL = iMetric;
350 iWid -= iMetric;
351 iHeiL -= iMetric * 2;
352 }
353 if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
354 {
355 iYR = iMetric;
356 iWid -= iMetric;
357 iHeiR -= iMetric * 2;
358 }
359 painter.fillRect(QRect(0, iYL, iMetric, iHeiL), grad7);
360 painter.fillRect(QRect(width() - iMetric, iYR, iMetric, iHeiR), grad8);
361 painter.fillRect(QRect(iX, 0, iWid, iMetric), grad5);
362 painter.fillRect(QRect(iX, height() - iMetric, iWid, iMetric), grad6);
363
364#else /* !VBOX_WS_MAC */
365
366 /* Prepare painter: */
367 QPainter painter(this);
368
369 /* Prepare palette colors: */
370 const QPalette pal = palette();
371 const QColor color0 = m_fCurrent ? pal.color(QPalette::Base)
372 : m_fHovered ? pal.color(QPalette::Base).darker(102)
373 : pal.color(QPalette::Button).darker(102);
374 QColor color1 = color0;
375 color1.setAlpha(0);
376 QColor color2 = pal.color(QPalette::Shadow);
377
378 /* Invent pixel metric: */
379 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
380
381 /* Top-left corner: */
382 QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
383 {
384 grad1.setColorAt(0, color0);
385 grad1.setColorAt(.8, color1);
386 grad1.setColorAt(.9, color2);
387 grad1.setColorAt(1, color1);
388 }
389 /* Bottom-left corner: */
390 QRadialGradient grad2(QPointF(iMetric, height() - iMetric), iMetric);
391 {
392 grad2.setColorAt(0, color0);
393 grad2.setColorAt(.8, color1);
394 grad2.setColorAt(.9, color2);
395 grad2.setColorAt(1, color1);
396 }
397 /* Top-right corner: */
398 QRadialGradient grad3(QPointF(width() - iMetric, iMetric), iMetric);
399 {
400 grad3.setColorAt(0, color0);
401 grad3.setColorAt(.8, color1);
402 grad3.setColorAt(.9, color2);
403 grad3.setColorAt(1, color1);
404 }
405 /* Botom-right corner: */
406 QRadialGradient grad4(QPointF(width() - iMetric, height() - iMetric), iMetric);
407 {
408 grad4.setColorAt(0, color0);
409 grad4.setColorAt(.8, color1);
410 grad4.setColorAt(.9, color2);
411 grad4.setColorAt(1, color1);
412 }
413
414 /* Left line: */
415 QLinearGradient grad5(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
416 {
417 grad5.setColorAt(0, color1);
418 grad5.setColorAt(.1, color2);
419 grad5.setColorAt(.2, color1);
420 grad5.setColorAt(1, color0);
421 }
422 /* Right line: */
423 QLinearGradient grad6(QPointF(width(), height() - iMetric), QPointF(width() - iMetric, height() - iMetric));
424 {
425 grad6.setColorAt(0, color1);
426 grad6.setColorAt(.1, color2);
427 grad6.setColorAt(.2, color1);
428 grad6.setColorAt(1, color0);
429 }
430 /* Top line: */
431 QLinearGradient grad7(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
432 {
433 grad7.setColorAt(0, color1);
434 grad7.setColorAt(.1, color2);
435 grad7.setColorAt(.2, color1);
436 grad7.setColorAt(1, color0);
437 }
438 /* Bottom line: */
439 QLinearGradient grad8(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
440 {
441 grad8.setColorAt(0, color1);
442 grad8.setColorAt(.1, color2);
443 grad8.setColorAt(.2, color1);
444 grad8.setColorAt(1, color0);
445 }
446
447 /* Paint: */
448 painter.fillRect(QRect(iMetric, iMetric, width() - iMetric * 2, height() - iMetric * 2), color0);
449
450 if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
451 {
452 painter.fillRect(QRect(0, 0, iMetric, iMetric), grad1);
453 painter.fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad2);
454 }
455 if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
456 {
457 painter.fillRect(QRect(width() - iMetric, 0, iMetric, iMetric), grad3);
458 painter.fillRect(QRect(width() - iMetric, height() - iMetric, iMetric, iMetric), grad4);
459 }
460
461 int iX = 0;
462 int iYL = 0;
463 int iYR = 0;
464 int iWid = width();
465 int iHeiL = height();
466 int iHeiR = height();
467 if (m_enmPosition == PositionStyle_Left || m_enmPosition == PositionStyle_Single)
468 {
469 iX = iMetric;
470 iYL = iMetric;
471 iWid -= iMetric;
472 iHeiL -= iMetric * 2;
473 }
474 if (m_enmPosition == PositionStyle_Right || m_enmPosition == PositionStyle_Single)
475 {
476 iYR = iMetric;
477 iWid -= iMetric;
478 iHeiR -= iMetric * 2;
479 }
480
481 QPainterPath path5;
482 path5.moveTo(0, 0);
483 path5.lineTo(iMetric, iMetric);
484 path5.lineTo(iMetric, height() - iMetric);
485 path5.lineTo(0, height());
486 path5.closeSubpath();
487 painter.setClipPath(path5);
488 painter.fillRect(QRect(0, iYL, iMetric, iHeiL), grad5);
489 painter.setClipping(false);
490
491 QPainterPath path6;
492 path6.moveTo(width(), 0);
493 path6.lineTo(width() - iMetric, iMetric);
494 path6.lineTo(width() - iMetric, height() - iMetric);
495 path6.lineTo(width(), height());
496 path6.closeSubpath();
497 painter.setClipPath(path6);
498 painter.fillRect(QRect(width() - iMetric, iYR, iMetric, iHeiR), grad6);
499 painter.setClipping(false);
500
501 QPainterPath path7;
502 path7.moveTo(0, 0);
503 path7.lineTo(iMetric, iMetric);
504 path7.lineTo(width() - iMetric, iMetric);
505 path7.lineTo(width(), 0);
506 path7.closeSubpath();
507 painter.setClipPath(path7);
508 painter.fillRect(QRect(iX, 0, iWid, iMetric), grad7);
509 painter.setClipping(false);
510
511 QPainterPath path8;
512 path8.moveTo(0, height());
513 path8.lineTo(iMetric, height() - iMetric);
514 path8.lineTo(width() - iMetric, height() - iMetric);
515 path8.lineTo(width(), height());
516 path8.closeSubpath();
517 painter.setClipPath(path8);
518 painter.fillRect(QRect(iX, height() - iMetric, iWid, iMetric), grad8);
519 painter.setClipping(false);
520
521#endif /* !VBOX_WS_MAC */
522}
523
524void UITabBarItem::mousePressEvent(QMouseEvent *pEvent)
525{
526 /* We are interested in left button only: */
527 if (pEvent->button() != Qt::LeftButton)
528 return QWidget::mousePressEvent(pEvent);
529
530 /* Remember mouse-press position: */
531 m_mousePressPosition = pEvent->globalPos();
532}
533
534void UITabBarItem::mouseReleaseEvent(QMouseEvent *pEvent)
535{
536 /* We are interested in left button only: */
537 if (pEvent->button() != Qt::LeftButton)
538 return QWidget::mouseReleaseEvent(pEvent);
539
540 /* Forget mouse-press position: */
541 m_mousePressPosition = QPoint();
542
543 /* Notify listeners about the item was clicked: */
544 emit sigClicked(this);
545}
546
547void UITabBarItem::mouseMoveEvent(QMouseEvent *pEvent)
548{
549 /* Make sure item isn't already dragged: */
550 if (m_mousePressPosition.isNull())
551 return QWidget::mouseMoveEvent(pEvent);
552
553 /* Make sure item is now being dragged: */
554 if (QLineF(pEvent->globalPos(), m_mousePressPosition).length() < QApplication::startDragDistance())
555 return QWidget::mouseMoveEvent(pEvent);
556
557 /* Revoke hovered state: */
558#ifdef VBOX_WS_MAC
559 m_pLayoutStacked->setCurrentWidget(m_pLabelIcon);
560#endif
561 m_fHovered = false;
562 /* And call for repaint: */
563 update();
564
565 /* Initialize dragging: */
566 m_mousePressPosition = QPoint();
567 QDrag *pDrag = new QDrag(this);
568 connect(pDrag, &QObject::destroyed, this, &UITabBarItem::sigDragObjectDestroy);
569 QMimeData *pMimeData = new QMimeData;
570 pMimeData->setData(MimeType, uuid().toByteArray());
571 pDrag->setMimeData(pMimeData);
572 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
573 pDrag->setPixmap(m_pAction->icon().pixmap(window()->windowHandle(), QSize(iMetric, iMetric)));
574 pDrag->exec();
575}
576
577void UITabBarItem::enterEvent(QEvent *pEvent)
578{
579 /* Make sure button isn't hovered: */
580 if (m_fHovered)
581 return QWidget::enterEvent(pEvent);
582
583 /* Invert hovered state: */
584#ifdef VBOX_WS_MAC
585 m_pLayoutStacked->setCurrentWidget(m_pButtonClose);
586#endif
587 m_fHovered = true;
588 /* And call for repaint: */
589 update();
590}
591
592void UITabBarItem::leaveEvent(QEvent *pEvent)
593{
594 /* Make sure button is hovered: */
595 if (!m_fHovered)
596 return QWidget::enterEvent(pEvent);
597
598 /* Invert hovered state: */
599#ifdef VBOX_WS_MAC
600 m_pLayoutStacked->setCurrentWidget(m_pLabelIcon);
601#endif
602 m_fHovered = false;
603 /* And call for repaint: */
604 update();
605}
606
607void UITabBarItem::prepare()
608{
609 /* Configure self: */
610 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
611
612 /* Create main layout: */
613 m_pLayout = new QHBoxLayout(this);
614 if (m_pLayout)
615 {
616 /* Invent pixel metric: */
617 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
618#ifdef VBOX_WS_MAC
619 const int iMargin = iMetric / 4;
620#else
621 const int iMargin = iMetric / 2;
622#endif
623 const int iSpacing = iMargin / 2;
624#ifdef VBOX_WS_MAC
625 const int iMetricCloseButton = iMetric * 3 / 4;
626#else
627 const int iMetricCloseButton = iMetric * 2 / 3;
628#endif
629
630 /* Configure layout: */
631#ifdef VBOX_WS_MAC
632 m_pLayout->setContentsMargins(iMargin + iSpacing, iMargin, iMargin + iSpacing, iMargin);
633#else
634 m_pLayout->setContentsMargins(iMargin + iSpacing, iMargin, iMargin, iMargin);
635#endif
636 m_pLayout->setSpacing(iSpacing);
637
638 /* Create icon label: */
639 m_pLabelIcon = new QLabel;
640 if (m_pLabelIcon)
641 {
642 /* Configure label: */
643 m_pLabelIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
644 }
645
646 /* Create name label: */
647 m_pLabelName = new QLabel;
648 if (m_pLabelName)
649 {
650 /* Configure label: */
651 m_pLabelName->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
652 }
653
654 /* Create close button: */
655 m_pButtonClose = new QToolButton;
656 if (m_pButtonClose)
657 {
658 /* Configure button: */
659 m_pButtonClose->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
660 m_pButtonClose->setIconSize(QSize(iMetricCloseButton, iMetricCloseButton));
661 m_pButtonClose->setIcon(UIIconPool::iconSet(":/close_16px.png"));
662#ifdef VBOX_WS_MAC
663 m_pButtonClose->setStyleSheet("QToolButton { border: 0px }");
664#else
665 m_pButtonClose->setAutoRaise(true);
666#endif
667 connect(m_pButtonClose, &QToolButton::clicked, this, &UITabBarItem::sltCloseClicked);
668 }
669
670#ifdef VBOX_WS_MAC
671 /* Create stacked-layout: */
672 m_pLayoutStacked = new QStackedLayout(m_pLayout);
673 if (m_pLayoutStacked)
674 {
675 m_pLayoutStacked->setAlignment(Qt::AlignCenter);
676
677 /* Add icon-label and close-button into stacked-layout: */
678 m_pLayoutStacked->addWidget(m_pLabelIcon);
679 m_pLayoutStacked->addWidget(m_pButtonClose);
680 m_pLayoutStacked->setAlignment(m_pLabelIcon, Qt::AlignCenter);
681 m_pLayoutStacked->setAlignment(m_pButtonClose, Qt::AlignCenter);
682
683 /* Add stacked-layout into main-layout: */
684 m_pLayout->addLayout(m_pLayoutStacked);
685 }
686
687 /* Add name-label into main-layout: */
688 m_pLayout->addWidget(m_pLabelName);
689#else /* !VBOX_WS_MAC */
690 /* Add everything into main-layout: */
691 m_pLayout->addWidget(m_pLabelIcon);
692 m_pLayout->addWidget(m_pLabelName);
693 m_pLayout->addWidget(m_pButtonClose);
694#endif /* !VBOX_WS_MAC */
695 }
696
697 /* Update pixmap: */
698 updatePixmap();
699
700 /* Apply language settings: */
701 retranslateUi();
702}
703
704void UITabBarItem::updatePixmap()
705{
706 /* Configure label icon: */
707 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
708 m_pLabelIcon->setPixmap(m_pAction->icon().pixmap(window()->windowHandle(), QSize(iMetric, iMetric)));
709}
710
711
712/*********************************************************************************************************************************
713* Class UITabBar implementation. *
714*********************************************************************************************************************************/
715
716UITabBar::UITabBar(Alignment enmAlignment, QWidget *pParent /* = 0 */)
717 : QWidget(pParent)
718 , m_enmAlignment(enmAlignment)
719 , m_pLayoutMain(0)
720 , m_pLayoutTab(0)
721 , m_pCurrentItem(0)
722 , m_pItemToken(0)
723 , m_fDropAfterTokenItem(0)
724{
725 /* Prepare: */
726 prepare();
727}
728
729QUuid UITabBar::addTab(const QAction *pAction)
730{
731 /* Generate unique ID: */
732 const QUuid uuid = QUuid::createUuid();
733 /* Create new tab item: */
734 UITabBarItem *pItem = new UITabBarItem(uuid, pAction);
735 AssertPtrReturn(pItem, QUuid());
736 {
737 /* Configure item: */
738 connect(pItem, &UITabBarItem::sigClicked, this, &UITabBar::sltHandleMakeChildCurrent);
739 connect(pItem, &UITabBarItem::sigCloseClicked, this, &UITabBar::sltHandleChildClose);
740 connect(pItem, &UITabBarItem::sigDragObjectDestroy, this, &UITabBar::sltHandleDragObjectDestroy);
741 /* Add item into layout and list: */
742 switch (m_enmAlignment)
743 {
744 case Align_Left:
745 m_pLayoutTab->addWidget(pItem);
746 m_aItems.append(pItem);
747 break;
748 case Align_Right:
749 m_pLayoutTab->insertWidget(0, pItem);
750 m_aItems.prepend(pItem);
751 break;
752 }
753 /* Update children styles: */
754 updateChildrenStyles();
755 /* Return unique ID: */
756 return uuid;
757 }
758}
759
760bool UITabBar::removeTab(const QUuid &uuid)
761{
762 /* Prepare result: */
763 bool fSuccess = false;
764
765 /* Do we need to bother about current item? */
766 bool fMoveCurrent = m_pCurrentItem->uuid() == uuid;
767
768 /* Search through all the items we have: */
769 for (int i = 0; i < m_aItems.size(); ++i)
770 {
771 /* Get iterated item: */
772 UITabBarItem *pItem = m_aItems.at(i);
773 /* If that item is what we are looking for: */
774 if (pItem->uuid() == uuid)
775 {
776 /* Delete it and wipe it from the list: */
777 delete pItem;
778 m_aItems[i] = 0;
779 fSuccess = true;
780 }
781 }
782 /* Flush wiped out items: */
783 m_aItems.removeAll(0);
784
785 /* If we had removed current item: */
786 if (fMoveCurrent)
787 {
788 /* Mark it null initially: */
789 m_pCurrentItem = 0;
790 /* But choose something suitable if we have: */
791 if (!m_aItems.isEmpty())
792 sltHandleMakeChildCurrent(m_aItems.first());
793 }
794
795 /* Update children styles: */
796 updateChildrenStyles();
797
798 /* Return result: */
799 return fSuccess;
800}
801
802bool UITabBar::setCurrent(const QUuid &uuid)
803{
804 /* Prepare result: */
805 bool fSuccess = false;
806
807 /* Search through all the items we have: */
808 for (int i = 0; i < m_aItems.size(); ++i)
809 {
810 /* Get iterated item: */
811 UITabBarItem *pItem = m_aItems.at(i);
812 /* If that item is what we are looking for: */
813 if (pItem->uuid() == uuid)
814 {
815 /* Make it current: */
816 sltHandleMakeChildCurrent(pItem);
817 fSuccess = true;
818 break;
819 }
820 }
821
822 /* Return result: */
823 return fSuccess;
824}
825
826QList<QUuid> UITabBar::tabOrder() const
827{
828 QList<QUuid> list;
829 foreach (UITabBarItem *pItem, m_aItems)
830 list << pItem->uuid();
831 return list;
832}
833
834void UITabBar::paintEvent(QPaintEvent *pEvent)
835{
836 /* Call to base-class: */
837 QWidget::paintEvent(pEvent);
838
839 /* If we have a token item: */
840 if (m_pItemToken)
841 {
842 /* Prepare painter: */
843 QPainter painter(this);
844
845 /* Paint drop token: */
846 QStyleOption option;
847 option.state |= QStyle::State_Horizontal;
848 const QRect geo = m_pItemToken->geometry();
849 option.rect = !m_fDropAfterTokenItem
850 ? QRect(geo.topLeft() - QPoint(5, 5),
851 geo.bottomLeft() + QPoint(0, 5))
852 : QRect(geo.topRight() - QPoint(0, 5),
853 geo.bottomRight() + QPoint(5, 5));
854 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator,
855 &option, &painter);
856 }
857}
858
859void UITabBar::dragEnterEvent(QDragEnterEvent *pEvent)
860{
861 /* Make sure event is valid: */
862 AssertPtrReturnVoid(pEvent);
863 /* And mime-data is set: */
864 const QMimeData *pMimeData = pEvent->mimeData();
865 AssertPtrReturnVoid(pMimeData);
866
867 /* Make sure mime-data format is valid: */
868 if (!pMimeData->hasFormat(UITabBarItem::MimeType))
869 return;
870
871 /* Accept drag-enter event: */
872 pEvent->acceptProposedAction();
873}
874
875void UITabBar::dragMoveEvent(QDragMoveEvent *pEvent)
876{
877 /* Make sure event is valid: */
878 AssertPtrReturnVoid(pEvent);
879 /* And mime-data is set: */
880 const QMimeData *pMimeData = pEvent->mimeData();
881 AssertPtrReturnVoid(pMimeData);
882
883 /* Make sure mime-data format is valid: */
884 if (!pMimeData->hasFormat(UITabBarItem::MimeType))
885 return;
886
887 /* Reset token: */
888 m_pItemToken = 0;
889 m_fDropAfterTokenItem = true;
890
891 /* Get event position: */
892 const QPoint pos = pEvent->pos();
893 /* Search for most suitable item: */
894 foreach (UITabBarItem *pItem, m_aItems)
895 {
896 /* Advance token: */
897 m_pItemToken = pItem;
898 const QRect geo = m_pItemToken->geometry();
899 if (pos.x() < geo.center().x())
900 {
901 m_fDropAfterTokenItem = false;
902 break;
903 }
904 }
905
906 /* Update: */
907 update();
908}
909
910void UITabBar::dragLeaveEvent(QDragLeaveEvent * /* pEvent */)
911{
912 /* Reset token: */
913 m_pItemToken = 0;
914 m_fDropAfterTokenItem = true;
915
916 /* Update: */
917 update();
918}
919
920void UITabBar::dropEvent(QDropEvent *pEvent)
921{
922 /* Make sure event is valid: */
923 AssertPtrReturnVoid(pEvent);
924 /* And mime-data is set: */
925 const QMimeData *pMimeData = pEvent->mimeData();
926 AssertPtrReturnVoid(pMimeData);
927
928 /* Make sure mime-data format is valid: */
929 if (!pMimeData->hasFormat(UITabBarItem::MimeType))
930 return;
931
932 /* Make sure token-item set: */
933 if (!m_pItemToken)
934 return;
935
936 /* Determine ID of token-item: */
937 const QUuid tokenUuid = m_pItemToken->uuid();
938 /* Determine ID of dropped-item: */
939 const QUuid droppedUuid = pMimeData->data(UITabBarItem::MimeType);
940
941 /* Make sure these uuids are different: */
942 if (droppedUuid == tokenUuid)
943 return;
944
945 /* Search for an item with dropped ID: */
946 UITabBarItem *pItemDropped = 0;
947 foreach (UITabBarItem *pItem, m_aItems)
948 {
949 if (pItem->uuid() == droppedUuid)
950 {
951 pItemDropped = pItem;
952 break;
953 }
954 }
955
956 /* Make sure dropped-item found: */
957 if (!pItemDropped)
958 return;
959
960 /* Remove dropped-item: */
961 m_aItems.removeAll(pItemDropped);
962 m_pLayoutTab->removeWidget(pItemDropped);
963 /* Insert dropped-item into position of token-item: */
964 int iPosition = m_aItems.indexOf(m_pItemToken);
965 AssertReturnVoid(iPosition != -1);
966 if (m_fDropAfterTokenItem)
967 ++iPosition;
968 m_aItems.insert(iPosition, pItemDropped);
969 m_pLayoutTab->insertWidget(iPosition, pItemDropped);
970
971 /* Update children styles: */
972 updateChildrenStyles();
973}
974
975void UITabBar::sltHandleMakeChildCurrent(UITabBarItem *pItem)
976{
977 /* Make sure item exists: */
978 AssertPtrReturnVoid(pItem);
979
980 /* Remove current mark from current item if exists: */
981 if (m_pCurrentItem)
982 m_pCurrentItem->setCurrent(false);
983
984 /* Assign new current item: */
985 m_pCurrentItem = pItem;
986
987 /* Place current mark onto current item if exists: */
988 if (m_pCurrentItem)
989 m_pCurrentItem->setCurrent(true);
990
991 /* Notify listeners: */
992 emit sigCurrentTabChanged(pItem->uuid());
993}
994
995void UITabBar::sltHandleChildClose(UITabBarItem *pItem)
996{
997 /* Make sure item exists: */
998 AssertPtrReturnVoid(pItem);
999
1000 /* Notify listeners: */
1001 emit sigTabRequestForClosing(pItem->uuid());
1002}
1003
1004void UITabBar::sltHandleDragObjectDestroy()
1005{
1006 /* Reset token: */
1007 m_pItemToken = 0;
1008 m_fDropAfterTokenItem = true;
1009
1010 /* Update: */
1011 update();
1012}
1013
1014void UITabBar::prepare()
1015{
1016 /* Track D&D events: */
1017 setAcceptDrops(true);
1018
1019 /* Create main layout: */
1020 m_pLayoutMain = new QHBoxLayout(this);
1021 AssertPtrReturnVoid(m_pLayoutMain);
1022 {
1023 /* Configure layout: */
1024 m_pLayoutMain->setSpacing(0);
1025 m_pLayoutMain->setContentsMargins(0, 0, 0, 0);
1026
1027 /* Add strech to beginning: */
1028 if (m_enmAlignment == Align_Right)
1029 m_pLayoutMain->addStretch();
1030
1031 /* Create tab layout: */
1032 m_pLayoutTab = new QHBoxLayout;
1033 AssertPtrReturnVoid(m_pLayoutTab);
1034 {
1035 /* Add into layout: */
1036 m_pLayoutMain->addLayout(m_pLayoutTab);
1037 }
1038
1039 /* Add strech to end: */
1040 if (m_enmAlignment == Align_Left)
1041 m_pLayoutMain->addStretch();
1042 }
1043}
1044
1045void UITabBar::updateChildrenStyles()
1046{
1047 /* Single child has corresponding (rounded) style: */
1048 if (m_aItems.size() == 1)
1049 m_aItems.first()->setPositionStyle(UITabBarItem::PositionStyle_Single);
1050 /* If there are more than one child: */
1051 else if (m_aItems.size() > 1)
1052 {
1053 /* First make all children have no rounded sides: */
1054 foreach (UITabBarItem *pItem, m_aItems)
1055 pItem->setPositionStyle(UITabBarItem::PositionStyle_Middle);
1056 /* Then make first child rounded left, while last rounded right: */
1057 m_aItems.first()->setPositionStyle(UITabBarItem::PositionStyle_Left);
1058 m_aItems.last()->setPositionStyle(UITabBarItem::PositionStyle_Right);
1059 }
1060 /* Repaint: */
1061 update();
1062}
1063
1064#include "UITabBar.moc"
1065
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use