VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extensions/QIArrowSplitter.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
RevLine 
[26714]1/* $Id: QIArrowSplitter.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
[25177]2/** @file
[71900]3 * VBox Qt GUI - Qt extensions: QIArrowSplitter class implementation.
[25177]4 */
5
6/*
[106061]7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
[25177]8 *
[96407]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
[25177]26 */
27
[51873]28/* Qt includes: */
[76606]29#include <QApplication>
30#include <QHBoxLayout>
31#include <QStyle>
32#include <QTextEdit>
[25177]33
[51873]34/* GUI includes: */
[76606]35#include "QIArrowSplitter.h"
36#include "QIArrowButtonPress.h"
37#include "QIArrowButtonSwitch.h"
38#include "UIDesktopWidgetWatchdog.h"
39#include "UIIconPool.h"
[104358]40#include "UITranslationEventListener.h"
[51873]41
42/* Other VBox includes: */
[76606]43#include "iprt/assert.h"
[51873]44
45
46/** QTextEdit extension
47 * taking into account text-document size-hint.
48 * @note Used with QIMessageBox class only.
49 * @todo Should be moved/renamed accordingly. */
50class QIDetailsBrowser : public QTextEdit
[25177]51{
[51873]52 Q_OBJECT;
[25177]53
[51873]54public:
[25177]55
[71395]56 /** Constructs details-browser passing @a pParent to the base-class. */
[51873]57 QIDetailsBrowser(QWidget *pParent = 0);
[25177]58
[51873]59 /** Returns minimum size-hint. */
[103988]60 QSize minimumSizeHint() const RT_OVERRIDE RT_FINAL;
[51873]61 /** Returns size-hint. */
[103988]62 QSize sizeHint() const RT_OVERRIDE RT_FINAL;
[25177]63
[51873]64 /** Update scroll-bars. */
65 void updateScrollBars();
66};
[25177]67
[71395]68
69/*********************************************************************************************************************************
70* Class QIDetailsBrowser implementation. *
71*********************************************************************************************************************************/
72
[51873]73QIDetailsBrowser::QIDetailsBrowser(QWidget *pParent /* = 0 */)
74 : QTextEdit(pParent)
75{
76 /* Prepare: */
77 setReadOnly(true);
78}
[45307]79
[51873]80QSize QIDetailsBrowser::minimumSizeHint() const
81{
82 /* Get document size as the basis: */
83 QSize documentSize = document()->size().toSize();
84 /* But only document ideal-width can advice wise width: */
[56217]85 const int iDocumentIdealWidth = (int)document()->idealWidth();
[51873]86 /* Moreover we should take document margins into account: */
[56217]87 const int iDocumentMargin = (int)document()->documentMargin();
[51873]88
89 /* Compose minimum size-hint on the basis of values above: */
90 documentSize.setWidth(iDocumentIdealWidth + iDocumentMargin);
91 documentSize.setHeight(documentSize.height() + iDocumentMargin);
92
93 /* Get 40% of the screen-area to limit the resulting hint: */
[97682]94 const QSize screenGeometryDot4 = gpDesktop->screenGeometry(this).size() * .4;
[51873]95
96 /* Calculate minimum size-hint which is document-size limited by screen-area: */
97 QSize mSizeHint = documentSize.boundedTo(screenGeometryDot4);
98
99 /* If there is not enough of vertical space: */
100 if (mSizeHint.height() < documentSize.height())
101 {
102 /* We should also take into account vertical scroll-bar extent: */
103 int iExtent = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
104 mSizeHint.setWidth(mSizeHint.width() + iExtent);
105 }
106
107 /* Always bound cached hint by 40% of current screen-area: */
108 return mSizeHint;
[25177]109}
110
[51873]111QSize QIDetailsBrowser::sizeHint() const
[25177]112{
[51873]113 /* Return minimum size-hint: */
114 return minimumSizeHint();
[25177]115}
116
[51873]117void QIDetailsBrowser::updateScrollBars()
[25177]118{
[51873]119 /* Some Qt issue prevents scroll-bars from update.. */
120 Qt::ScrollBarPolicy horizontalPolicy = horizontalScrollBarPolicy();
121 Qt::ScrollBarPolicy verticalPolicy = verticalScrollBarPolicy();
122 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
123 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
124 setHorizontalScrollBarPolicy(horizontalPolicy);
125 setVerticalScrollBarPolicy(verticalPolicy);
[25177]126}
127
[51873]128
[71395]129/*********************************************************************************************************************************
130* Class QIArrowSplitter implementation. *
131*********************************************************************************************************************************/
132
[51873]133QIArrowSplitter::QIArrowSplitter(QWidget *pParent /* = 0 */)
[104358]134 : QWidget(pParent)
[51873]135 , m_pMainLayout(0)
136 , m_pSwitchButton(0)
137 , m_pBackButton(0)
138 , m_pNextButton(0)
139 , m_pDetailsBrowser(0)
140 , m_iDetailsIndex(-1)
[25177]141{
[51873]142 /* Prepare: */
143 prepare();
[25177]144}
145
[45307]146QSize QIArrowSplitter::minimumSizeHint() const
147{
[51873]148 /* Get minimum size-hints: */
149 const QSize switchButtonHint = m_pSwitchButton->minimumSizeHint();
150 const QSize backButtonHint = m_pBackButton->minimumSizeHint();
151 const QSize nextButtonHint = m_pNextButton->minimumSizeHint();
152 const QSize detailsBrowserHint = m_pDetailsBrowser->minimumSizeHint();
[45307]153
154 /* Calculate width-hint: */
155 int iWidthHint = 0;
156 iWidthHint += switchButtonHint.width();
[51873]157 iWidthHint += 100 /* button spacing */;
[45307]158 iWidthHint += backButtonHint.width();
159 iWidthHint += nextButtonHint.width();
[51873]160 iWidthHint = qMax(iWidthHint, detailsBrowserHint.width());
[45307]161
162 /* Calculate height-hint: */
163 int iHeightHint = 0;
164 iHeightHint = qMax(iHeightHint, switchButtonHint.height());
165 iHeightHint = qMax(iHeightHint, backButtonHint.height());
166 iHeightHint = qMax(iHeightHint, nextButtonHint.height());
[51873]167 if (m_pDetailsBrowser->isVisible())
168 iHeightHint += m_pMainLayout->spacing() + detailsBrowserHint.height();
[45307]169
170 /* Return result: */
171 return QSize(iWidthHint, iHeightHint);
172}
173
[51873]174void QIArrowSplitter::setName(const QString &strName)
[25177]175{
[51873]176 /* Assign name for the switch-button: */
177 m_pSwitchButton->setText(strName);
178 /* Update size-hints: */
179 sltUpdateSizeHints();
[25177]180}
181
[51873]182void QIArrowSplitter::setDetails(const QStringPairList &details)
[25177]183{
[51873]184 /* Assign new details: */
185 m_details = details;
186 /* Reset the details-list index: */
187 m_iDetailsIndex = m_details.isEmpty() ? -1 : 0;
188 /* Update navigation-buttons visibility: */
189 sltUpdateNavigationButtonsVisibility();
190 /* Update details-browser visibility: */
191 sltUpdateDetailsBrowserVisibility();
192 /* Update details: */
193 updateDetails();
194}
[25177]195
[51873]196void QIArrowSplitter::sltUpdateSizeHints()
197{
198 /* Let parent layout know our size-hint changed: */
199 updateGeometry();
200 /* Notify parent about our size-hint changed: */
201 emit sigSizeHintChange();
202 /* Update details-browser scroll-bars: */
203 m_pDetailsBrowser->updateScrollBars();
204}
[25177]205
[51873]206void QIArrowSplitter::sltUpdateNavigationButtonsVisibility()
207{
208 /* Depending on switch-button state: */
209 const bool fExpanded = m_pSwitchButton->isExpanded();
210 /* Update back/next button visibility: */
211 m_pBackButton->setVisible(m_details.size() > 1 && fExpanded);
212 m_pNextButton->setVisible(m_details.size() > 1 && fExpanded);
213}
214
215void QIArrowSplitter::sltUpdateDetailsBrowserVisibility()
216{
217 /* Update details-browser visibility according switch-button state: */
218 m_pDetailsBrowser->setVisible(m_details.size() > 0 && m_pSwitchButton->isExpanded());
219 /* Update size-hints: */
220 sltUpdateSizeHints();
221}
222
223void QIArrowSplitter::sltSwitchDetailsPageBack()
224{
225 /* Make sure details-page index feats the bounds: */
226 AssertReturnVoid(m_iDetailsIndex > 0);
227 /* Decrease details-list index: */
228 --m_iDetailsIndex;
229 /* Update details: */
230 updateDetails();
231}
232
233void QIArrowSplitter::sltSwitchDetailsPageNext()
234{
235 /* Make sure details-page index feats the bounds: */
236 AssertReturnVoid(m_iDetailsIndex < m_details.size() - 1);
237 /* Increase details-list index: */
238 ++m_iDetailsIndex;
239 /* Update details: */
240 updateDetails();
241}
242
[104358]243void QIArrowSplitter::sltRetranslateUI()
[71395]244{
245 /* Update details: */
246 updateDetails();
247}
248
[51873]249void QIArrowSplitter::prepare()
250{
251 /* Create main-layout: */
252 m_pMainLayout = new QVBoxLayout(this);
253 AssertPtrReturnVoid(m_pMainLayout);
[25177]254 {
[51873]255 /* Configure main-layout: */
256 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
[70523]257#ifdef VBOX_WS_MAC
258 m_pMainLayout->setSpacing(5);
259#else
260 m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
261#endif
[51873]262 /* Create button-layout: */
263 QHBoxLayout *pButtonLayout = new QHBoxLayout;
264 AssertPtrReturnVoid(pButtonLayout);
[25177]265 {
[55932]266 /* Determine icon metric: */
267 const QStyle *pStyle = QApplication::style();
[56217]268 const int iIconMetric = (int)(pStyle->pixelMetric(QStyle::PM_SmallIconSize) * .625);
[51873]269 /* Configure button-layout: */
270 pButtonLayout->setContentsMargins(0, 0, 0, 0);
271 pButtonLayout->setSpacing(0);
272 /* Create switch-button: */
273 m_pSwitchButton = new QIArrowButtonSwitch;
274 AssertPtrReturnVoid(m_pSwitchButton);
[25177]275 {
[51873]276 /* Configure switch-button: */
[55932]277 m_pSwitchButton->setIconSize(QSize(iIconMetric, iIconMetric));
[62143]278 m_pSwitchButton->setIcons(UIIconPool::iconSet(":/arrow_right_10px.png"),
279 UIIconPool::iconSet(":/arrow_down_10px.png"));
[68079]280 connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateNavigationButtonsVisibility);
281 connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateDetailsBrowserVisibility);
282
[51873]283 /* Add switch-button into button-layout: */
284 pButtonLayout->addWidget(m_pSwitchButton);
[25177]285 }
[51873]286 /* Add stretch: */
287 pButtonLayout->addStretch();
288 /* Create back-button: */
289 m_pBackButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Back);
290 AssertPtrReturnVoid(m_pBackButton);
[25177]291 {
[51873]292 /* Configure back-button: */
[55932]293 m_pBackButton->setIconSize(QSize(iIconMetric, iIconMetric));
[51873]294 m_pBackButton->setIcon(UIIconPool::iconSet(":/arrow_left_10px.png"));
[68079]295 connect(m_pBackButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageBack);
296
[51873]297 /* Add back-button into button-layout: */
298 pButtonLayout->addWidget(m_pBackButton);
[25177]299 }
[51873]300 /* Create next-button: */
301 m_pNextButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Next);
302 AssertPtrReturnVoid(m_pNextButton);
[25177]303 {
[51873]304 /* Configure next-button: */
[55932]305 m_pNextButton->setIconSize(QSize(iIconMetric, iIconMetric));
[51873]306 m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_right_10px.png"));
[68079]307 connect(m_pNextButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageNext);
308
[51873]309 /* Add next-button into button-layout: */
310 pButtonLayout->addWidget(m_pNextButton);
[25177]311 }
[51873]312 /* Add button layout into main-layout: */
313 m_pMainLayout->addLayout(pButtonLayout);
314 /* Update navigation-buttons visibility: */
315 sltUpdateNavigationButtonsVisibility();
[25177]316 }
[51873]317 /* Create details-browser: */
318 m_pDetailsBrowser = new QIDetailsBrowser;
319 AssertPtrReturnVoid(m_pDetailsBrowser);
320 {
321 /* Add details-browser into main-layout: */
322 m_pMainLayout->addWidget(m_pDetailsBrowser);
323 /* Update details-browser visibility: */
324 sltUpdateDetailsBrowserVisibility();
325 /* Update details: */
326 updateDetails();
327 }
[25177]328 }
329
[51873]330 /* Apply size-policy finally: */
331 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
[104358]332 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
333 this, &QIArrowSplitter::sltRetranslateUI);
[25177]334}
335
[51873]336void QIArrowSplitter::updateDetails()
337{
338 /* If details are empty: */
339 if (m_details.isEmpty())
340 {
341 /* Make sure details-list index is invalid: */
342 AssertReturnVoid(m_iDetailsIndex == -1);
343
344 /* Reset name: */
345 setName(QString());
346 }
347 /* If details are NOT empty: */
348 else
349 {
350 /* Make sure details-list index feats the bounds: */
351 AssertReturnVoid(m_iDetailsIndex >= 0 && m_iDetailsIndex < m_details.size());
352
353 /* Single page: */
354 if (m_details.size() == 1)
355 {
[62038]356 setName(tr("&Details"));
[51873]357 m_pBackButton->setEnabled(false);
358 m_pNextButton->setEnabled(false);
359 }
360 /* Multi-paging: */
361 else if (m_details.size() > 1)
362 {
[62038]363 setName(tr("&Details (%1 of %2)").arg(m_iDetailsIndex + 1).arg(m_details.size()));
[51873]364 m_pBackButton->setEnabled(m_iDetailsIndex > 0);
365 m_pNextButton->setEnabled(m_iDetailsIndex < m_details.size() - 1);
366 }
367
368 /* Update details-browser: */
369 const QString strFirstPart = m_details[m_iDetailsIndex].first;
370 const QString strSecondPart = m_details[m_iDetailsIndex].second;
371 if (strFirstPart.isEmpty())
372 m_pDetailsBrowser->setText(strSecondPart);
373 else
374 m_pDetailsBrowser->setText(QString("%1<br>%2").arg(strFirstPart, strSecondPart));
375 }
376 /* Update size-hints: */
377 sltUpdateSizeHints();
378}
379
[71900]380
[51873]381#include "QIArrowSplitter.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