VirtualBox

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

Last change on this file was 104358, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/* $Id: QIArrowSplitter.cpp 104358 2024-04-18 05:33:40Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QIArrowSplitter class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-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 <QHBoxLayout>
31#include <QStyle>
32#include <QTextEdit>
33
34/* GUI includes: */
35#include "QIArrowSplitter.h"
36#include "QIArrowButtonPress.h"
37#include "QIArrowButtonSwitch.h"
38#include "UIDesktopWidgetWatchdog.h"
39#include "UIIconPool.h"
40#include "UITranslationEventListener.h"
41
42/* Other VBox includes: */
43#include "iprt/assert.h"
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
51{
52 Q_OBJECT;
53
54public:
55
56 /** Constructs details-browser passing @a pParent to the base-class. */
57 QIDetailsBrowser(QWidget *pParent = 0);
58
59 /** Returns minimum size-hint. */
60 QSize minimumSizeHint() const RT_OVERRIDE RT_FINAL;
61 /** Returns size-hint. */
62 QSize sizeHint() const RT_OVERRIDE RT_FINAL;
63
64 /** Update scroll-bars. */
65 void updateScrollBars();
66};
67
68
69/*********************************************************************************************************************************
70* Class QIDetailsBrowser implementation. *
71*********************************************************************************************************************************/
72
73QIDetailsBrowser::QIDetailsBrowser(QWidget *pParent /* = 0 */)
74 : QTextEdit(pParent)
75{
76 /* Prepare: */
77 setReadOnly(true);
78}
79
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: */
85 const int iDocumentIdealWidth = (int)document()->idealWidth();
86 /* Moreover we should take document margins into account: */
87 const int iDocumentMargin = (int)document()->documentMargin();
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: */
94 const QSize screenGeometryDot4 = gpDesktop->screenGeometry(this).size() * .4;
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;
109}
110
111QSize QIDetailsBrowser::sizeHint() const
112{
113 /* Return minimum size-hint: */
114 return minimumSizeHint();
115}
116
117void QIDetailsBrowser::updateScrollBars()
118{
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);
126}
127
128
129/*********************************************************************************************************************************
130* Class QIArrowSplitter implementation. *
131*********************************************************************************************************************************/
132
133QIArrowSplitter::QIArrowSplitter(QWidget *pParent /* = 0 */)
134 : QWidget(pParent)
135 , m_pMainLayout(0)
136 , m_pSwitchButton(0)
137 , m_pBackButton(0)
138 , m_pNextButton(0)
139 , m_pDetailsBrowser(0)
140 , m_iDetailsIndex(-1)
141{
142 /* Prepare: */
143 prepare();
144}
145
146QSize QIArrowSplitter::minimumSizeHint() const
147{
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();
153
154 /* Calculate width-hint: */
155 int iWidthHint = 0;
156 iWidthHint += switchButtonHint.width();
157 iWidthHint += 100 /* button spacing */;
158 iWidthHint += backButtonHint.width();
159 iWidthHint += nextButtonHint.width();
160 iWidthHint = qMax(iWidthHint, detailsBrowserHint.width());
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());
167 if (m_pDetailsBrowser->isVisible())
168 iHeightHint += m_pMainLayout->spacing() + detailsBrowserHint.height();
169
170 /* Return result: */
171 return QSize(iWidthHint, iHeightHint);
172}
173
174void QIArrowSplitter::setName(const QString &strName)
175{
176 /* Assign name for the switch-button: */
177 m_pSwitchButton->setText(strName);
178 /* Update size-hints: */
179 sltUpdateSizeHints();
180}
181
182void QIArrowSplitter::setDetails(const QStringPairList &details)
183{
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}
195
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}
205
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
243void QIArrowSplitter::sltRetranslateUI()
244{
245 /* Update details: */
246 updateDetails();
247}
248
249void QIArrowSplitter::prepare()
250{
251 /* Create main-layout: */
252 m_pMainLayout = new QVBoxLayout(this);
253 AssertPtrReturnVoid(m_pMainLayout);
254 {
255 /* Configure main-layout: */
256 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
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
262 /* Create button-layout: */
263 QHBoxLayout *pButtonLayout = new QHBoxLayout;
264 AssertPtrReturnVoid(pButtonLayout);
265 {
266 /* Determine icon metric: */
267 const QStyle *pStyle = QApplication::style();
268 const int iIconMetric = (int)(pStyle->pixelMetric(QStyle::PM_SmallIconSize) * .625);
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);
275 {
276 /* Configure switch-button: */
277 m_pSwitchButton->setIconSize(QSize(iIconMetric, iIconMetric));
278 m_pSwitchButton->setIcons(UIIconPool::iconSet(":/arrow_right_10px.png"),
279 UIIconPool::iconSet(":/arrow_down_10px.png"));
280 connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateNavigationButtonsVisibility);
281 connect(m_pSwitchButton, &QIArrowButtonSwitch::sigClicked, this, &QIArrowSplitter::sltUpdateDetailsBrowserVisibility);
282
283 /* Add switch-button into button-layout: */
284 pButtonLayout->addWidget(m_pSwitchButton);
285 }
286 /* Add stretch: */
287 pButtonLayout->addStretch();
288 /* Create back-button: */
289 m_pBackButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Back);
290 AssertPtrReturnVoid(m_pBackButton);
291 {
292 /* Configure back-button: */
293 m_pBackButton->setIconSize(QSize(iIconMetric, iIconMetric));
294 m_pBackButton->setIcon(UIIconPool::iconSet(":/arrow_left_10px.png"));
295 connect(m_pBackButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageBack);
296
297 /* Add back-button into button-layout: */
298 pButtonLayout->addWidget(m_pBackButton);
299 }
300 /* Create next-button: */
301 m_pNextButton = new QIArrowButtonPress(QIArrowButtonPress::ButtonType_Next);
302 AssertPtrReturnVoid(m_pNextButton);
303 {
304 /* Configure next-button: */
305 m_pNextButton->setIconSize(QSize(iIconMetric, iIconMetric));
306 m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_right_10px.png"));
307 connect(m_pNextButton, &QIArrowButtonPress::sigClicked, this, &QIArrowSplitter::sltSwitchDetailsPageNext);
308
309 /* Add next-button into button-layout: */
310 pButtonLayout->addWidget(m_pNextButton);
311 }
312 /* Add button layout into main-layout: */
313 m_pMainLayout->addLayout(pButtonLayout);
314 /* Update navigation-buttons visibility: */
315 sltUpdateNavigationButtonsVisibility();
316 }
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 }
328 }
329
330 /* Apply size-policy finally: */
331 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
332 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
333 this, &QIArrowSplitter::sltRetranslateUI);
334}
335
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 {
356 setName(tr("&Details"));
357 m_pBackButton->setEnabled(false);
358 m_pNextButton->setEnabled(false);
359 }
360 /* Multi-paging: */
361 else if (m_details.size() > 1)
362 {
363 setName(tr("&Details (%1 of %2)").arg(m_iDetailsIndex + 1).arg(m_details.size()));
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
380
381#include "QIArrowSplitter.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use