VirtualBox

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

Last change on this file since 105266 was 104585, checked in by vboxsync, 11 months ago

FE/Qt: bugref:10450: Get rid of pre-5.11 code related to QFontMetrics.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.1 KB
Line 
1/* $Id: UIToolsItem.cpp 104585 2024-05-13 11:37:59Z 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 const QSize minimumNameSize = QSize(fm.horizontalAdvance(strNameCompressedTo15Letters), fm.height());
648
649 /* Update linked values: */
650 if (m_minimumNameSize != minimumNameSize)
651 {
652 m_minimumNameSize = minimumNameSize;
653 updateGeometry();
654 }
655}
656
657void UIToolsItem::updateMaximumNameWidth()
658{
659 /* Prepare variables: */
660 const int iMargin = data(ToolsItemData_Margin).toInt();
661 const int iSpacing = data(ToolsItemData_Spacing).toInt();
662
663 /* Calculate new maximum name width: */
664 int iMaximumNameWidth = (int)geometry().width();
665 iMaximumNameWidth -= iMargin; /* left margin */
666 iMaximumNameWidth -= m_pixmapSize.width(); /* pixmap width */
667 iMaximumNameWidth -= iSpacing; /* spacing between pixmap and name(s) */
668 iMaximumNameWidth -= iMargin; /* right margin */
669
670 /* Update linked values: */
671 if (m_iMaximumNameWidth != iMaximumNameWidth)
672 {
673 m_iMaximumNameWidth = iMaximumNameWidth;
674 updateVisibleName();
675 }
676}
677
678void UIToolsItem::updateVisibleName()
679{
680 /* Prepare variables: */
681 QPaintDevice *pPaintDevice = model()->paintDevice();
682
683 /* Calculate new visible name: */
684 const QString strVisibleName = compressText(m_nameFont, pPaintDevice, m_strName, m_iMaximumNameWidth);
685
686 /* Update linked values: */
687 if (m_strVisibleName != strVisibleName)
688 {
689 m_strVisibleName = strVisibleName;
690 update();
691 }
692}
693
694/* static */
695int UIToolsItem::textWidthMonospace(const QFont &font, QPaintDevice *pPaintDevice, int iCount)
696{
697 /* Return text width, based on font-metrics: */
698 const QFontMetrics fm(font, pPaintDevice);
699 QString strString;
700 strString.fill('_', iCount);
701 return fm.horizontalAdvance(strString);
702}
703
704/* static */
705QString UIToolsItem::compressText(const QFont &font, QPaintDevice *pPaintDevice, QString strText, int iWidth)
706{
707 /* Check if passed text is empty: */
708 if (strText.isEmpty())
709 return strText;
710
711 /* Check if passed text already fits maximum width: */
712 const QFontMetrics fm(font, pPaintDevice);
713 if (fm.horizontalAdvance(strText) <= iWidth)
714 return strText;
715
716 /* Truncate otherwise: */
717 const QString strEllipsis = QString("...");
718 const int iEllipsisWidth = fm.horizontalAdvance(strEllipsis + " ");
719 while (!strText.isEmpty() && fm.horizontalAdvance(strText) + iEllipsisWidth > iWidth)
720 strText.truncate(strText.size() - 1);
721 return strText + strEllipsis;
722}
723
724void UIToolsItem::paintBackground(QPainter *pPainter, const QRect &rectangle) const
725{
726 /* Save painter: */
727 pPainter->save();
728
729 /* Prepare variables: */
730 const QPalette pal = QApplication::palette();
731
732 /* Selection background: */
733 if (model()->currentItem() == this)
734 {
735 /* Prepare color: */
736 const QColor backgroundColor = isEnabled()
737 ? pal.color(QPalette::Active, QPalette::Highlight)
738 : pal.color(QPalette::Disabled, QPalette::Window);
739 /* Draw gradient: */
740 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
741 bgGrad.setColorAt(0, backgroundColor.lighter(m_iHighlightLightnessStart));
742 bgGrad.setColorAt(1, backgroundColor.lighter(m_iHighlightLightnessFinal));
743 pPainter->fillRect(rectangle, bgGrad);
744
745 if (isEnabled() && isHovered())
746 {
747 /* Prepare color: */
748 QColor animationColor1 = QColor(Qt::white);
749 QColor animationColor2 = QColor(Qt::white);
750#ifdef VBOX_WS_MAC
751 animationColor1.setAlpha(90);
752#else
753 animationColor1.setAlpha(30);
754#endif
755 animationColor2.setAlpha(0);
756 /* Draw hovering animated gradient: */
757 QRect animatedRect = rectangle;
758 animatedRect.setWidth(animatedRect.height());
759 const int iLength = 2 * animatedRect.width() + rectangle.width();
760 const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
761 animatedRect.moveLeft(iShift);
762 QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
763 bgAnimatedGrad.setColorAt(0, animationColor2);
764 bgAnimatedGrad.setColorAt(0.1, animationColor2);
765 bgAnimatedGrad.setColorAt(0.5, animationColor1);
766 bgAnimatedGrad.setColorAt(0.9, animationColor2);
767 bgAnimatedGrad.setColorAt(1, animationColor2);
768 pPainter->fillRect(rectangle, bgAnimatedGrad);
769 }
770 }
771 /* Hovering background: */
772 else if (isHovered())
773 {
774 /* Prepare color: */
775 const QColor backgroundColor = isEnabled()
776 ? pal.color(QPalette::Active, QPalette::Highlight)
777 : pal.color(QPalette::Disabled, QPalette::Window);
778 /* Draw gradient: */
779 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
780 bgGrad.setColorAt(0, backgroundColor.lighter(m_iHoverLightnessStart));
781 bgGrad.setColorAt(1, backgroundColor.lighter(m_iHoverLightnessFinal));
782 pPainter->fillRect(rectangle, bgGrad);
783
784 if (isEnabled())
785 {
786 /* Prepare color: */
787 QColor animationColor1 = QColor(Qt::white);
788 QColor animationColor2 = QColor(Qt::white);
789#ifdef VBOX_WS_MAC
790 animationColor1.setAlpha(120);
791#else
792 animationColor1.setAlpha(50);
793#endif
794 animationColor2.setAlpha(0);
795 /* Draw hovering animated gradient: */
796 QRect animatedRect = rectangle;
797 animatedRect.setWidth(animatedRect.height());
798 const int iLength = 2 * animatedRect.width() + rectangle.width();
799 const int iShift = - animatedRect.width() + iLength * animatedValue() / 100;
800 animatedRect.moveLeft(iShift);
801 QLinearGradient bgAnimatedGrad(animatedRect.topLeft(), animatedRect.bottomRight());
802 bgAnimatedGrad.setColorAt(0, animationColor2);
803 bgAnimatedGrad.setColorAt(0.1, animationColor2);
804 bgAnimatedGrad.setColorAt(0.5, animationColor1);
805 bgAnimatedGrad.setColorAt(0.9, animationColor2);
806 bgAnimatedGrad.setColorAt(1, animationColor2);
807 pPainter->fillRect(rectangle, bgAnimatedGrad);
808 }
809 }
810 /* Default background: */
811 else
812 {
813 /* Prepare color: */
814 const QColor backgroundColor = isEnabled()
815 ? pal.color(QPalette::Active, QPalette::Window)
816 : pal.color(QPalette::Disabled, QPalette::Window);
817 /* Draw gradient: */
818 QLinearGradient bgGrad(rectangle.topLeft(), rectangle.bottomLeft());
819 bgGrad.setColorAt(0, backgroundColor.lighter(m_iDefaultLightnessStart));
820 bgGrad.setColorAt(1, backgroundColor.lighter(m_iDefaultLightnessFinal));
821 pPainter->fillRect(rectangle, bgGrad);
822 }
823
824 /* Restore painter: */
825 pPainter->restore();
826}
827
828void UIToolsItem::paintFrame(QPainter *pPainter, const QRect &rectangle) const
829{
830 /* Don't paint frame for disabled items: */
831 if (!isEnabled())
832 return;
833
834 /* Save painter: */
835 pPainter->save();
836
837 /* Prepare colors: */
838 const QPalette pal = QApplication::palette();
839 QColor strokeColor;
840
841 /* Selection frame: */
842 if (model()->currentItem() == this)
843 strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightnessStart - 40);
844 /* Hovering frame: */
845 else if (isHovered())
846 strokeColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHoverLightnessStart - 40);
847 /* Default frame: */
848 else
849 strokeColor = pal.color(QPalette::Active, QPalette::Window).lighter(m_iDefaultLightnessStart);
850
851 /* Create/assign pen: */
852 QPen pen(strokeColor);
853 pen.setWidth(0);
854 pPainter->setPen(pen);
855
856 /* Draw borders: */
857 pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
858 pPainter->drawLine(rectangle.bottomLeft(), rectangle.bottomRight());
859 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
860 pPainter->drawLine(rectangle.topRight(), rectangle.bottomRight());
861
862 /* Restore painter: */
863 pPainter->restore();
864}
865
866void UIToolsItem::paintToolInfo(QPainter *pPainter, const QRect &rectangle) const
867{
868 /* Prepare variables: */
869 const int iFullHeight = rectangle.height();
870 const int iMargin = data(ToolsItemData_Margin).toInt();
871 const int iSpacing = data(ToolsItemData_Spacing).toInt();
872 const QPalette pal = QApplication::palette();
873
874 /* Selected or hovered item foreground: */
875 if (model()->currentItem() == this || isHovered())
876 {
877 /* Prepare palette: */
878 const QPalette pal = QApplication::palette();
879
880 /* Get background color: */
881 const QColor highlight = pal.color(QPalette::Active, QPalette::Highlight);
882 const QColor background = model()->currentItem() == this
883 ? highlight.lighter(m_iHighlightLightnessStart)
884 : highlight.lighter(m_iHoverLightnessStart);
885
886 /* Gather foreground color for background one: */
887 const QColor foreground = suitableForegroundColor(pal, background);
888 pPainter->setPen(foreground);
889 }
890 /* Default item foreground: */
891 else
892 {
893 const QColor textColor = isEnabled()
894 ? pal.color(QPalette::Active, QPalette::Text)
895 : pal.color(QPalette::Disabled, QPalette::Text);
896 pPainter->setPen(textColor);
897 }
898
899 /* Paint left column: */
900 {
901 /* Prepare variables: */
902 int iPixmapX = iMargin;
903 int iPixmapY = (iFullHeight - m_pixmap.height() / m_pixmap.devicePixelRatio()) / 2;
904 /* Paint pixmap: */
905 paintPixmap(/* Painter: */
906 pPainter,
907 /* Point to paint in: */
908 QPoint(iPixmapX, iPixmapY),
909 /* Pixmap to paint: */
910 m_pixmap);
911 }
912
913 /* Paint right column: */
914 {
915 /* Prepare variables: */
916 int iNameX = iMargin + m_pixmapSize.width() + iSpacing;
917 int iNameY = (iFullHeight - m_minimumNameSize.height()) / 2;
918 /* Paint name: */
919 paintText(/* Painter: */
920 pPainter,
921 /* Point to paint in: */
922 QPoint(iNameX, iNameY),
923 /* Font to paint text: */
924 m_nameFont,
925 /* Paint device: */
926 model()->paintDevice(),
927 /* Text to paint: */
928 m_strVisibleName);
929 }
930}
931
932/* static */
933void UIToolsItem::paintPixmap(QPainter *pPainter, const QPoint &point,
934 const QPixmap &pixmap)
935{
936 /* Draw pixmap: */
937 pPainter->drawPixmap(point, pixmap);
938}
939
940/* static */
941void UIToolsItem::paintText(QPainter *pPainter, QPoint point,
942 const QFont &font, QPaintDevice *pPaintDevice,
943 const QString &strText)
944{
945 /* Save painter: */
946 pPainter->save();
947
948 /* Assign font: */
949 pPainter->setFont(font);
950
951 /* Calculate ascent: */
952 QFontMetrics fm(font, pPaintDevice);
953 point += QPoint(0, fm.ascent());
954
955 /* Draw text: */
956 pPainter->drawText(point, strText);
957
958 /* Restore painter: */
959 pPainter->restore();
960}
Note: See TracBrowser for help on using the repository browser.

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