--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/widgets/qscrollarea.cpp Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscrollarea.h"
+#include "private/qscrollarea_p.h"
+
+#ifndef QT_NO_SCROLLAREA
+
+#include "qscrollbar.h"
+#include "qlayout.h"
+#include "qstyle.h"
+#include "qapplication.h"
+#include "qvariant.h"
+#include "qdebug.h"
+#include "private/qlayoutengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QScrollArea
+
+ \brief The QScrollArea class provides a scrolling view onto
+ another widget.
+
+ \ingroup basicwidgets
+
+
+ A scroll area is used to display the contents of a child widget
+ within a frame. If the widget exceeds the size of the frame, the
+ view can provide scroll bars so that the entire area of the child
+ widget can be viewed. The child widget must be specified with
+ setWidget(). For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qscrollarea.cpp 0
+
+ The code above creates a scroll area (shown in the images below)
+ containing an image label. When scaling the image, the scroll area
+ can provide the necessary scroll bars:
+
+ \table
+ \row
+ \o \inlineimage qscrollarea-noscrollbars.png
+ \o \inlineimage qscrollarea-onescrollbar.png
+ \o \inlineimage qscrollarea-twoscrollbars.png
+ \endtable
+
+ The scroll bars appearance depends on the currently set \l
+ {Qt::ScrollBarPolicy}{scroll bar policies}. You can control the
+ appearance of the scroll bars using the inherited functionality
+ from QAbstractScrollArea.
+
+ For example, you can set the
+ QAbstractScrollArea::horizontalScrollBarPolicy and
+ QAbstractScrollArea::verticalScrollBarPolicy properties. Or if you
+ want the scroll bars to adjust dynamically when the contents of
+ the scroll area changes, you can use the \l
+ {QAbstractScrollArea::horizontalScrollBar()}{horizontalScrollBar()}
+ and \l
+ {QAbstractScrollArea::verticalScrollBar()}{verticalScrollBar()}
+ functions (which enable you to access the scroll bars) and set the
+ scroll bars' values whenever the scroll area's contents change,
+ using the QScrollBar::setValue() function.
+
+ You can retrieve the child widget using the widget() function. The
+ view can be made to be resizable with the setWidgetResizable()
+ function. The alignment of the widget can be specified with
+ setAlignment().
+
+ Two convenience functions ensureVisible() and
+ ensureWidgetVisible() ensure a certain region of the contents is
+ visible inside the viewport, by scrolling the contents if
+ necessary.
+
+ \section1 Size Hints and Layouts
+
+ When using a scroll area to display the contents of a custom
+ widget, it is important to ensure that the
+ \l{QWidget::sizeHint}{size hint} of the child widget is set to a
+ suitable value. If a standard QWidget is used for the child
+ widget, it may be necessary to call QWidget::setMinimumSize() to
+ ensure that the contents of the widget are shown correctly within
+ the scroll area.
+
+ If a scroll area is used to display the contents of a widget that
+ contains child widgets arranged in a layout, it is important to
+ realise that the size policy of the layout will also determine the
+ size of the widget. This is especially useful to know if you intend
+ to dynamically change the contents of the layout. In such cases,
+ setting the layout's \l{QLayout::sizeConstraint}{size constraint}
+ property to one which provides constraints on the minimum and/or
+ maximum size of the layout (e.g., QLayout::SetMinAndMaxSize) will
+ cause the size of the scroll area to be updated whenever the
+ contents of the layout changes.
+
+ For a complete example using the QScrollArea class, see the \l
+ {widgets/imageviewer}{Image Viewer} example. The example shows how
+ to combine QLabel and QScrollArea to display an image.
+
+ \sa QAbstractScrollArea, QScrollBar, {Image Viewer Example}
+*/
+
+
+/*!
+ Constructs an empty scroll area with the given \a parent.
+
+ \sa setWidget()
+*/
+QScrollArea::QScrollArea(QWidget *parent)
+ : QAbstractScrollArea(*new QScrollAreaPrivate,parent)
+{
+ Q_D(QScrollArea);
+ d->viewport->setBackgroundRole(QPalette::NoRole);
+ d->vbar->setSingleStep(20);
+ d->hbar->setSingleStep(20);
+ d->layoutChildren();
+}
+
+/*!
+ \internal
+*/
+QScrollArea::QScrollArea(QScrollAreaPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ Q_D(QScrollArea);
+ d->viewport->setBackgroundRole(QPalette::NoRole);
+ d->vbar->setSingleStep(20);
+ d->hbar->setSingleStep(20);
+ d->layoutChildren();
+}
+
+/*!
+ Destroys the scroll area and its child widget.
+
+ \sa setWidget()
+*/
+QScrollArea::~QScrollArea()
+{
+}
+
+void QScrollAreaPrivate::updateWidgetPosition()
+{
+ Q_Q(QScrollArea);
+ Qt::LayoutDirection dir = q->layoutDirection();
+ QRect scrolled = QStyle::visualRect(dir, viewport->rect(), QRect(QPoint(-hbar->value(), -vbar->value()), widget->size()));
+ QRect aligned = QStyle::alignedRect(dir, alignment, widget->size(), viewport->rect());
+ widget->move(widget->width() < viewport->width() ? aligned.x() : scrolled.x(),
+ widget->height() < viewport->height() ? aligned.y() : scrolled.y());
+}
+
+void QScrollAreaPrivate::updateScrollBars()
+{
+ Q_Q(QScrollArea);
+ if (!widget)
+ return;
+ QSize p = viewport->size();
+ QSize m = q->maximumViewportSize();
+
+ QSize min = qSmartMinSize(widget);
+ QSize max = qSmartMaxSize(widget);
+
+ if (resizable) {
+ if ((widget->layout() ? widget->layout()->hasHeightForWidth() : widget->sizePolicy().hasHeightForWidth())) {
+ QSize p_hfw = p.expandedTo(min).boundedTo(max);
+ int h = widget->heightForWidth( p_hfw.width() );
+ min = QSize(p_hfw.width(), qMax(p_hfw.height(), h));
+ }
+ }
+
+ if ((resizable && m.expandedTo(min) == m && m.boundedTo(max) == m)
+ || (!resizable && m.expandedTo(widget->size()) == m))
+ p = m; // no scroll bars needed
+
+ if (resizable)
+ widget->resize(p.expandedTo(min).boundedTo(max));
+ QSize v = widget->size();
+
+ hbar->setRange(0, v.width() - p.width());
+ hbar->setPageStep(p.width());
+ vbar->setRange(0, v.height() - p.height());
+ vbar->setPageStep(p.height());
+ updateWidgetPosition();
+
+}
+
+/*!
+ Returns the scroll area's widget, or 0 if there is none.
+
+ \sa setWidget()
+*/
+
+QWidget *QScrollArea::widget() const
+{
+ Q_D(const QScrollArea);
+ return d->widget;
+}
+
+/*!
+ \fn void QScrollArea::setWidget(QWidget *widget)
+
+ Sets the scroll area's \a widget.
+
+ The \a widget becomes a child of the scroll area, and will be
+ destroyed when the scroll area is deleted or when a new widget is
+ set.
+
+ The widget's \l{QWidget::setAutoFillBackground()}{autoFillBackground}
+ property will be set to \c{true}.
+
+ If the scroll area is visible when the \a widget is
+ added, you must \l{QWidget::}{show()} it explicitly.
+
+ Note that You must add the layout of \a widget before you call
+ this function; if you add it later, the \a widget will not be
+ visible - regardless of when you \l{QWidget::}{show()} the scroll
+ area. In this case, you can also not \l{QWidget::}{show()} the \a
+ widget later.
+
+ \sa widget()
+*/
+void QScrollArea::setWidget(QWidget *widget)
+{
+ Q_D(QScrollArea);
+ if (widget == d->widget || !widget)
+ return;
+
+ delete d->widget;
+ d->widget = 0;
+ d->hbar->setValue(0);
+ d->vbar->setValue(0);
+ if (widget->parentWidget() != d->viewport)
+ widget->setParent(d->viewport);
+ if (!widget->testAttribute(Qt::WA_Resized))
+ widget->resize(widget->sizeHint());
+ d->widget = widget;
+ d->widget->setAutoFillBackground(true);
+ widget->installEventFilter(this);
+ d->widgetSize = QSize();
+ d->updateScrollBars();
+ d->widget->show();
+
+}
+
+/*!
+ Removes the scroll area's widget, and passes ownership of the
+ widget to the caller.
+
+ \sa widget()
+ */
+QWidget *QScrollArea::takeWidget()
+{
+ Q_D(QScrollArea);
+ QWidget *w = d->widget;
+ d->widget = 0;
+ if (w)
+ w->setParent(0);
+ return w;
+}
+
+/*!
+ \reimp
+ */
+bool QScrollArea::event(QEvent *e)
+{
+ Q_D(QScrollArea);
+ if (e->type() == QEvent::StyleChange || e->type() == QEvent::LayoutRequest) {
+ d->updateScrollBars();
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (QApplication::keypadNavigationEnabled()) {
+ if (e->type() == QEvent::Show)
+ QApplication::instance()->installEventFilter(this);
+ else if (e->type() == QEvent::Hide)
+ QApplication::instance()->removeEventFilter(this);
+ }
+#endif
+ return QAbstractScrollArea::event(e);
+}
+
+
+/*!
+ \reimp
+ */
+bool QScrollArea::eventFilter(QObject *o, QEvent *e)
+{
+ Q_D(QScrollArea);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (d->widget && o != d->widget && e->type() == QEvent::FocusIn
+ && QApplication::keypadNavigationEnabled()) {
+ if (o->isWidgetType())
+ ensureWidgetVisible(static_cast<QWidget *>(o));
+ }
+#endif
+ if (o == d->widget && e->type() == QEvent::Resize)
+ d->updateScrollBars();
+
+ return false;
+}
+
+/*!
+ \reimp
+ */
+void QScrollArea::resizeEvent(QResizeEvent *)
+{
+ Q_D(QScrollArea);
+ d->updateScrollBars();
+
+}
+
+
+/*!\reimp
+ */
+void QScrollArea::scrollContentsBy(int, int)
+{
+ Q_D(QScrollArea);
+ if (!d->widget)
+ return;
+ d->updateWidgetPosition();
+}
+
+
+/*!
+ \property QScrollArea::widgetResizable
+ \brief whether the scroll area should resize the view widget
+
+ If this property is set to false (the default), the scroll area
+ honors the size of its widget. Regardless of this property, you
+ can programmatically resize the widget using widget()->resize(),
+ and the scroll area will automatically adjust itself to the new
+ size.
+
+ If this property is set to true, the scroll area will
+ automatically resize the widget in order to avoid scroll bars
+ where they can be avoided, or to take advantage of extra space.
+*/
+bool QScrollArea::widgetResizable() const
+{
+ Q_D(const QScrollArea);
+ return d->resizable;
+}
+
+void QScrollArea::setWidgetResizable(bool resizable)
+{
+ Q_D(QScrollArea);
+ d->resizable = resizable;
+ updateGeometry();
+ d->updateScrollBars();
+}
+
+/*!
+ \reimp
+ */
+QSize QScrollArea::sizeHint() const
+{
+ Q_D(const QScrollArea);
+ int f = 2 * d->frameWidth;
+ QSize sz(f, f);
+ int h = fontMetrics().height();
+ if (d->widget) {
+ if (!d->widgetSize.isValid())
+ d->widgetSize = d->resizable ? d->widget->sizeHint() : d->widget->size();
+ sz += d->widgetSize;
+ } else {
+ sz += QSize(12 * h, 8 * h);
+ }
+ if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
+ sz.setWidth(sz.width() + d->vbar->sizeHint().width());
+ if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
+ sz.setHeight(sz.height() + d->hbar->sizeHint().height());
+ return sz.boundedTo(QSize(36 * h, 24 * h));
+}
+
+
+
+/*!
+ \reimp
+ */
+bool QScrollArea::focusNextPrevChild(bool next)
+{
+ if (QWidget::focusNextPrevChild(next)) {
+ if (QWidget *fw = focusWidget())
+ ensureWidgetVisible(fw);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Scrolls the contents of the scroll area so that the point (\a x, \a y) is visible
+ inside the region of the viewport with margins specified in pixels by \a xmargin and
+ \a ymargin. If the specified point cannot be reached, the contents are scrolled to
+ the nearest valid position. The default value for both margins is 50 pixels.
+*/
+void QScrollArea::ensureVisible(int x, int y, int xmargin, int ymargin)
+{
+ Q_D(QScrollArea);
+
+ int logicalX = QStyle::visualPos(layoutDirection(), d->viewport->rect(), QPoint(x, y)).x();
+
+ if (logicalX - xmargin < d->hbar->value()) {
+ d->hbar->setValue(qMax(0, logicalX - xmargin));
+ } else if (logicalX > d->hbar->value() + d->viewport->width() - xmargin) {
+ d->hbar->setValue(qMin(logicalX - d->viewport->width() + xmargin, d->hbar->maximum()));
+ }
+
+ if (y - ymargin < d->vbar->value()) {
+ d->vbar->setValue(qMax(0, y - ymargin));
+ } else if (y > d->vbar->value() + d->viewport->height() - ymargin) {
+ d->vbar->setValue(qMin(y - d->viewport->height() + ymargin, d->vbar->maximum()));
+ }
+}
+
+/*!
+ \since 4.2
+
+ Scrolls the contents of the scroll area so that the \a childWidget
+ of QScrollArea::widget() is visible inside the viewport with
+ margins specified in pixels by \a xmargin and \a ymargin. If the
+ specified point cannot be reached, the contents are scrolled to
+ the nearest valid position. The default value for both margins is
+ 50 pixels.
+
+*/
+void QScrollArea::ensureWidgetVisible(QWidget *childWidget, int xmargin, int ymargin)
+{
+ Q_D(QScrollArea);
+
+ if (!d->widget->isAncestorOf(childWidget))
+ return;
+
+ const QRect microFocus = childWidget->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ const QRect defaultMicroFocus =
+ childWidget->QWidget::inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QRect focusRect = (microFocus != defaultMicroFocus)
+ ? QRect(childWidget->mapTo(d->widget, microFocus.topLeft()), microFocus.size())
+ : QRect(childWidget->mapTo(d->widget, QPoint(0,0)), childWidget->size());
+ const QRect visibleRect(-d->widget->pos(), d->viewport->size());
+
+ if (visibleRect.contains(focusRect))
+ return;
+
+ focusRect.adjust(-xmargin, -ymargin, xmargin, ymargin);
+
+ if (focusRect.width() > visibleRect.width())
+ d->hbar->setValue(focusRect.center().x() - d->viewport->width() / 2);
+ else if (focusRect.right() > visibleRect.right())
+ d->hbar->setValue(focusRect.right() - d->viewport->width());
+ else
+ d->hbar->setValue(focusRect.left());
+
+ if (focusRect.height() > visibleRect.height())
+ d->vbar->setValue(focusRect.center().y() - d->viewport->height() / 2);
+ else if (focusRect.bottom() > visibleRect.bottom())
+ d->vbar->setValue(focusRect.bottom() - d->viewport->height());
+ else
+ d->vbar->setValue(focusRect.top());
+}
+
+
+/*!
+ \property QScrollArea::alignment
+ \brief the alignment of the scroll area's widget
+ \since 4.2
+
+ By default, the widget stays rooted to the top-left corner of the
+ scroll area.
+*/
+
+void QScrollArea::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QScrollArea);
+ d->alignment = alignment;
+ if (d->widget)
+ d->updateWidgetPosition();
+}
+
+Qt::Alignment QScrollArea::alignment() const
+{
+ Q_D(const QScrollArea);
+ return d->alignment;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SCROLLAREA