VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsScrollBar.cpp@ 103988

Last change on this file since 103988 was 98103, checked in by vboxsync, 21 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: UIGraphicsScrollBar.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIGraphicsScrollBar class implementation.
4 */
5
6/*
7 * Copyright (C) 2019-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 <QApplication>
30#include <QGraphicsScene>
31#include <QGraphicsSceneMouseEvent>
32#include <QPainter>
33#include <QPropertyAnimation>
34#include <QSignalTransition>
35#include <QState>
36#include <QStateMachine>
37#include <QStyle>
38#include <QStyleOptionGraphicsItem>
39#include <QTimer>
40#include <QTimerEvent>
41
42/* GUI includes: */
43#include "UIGraphicsButton.h"
44#include "UIGraphicsScrollBar.h"
45#include "UIIconPool.h"
46
47
48/** Small functor for QTimer::singleShot worker to perform delayed scrolling. */
49class ScrollFunctor
50{
51public:
52
53 /** Performs delayed scrolling of specified @a pScrollBar to certain @a pos. */
54 ScrollFunctor(UIGraphicsScrollBar *pScrollBar, const QPointF &pos)
55 : m_pScrollBar(pScrollBar)
56 , m_pos(pos)
57 {}
58
59 /** Contains functor's body. */
60 void operator()()
61 {
62 m_pScrollBar->scrollTo(m_pos, 100);
63 }
64
65private:
66
67 /** Holds the scroll-bar reference to scroll. */
68 UIGraphicsScrollBar *m_pScrollBar;
69 /** Holds the position to scroll to. */
70 QPointF m_pos;
71};
72
73
74/** QIGraphicsWidget subclass providing GUI with graphics scroll-bar taken. */
75class UIGraphicsScrollBarToken : public QIGraphicsWidget
76{
77 Q_OBJECT;
78
79signals:
80
81 /** Notifies listeners about mouse moved to certain @a pos. */
82 void sigMouseMoved(const QPointF &pos);
83
84public:
85
86 /** Constructs graphics scroll-bar token passing @a pParent to the base-class. */
87 UIGraphicsScrollBarToken(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent = 0);
88
89 /** Returns minimum size-hint. */
90 virtual QSizeF minimumSizeHint() const RT_OVERRIDE;
91
92 /** Returns whether token is hovered. */
93 bool isHovered() const { return m_fHovered; }
94
95protected:
96
97 /** Performs painting using passed @a pPainter, @a pOptions and optionally specified @a pWidget. */
98 virtual void paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *pWidget = 0) RT_OVERRIDE;
99
100 /** Handles mouse-press @a pEvent. */
101 virtual void mousePressEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
102 /** Handles mouse-release @a pEvent. */
103 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent) RT_OVERRIDE;
104
105 /** Handles hover enter @a pEvent. */
106 virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
107 /** Handles hover leave @a pEvent. */
108 virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent) RT_OVERRIDE;
109
110private:
111
112 /** Prepares all. */
113 void prepare();
114 /** Updates scroll-bar extent value. */
115 void updateExtent();
116
117 /** Holds the orientation. */
118 const Qt::Orientation m_enmOrientation;
119
120 /** Holds the scroll-bar extent. */
121 int m_iExtent;
122
123 /** Holds whether item is hovered. */
124 bool m_fHovered;
125};
126
127
128/*********************************************************************************************************************************
129* Class UIGraphicsScrollBarToken implementation. *
130*********************************************************************************************************************************/
131
132UIGraphicsScrollBarToken::UIGraphicsScrollBarToken(Qt::Orientation enmOrientation, QIGraphicsWidget *pParent /* = 0 */)
133 : QIGraphicsWidget(pParent)
134 , m_enmOrientation(enmOrientation)
135 , m_iExtent(0)
136 , m_fHovered(false)
137{
138 prepare();
139}
140
141QSizeF UIGraphicsScrollBarToken::minimumSizeHint() const
142{
143 /* Calculate minimum size-hint depending on orientation: */
144 switch (m_enmOrientation)
145 {
146#ifdef VBOX_WS_MAC
147 case Qt::Horizontal: return QSizeF(2 * m_iExtent, m_iExtent);
148 case Qt::Vertical: return QSizeF(m_iExtent, 2 * m_iExtent);
149#else
150 case Qt::Horizontal: return QSizeF(m_iExtent, m_iExtent);
151 case Qt::Vertical: return QSizeF(m_iExtent, m_iExtent);
152#endif
153 }
154
155 /* Call to base-class: */
156 return QIGraphicsWidget::minimumSizeHint();
157}
158
159void UIGraphicsScrollBarToken::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
160{
161 /* Save painter: */
162 pPainter->save();
163
164 /* Prepare color: */
165 const QPalette pal = QApplication::palette();
166
167#ifdef VBOX_WS_MAC
168
169 /* Draw background: */
170 QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window).darker(190);
171 QRectF actualRectangle = pOptions->rect;
172 actualRectangle.setLeft(pOptions->rect.left() + .22 * pOptions->rect.width());
173 actualRectangle.setRight(pOptions->rect.right() - .22 * pOptions->rect.width());
174 const double dRadius = actualRectangle.width() / 2;
175 QPainterPath painterPath = QPainterPath(QPoint(actualRectangle.x(), actualRectangle.y() + dRadius));
176 painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y(), 2 * dRadius, 2 * dRadius), 180, -180);
177 painterPath.lineTo(actualRectangle.x() + 2 * dRadius, actualRectangle.y() + actualRectangle.height() - dRadius);
178 painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y() + actualRectangle.height() - 2 * dRadius, 2 * dRadius, 2 * dRadius), 0, -180);
179 painterPath.closeSubpath();
180 pPainter->setClipPath(painterPath);
181 pPainter->fillRect(actualRectangle, backgroundColor);
182
183#else
184
185 /* Draw background: */
186 QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window).darker(140);
187 pPainter->fillRect(pOptions->rect, backgroundColor);
188
189#endif
190
191 /* Restore painter: */
192 pPainter->restore();
193}
194
195void UIGraphicsScrollBarToken::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
196{
197 /* Call to base-class: */
198 QIGraphicsWidget::mousePressEvent(pEvent);
199
200 /* Accept event to be able to receive mouse move events: */
201 pEvent->accept();
202}
203
204void UIGraphicsScrollBarToken::mouseMoveEvent(QGraphicsSceneMouseEvent *pEvent)
205{
206 /* Call to base-class: */
207 QIGraphicsWidget::mouseMoveEvent(pEvent);
208
209 /* Let listeners know about our mouse move events. */
210 emit sigMouseMoved(mapToParent(pEvent->pos()));
211}
212
213void UIGraphicsScrollBarToken::hoverMoveEvent(QGraphicsSceneHoverEvent *)
214{
215 if (!m_fHovered)
216 m_fHovered = true;
217}
218
219void UIGraphicsScrollBarToken::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
220{
221 if (m_fHovered)
222 m_fHovered = false;
223}
224
225void UIGraphicsScrollBarToken::prepare()
226{
227 setAcceptHoverEvents(true);
228
229 updateExtent();
230 resize(minimumSizeHint());
231}
232
233void UIGraphicsScrollBarToken::updateExtent()
234{
235 m_iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
236 updateGeometry();
237}
238
239
240/*********************************************************************************************************************************
241* Class UIGraphicsScrollBar implementation. *
242*********************************************************************************************************************************/
243
244UIGraphicsScrollBar::UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QGraphicsScene *pScene)
245 : m_enmOrientation(enmOrientation)
246 , m_fAutoHideMode(fAutoHideMode)
247 , m_iExtent(-1)
248 , m_iMinimum(0)
249 , m_iMaximum(100)
250 , m_iValue(0)
251 , m_pButton1(0)
252 , m_pButton2(0)
253 , m_pToken(0)
254 , m_fHovered(false)
255 , m_iHoverOnTimerId(0)
256 , m_iHoverOffTimerId(0)
257 , m_iHoveringValue(0)
258 , m_fScrollInProgress(false)
259#ifdef VBOX_WS_MAC
260 , m_fRevealed(false)
261 , m_iRevealingValue(m_fAutoHideMode ? 0 : 50)
262 , m_iRevealOnTimerId(0)
263 , m_iRevealOffTimerId(0)
264#endif
265{
266 pScene->addItem(this);
267 prepare();
268}
269
270UIGraphicsScrollBar::UIGraphicsScrollBar(Qt::Orientation enmOrientation, bool fAutoHideMode, QIGraphicsWidget *pParent /* = 0 */)
271 : QIGraphicsWidget(pParent)
272 , m_enmOrientation(enmOrientation)
273 , m_fAutoHideMode(fAutoHideMode)
274 , m_iExtent(-1)
275 , m_iMinimum(0)
276 , m_iMaximum(100)
277 , m_iValue(0)
278 , m_pButton1(0)
279 , m_pButton2(0)
280 , m_pToken(0)
281 , m_fHovered(false)
282 , m_iHoverOnTimerId(0)
283 , m_iHoverOffTimerId(0)
284 , m_iHoveringValue(0)
285 , m_fScrollInProgress(false)
286#ifdef VBOX_WS_MAC
287 , m_fRevealed(false)
288 , m_iRevealingValue(m_fAutoHideMode ? 0 : 50)
289 , m_iRevealOnTimerId(0)
290 , m_iRevealOffTimerId(0)
291#endif
292{
293 prepare();
294}
295
296QSizeF UIGraphicsScrollBar::minimumSizeHint() const
297{
298 /* Calculate minimum size-hint depending on orientation: */
299 switch (m_enmOrientation)
300 {
301 case Qt::Horizontal: return QSizeF(3 * m_iExtent, m_iExtent);
302 case Qt::Vertical: return QSizeF(m_iExtent, 3 * m_iExtent);
303 }
304
305 /* Call to base-class: */
306 return QIGraphicsWidget::minimumSizeHint();
307}
308
309int UIGraphicsScrollBar::step() const
310{
311 return 2 * QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
312}
313
314int UIGraphicsScrollBar::pageStep() const
315{
316 return 3 * step();
317}
318
319void UIGraphicsScrollBar::setMinimum(int iMinimum)
320{
321 m_iMinimum = iMinimum;
322 if (m_iMaximum < m_iMinimum)
323 m_iMaximum = m_iMinimum;
324 if (m_iValue < m_iMinimum)
325 {
326 m_iValue = m_iMinimum;
327 emit sigValueChanged(m_iValue);
328 }
329 layoutToken();
330}
331
332int UIGraphicsScrollBar::minimum() const
333{
334 return m_iMinimum;
335}
336
337void UIGraphicsScrollBar::setMaximum(int iMaximum)
338{
339 m_iMaximum = iMaximum;
340 if (m_iMinimum > m_iMaximum)
341 m_iMinimum = m_iMaximum;
342 if (m_iValue > m_iMaximum)
343 {
344 m_iValue = m_iMaximum;
345 emit sigValueChanged(m_iValue);
346 }
347 layoutToken();
348}
349
350int UIGraphicsScrollBar::maximum() const
351{
352 return m_iMaximum;
353}
354
355void UIGraphicsScrollBar::setValue(int iValue)
356{
357 if (iValue > m_iMaximum)
358 iValue = m_iMaximum;
359 if (iValue < m_iMinimum)
360 iValue = m_iMinimum;
361 m_iValue = iValue;
362 emit sigValueChanged(m_iValue);
363 layoutToken();
364}
365
366int UIGraphicsScrollBar::value() const
367{
368 return m_iValue;
369}
370
371void UIGraphicsScrollBar::scrollTo(const QPointF &desiredPos, int iDelay /* = 500 */)
372{
373 /* Prepare current, desired and intermediate positions: */
374 const QPointF currentPos = actualTokenPosition();
375 const int iCurrentY = currentPos.y();
376 const int iCurrentX = currentPos.x();
377 const int iDesiredY = desiredPos.y();
378 const int iDesiredX = desiredPos.x();
379 QPointF intermediatePos;
380
381 /* Calculate intermediate position depending on orientation: */
382 switch (m_enmOrientation)
383 {
384 case Qt::Horizontal:
385 {
386 if (iCurrentX < iDesiredX)
387 {
388 intermediatePos.setY(desiredPos.y());
389 intermediatePos.setX(iCurrentX + pageStep() < iDesiredX ? iCurrentX + pageStep() : iDesiredX);
390 }
391 else if (iCurrentX > iDesiredX)
392 {
393 intermediatePos.setY(desiredPos.y());
394 intermediatePos.setX(iCurrentX - pageStep() > iDesiredX ? iCurrentX - pageStep() : iDesiredX);
395 }
396 break;
397 }
398 case Qt::Vertical:
399 {
400 if (iCurrentY < iDesiredY)
401 {
402 intermediatePos.setX(desiredPos.x());
403 intermediatePos.setY(iCurrentY + pageStep() < iDesiredY ? iCurrentY + pageStep() : iDesiredY);
404 }
405 else if (iCurrentY > iDesiredY)
406 {
407 intermediatePos.setX(desiredPos.x());
408 intermediatePos.setY(iCurrentY - pageStep() > iDesiredY ? iCurrentY - pageStep() : iDesiredY);
409 }
410 break;
411 }
412 }
413
414 /* Move token to intermediate position: */
415 if (!intermediatePos.isNull())
416 sltTokenMoved(intermediatePos);
417
418 /* Continue, if we haven't reached required position: */
419 if ((intermediatePos != desiredPos) && m_fScrollInProgress)
420 QTimer::singleShot(iDelay, ScrollFunctor(this, desiredPos));
421}
422
423void UIGraphicsScrollBar::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
424{
425 /* Call to base-class: */
426 QIGraphicsWidget::resizeEvent(pEvent);
427
428 /* Layout widgets: */
429 layoutWidgets();
430}
431
432void UIGraphicsScrollBar::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget *)
433{
434 /* Acquire rectangle: */
435 const QRect rectangle = pOptions->rect;
436 /* Paint background: */
437 paintBackground(pPainter, rectangle);
438}
439
440void UIGraphicsScrollBar::mousePressEvent(QGraphicsSceneMouseEvent *pEvent)
441{
442 /* Call to base-class: */
443 QIGraphicsWidget::mousePressEvent(pEvent);
444
445 /* Mark event accepted so that it couldn't
446 * influence underlying widgets: */
447 pEvent->accept();
448
449 /* Start scrolling sequence: */
450 m_fScrollInProgress = true;
451 scrollTo(pEvent->pos());
452}
453
454void UIGraphicsScrollBar::mouseReleaseEvent(QGraphicsSceneMouseEvent *pEvent)
455{
456 /* Call to base-class: */
457 QIGraphicsWidget::mousePressEvent(pEvent);
458
459 /* Mark event accepted so that it couldn't
460 * influence underlying widgets: */
461 pEvent->accept();
462
463 /* Stop scrolling if any: */
464 m_fScrollInProgress = false;
465}
466
467void UIGraphicsScrollBar::hoverMoveEvent(QGraphicsSceneHoverEvent *)
468{
469 /* Only if not yet hovered, that way we
470 * make sure trigger emitted just once: */
471 if (!m_fHovered)
472 {
473 /* Start hover-on timer, handled in timerEvent() below: */
474 m_iHoverOnTimerId = startTimer(m_fAutoHideMode ? 400 : 100);
475 m_fHovered = true;
476 }
477 /* Update in any case: */
478 update();
479}
480
481void UIGraphicsScrollBar::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
482{
483 /* Only if it's still hovered, that way we
484 * make sure trigger emitted just once: */
485 if (m_fHovered)
486 {
487 /* Start hover-off timer, handled in timerEvent() below: */
488 m_iHoverOffTimerId = startTimer(m_fAutoHideMode ? 1000 : 100);
489 m_fHovered = false;
490 }
491 /* Update in any case: */
492 update();
493}
494
495void UIGraphicsScrollBar::timerEvent(QTimerEvent *pEvent)
496{
497 /* Kill timer in any case: */
498 const int iTimerId = pEvent->timerId();
499 killTimer(iTimerId);
500
501 /* If that is hover-on timer: */
502 if (m_iHoverOnTimerId != 0 && iTimerId == m_iHoverOnTimerId)
503 {
504 /* Wait for timer no more: */
505 m_iHoverOnTimerId = 0;
506 /* Emit hover-on trigger if hovered: */
507 if (m_fHovered)
508 emit sigHoverEnter();
509 /* Update in any case: */
510 update();
511 }
512
513 else
514
515 /* If that is hover-off timer: */
516 if (m_iHoverOffTimerId != 0 && iTimerId == m_iHoverOffTimerId)
517 {
518 /* Wait for timer no more: */
519 m_iHoverOffTimerId = 0;
520 /* Emit hover-off trigger if not hovered: */
521 if (!m_fHovered && !m_pToken->isHovered())
522 emit sigHoverLeave();
523 /* Update in any case: */
524 update();
525 }
526
527#ifdef VBOX_WS_MAC
528
529 else
530
531 /* If that is reveal-in timer: */
532 if (m_iRevealOnTimerId != 0 && iTimerId == m_iRevealOnTimerId)
533 {
534 /* Wait for timer no more: */
535 m_iRevealOnTimerId = 0;
536 }
537
538 else
539
540 /* If that is reveal-out timer: */
541 if (m_iRevealOffTimerId != 0 && iTimerId == m_iRevealOffTimerId)
542 {
543 /* Wait for timer no more: */
544 m_iRevealOffTimerId = 0;
545 /* Emit reveal-out signal if not hovered or was reinvoked not long time ago: */
546 if (!m_fHovered && !m_pToken->isHovered() && m_iRevealOnTimerId == 0)
547 emit sigRevealLeave();
548 /* Restart timer otherwise: */
549 else
550 m_iRevealOffTimerId = startTimer(m_fAutoHideMode ? 2000 : 100);
551 /* Update in any case: */
552 update();
553 }
554
555#endif
556}
557
558void UIGraphicsScrollBar::sltButton1Clicked()
559{
560 setValue(value() - step());
561}
562
563void UIGraphicsScrollBar::sltButton2Clicked()
564{
565 setValue(value() + step());
566}
567
568void UIGraphicsScrollBar::sltTokenMoved(const QPointF &pos)
569{
570 /* Depending on orientation: */
571 double dRatio = 0;
572 switch (m_enmOrientation)
573 {
574 case Qt::Horizontal:
575 {
576 /* We have to calculate the X coord of the token, leaving Y untouched: */
577#ifdef VBOX_WS_MAC
578 const int iMin = 0;
579 const int iMax = size().width() - 2 * m_iExtent;
580#else
581 const int iMin = m_iExtent;
582 const int iMax = size().width() - 2 * m_iExtent;
583#endif
584 int iX = pos.x() - m_iExtent / 2;
585 iX = qMax(iX, iMin);
586 iX = qMin(iX, iMax);
587 /* And then calculate new ratio to update value finally: */
588 dRatio = iMax > iMin ? (double)(iX - iMin) / (iMax - iMin) : 0;
589 break;
590 }
591 case Qt::Vertical:
592 {
593 /* We have to calculate the Y coord of the token, leaving X untouched: */
594#ifdef VBOX_WS_MAC
595 const int iMin = 0;
596 const int iMax = size().height() - 2 * m_iExtent;
597#else
598 const int iMin = m_iExtent;
599 const int iMax = size().height() - 2 * m_iExtent;
600#endif
601 int iY = pos.y() - m_iExtent / 2;
602 iY = qMax(iY, iMin);
603 iY = qMin(iY, iMax);
604 /* And then calculate new ratio to update value finally: */
605 dRatio = iMax > iMin ? (double)(iY - iMin) / (iMax - iMin) : 0;
606 break;
607 }
608 }
609
610 /* Update value according to calculated ratio: */
611 m_iValue = dRatio * (m_iMaximum - m_iMinimum) + m_iMinimum;
612 emit sigValueChanged(m_iValue);
613 layoutToken();
614}
615
616void UIGraphicsScrollBar::sltStateLeftDefault()
617{
618 if (m_pButton1)
619 m_pButton1->hide();
620 if (m_pButton2)
621 m_pButton2->hide();
622 if (m_pToken)
623 m_pToken->hide();
624}
625
626void UIGraphicsScrollBar::sltStateLeftHovered()
627{
628 if (m_pButton1)
629 m_pButton1->hide();
630 if (m_pButton2)
631 m_pButton2->hide();
632 if (m_pToken)
633 m_pToken->hide();
634}
635
636void UIGraphicsScrollBar::sltStateEnteredDefault()
637{
638 if (m_pButton1)
639 m_pButton1->hide();
640 if (m_pButton2)
641 m_pButton2->hide();
642 if (m_pToken)
643 m_pToken->hide();
644}
645
646void UIGraphicsScrollBar::sltStateEnteredHovered()
647{
648 if (m_pButton1)
649 m_pButton1->show();
650 if (m_pButton2)
651 m_pButton2->show();
652 if (m_pToken)
653 m_pToken->show();
654}
655
656#ifdef VBOX_WS_MAC
657void UIGraphicsScrollBar::sltHandleRevealingStart()
658{
659 /* Only if not yet revealed, that way we
660 * make sure trigger emitted just once: */
661 if (!m_fRevealed)
662 {
663 /* Mark token revealed: */
664 m_fRevealed = true;
665 /* Emit reveal signal immediately: */
666 emit sigRevealEnter();
667 }
668
669 /* Restart fresh sustain timer: */
670 m_iRevealOnTimerId = startTimer(m_fAutoHideMode ? 1000 : 100);
671}
672
673void UIGraphicsScrollBar::sltStateEnteredFaded()
674{
675 /* Mark token faded: */
676 m_fRevealed = false;
677}
678
679void UIGraphicsScrollBar::sltStateEnteredRevealed()
680{
681 /* Start reveal-out timer: */
682 m_iRevealOffTimerId = startTimer(m_fAutoHideMode ? 2000 : 100);
683}
684#endif /* VBOX_WS_MAC */
685
686void UIGraphicsScrollBar::prepare()
687{
688 /* Configure self: */
689 setAcceptHoverEvents(true);
690
691 /* Prepare/layout widgets: */
692 prepareWidgets();
693 updateExtent();
694 layoutWidgets();
695
696 /* Prepare animation: */
697 prepareAnimation();
698}
699
700void UIGraphicsScrollBar::prepareWidgets()
701{
702 prepareButtons();
703 prepareToken();
704}
705
706void UIGraphicsScrollBar::prepareButtons()
707{
708#ifndef VBOX_WS_MAC
709 /* Create buttons depending on orientation: */
710 switch (m_enmOrientation)
711 {
712 case Qt::Horizontal:
713 {
714 m_pButton1 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_left_10px.png"));
715 m_pButton2 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_right_10px.png"));
716 break;
717 }
718 case Qt::Vertical:
719 {
720 m_pButton1 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_up_10px.png"));
721 m_pButton2 = new UIGraphicsButton(this, UIIconPool::iconSet(":/arrow_down_10px.png"));
722 break;
723 }
724 }
725
726 if (m_pButton1)
727 {
728 /* We use 10px icons, not 16px, let buttons know that: */
729 m_pButton1->setIconScaleIndex((double)10 / 16);
730 /* Also we want to have buttons react on mouse presses for auto-repeat feature: */
731 m_pButton1->setClickPolicy(UIGraphicsButton::ClickPolicy_OnPress);
732 connect(m_pButton1, &UIGraphicsButton::sigButtonClicked,
733 this, &UIGraphicsScrollBar::sltButton1Clicked);
734 }
735 if (m_pButton2)
736 {
737 /* We use 10px icons, not 16px, let buttons know that: */
738 m_pButton2->setIconScaleIndex((double)10 / 16);
739 /* Also we want to have buttons react on mouse presses for auto-repeat feature: */
740 m_pButton2->setClickPolicy(UIGraphicsButton::ClickPolicy_OnPress);
741 connect(m_pButton2, &UIGraphicsButton::sigButtonClicked,
742 this, &UIGraphicsScrollBar::sltButton2Clicked);
743 }
744#endif
745}
746
747void UIGraphicsScrollBar::prepareToken()
748{
749 /* Create token: */
750 m_pToken = new UIGraphicsScrollBarToken(m_enmOrientation, this);
751 if (m_pToken)
752 connect(m_pToken, &UIGraphicsScrollBarToken::sigMouseMoved,
753 this, &UIGraphicsScrollBar::sltTokenMoved);
754}
755
756void UIGraphicsScrollBar::prepareAnimation()
757{
758 prepareHoveringAnimation();
759#ifdef VBOX_WS_MAC
760 prepareRevealingAnimation();
761#endif
762}
763
764void UIGraphicsScrollBar::prepareHoveringAnimation()
765{
766 /* Create hovering animation machine: */
767 QStateMachine *pHoveringMachine = new QStateMachine(this);
768 if (pHoveringMachine)
769 {
770 /* Create 'default' state: */
771 QState *pStateDefault = new QState(pHoveringMachine);
772 /* Create 'hovered' state: */
773 QState *pStateHovered = new QState(pHoveringMachine);
774
775 /* Configure 'default' state: */
776 if (pStateDefault)
777 {
778 /* When we entering default state => we assigning hoveringValue to 0: */
779 pStateDefault->assignProperty(this, "hoveringValue", 0);
780 connect(pStateDefault, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredDefault);
781
782 /* Add state transitions: */
783 QSignalTransition *pDefaultToHovered = pStateDefault->addTransition(this, SIGNAL(sigHoverEnter()), pStateHovered);
784 if (pDefaultToHovered)
785 {
786 connect(pDefaultToHovered, &QSignalTransition::triggered, this, &UIGraphicsScrollBar::sltStateLeftDefault);
787
788 /* Create forward animation: */
789 QPropertyAnimation *pHoveringAnimationForward = new QPropertyAnimation(this, "hoveringValue", this);
790 if (pHoveringAnimationForward)
791 {
792 pHoveringAnimationForward->setDuration(200);
793 pHoveringAnimationForward->setStartValue(0);
794 pHoveringAnimationForward->setEndValue(100);
795
796 /* Add to transition: */
797 pDefaultToHovered->addAnimation(pHoveringAnimationForward);
798 }
799 }
800 }
801
802 /* Configure 'hovered' state: */
803 if (pStateHovered)
804 {
805 /* When we entering hovered state => we assigning hoveringValue to 100: */
806 pStateHovered->assignProperty(this, "hoveringValue", 100);
807 connect(pStateHovered, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredHovered);
808
809 /* Add state transitions: */
810 QSignalTransition *pHoveredToDefault = pStateHovered->addTransition(this, SIGNAL(sigHoverLeave()), pStateDefault);
811 if (pHoveredToDefault)
812 {
813 connect(pHoveredToDefault, &QSignalTransition::triggered, this, &UIGraphicsScrollBar::sltStateLeftHovered);
814
815 /* Create backward animation: */
816 QPropertyAnimation *pHoveringAnimationBackward = new QPropertyAnimation(this, "hoveringValue", this);
817 if (pHoveringAnimationBackward)
818 {
819 pHoveringAnimationBackward->setDuration(200);
820 pHoveringAnimationBackward->setStartValue(100);
821 pHoveringAnimationBackward->setEndValue(0);
822
823 /* Add to transition: */
824 pHoveredToDefault->addAnimation(pHoveringAnimationBackward);
825 }
826 }
827 }
828
829 /* Initial state is 'default': */
830 pHoveringMachine->setInitialState(pStateDefault);
831 /* Start state-machine: */
832 pHoveringMachine->start();
833 }
834}
835
836#ifdef VBOX_WS_MAC
837void UIGraphicsScrollBar::prepareRevealingAnimation()
838{
839 /* Create revealing animation machine: */
840 QStateMachine *pRevealingMachine = new QStateMachine(this);
841 if (pRevealingMachine)
842 {
843 /* Create 'faded' state: */
844 QState *pStateFaded = new QState(pRevealingMachine);
845 /* Create 'revealed' state: */
846 QState *pStateRevealed = new QState(pRevealingMachine);
847
848 /* Configure 'faded' state: */
849 if (pStateFaded)
850 {
851 /* When we entering fade state => we assigning revealingValue to 0: */
852 pStateFaded->assignProperty(this, "revealingValue", m_fAutoHideMode ? 0 : 50);
853 connect(pStateFaded, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredFaded);
854
855 /* Add state transitions: */
856 QSignalTransition *pFadeToRevealed = pStateFaded->addTransition(this, SIGNAL(sigRevealEnter()), pStateRevealed);
857 if (pFadeToRevealed)
858 {
859 /* Create forward animation: */
860 QPropertyAnimation *pRevealingAnimationForward = new QPropertyAnimation(this, "revealingValue", this);
861 if (pRevealingAnimationForward)
862 {
863 pRevealingAnimationForward->setDuration(200);
864 pRevealingAnimationForward->setStartValue(m_fAutoHideMode ? 0 : 50);
865 pRevealingAnimationForward->setEndValue(100);
866
867 /* Add to transition: */
868 pFadeToRevealed->addAnimation(pRevealingAnimationForward);
869 }
870 }
871 }
872
873 /* Configure 'revealed' state: */
874 if (pStateRevealed)
875 {
876 /* When we entering revealed state => we assigning revealingValue to 100: */
877 pStateRevealed->assignProperty(this, "revealingValue", 100);
878 connect(pStateRevealed, &QState::propertiesAssigned, this, &UIGraphicsScrollBar::sltStateEnteredRevealed);
879
880 /* Add state transitions: */
881 QSignalTransition *pRevealedToFaded = pStateRevealed->addTransition(this, SIGNAL(sigRevealLeave()), pStateFaded);
882 if (pRevealedToFaded)
883 {
884 /* Create backward animation: */
885 QPropertyAnimation *pRevealingAnimationBackward = new QPropertyAnimation(this, "revealingValue", this);
886 if (pRevealingAnimationBackward)
887 {
888 pRevealingAnimationBackward->setDuration(200);
889 pRevealingAnimationBackward->setStartValue(100);
890 pRevealingAnimationBackward->setEndValue(m_fAutoHideMode ? 0 : 50);
891
892 /* Add to transition: */
893 pRevealedToFaded->addAnimation(pRevealingAnimationBackward);
894 }
895 }
896 }
897
898 /* Initial state is 'fade': */
899 pRevealingMachine->setInitialState(pStateFaded);
900 /* Start state-machine: */
901 pRevealingMachine->start();
902 }
903
904 /* Install self-listener: */
905 connect(this, &UIGraphicsScrollBar::sigValueChanged, this, &UIGraphicsScrollBar::sltHandleRevealingStart);
906}
907#endif /* VBOX_WS_MAC */
908
909void UIGraphicsScrollBar::updateExtent()
910{
911 /* Make sure extent value is not smaller than the button size: */
912 m_iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
913#ifndef VBOX_WS_MAC
914 m_iExtent = qMax(m_iExtent, (int)m_pButton1->minimumSizeHint().width());
915 m_iExtent = qMax(m_iExtent, (int)m_pButton2->minimumSizeHint().width());
916#endif
917 updateGeometry();
918}
919
920void UIGraphicsScrollBar::layoutWidgets()
921{
922 layoutButtons();
923 layoutToken();
924}
925
926void UIGraphicsScrollBar::layoutButtons()
927{
928#ifndef VBOX_WS_MAC
929 // WORKAROUND:
930 // We are calculating proper button shift delta, because
931 // button size can be smaller than scroll-bar extent value.
932
933 int iDelta1 = 0;
934 if (m_iExtent > m_pButton1->minimumSizeHint().width())
935 iDelta1 = (m_iExtent - m_pButton1->minimumSizeHint().width() + 1) / 2;
936 m_pButton1->setPos(iDelta1, iDelta1);
937
938 int iDelta2 = 0;
939 if (m_iExtent > m_pButton2->minimumSizeHint().width())
940 iDelta2 = (m_iExtent - m_pButton2->minimumSizeHint().width() + 1) / 2;
941 m_pButton2->setPos(size().width() - m_iExtent + iDelta2, size().height() - m_iExtent + iDelta2);
942#endif
943}
944
945void UIGraphicsScrollBar::layoutToken()
946{
947 m_pToken->setPos(actualTokenPosition());
948 update();
949}
950
951QPoint UIGraphicsScrollBar::actualTokenPosition() const
952{
953 /* We calculating ratio on the basis of current/minimum/maximum values: */
954 const double dRatio = m_iMaximum > m_iMinimum ? (double)(m_iValue - m_iMinimum) / (m_iMaximum - m_iMinimum) : 0;
955
956 /* Prepare result: */
957 QPoint position;
958
959 /* Depending on orientation: */
960 switch (m_enmOrientation)
961 {
962 case Qt::Horizontal:
963 {
964 /* We have to adjust the X coord of the token, leaving Y unchanged: */
965#ifdef VBOX_WS_MAC
966 const int iMin = 0;
967 const int iMax = size().width() - 2 * m_iExtent;
968#else
969 const int iMin = m_iExtent;
970 const int iMax = size().width() - 2 * m_iExtent;
971#endif
972 int iX = dRatio * (iMax - iMin) + iMin;
973 position = QPoint(iX, 0);
974 break;
975 }
976 case Qt::Vertical:
977 {
978 /* We have to adjust the Y coord of the token, leaving X unchanged: */
979#ifdef VBOX_WS_MAC
980 const int iMin = 0;
981 const int iMax = size().height() - 2 * m_iExtent;
982#else
983 const int iMin = m_iExtent;
984 const int iMax = size().height() - 2 * m_iExtent;
985#endif
986 int iY = dRatio * (iMax - iMin) + iMin;
987 position = QPoint(0, iY);
988 break;
989 }
990 }
991
992 /* Return result: */
993 return position;
994}
995
996void UIGraphicsScrollBar::paintBackground(QPainter *pPainter, const QRect &rectangle) const
997{
998 /* Save painter: */
999 pPainter->save();
1000
1001 /* Prepare color: */
1002 const QPalette pal = QApplication::palette();
1003
1004#ifdef VBOX_WS_MAC
1005
1006 /* Draw background if necessary: */
1007 pPainter->save();
1008 QColor windowColor = pal.color(QPalette::Active, QPalette::Window);
1009 if (m_fAutoHideMode)
1010 windowColor.setAlpha(255 * ((double)m_iHoveringValue / 100));
1011 pPainter->fillRect(rectangle, windowColor);
1012 pPainter->restore();
1013
1014 /* Draw frame if necessary: */
1015 pPainter->save();
1016 QColor frameColor = pal.color(QPalette::Active, QPalette::Window);
1017 if (m_fAutoHideMode)
1018 frameColor.setAlpha(255 * ((double)m_iHoveringValue / 100));
1019 frameColor = frameColor.darker(120);
1020 pPainter->setPen(frameColor);
1021 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
1022 pPainter->restore();
1023
1024 /* Emulate token when necessary: */
1025 if (m_iHoveringValue < 100)
1026 {
1027 QColor tokenColor = pal.color(QPalette::Active, QPalette::Window);
1028 tokenColor.setAlpha(255 * ((double)m_iRevealingValue / 100));
1029 tokenColor = tokenColor.darker(190);
1030 QRectF tokenRectangle = QRect(actualTokenPosition(), QSize(m_iExtent, 2 * m_iExtent));
1031 QRectF actualRectangle = tokenRectangle;
1032 if (m_fAutoHideMode)
1033 {
1034 actualRectangle.setLeft(tokenRectangle.left() + .22 * tokenRectangle.width() + .22 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
1035 actualRectangle.setRight(tokenRectangle.right() - .22 * tokenRectangle.width() + .22 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100 - 1);
1036 }
1037 else
1038 {
1039 actualRectangle.setLeft(tokenRectangle.left() + .22 * tokenRectangle.width());
1040 actualRectangle.setRight(tokenRectangle.right() - .22 * tokenRectangle.width() - 1);
1041 }
1042 const double dRadius = actualRectangle.width() / 2;
1043 QPainterPath painterPath = QPainterPath(QPoint(actualRectangle.x(), actualRectangle.y() + dRadius));
1044 painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y(), 2 * dRadius, 2 * dRadius), 180, -180);
1045 painterPath.lineTo(actualRectangle.x() + 2 * dRadius, actualRectangle.y() + actualRectangle.height() - dRadius);
1046 painterPath.arcTo(QRectF(actualRectangle.x(), actualRectangle.y() + actualRectangle.height() - 2 * dRadius, 2 * dRadius, 2 * dRadius), 0, -180);
1047 painterPath.closeSubpath();
1048 pPainter->setClipPath(painterPath);
1049 pPainter->fillRect(actualRectangle, tokenColor);
1050 }
1051
1052#else
1053
1054 /* Draw background: */
1055 QColor backgroundColor = pal.color(QPalette::Active, QPalette::Window);
1056 backgroundColor.setAlpha(50 + (double)m_iHoveringValue / 100 * 150);
1057 QRect actualRectangle = rectangle;
1058 actualRectangle.setLeft(actualRectangle.left() + .85 * actualRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
1059 pPainter->fillRect(actualRectangle, backgroundColor);
1060
1061 /* Emulate token when necessary: */
1062 if (m_iHoveringValue < 100)
1063 {
1064 QColor tokenColor = pal.color(QPalette::Active, QPalette::Window).darker(140);
1065 QRect tokenRectangle = QRect(actualTokenPosition(), QSize(m_iExtent, m_iExtent));
1066 tokenRectangle.setLeft(tokenRectangle.left() + .85 * tokenRectangle.width() * ((double)100 - m_iHoveringValue) / 100);
1067 pPainter->fillRect(tokenRectangle, tokenColor);
1068 }
1069
1070#endif
1071
1072 /* Restore painter: */
1073 pPainter->restore();
1074}
1075
1076
1077#include "UIGraphicsScrollBar.moc"
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