source: trunk/src/gui/widgets/qscrollarea.cpp@ 5

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 16.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscrollarea.h"
43#include "private/qscrollarea_p.h"
44
45#ifndef QT_NO_SCROLLAREA
46
47#include "qscrollbar.h"
48#include "qlayout.h"
49#include "qstyle.h"
50#include "qapplication.h"
51#include "qvariant.h"
52#include "qdebug.h"
53#include "private/qlayoutengine_p.h"
54
55QT_BEGIN_NAMESPACE
56
57/*!
58 \class QScrollArea
59
60 \brief The QScrollArea class provides a scrolling view onto
61 another widget.
62
63 \ingroup basicwidgets
64 \mainclass
65
66 A scroll area is used to display the contents of a child widget
67 within a frame. If the widget exceeds the size of the frame, the
68 view can provide scroll bars so that the entire area of the child
69 widget can be viewed. The child widget must be specified with
70 setWidget(). For example:
71
72 \snippet doc/src/snippets/code/src_gui_widgets_qscrollarea.cpp 0
73
74 The code above creates a scroll area (shown in the images below)
75 containing an image label. When scaling the image, the scroll area
76 can provide the necessary scroll bars:
77
78 \table
79 \row
80 \o \inlineimage qscrollarea-noscrollbars.png
81 \o \inlineimage qscrollarea-onescrollbar.png
82 \o \inlineimage qscrollarea-twoscrollbars.png
83 \endtable
84
85 The scroll bars appearance depends on the currently set \l
86 {Qt::ScrollBarPolicy}{scroll bar policies}. You can control the
87 appearance of the scroll bars using the inherited functionality
88 from QAbstractScrollArea.
89
90 For example, you can set the
91 QAbstractScrollArea::horizontalScrollBarPolicy and
92 QAbstractScrollArea::verticalScrollBarPolicy properties. Or if you
93 want the scroll bars to adjust dynamically when the contents of
94 the scroll area changes, you can use the \l
95 {QAbstractScrollArea::horizontalScrollBar()}{horizontalScrollBar()}
96 and \l
97 {QAbstractScrollArea::verticalScrollBar()}{verticalScrollBar()}
98 functions (which enable you to access the scroll bars) and set the
99 scroll bars' values whenever the scroll area's contents change,
100 using the QScrollBar::setValue() function.
101
102 You can retrieve the child widget using the widget() function. The
103 view can be made to be resizable with the setWidgetResizable()
104 function. The alignment of the widget can be specified with
105 setAlignment().
106
107 Two convenience functions ensureVisible() and
108 ensureWidgetVisible() ensure a certain region of the contents is
109 visible inside the viewport, by scrolling the contents if
110 necessary.
111
112 \section1 Size Hints and Layouts
113
114 When using a scroll area to display the contents of a custom
115 widget, it is important to ensure that the
116 \l{QWidget::sizeHint}{size hint} of the child widget is set to a
117 suitable value. If a standard QWidget is used for the child
118 widget, it may be necessary to call QWidget::setMinimumSize() to
119 ensure that the contents of the widget are shown correctly within
120 the scroll area.
121
122 If a scroll area is used to display the contents of a widget that
123 contains child widgets arranged in a layout, it is important to
124 realise that the size policy of the layout will also determine the
125 size of the widget. This is especially useful to know if you intend
126 to dynamically change the contents of the layout. In such cases,
127 setting the layout's \l{QLayout::sizeConstraint}{size constraint}
128 property to one which provides constraints on the minimum and/or
129 maximum size of the layout (e.g., QLayout::SetMinAndMaxSize) will
130 cause the size of the the scroll area to be updated whenever the
131 contents of the layout changes.
132
133 For a complete example using the QScrollArea class, see the \l
134 {widgets/imageviewer}{Image Viewer} example. The example shows how
135 to combine QLabel and QScrollArea to display an image.
136
137 \sa QAbstractScrollArea, QScrollBar, {Image Viewer Example}
138*/
139
140
141/*!
142 Constructs an empty scroll area with the given \a parent.
143
144 \sa setWidget()
145*/
146QScrollArea::QScrollArea(QWidget *parent)
147 : QAbstractScrollArea(*new QScrollAreaPrivate,parent)
148{
149 Q_D(QScrollArea);
150 d->viewport->setBackgroundRole(QPalette::NoRole);
151 d->vbar->setSingleStep(20);
152 d->hbar->setSingleStep(20);
153 d->layoutChildren();
154}
155
156/*!
157 \internal
158*/
159QScrollArea::QScrollArea(QScrollAreaPrivate &dd, QWidget *parent)
160 : QAbstractScrollArea(dd, parent)
161{
162 Q_D(QScrollArea);
163 d->viewport->setBackgroundRole(QPalette::NoRole);
164 d->vbar->setSingleStep(20);
165 d->hbar->setSingleStep(20);
166 d->layoutChildren();
167}
168
169/*!
170 Destroys the scroll area and its child widget.
171
172 \sa setWidget()
173*/
174QScrollArea::~QScrollArea()
175{
176}
177
178void QScrollAreaPrivate::updateWidgetPosition()
179{
180 Q_Q(QScrollArea);
181 Qt::LayoutDirection dir = q->layoutDirection();
182 QRect scrolled = QStyle::visualRect(dir, viewport->rect(), QRect(QPoint(-hbar->value(), -vbar->value()), widget->size()));
183 QRect aligned = QStyle::alignedRect(dir, alignment, widget->size(), viewport->rect());
184 widget->move(widget->width() < viewport->width() ? aligned.x() : scrolled.x(),
185 widget->height() < viewport->height() ? aligned.y() : scrolled.y());
186}
187
188void QScrollAreaPrivate::updateScrollBars()
189{
190 Q_Q(QScrollArea);
191 if (!widget)
192 return;
193 QSize p = viewport->size();
194 QSize m = q->maximumViewportSize();
195
196 QSize min = qSmartMinSize(widget);
197 QSize max = qSmartMaxSize(widget);
198
199 if (resizable) {
200 if ((widget->layout() ? widget->layout()->hasHeightForWidth() : widget->sizePolicy().hasHeightForWidth())) {
201 QSize p_hfw = p.expandedTo(min).boundedTo(max);
202 int h = widget->heightForWidth( p_hfw.width() );
203 min = QSize(p_hfw.width(), qMax(p_hfw.height(), h));
204 }
205 }
206
207 if ((resizable && m.expandedTo(min) == m && m.boundedTo(max) == m)
208 || (!resizable && m.expandedTo(widget->size()) == m))
209 p = m; // no scroll bars needed
210
211 if (resizable)
212 widget->resize(p.expandedTo(min).boundedTo(max));
213 QSize v = widget->size();
214
215 hbar->setRange(0, v.width() - p.width());
216 hbar->setPageStep(p.width());
217 vbar->setRange(0, v.height() - p.height());
218 vbar->setPageStep(p.height());
219 updateWidgetPosition();
220
221}
222
223/*!
224 Returns the scroll area's widget, or 0 if there is none.
225
226 \sa setWidget()
227*/
228
229QWidget *QScrollArea::widget() const
230{
231 Q_D(const QScrollArea);
232 return d->widget;
233}
234
235/*!
236 \fn void QScrollArea::setWidget(QWidget *widget)
237
238 Sets the scroll area's \a widget.
239
240 The \a widget becomes a child of the scroll area, and will be
241 destroyed when the scroll area is deleted or when a new widget is
242 set.
243
244 The widget's \l{QWidget::setAutoFillBackground()}{autoFillBackground}
245 property will be set to \c{true}.
246
247 If the scroll area is visible when the \a widget is
248 added, you must \l{QWidget::}{show()} it explicitly.
249
250 Note that You must add the layout of \a widget before you call
251 this function; if you add it later, the \a widget will not be
252 visible - regardless of when you \l{QWidget::}{show()} the scroll
253 area. In this case, you can also not \l{QWidget::}{show()} the \a
254 widget later.
255
256 \sa widget()
257*/
258void QScrollArea::setWidget(QWidget *widget)
259{
260 Q_D(QScrollArea);
261 if (widget == d->widget || !widget)
262 return;
263
264 delete d->widget;
265 d->widget = 0;
266 d->hbar->setValue(0);
267 d->vbar->setValue(0);
268 if (widget->parentWidget() != d->viewport)
269 widget->setParent(d->viewport);
270 if (!widget->testAttribute(Qt::WA_Resized))
271 widget->resize(widget->sizeHint());
272 d->widget = widget;
273 d->widget->setAutoFillBackground(true);
274 widget->installEventFilter(this);
275 d->widgetSize = QSize();
276 d->updateScrollBars();
277 d->widget->show();
278
279}
280
281/*!
282 Removes the scroll area's widget, and passes ownership of the
283 widget to the caller.
284
285 \sa widget()
286 */
287QWidget *QScrollArea::takeWidget()
288{
289 Q_D(QScrollArea);
290 QWidget *w = d->widget;
291 d->widget = 0;
292 if (w)
293 w->setParent(0);
294 return w;
295}
296
297/*!
298 \reimp
299 */
300bool QScrollArea::event(QEvent *e)
301{
302 Q_D(QScrollArea);
303 if (e->type() == QEvent::StyleChange || e->type() == QEvent::LayoutRequest) {
304 d->updateScrollBars();
305 }
306#ifdef QT_KEYPAD_NAVIGATION
307 else if (QApplication::keypadNavigationEnabled()) {
308 if (e->type() == QEvent::Show)
309 QApplication::instance()->installEventFilter(this);
310 else if (e->type() == QEvent::Hide)
311 QApplication::instance()->removeEventFilter(this);
312 }
313#endif
314 return QAbstractScrollArea::event(e);
315}
316
317
318/*!
319 \reimp
320 */
321bool QScrollArea::eventFilter(QObject *o, QEvent *e)
322{
323 Q_D(QScrollArea);
324#ifdef QT_KEYPAD_NAVIGATION
325 if (d->widget && o != d->widget && e->type() == QEvent::FocusIn
326 && QApplication::keypadNavigationEnabled()) {
327 if (o->isWidgetType())
328 ensureWidgetVisible(static_cast<QWidget *>(o));
329 }
330#endif
331 if (o == d->widget && e->type() == QEvent::Resize)
332 d->updateScrollBars();
333
334 return false;
335}
336
337/*!
338 \reimp
339 */
340void QScrollArea::resizeEvent(QResizeEvent *)
341{
342 Q_D(QScrollArea);
343 d->updateScrollBars();
344
345}
346
347
348/*!\reimp
349 */
350void QScrollArea::scrollContentsBy(int, int)
351{
352 Q_D(QScrollArea);
353 if (!d->widget)
354 return;
355 d->updateWidgetPosition();
356}
357
358
359/*!
360 \property QScrollArea::widgetResizable
361 \brief whether the scroll area should resize the view widget
362
363 If this property is set to false (the default), the scroll area
364 honors the size of its widget. Regardless of this property, you
365 can programmatically resize the widget using widget()->resize(),
366 and the scroll area will automatically adjust itself to the new
367 size.
368
369 If this property is set to true, the scroll area will
370 automatically resize the widget in order to avoid scroll bars
371 where they can be avoided, or to take advantage of extra space.
372*/
373bool QScrollArea::widgetResizable() const
374{
375 Q_D(const QScrollArea);
376 return d->resizable;
377}
378
379void QScrollArea::setWidgetResizable(bool resizable)
380{
381 Q_D(QScrollArea);
382 d->resizable = resizable;
383 updateGeometry();
384 d->updateScrollBars();
385}
386
387/*!
388 \reimp
389 */
390QSize QScrollArea::sizeHint() const
391{
392 Q_D(const QScrollArea);
393 int f = 2 * d->frameWidth;
394 QSize sz(f, f);
395 int h = fontMetrics().height();
396 if (d->widget) {
397 if (!d->widgetSize.isValid())
398 d->widgetSize = d->resizable ? d->widget->sizeHint() : d->widget->size();
399 sz += d->widgetSize;
400 } else {
401 sz += QSize(12 * h, 8 * h);
402 }
403 if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
404 sz.setWidth(sz.width() + d->vbar->sizeHint().width());
405 if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
406 sz.setHeight(sz.height() + d->hbar->sizeHint().height());
407 return sz.boundedTo(QSize(36 * h, 24 * h));
408}
409
410
411
412/*!
413 \reimp
414 */
415bool QScrollArea::focusNextPrevChild(bool next)
416{
417 if (QWidget::focusNextPrevChild(next)) {
418 if (QWidget *fw = focusWidget())
419 ensureWidgetVisible(fw);
420 return true;
421 }
422 return false;
423}
424
425/*!
426 Scrolls the contents of the scroll area so that the point (\a x, \a y) is visible
427 inside the region of the viewport with margins specified in pixels by \a xmargin and
428 \a ymargin. If the specified point cannot be reached, the contents are scrolled to
429 the nearest valid position. The default value for both margins is 50 pixels.
430*/
431void QScrollArea::ensureVisible(int x, int y, int xmargin, int ymargin)
432{
433 Q_D(QScrollArea);
434
435 int logicalX = QStyle::visualPos(layoutDirection(), d->viewport->rect(), QPoint(x, y)).x();
436
437 if (logicalX - xmargin < d->hbar->value()) {
438 d->hbar->setValue(qMax(0, logicalX - xmargin));
439 } else if (logicalX > d->hbar->value() + d->viewport->width() - xmargin) {
440 d->hbar->setValue(qMin(logicalX - d->viewport->width() + xmargin, d->hbar->maximum()));
441 }
442
443 if (y - ymargin < d->vbar->value()) {
444 d->vbar->setValue(qMax(0, y - ymargin));
445 } else if (y > d->vbar->value() + d->viewport->height() - ymargin) {
446 d->vbar->setValue(qMin(y - d->viewport->height() + ymargin, d->vbar->maximum()));
447 }
448}
449
450/*!
451 \since 4.2
452
453 Scrolls the contents of the scroll area so that the \a childWidget
454 of QScrollArea::widget() is visible inside the viewport with
455 margins specified in pixels by \a xmargin and \a ymargin. If the
456 specified point cannot be reached, the contents are scrolled to
457 the nearest valid position. The default value for both margins is
458 50 pixels.
459
460*/
461void QScrollArea::ensureWidgetVisible(QWidget *childWidget, int xmargin, int ymargin)
462{
463 Q_D(QScrollArea);
464
465 if (!d->widget->isAncestorOf(childWidget))
466 return;
467
468 const QRect microFocus = childWidget->inputMethodQuery(Qt::ImMicroFocus).toRect();
469 const QRect defaultMicroFocus =
470 childWidget->QWidget::inputMethodQuery(Qt::ImMicroFocus).toRect();
471 QRect focusRect = (microFocus != defaultMicroFocus)
472 ? QRect(childWidget->mapTo(d->widget, microFocus.topLeft()), microFocus.size())
473 : QRect(childWidget->mapTo(d->widget, QPoint(0,0)), childWidget->size());
474 const QRect visibleRect(-d->widget->pos(), d->viewport->size());
475
476 if (visibleRect.contains(focusRect))
477 return;
478
479 focusRect.adjust(-xmargin, -ymargin, xmargin, ymargin);
480
481 if (focusRect.width() > visibleRect.width())
482 d->hbar->setValue(focusRect.center().x() - d->viewport->width() / 2);
483 else if (focusRect.right() > visibleRect.right())
484 d->hbar->setValue(focusRect.right() - d->viewport->width());
485 else
486 d->hbar->setValue(focusRect.left());
487
488 if (focusRect.height() > visibleRect.height())
489 d->vbar->setValue(focusRect.center().y() - d->viewport->height() / 2);
490 else if (focusRect.bottom() > visibleRect.bottom())
491 d->vbar->setValue(focusRect.bottom() - d->viewport->height());
492 else
493 d->vbar->setValue(focusRect.top());
494}
495
496
497/*!
498 \property QScrollArea::alignment
499 \brief the alignment of the scroll area's widget
500 \since 4.2
501
502 By default, the widget stays rooted to the top-left corner of the
503 scroll area.
504*/
505
506void QScrollArea::setAlignment(Qt::Alignment alignment)
507{
508 Q_D(QScrollArea);
509 d->alignment = alignment;
510 if (d->widget)
511 d->updateWidgetPosition();
512}
513
514Qt::Alignment QScrollArea::alignment() const
515{
516 Q_D(const QScrollArea);
517 return d->alignment;
518}
519
520QT_END_NAMESPACE
521
522#endif // QT_NO_SCROLLAREA
Note: See TracBrowser for help on using the repository browser.