VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/tools/UIToolsItem.cpp@ 103977

Last change on this file since 103977 was 101560, checked in by vboxsync, 15 months ago

FE/Qt: bugref:10450: Bits forgotten in r159636.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: UIToolsItem.cpp 101560 2023-10-23 16:10:12Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIToolsItem class definition.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QAccessibleObject>
30#include <QApplication>
31#include <QGraphicsScene>
32#include <QPainter>
33#include <QPropertyAnimation>
34#include <QSignalTransition>
35#include <QStateMachine>
36#include <QStyle>
37#include <QStyleOptionGraphicsItem>
38#include <QWindow>
39
40/* GUI includes: */
41#include "UIImageTools.h"
42#include "UITools.h"
43#include "UIToolsItem.h"
44#include "UIToolsModel.h"
45#include "UIToolsView.h"
46#include "UIVirtualBoxManager.h"
47
48
49/** QAccessibleObject extension used as an accessibility interface for Tools-view items. */
50class UIAccessibilityInterfaceForUIToolsItem : public QAccessibleObject
51{
52public:
53
54 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
55 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
56 {
57 /* Creating Tools-view accessibility interface: */
58 if (pObject && strClassname == QLatin1String("UIToolsItem"))
59 return new UIAccessibilityInterfaceForUIToolsItem(pObject);
60
61 /* Null by default: */
62 return 0;
63 }
64
65 /** Constructs an accessibility interface passing @a pObject to the base-class. */
66 UIAccessibilityInterfaceForUIToolsItem(QObject *pObject)
67 : QAccessibleObject(pObject)
68 {}
69
70 /** Returns the parent. */
71 virtual QAccessibleInterface *parent() const RT_OVERRIDE
72 {
73 /* Make sure item still alive: */
74 AssertPtrReturn(item(), 0);
75
76 /* Return the parent: */
77 return QAccessible::queryAccessibleInterface(item()->model()->tools()->view());
78 }
79
80 /** Returns the number of children. */
81 virtual int childCount() const RT_OVERRIDE
82 {
83 /* Make sure item still alive: */
84 AssertPtrReturn(item(), 0);
85
86 /* Zero: */
87 return 0;
88 }
89
90 /** Returns the child with the passed @a iIndex. */
91 virtual QAccessibleInterface *child(int) const RT_OVERRIDE
92 {
93 /* Make sure item still alive: */
94 AssertPtrReturn(item(), 0);
95
96 /* Null: */
97 return 0;
98 }
99
100 /** Returns the index of the passed @a pChild. */
101 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE
102 {
103 /* Search for corresponding child: */
104 for (int i = 0; i < childCount(); ++i)
105 if (child(i) == pChild)
106 return i;
107
108 /* -1 by default: */
109 return -1;
110 }
111
112 /** Returns the rect. */
113 virtual QRect rect() const RT_OVERRIDE
114 {
115 /* Now goes the mapping: */
116 const QSize itemSize = item()->size().toSize();
117 const QPointF itemPosInScene = item()->mapToScene(QPointF(0, 0));
118 const QPoint itemPosInView = item()->model()->tools()->view()->mapFromScene(itemPosInScene);
119 const QPoint itemPosInScreen = item()->model()->tools()->view()->mapToGlobal(itemPosInView);
120 const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
121 return itemRectInScreen;
122 }
123
124 /** Returns a text for the passed @a enmTextRole. */
125 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE
126 {
127 /* Make sure item still alive: */
128 AssertPtrReturn(item(), QString());
129
130 switch (enmTextRole)
131 {
132 case QAccessible::Name: return item()->name();
133 /// @todo handle!
134 //case QAccessible::Description: return item()->description();
135 default: break;
136 }
137
138 /* Null-string by default: */
139 return QString();
140 }
141
142 /** Returns the role. */
143 virtual QAccessible::Role role() const RT_OVERRIDE
144 {
145 /* Make sure item still alive: */
146 AssertPtrReturn(item(), QAccessible::NoRole);
147
148 /* ListItem by default: */
149 return QAccessible::ListItem;
150 }
151
152 /** Returns the state. */
153 virtual QAccessible::State state() const RT_OVERRIDE
154 {
155 /* Make sure item still alive: */
156 AssertPtrReturn(item(), QAccessible::State());
157
158 /* Compose the state: */
159 QAccessible::State state;
160 state.focusable = true;
161 state.selectable = true;
162
163 /* Compose the state of current item: */
164 if (item() && item() == item()->model()->currentItem())
165 {
166 state.active = true;
167 state.focused = true;
168 state.selected = true;
169 }
170
171 /* Return the state: */
172 return state;
173 }
174
175private:
176
177 /** Returns corresponding Tools-view item. */
178 UIToolsItem *item() const { return qobject_cast<UIToolsItem*>(object()); }
179};
180
181
182/*********************************************************************************************************************************
183* Class UIToolsItem implementation. *
184*********************************************************************************************************************************/
185
186UIToolsItem::UIToolsItem(QGraphicsScene *pScene,
187 UIToolClass enmClass, UIToolType enmType,
188 const QString &strName, const QIcon &icon)
189 : m_pScene(pScene)
190 , m_enmClass(enmClass)
191 , m_enmType(enmType)
192 , m_icon(icon)
193 , m_strName(strName)
194 , m_fHovered(false)
195 , m_pHoveringMachine(0)
196 , m_pHoveringAnimationForward(0)
197 , m_pHoveringAnimationBackward(0)
198 , m_iAnimationDuration(400)
199 , m_iDefaultValue(0)
200 , m_iHoveredValue(100)
201 , m_iAnimatedValue(m_iDefaultValue)
202 , m_iDefaultLightnessStart(0)
203 , m_iDefaultLightnessFinal(0)
204 , m_iHoverLightnessStart(0)
205 , m_iHoverLightnessFinal(0)
206 , m_iHighlightLightnessStart(0)
207 , m_iHighlightLightnessFinal(0)
208 , m_iPreviousMinimumWidthHint(0)
209 , m_iPreviousMinimumHeightHint(0)
210 , m_iMaximumNameWidth(0)
211{
212 /* Prepare: */
213 prepare();
214}
215
216UIToolsItem::~UIToolsItem()
217{
218 /* Cleanup: */
219 cleanup();
220}
221
222UIToolsModel *UIToolsItem::model() const
223{
224 UIToolsModel *pModel = qobject_cast<UIToolsModel*>(QIGraphicsWidget::scene()->parent());
225 AssertMsg(pModel, ("Incorrect graphics scene parent set!"));
226 return pModel;
227}
228
229void UIToolsItem::reconfigure(UIToolClass enmClass, UIToolType enmType,
230 const QIcon &icon, const QString &strName)
231{
232 /* If class is changed: */
233 if (m_enmClass != enmClass)
234 {
235 /* Update linked values: */
236 m_enmClass = enmClass;
237 }
238
239 /* If type is changed: */
240 if (m_enmType != enmType)
241 {
242 /* Update linked values: */
243 m_enmType = enmType;
244 }
245
246 /* Update linked values: */
247 m_icon = icon;
248 updatePixmap();
249
250 /* Update name finally: */
251 reconfigure(strName);
252}
253
254void UIToolsItem::reconfigure(const QString &strName)
255{
256 /* If name is changed: */
257 if (m_strName != strName)
258 {
259 /* Update linked values: */
260 m_strName = strName;
261 updateMinimumNameSize();
262 updateVisibleName();
263 }
264}
265
266UIToolClass UIToolsItem::itemClass() const
267{
268 return m_enmClass;
269}
270
271UIToolType UIToolsItem::itemType() const
272{
273 return m_enmType;
274}
275
276const QIcon &UIToolsItem::icon() const
277{
278 return m_icon;
279}
280
281const QString &UIToolsItem::name() const
282{
283 return m_strName;
284}
285
286void UIToolsItem::setEnabled(bool fEnabled)
287{
288 /* Call to base-class: */
289 QIGraphicsWidget::setEnabled(fEnabled);
290
291 /* Update linked values: */
292 updatePixmap();
293}
294
295void UIToolsItem::setHovered(bool fHovered)
296{
297 m_fHovered = fHovered;
298 if (m_fHovered)
299 emit sigHoverEnter();
300 else
301 emit sigHoverLeave();
302}
303
304bool UIToolsItem::isHovered() const
305{
306 return m_fHovered;
307}
308
309void UIToolsItem::updateGeometry()
310{
311 /* Call to base-class: */
312 QIGraphicsWidget::updateGeometry();
313
314 /* We should notify Tools-model if minimum-width-hint was changed: */
315 const int iMinimumWidthHint = minimumWidthHint();
316 if (m_iPreviousMinimumWidthHint != iMinimumWidthHint)
317 {
318 /* Save new minimum-width-hint, notify listener: */
319 m_iPreviousMinimumWidthHint = iMinimumWidthHint;
320 emit sigMinimumWidthHintChanged(m_iPreviousMinimumWidthHint);
321 }
322 /* We should notify Tools-model if minimum-height-hint was changed: */
323 const int iMinimumHeightHint = minimumHeightHint();
324 if (m_iPreviousMinimumHeightHint != iMinimumHeightHint)
325 {
326 /* Save new minimum-height-hint, notify listener: */
327 m_iPreviousMinimumHeightHint = iMinimumHeightHint;
328 emit sigMinimumHeightHintChanged(m_iPreviousMinimumHeightHint);
329 }
330}
331
332int UIToolsItem::minimumWidthHint() const
333{
334 /* Prepare variables: */
335 const int iMargin = data(ToolsItemData_Margin).toInt();
336 const int iSpacing = data(ToolsItemData_Spacing).toInt();
337
338 /* Calculating proposed width: */
339 int iProposedWidth = 0;
340
341 /* Two margins: */
342 iProposedWidth += 2 * iMargin;
343 /* And Tools-item content to take into account: */
344 int iToolsItemWidth = m_pixmapSize.width() +
345 iSpacing +
346 m_minimumNameSize.width();
347 iProposedWidth += iToolsItemWidth;
348
349 /* Return result: */
350 return iProposedWidth;
351}
352
353int UIToolsItem::minimumHeightHint() const
354{
355 /* Prepare variables: */
356 const int iMargin = data(ToolsItemData_Margin).toInt();
357
358 /* Calculating proposed height: */
359 int iProposedHeight = 0;
360
361 /* Two margins: */
362 iProposedHeight += 2 * iMargin;
363 /* And Tools-item content to take into account: */
364 int iToolsItemHeight = qMax(m_pixmapSize.height(),
365 m_minimumNameSize.height());
366 iProposedHeight += iToolsItemHeight;
367
368 /* Return result: */
369 return iProposedHeight;
370}
371
372QSizeF UIToolsItem::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
373{
374 /* If Qt::MinimumSize requested: */
375 if (enmWhich == Qt::MinimumSize)
376 return QSizeF(minimumWidthHint(), minimumHeightHint());
377 /* Else call to base-class: */
378 return QIGraphicsWidget::sizeHint(enmWhich, constraint);
379}
380
381void UIToolsItem::showEvent(QShowEvent *pEvent)
382{
383 /* Call to base-class: */
384 QIGraphicsWidget::showEvent(pEvent);
385
386 /* Update pixmap: */
387 updatePixmap();
388}
389
390void UIToolsItem::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
391{
392 /* Call to base-class: */
393 QIGraphicsWidget::resizeEvent(pEvent);
394
395 /* What is the new geometry? */
396 const QRectF newGeometry = geometry();
397
398 /* Should we update visible name? */
399 if (previousGeometry().width() != newGeometry.width())
400 updateMaximumNameWidth();
401
402 /* Remember the new geometry: */
403 setPreviousGeometry(newGeometry);
404}
405
406void UIToolsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
407{
408 if (!m_fHovered)
409 {
410 m_fHovered = true;
411 emit sigHoverEnter();
412 update();
413 }
414}
415
416void UIToolsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
417{
418 if (m_fHovered)
419 {
420 m_fHovered = false;
421 emit sigHoverLeave();
422 update();
423 }
424}
425
426void UIToolsItem::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget * /* pWidget = 0 */)
427{
428 /* Acquire rectangle: */
429 const QRect rectangle = pOptions->rect;
430
431 /* Paint background: */
432 paintBackground(pPainter, rectangle);
433 /* Paint frame: */
434 paintFrame(pPainter, rectangle);
435 /* Paint tool info: */
436 paintToolInfo(pPainter, rectangle);
437}
438
439void UIToolsItem::sltHandleWindowRemapped()
440{
441 /* Update pixmap: */
442 updatePixmap();
443}
444
445void UIToolsItem::prepare()
446{
447 /* Add item to the scene: */
448 AssertMsg(m_pScene, ("Incorrect scene passed!"));
449 m_pScene->addItem(this);
450
451 /* Install Tools-view item accessibility interface factory: */
452 QAccessible::installFactory(UIAccessibilityInterfaceForUIToolsItem::pFactory);
453
454 /* Prepare color tones: */
455#if defined(VBOX_WS_MAC)
456 m_iDefaultLightnessStart = 120;
457 m_iDefaultLightnessFinal = 110;
458 m_iHoverLightnessStart = 125;
459 m_iHoverLightnessFinal = 115;
460 m_iHighlightLightnessStart = 115;
461 m_iHighlightLightnessFinal = 105;
462#elif defined(VBOX_WS_WIN)
463 m_iDefaultLightnessStart = 120;
464 m_iDefaultLightnessFinal = 110;
465 m_iHoverLightnessStart = 220;
466 m_iHoverLightnessFinal = 210;
467 m_iHighlightLightnessStart = 190;
468 m_iHighlightLightnessFinal = 180;
469#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
470 m_iDefaultLightnessStart = 110;
471 m_iDefaultLightnessFinal = 100;
472 m_iHoverLightnessStart = 125;
473 m_iHoverLightnessFinal = 115;
474 m_iHighlightLightnessStart = 110;
475 m_iHighlightLightnessFinal = 100;
476#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
477
478 /* Prepare fonts: */
479 m_nameFont = font();
480 m_nameFont.setWeight(QFont::Bold);
481
482 /* Configure item options: */
483 setOwnedByLayout(false);
484 setAcceptHoverEvents(true);
485 setFocusPolicy(Qt::NoFocus);
486 setFlag(QGraphicsItem::ItemIsSelectable, false);
487
488 /* Prepare hover animation: */
489 prepareHoverAnimation();
490 /* Prepare connections: */
491 prepareConnections();
492
493 /* Init: */
494 updatePixmap();
495 updateMinimumNameSize();
496 updateVisibleName();
497}
498
499void UIToolsItem::prepareHoverAnimation()
500{
501 /* Create hovering animation machine: */
502 m_pHoveringMachine = new QStateMachine(this);
503 if (m_pHoveringMachine)
504 {
505 /* Create 'default' state: */
506 QState *pStateDefault = new QState(m_pHoveringMachine);
507 /* Create 'hovered' state: */
508 QState *pStateHovered = new QState(m_pHoveringMachine);
509
510 /* Configure 'default' state: */
511 if (pStateDefault)
512 {
513 /* When we entering default state => we assigning animatedValue to m_iDefaultValue: */
514 pStateDefault->assignProperty(this, "animatedValue", m_iDefaultValue);
515
516 /* Add state transitions: */
517 QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
518 if (pDefaultToHovered)
519 {
520 /* Create forward animation: */
521 m_pHoveringAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
522 if (m_pHoveringAnimationForward)
523 {
524 m_pHoveringAnimationForward->setDuration(m_iAnimationDuration);
525 m_pHoveringAnimationForward->setStartValue(m_iDefaultValue);
526 m_pHoveringAnimationForward->setEndValue(m_iHoveredValue);
527
528 /* Add to transition: */
529 pDefaultToHovered->addAnimation(m_pHoveringAnimationForward);
530 }
531 }
532 }
533
534 /* Configure 'hovered' state: */
535 if (pStateHovered)
536 {
537 /* When we entering hovered state => we assigning animatedValue to m_iHoveredValue: */
538 pStateHovered->assignProperty(this, "animatedValue", m_iHoveredValue);
539
540 /* Add state transitions: */
541 QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
542 if (pHoveredToDefault)
543 {
544 /* Create backward animation: */
545 m_pHoveringAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
546 if (m_pHoveringAnimationBackward)
547 {
548 m_pHoveringAnimationBackward->setDuration(m_iAnimationDuration);
549 m_pHoveringAnimationBackward->setStartValue(m_iHoveredValue);
550 m_pHoveringAnimationBackward->setEndValue(m_iDefaultValue);
551
552 /* Add to transition: */
553 pHoveredToDefault->addAnimation(m_pHoveringAnimationBackward);
554 }
555 }
556 }
557
558 /* Initial state is 'default': */
559 m_pHoveringMachine->setInitialState(pStateDefault);
560 /* Start state-machine: */
561 m_pHoveringMachine->start();
562 }
563}
564
565void UIToolsItem::prepareConnections()
566{
567 /* This => model connections: */
568 connect(this, &UIToolsItem::sigMinimumWidthHintChanged,
569 model(), &UIToolsModel::sltItemMinimumWidthHintChanged);
570 connect(this, &UIToolsItem::sigMinimumHeightHintChanged,
571 model(), &UIToolsModel::sltItemMinimumHeightHintChanged);
572
573 /* Manager => this connections: */
574 connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
575 this, &UIToolsItem::sltHandleWindowRemapped);
576}
577
578void UIToolsItem::cleanup()
579{
580 /* If that item is focused: */
581 if (model()->focusItem() == this)
582 {
583 /* Unset the focus item: */
584 model()->setFocusItem(0);
585 }
586 /* If that item is current: */
587 if (model()->currentItem() == this)
588 {
589 /* Unset the current item: */
590 model()->setCurrentItem(0);
591 }
592 /* If that item is in navigation list: */
593 if (model()->navigationList().contains(this))
594 {
595 /* Remove item from the navigation list: */
596 model()->removeFromNavigationList(this);
597 }
598}
599
600QVariant UIToolsItem::data(int iKey) const
601{
602 /* Provide other members with required data: */
603 switch (iKey)
604 {
605 /* Layout hints: */
606 case ToolsItemData_Margin: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 3 * 2;
607 case ToolsItemData_Spacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
608
609 /* Default: */
610 default: break;
611 }
612 return QVariant();
613}
614
615void UIToolsItem::updatePixmap()
616{
617 /* Prepare variables: */
618 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * 1.5;
619
620 /* Prepare new pixmap size: */
621 const QSize pixmapSize = QSize(iIconMetric, iIconMetric);
622 const qreal fDevicePixelRatio = gpManager->windowHandle() ? gpManager->windowHandle()->devicePixelRatio() : 1;
623 const QPixmap pixmap = m_icon.pixmap(pixmapSize, fDevicePixelRatio, isEnabled() ? QIcon::Normal : QIcon::Disabled);
624 /* Update linked values: */
625 if (m_pixmapSize != pixmapSize)
626 {
627 m_pixmapSize = pixmapSize;
628 updateMaximumNameWidth();
629 updateGeometry();
630 }
631 if (m_pixmap.toImage() != pixmap.toImage())
632 {
633 m_pixmap = pixmap;
634 update();
635 }
636}
637
638void UIToolsItem::updateMinimumNameSize()
639{
640 /* Prepare variables: */
641 QPaintDevice *pPaintDevice = model()->paintDevice();
642
643 /* Calculate new minimum name size: */
644 const QFontMetrics fm(m_nameFont, pPaintDevice);
645 const int iWidthOf15Letters = textWidthMonospace(m_nameFont, pPaintDevice, 15);
646 const QString strNameCompressedTo15Letters = compressText(m_nameFont, pPaintDevice, m_strName, iWidthOf15Letters);
647#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
648 const QSize minimumNameSize = QSize(fm.horizontalAdvance(strNameCompressedTo15Letters), fm.height());
649#else
650 const QSize minimumNameSize = QSize(fm.width(strNameCompressedTo15Letters), fm.height());
651#endif
652
653 /* Update linked values: */
654 if (m_minimumNameSize != minimumNameSize)
655 {
656 m_minimumNameSize = minimumNameSize;
657 updateGeometry();
658 }
659}
660
661void UIToolsItem::updateMaximumNameWidth()
662{
663 /* Prepare variables: */
664 const int iMargin = data(ToolsItemData_Margin).toInt();
665 const int iSpacing = data(ToolsItemData_Spacing).toInt();
666
667 /* Calculate new maximum name width: */
668 int iMaximumNameWidth = (int)geometry().width();
669 iMaximumNameWidth -= iMargin; /* left margin */
670 iMaximumNameWidth -= m_pixmapSize.width(); /* pixmap width */
671 iMaximumNameWidth -= iSpacing; /* spacing between pixmap and name(s) */
672 iMaximumNameWidth -= iMargin; /* right margin */
673
674 /* Update linked values: */
675 if (m_iMaximumNameWidth != iMaximumNameWidth)
676 {
677 m_iMaximumNameWidth = iMaximumNameWidth;
678 updateVisibleName();
679 }
680}
681
682void UIToolsItem::updateVisibleName()
683{
684 /* Prepare variables: */
685 QPaintDevice *pPaintDevice = model()->paintDevice();
686
687 /* Calculate new visible name: */
688 const QString strVisibleName = compressText(m_nameFont, pPaintDevice, m_strName, m_iMaximumNameWidth);
689
690 /* Update linked values: */
691 if (m_strVisibleName != strVisibleName)
692 {
693 m_strVisibleName = strVisibleName;
694 update();
695 }
696}
697
698/* static */
699int UIToolsItem::textWidthMonospace(const QFont &font, QPaintDevice *pPaintDevice, int iCount)
700{
701 /* Return text width, based on font-metrics: */
702 QFontMetrics fm(font, pPaintDevice);
703 QString strString;
704 strString.fill('_', iCount);
705#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
706 return fm.horizontalAdvance(strString);
707#else
708 return fm.width(strString);
709#endif
710}
711
712/* static */
713QString UIToolsItem::compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth)
714{
715 /* Check if passed text is empty: */
716 if (strText.isEmpty())
717 return strText;
718
719 /* Check if passed text already fits maximum width: */
720 QFontMetrics fm(font, pPaintDevice);
721#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
722 if (fm.horizontalAdvance(strText) <= iWidth)
723#else
724 if (fm.width(strText) <= iWidth)
725#endif
726 return strText;
727
728 /* Truncate otherwise: */
729 QString strEllipsis = QString("...");
730#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
731 int iEllipsisWidth = fm.horizontalAdvance(strEllipsis + " ");
732 while (!strText.isEmpty() && fm.horizontalAdvance(strText) + iEllipsisWidth > iWidth)
733 strText.truncate(strText.size() - 1);
734#else
735 int iEllipsisWidth = fm.width(strEllipsis + " ");
736 while (!strText.isEmpty() && fm.width(strText) + iEllipsisWidth > iWidth)
737 strText.truncate(strText.size() - 1);
738#endif
739 return strText + strEllipsis;
740}
741
742void UIToolsItem::paintBackground(QPainter *pPainter, const QRect &rectangle) const
743{
744 /* Save painter: */
745 pPainter->save();
746
747 /* Prepare variables: */
748 const QPalette pal = QApplication::palette();
749
750 /* Selection background: */
751 if (model()->currentItem() == this)
752 {
753 /* Prepare color: */
754 const QColor backgroundColor = isEnabled()
755 ? pal.color(QPalette::Active, QPalette::Highlight)
756 : pal.color(QPalette::Disabled, QPalette::Window);
757 /* Draw gradient: */
758 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
759 bgGrad.setColorAt(0, backgroundColor.lighter(m_iHighlightLightnessStart));
760 bgGrad.setColorAt(1, backgroundColor.lighter(m_iHighlightLightnessFinal));
761 pPainter->fillRect(rectangle, bgGrad);
762
763 if (isEnabled() && isHovered())
764 {
765 /* Prepare color: */
766 QColor animationColor1 = QColor(Qt::white);
767 QColor animationColor2 = QColor(Qt::white);
768#ifdef VBOX_WS_MAC
769 animationColor1.setAlpha(90);
770#else
771 animationColor1.setAlpha(30);
772#endif
773 animationColor2.setAlpha(0);
774 /* Draw hovering animated gradient: */
775 QRect animatedRect = rectangle;
776 animatedRect.setWidth(animatedRect.height());
777 const int iLength = 2 * animatedRect.width() + rectangle.width();
778 const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
779 animatedRect.moveLeft(iShift);
780 QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
781 bgAnimatedGrad.setColorAt(0, animationColor2);
782 bgAnimatedGrad.setColorAt(0.1, animationColor2);
783 bgAnimatedGrad.setColorAt(0.5, animationColor1);
784 bgAnimatedGrad.setColorAt(0.9, animationColor2);
785 bgAnimatedGrad.setColorAt(1, animationColor2);
786 pPainter->fillRect(rectangle, bgAnimatedGrad);
787 }
788 }
789 /* Hovering background: */
790 else if (isHovered())
791 {
792 /* Prepare color: */
793 const QColor backgroundColor = isEnabled()
794 ? pal.color(QPalette::Active, QPalette::Highlight)
795 : pal.color(QPalette::Disabled, QPalette::Window);
796 /* Draw gradient: */
797 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
798 bgGrad.setColorAt(0, backgroundColor.lighter(m_iHoverLightnessStart));
799 bgGrad.setColorAt(1, backgroundColor.lighter(m_iHoverLightnessFinal));
800 pPainter->fillRect(rectangle, bgGrad);
801
802 if (isEnabled())
803 {
804 /* Prepare color: */
805 QColor animationColor1 = QColor(Qt::white);
806 QColor animationColor2 = QColor(Qt::white);
807#ifdef VBOX_WS_MAC
808 animationColor1.setAlpha(120);
809#else
810 animationColor1.setAlpha(50);
811#endif
812 animationColor2.setAlpha(0);
813 /* Draw hovering animated gradient: */
814 QRect animatedRect = rectangle;
815 animatedRect.setWidth(animatedRect.height());
816 const int iLength = 2 * animatedRect.width() + rectangle.width();
817 const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
818 animatedRect.moveLeft(iShift);
819 QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
820 bgAnimatedGrad.setColorAt(0, animationColor2);
821 bgAnimatedGrad.setColorAt(0.1, animationColor2);
822 bgAnimatedGrad.setColorAt(0.5, animationColor1);
823 bgAnimatedGrad.setColorAt(0.9, animationColor2);
824 bgAnimatedGrad.setColorAt(1, animationColor2);
825 pPainter->fillRect(rectangle, bgAnimatedGrad);
826 }
827 }
828 /* Default background: */
829 else
830 {
831 /* Prepare color: */
832 const QColor backgroundColor = isEnabled()
833 ? pal.color(QPalette::Active, QPalette::Window)
834 : pal.color(QPalette::Disabled, QPalette::Window);
835 /* Draw gradient: */
836 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
837 bgGrad.setColorAt(0, backgroundColor.lighter(m_iDefaultLightnessStart));
838 bgGrad.setColorAt(1, backgroundColor.lighter(m_iDefaultLightnessFinal));
839 pPainter->fillRect(rectangle, bgGrad);
840 }
841
842 /* Restore painter: */
843 pPainter->restore();
844}
845
846void UIToolsItem::paintFrame(QPainter *pPainter, const QRect &rectangle) const
847{
848 /* Don't paint frame for disabled items: */
849 if (!isEnabled())
850 return;
851
852 /* Save painter: */
853 pPainter->save();
854
855 /* Prepare colors: */
856 const QPalette pal = QApplication::palette();
857 QColor strokeColor;
858
859 /* Selection frame: */
860 if (model()->currentItem() == this)
861 strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightnessStart - 40);
862 /* Hovering frame: */
863 else if (isHovered())
864 strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHoverLightnessStart - 40);
865 /* Default frame: */
866 else
867 strokeColor = pal.color(QPalette::Active, QPalette::Window).lighter(m_iDefaultLightnessStart);
868
869 /* Create/assign pen: */
870 QPen pen(strokeColor);
871 pen.setWidth(0);
872 pPainter->setPen(pen);
873
874 /* Draw borders: */
875 pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
876 pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight());
877 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
878 pPainter->drawLine(rectangle.topRight(), rectangle.bottomRight());
879
880 /* Restore painter: */
881 pPainter->restore();
882}
883
884void UIToolsItem::paintToolInfo(QPainter *pPainter, const QRect &rectangle) const
885{
886 /* Prepare variables: */
887 const int iFullHeight = rectangle.height();
888 const int iMargin = data(ToolsItemData_Margin).toInt();
889 const int iSpacing = data(ToolsItemData_Spacing).toInt();
890 const QPalette pal = QApplication::palette();
891
892 /* Selected or hovered item foreground: */
893 if (model()->currentItem() == this || isHovered())
894 {
895 /* Prepare palette: */
896 const QPalette pal = QApplication::palette();
897
898 /* Get background color: */
899 const QColor highlight = pal.color(QPalette::Active, QPalette::Highlight);
900 const QColor background = model()->currentItem() == this
901 ? highlight.lighter(m_iHighlightLightnessStart)
902 : highlight.lighter(m_iHoverLightnessStart);
903
904 /* Gather foreground color for background one: */
905 const QColor foreground = suitableForegroundColor(pal, background);
906 pPainter->setPen(foreground);
907 }
908 /* Default item foreground: */
909 else
910 {
911 const QColor textColor = isEnabled()
912 ? pal.color(QPalette::Active, QPalette::Text)
913 : pal.color(QPalette::Disabled, QPalette::Text);
914 pPainter->setPen(textColor);
915 }
916
917 /* Paint left column: */
918 {
919 /* Prepare variables: */
920 int iPixmapX = iMargin;
921 int iPixmapY = (iFullHeight - m_pixmap.height() / m_pixmap.devicePixelRatio()) / 2;
922 /* Paint pixmap: */
923 paintPixmap(/* Painter: */
924 pPainter,
925 /* Point to paint in: */
926 QPoint(iPixmapX, iPixmapY),
927 /* Pixmap to paint: */
928 m_pixmap);
929 }
930
931 /* Paint right column: */
932 {
933 /* Prepare variables: */
934 int iNameX = iMargin + m_pixmapSize.width() + iSpacing;
935 int iNameY = (iFullHeight - m_minimumNameSize.height()) / 2;
936 /* Paint name: */
937 paintText(/* Painter: */
938 pPainter,
939 /* Point to paint in: */
940 QPoint(iNameX, iNameY),
941 /* Font to paint text: */
942 m_nameFont,
943 /* Paint device: */
944 model()->paintDevice(),
945 /* Text to paint: */
946 m_strVisibleName);
947 }
948}
949
950/* static */
951void UIToolsItem::paintPixmap(QPainter *pPainter, const QPoint &point,
952 const QPixmap &pixmap)
953{
954 /* Draw pixmap: */
955 pPainter->drawPixmap(point, pixmap);
956}
957
958/* static */
959void UIToolsItem::paintText(QPainter *pPainter, QPoint point,
960 const QFont &font, QPaintDevice *pPaintDevice,
961 const QString &strText)
962{
963 /* Save painter: */
964 pPainter->save();
965
966 /* Assign font: */
967 pPainter->setFont(font);
968
969 /* Calculate ascent: */
970 QFontMetrics fm(font, pPaintDevice);
971 point += QPoint(0, fm.ascent());
972
973 /* Draw text: */
974 pPainter->drawText(point, strText);
975
976 /* Restore painter: */
977 pPainter->restore();
978}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette