diff options
Diffstat (limited to 'src/quicktemplates2')
25 files changed, 648 insertions, 254 deletions
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 9c840493..f2442cb7 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -105,7 +105,7 @@ static const int AUTO_REPEAT_INTERVAL = 100; */ QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() : - down(false), explicitDown(false), pressed(false), checked(false), checkable(false), + down(false), explicitDown(false), pressed(false), keepPressed(false), checked(false), checkable(false), autoExclusive(false), autoRepeat(false), wasHeld(false), holdTimer(0), delayTimer(0), repeatTimer(0), repeatButton(Qt::NoButton), indicator(nullptr), group(nullptr) { @@ -525,7 +525,7 @@ void QQuickAbstractButton::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickAbstractButton); QQuickControl::mouseMoveEvent(event); - setPressed(contains(event->pos())); + setPressed(d->keepPressed || contains(event->pos())); if (d->autoRepeat) d->stopPressRepeat(); @@ -540,7 +540,7 @@ void QQuickAbstractButton::mouseReleaseEvent(QMouseEvent *event) bool wasPressed = d->pressed; setPressed(false); - if (contains(event->pos())) + if (d->keepPressed || contains(event->pos())) nextCheckState(); if (wasPressed) { diff --git a/src/quicktemplates2/qquickabstractbutton_p_p.h b/src/quicktemplates2/qquickabstractbutton_p_p.h index a7d4d6c2..688ad804 100644 --- a/src/quicktemplates2/qquickabstractbutton_p_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p_p.h @@ -82,6 +82,7 @@ public: bool down; bool explicitDown; bool pressed; + bool keepPressed; bool checked; bool checkable; bool autoExclusive; diff --git a/src/quicktemplates2/qquickapplicationwindow.cpp b/src/quicktemplates2/qquickapplicationwindow.cpp index 7f466fd7..7fb406c1 100644 --- a/src/quicktemplates2/qquickapplicationwindow.cpp +++ b/src/quicktemplates2/qquickapplicationwindow.cpp @@ -443,7 +443,7 @@ QQuickItem *QQuickApplicationWindow::contentItem() const The difference between \l Window::activeFocusItem and ApplicationWindow::activeFocusControl is that the former may point to a building block of a control, whereas the latter points to the enclosing control. For example, when SpinBox has focus, activeFocusItem points to - the editor and acticeFocusControl to the SpinBox itself. + the editor and activeFocusControl to the SpinBox itself. \sa Window::activeFocusItem */ @@ -622,47 +622,49 @@ public: void windowChange(QQuickWindow *wnd); - QQuickApplicationWindow *window; + QQuickWindow *window; }; void QQuickApplicationWindowAttachedPrivate::windowChange(QQuickWindow *wnd) { Q_Q(QQuickApplicationWindowAttached); - if (window && !QQuickApplicationWindowPrivate::get(window)) - window = nullptr; // being deleted (QTBUG-52731) + if (window == wnd) + return; - QQuickApplicationWindow *newWindow = qobject_cast<QQuickApplicationWindow *>(wnd); - if (window != newWindow) { - QQuickApplicationWindow *oldWindow = window; - if (oldWindow) { - QObject::disconnect(oldWindow, &QQuickApplicationWindow::activeFocusControlChanged, - q, &QQuickApplicationWindowAttached::activeFocusControlChanged); - QObject::disconnect(oldWindow, &QQuickApplicationWindow::headerChanged, - q, &QQuickApplicationWindowAttached::headerChanged); - QObject::disconnect(oldWindow, &QQuickApplicationWindow::footerChanged, - q, &QQuickApplicationWindowAttached::footerChanged); - } - if (newWindow) { - QObject::connect(newWindow, &QQuickApplicationWindow::activeFocusControlChanged, - q, &QQuickApplicationWindowAttached::activeFocusControlChanged); - QObject::connect(newWindow, &QQuickApplicationWindow::headerChanged, - q, &QQuickApplicationWindowAttached::headerChanged); - QObject::connect(newWindow, &QQuickApplicationWindow::footerChanged, - q, &QQuickApplicationWindowAttached::footerChanged); - } + QQuickApplicationWindow *oldWindow = qobject_cast<QQuickApplicationWindow *>(window); + if (oldWindow && !QQuickApplicationWindowPrivate::get(oldWindow)) + oldWindow = nullptr; // being deleted (QTBUG-52731) + + if (oldWindow) { + QObject::disconnect(oldWindow, &QQuickApplicationWindow::activeFocusControlChanged, + q, &QQuickApplicationWindowAttached::activeFocusControlChanged); + QObject::disconnect(oldWindow, &QQuickApplicationWindow::headerChanged, + q, &QQuickApplicationWindowAttached::headerChanged); + QObject::disconnect(oldWindow, &QQuickApplicationWindow::footerChanged, + q, &QQuickApplicationWindowAttached::footerChanged); + } - window = newWindow; - emit q->windowChanged(); - emit q->contentItemChanged(); - emit q->overlayChanged(); - - if ((oldWindow && oldWindow->activeFocusControl()) || (newWindow && newWindow->activeFocusControl())) - emit q->activeFocusControlChanged(); - if ((oldWindow && oldWindow->header()) || (newWindow && newWindow->header())) - emit q->headerChanged(); - if ((oldWindow && oldWindow->footer()) || (newWindow && newWindow->footer())) - emit q->footerChanged(); + QQuickApplicationWindow *newWindow = qobject_cast<QQuickApplicationWindow *>(wnd); + if (newWindow) { + QObject::connect(newWindow, &QQuickApplicationWindow::activeFocusControlChanged, + q, &QQuickApplicationWindowAttached::activeFocusControlChanged); + QObject::connect(newWindow, &QQuickApplicationWindow::headerChanged, + q, &QQuickApplicationWindowAttached::headerChanged); + QObject::connect(newWindow, &QQuickApplicationWindow::footerChanged, + q, &QQuickApplicationWindowAttached::footerChanged); } + + window = wnd; + emit q->windowChanged(); + emit q->contentItemChanged(); + emit q->overlayChanged(); + + if ((oldWindow && oldWindow->activeFocusControl()) || (newWindow && newWindow->activeFocusControl())) + emit q->activeFocusControlChanged(); + if ((oldWindow && oldWindow->header()) || (newWindow && newWindow->header())) + emit q->headerChanged(); + if ((oldWindow && oldWindow->footer()) || (newWindow && newWindow->footer())) + emit q->footerChanged(); } QQuickApplicationWindowAttached::QQuickApplicationWindowAttached(QObject *parent) @@ -698,7 +700,7 @@ QQuickApplicationWindowAttached::QQuickApplicationWindowAttached(QObject *parent QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const { Q_D(const QQuickApplicationWindowAttached); - return d->window; + return qobject_cast<QQuickApplicationWindow *>(d->window); } /*! @@ -711,7 +713,9 @@ QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const QQuickItem *QQuickApplicationWindowAttached::contentItem() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->contentItem() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->contentItem(); + return nullptr; } /*! @@ -728,7 +732,9 @@ QQuickItem *QQuickApplicationWindowAttached::contentItem() const QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->activeFocusControl() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->activeFocusControl(); + return nullptr; } /*! @@ -742,7 +748,9 @@ QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const QQuickItem *QQuickApplicationWindowAttached::header() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->header() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->header(); + return nullptr; } /*! @@ -756,7 +764,9 @@ QQuickItem *QQuickApplicationWindowAttached::header() const QQuickItem *QQuickApplicationWindowAttached::footer() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->footer() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->footer(); + return nullptr; } /*! @@ -769,7 +779,7 @@ QQuickItem *QQuickApplicationWindowAttached::footer() const QQuickOverlay *QQuickApplicationWindowAttached::overlay() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->overlay() : nullptr; + return QQuickOverlay::overlay(d->window); } QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickbutton.cpp b/src/quicktemplates2/qquickbutton.cpp index 19310713..591c4316 100644 --- a/src/quicktemplates2/qquickbutton.cpp +++ b/src/quicktemplates2/qquickbutton.cpp @@ -110,6 +110,17 @@ QQuickButton::QQuickButton(QQuickButtonPrivate &dd, QQuickItem *parent) : \qmlproperty bool QtQuick.Controls::Button::checkable This property holds whether the button is checkable. + + A checkable button toggles between checked (on) and unchecked (off) when + the user clicks on it or presses the space bar while the button has active + focus. + + Setting \l {AbstractButton::}{checked} to \c true forces this property to + \c true. + + The default value is \c false. + + \sa CheckBox, Switch */ void QQuickButton::checkableChange() @@ -120,8 +131,10 @@ void QQuickButton::checkableChange() /*! \qmlproperty bool QtQuick.Controls::Button::autoRepeat - This property holds whether the button repeats pressed(), released() - and clicked() signals while the button is pressed and held down. + This property holds whether the button repeats + \l {AbstractButton::}{pressed()}, \l {AbstractButton::}{released()} + and \l {AbstractButton::}{clicked()} signals while the button is pressed + and held down. The default value is \c false. */ diff --git a/src/quicktemplates2/qquickcheckbox.cpp b/src/quicktemplates2/qquickcheckbox.cpp index 1daf69dc..a777c2b4 100644 --- a/src/quicktemplates2/qquickcheckbox.cpp +++ b/src/quicktemplates2/qquickcheckbox.cpp @@ -52,9 +52,11 @@ QT_BEGIN_NAMESPACE CheckBox presents an option button that can be toggled on (checked) or off (unchecked). Check boxes are typically used to select one or more - options from a set of options. + options from a set of options. For larger sets of options, such as those + in a list, consider using \l CheckDelegate instead. - The state of the checkbox can be set with the \l {AbstractButton::}{checked} property. + CheckBox inherits its API from \l AbstractButton. For instance, the + state of the checkbox can be set with the \l {AbstractButton::}{checked} property. In addition to the checked and unchecked states, there is a third state: partially checked. The partially checked state can be enabled using the diff --git a/src/quicktemplates2/qquickcheckdelegate.cpp b/src/quicktemplates2/qquickcheckdelegate.cpp index 09be5d60..249aaa95 100644 --- a/src/quicktemplates2/qquickcheckdelegate.cpp +++ b/src/quicktemplates2/qquickcheckdelegate.cpp @@ -54,9 +54,14 @@ QT_BEGIN_NAMESPACE CheckDelegate presents an item delegate that can be toggled on (checked) or off (unchecked). Check delegates are typically used to select one or more - options from a set of options. - - The state of the check delegate can be set with the + options from a set of options in a list. For smaller sets of options, or + for options that need to be uniquely identifiable, consider using + \l CheckBox instead. + + CheckDelegate inherits its API from \l ItemDelegate, which is inherited + from AbstractButton. For instance, you can set \l {AbstractButton::text}{text}, + and react to \l {AbstractButton::clicked}{clicks} using the AbstractButton + API. The state of the check delegate can be set with the \l {AbstractButton::}{checked} property. In addition to the checked and unchecked states, there is a third state: @@ -75,7 +80,7 @@ QT_BEGIN_NAMESPACE } \endcode - \sa {Customizing CheckDelegate}, {Delegate Controls} + \sa {Customizing CheckDelegate}, {Delegate Controls}, CheckBox */ class QQuickCheckDelegatePrivate : public QQuickItemDelegatePrivate diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp index 31a92b5d..d9859b7d 100644 --- a/src/quicktemplates2/qquickcontainer.cpp +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -55,9 +55,11 @@ QT_BEGIN_NAMESPACE \section2 Using Containers - Container provides an API to \l {addItem}{add}, \l {insertItem}{insert}, + Typically, items are statically declared as children of Container, but it + is also possible to \l {addItem}{add}, \l {insertItem}{insert}, \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The - items in a container can be accessed using \l itemAt() or \l contentChildren. + items in a container can be accessed using \l itemAt() or + \l contentChildren. Most containers have the concept of a "current" item. The current item is specified via the \l currentIndex property, and can be accessed using the diff --git a/src/quicktemplates2/qquickcontrol.cpp b/src/quicktemplates2/qquickcontrol.cpp index 642e2e72..518255dd 100644 --- a/src/quicktemplates2/qquickcontrol.cpp +++ b/src/quicktemplates2/qquickcontrol.cpp @@ -70,8 +70,26 @@ QT_BEGIN_NAMESPACE events from the window system, and paints a representation of itself on the screen. + \section1 Control Layout + + The following diagram illustrates the layout of a typical control: + \image qtquickcontrols2-control.png + The \l {Item::}{implicitWidth} and \l {Item::}{implicitHeight} of a control + are typically based on the implicit sizes of the background and the content + item plus any \l {Control::}{padding}. These properties determine how large + the control will be when no explicit \l {Item::}{width} or + \l {Item::}{height} is specified. + + The \l {Control::}{background} item fills the entire width and height of the + control, unless an explicit size has been given for it. + + The geometry of the \l {Control::}{contentItem} is determined by the + padding. + + \section1 Event Handling + All controls, except non-interactive indicators, do not let clicks and touches through to items below them. For example, if \l Pane is used as the \l {ApplicationWindow::}{header} or \l {ApplicationWindow::}{footer} of @@ -397,6 +415,10 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem:: Q_D(QQuickControl); QQuickItem::itemChange(change, value); switch (change) { + case ItemVisibleHasChanged: + if (!value.boolValue) + setHovered(false); + break; case ItemParentHasChanged: if (value.item) { d->resolveFont(); @@ -432,6 +454,22 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem:: Control propagates explicit font properties from parent to children. If you change a specific property on a control's font, that property propagates to all of the control's children, overriding any system defaults for that property. + + \code + Page { + font.family: "Courier" + + Column { + Label { + text: qsTr("This will use Courier...") + } + + Switch { + text: qsTr("... and so will this") + } + } + } + \endcode */ QFont QQuickControl::font() const { @@ -458,9 +496,10 @@ void QQuickControl::resetFont() \qmlproperty real QtQuick.Controls::Control::availableWidth \readonly - This property holds the width available after deducting horizontal padding. + This property holds the width available to the \l contentItem after + deducting horizontal padding from the \l {Item::}{width} of the control. - \sa padding, leftPadding, rightPadding + \sa {Control Layout}, padding, leftPadding, rightPadding */ qreal QQuickControl::availableWidth() const { @@ -471,9 +510,10 @@ qreal QQuickControl::availableWidth() const \qmlproperty real QtQuick.Controls::Control::availableHeight \readonly - This property holds the height available after deducting vertical padding. + This property holds the height available to the \l contentItem after + deducting vertical padding from the \l {Item::}{height} of the control. - \sa padding, topPadding, bottomPadding + \sa {Control Layout}, padding, topPadding, bottomPadding */ qreal QQuickControl::availableHeight() const { @@ -485,7 +525,19 @@ qreal QQuickControl::availableHeight() const This property holds the default padding. - \sa availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding + Padding adds a space between each edge of the content item and the + background item, effectively controlling the size of the content item. To + specify a padding value for a specific edge of the control, set its + relevant property: + + \list + \li \l {Control::}{leftPadding} + \li \l {Control::}{rightPadding} + \li \l {Control::}{topPadding} + \li \l {Control::}{bottomPadding} + \endlist + + \sa {Control Layout}, availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding */ qreal QQuickControl::padding() const { @@ -527,7 +579,7 @@ void QQuickControl::resetPadding() This property holds the top padding. - \sa padding, bottomPadding, availableHeight + \sa {Control Layout}, padding, bottomPadding, availableHeight */ qreal QQuickControl::topPadding() const { @@ -554,7 +606,7 @@ void QQuickControl::resetTopPadding() This property holds the left padding. - \sa padding, rightPadding, availableWidth + \sa {Control Layout}, padding, rightPadding, availableWidth */ qreal QQuickControl::leftPadding() const { @@ -581,7 +633,7 @@ void QQuickControl::resetLeftPadding() This property holds the right padding. - \sa padding, leftPadding, availableWidth + \sa {Control Layout}, padding, leftPadding, availableWidth */ qreal QQuickControl::rightPadding() const { @@ -608,7 +660,7 @@ void QQuickControl::resetRightPadding() This property holds the bottom padding. - \sa padding, topPadding, availableHeight + \sa {Control Layout}, padding, topPadding, availableHeight */ qreal QQuickControl::bottomPadding() const { @@ -634,6 +686,12 @@ void QQuickControl::resetBottomPadding() \qmlproperty real QtQuick.Controls::Control::spacing This property holds the spacing. + + Spacing is useful for controls that have multiple or repetitive building + blocks. For example, some styles use spacing to determine the distance + between the text and indicator of \l CheckBox. Spacing is not enforced by + Control, so each style may interpret it differently, and some may ignore it + altogether. */ qreal QQuickControl::spacing() const { @@ -766,9 +824,11 @@ void QQuickControlPrivate::updateLocaleRecur(QQuickItem *item, const QLocale &l) This property holds whether the control is mirrored. This property is provided for convenience. A control is considered mirrored - when its visual layout direction is right-to-left. + when its visual layout direction is right-to-left; that is, when using a + right-to-left locale or when \l {LayoutMirroring::enabled}{LayoutMirroring.enabled} + is \c true. - \sa locale, {LayoutMirroring}{LayoutMirroring} + \sa locale, {LayoutMirroring}{LayoutMirroring}, {Right-to-left User Interfaces} */ bool QQuickControl::isMirrored() const { @@ -917,6 +977,10 @@ void QQuickControl::setHoverEnabled(bool enabled) \qmlproperty bool QtQuick.Controls::Control::wheelEnabled This property determines whether the control handles wheel events. The default value is \c false. + + \note Care must be taken when enabling wheel events for controls within scrollable items such + as \l Flickable, as the control will consume the events and hence interrupt scrolling of the + Flickable. */ bool QQuickControl::isWheelEnabled() const { @@ -939,9 +1003,30 @@ void QQuickControl::setWheelEnabled(bool enabled) This property holds the background item. + \code + Button { + id: control + text: qsTr("Button") + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + opacity: enabled ? 1 : 0.3 + color: control.down ? "#d0d0d0" : "#e0e0e0" + } + } + \endcode + \note If the background item has no explicit size specified, it automatically follows the control's size. In most cases, there is no need to specify width or height for a background item. + + \note Most controls use the implicit size of the background item to calculate + the implicit size of the control itself. If you replace the background item + with a custom one, you should also consider providing a sensible implicit + size for it (unless it is an item like \l Image which has its own implicit + size). + + \sa {Control Layout} */ QQuickItem *QQuickControl::background() const { @@ -972,7 +1057,28 @@ void QQuickControl::setBackground(QQuickItem *background) This property holds the visual content item. - \note The content item is automatically resized inside the \l padding of the control. + \note The content item is automatically resized to fit within the + \l padding of the control. + + \note Most controls use the implicit size of the content item to calculate + the implicit size of the control itself. If you replace the content item + with a custom one, you should also consider providing a sensible implicit + size for it (unless it is an item like \l Text which has its own implicit + size). + + \code + Button { + id: control + text: qsTr("Button") + contentItem: Label { + text: control.text + font: control.font + verticalAlignment: Text.AlignVCenter + } + } + \endcode + + \sa {Control Layout}, padding */ QQuickItem *QQuickControl::contentItem() const { diff --git a/src/quicktemplates2/qquickdial.cpp b/src/quicktemplates2/qquickdial.cpp index 3b096255..48d02faa 100644 --- a/src/quicktemplates2/qquickdial.cpp +++ b/src/quicktemplates2/qquickdial.cpp @@ -300,6 +300,8 @@ qreal QQuickDial::position() const Like the \l position property, angle is continuously updated while the handle is dragged. + The range is from \c -140 degrees to \c 140 degrees. + \sa position */ qreal QQuickDial::angle() const @@ -311,7 +313,18 @@ qreal QQuickDial::angle() const /*! \qmlproperty real QtQuick.Controls::Dial::stepSize - This property holds the step size. The default value is \c 0.0. + This property holds the step size. + + The step size determines the amount by which the dial's value + is increased and decreased when interacted with via the keyboard. + For example, a step size of \c 0.2, will result in the dial's + value increasing and decreasing in increments of \c 0.2. + + The step size is only respected for touch and mouse interaction + when \l snapMode is set to a value other than \c Dial.NoSnap. + + The default value is \c 0.0, which results in an effective step + size of \c 0.1 for keyboard interaction. \sa snapMode, increase(), decrease() */ @@ -460,7 +473,7 @@ void QQuickDial::decrease() } /*! - \qmlproperty component QtQuick.Controls::Dial::handle + \qmlproperty Item QtQuick.Controls::Dial::handle This property holds the handle of the dial. diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 4c9fbb62..bc1636de 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -69,6 +69,54 @@ QT_BEGIN_NAMESPACE of the window. \endtable + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + visible: true + + Drawer { + id: drawer + width: 0.66 * window.width + height: window.height + } + } + \endcode + + Drawer is a special type of popup that resides at one of the window \l {edge}{edges}. + By default, Drawer re-parents itself to the window \l {ApplicationWindow::}{overlay}, + and therefore operates on window coordinates. It is also possible to manually set the + \l {Popup::}{parent} to something else to make the drawer operate in a specific + coordinate space. + + Drawer can be configured to cover only part of its window edge. The following example + illustrates how Drawer can be positioned to appear below a window header: + + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + visible: true + + header: ToolBar { } + + Drawer { + y: header.height + width: window.width * 0.6 + height: window.height - header.height + } + } + \endcode + + The \l position property determines how much of the drawer is visible, as + a value between \c 0.0 and \c 1.0. It is not possible to set the x-coordinate + (or horizontal margins) of a drawer at the left or right window edge, or the + y-coordinate (or vertical margins) of a drawer at the top or bottom window edge. + In the image above, the application's contents are \e "pushed" across the screen. This is achieved by applying a translation to the contents: @@ -117,6 +165,7 @@ QQuickDrawerPrivate::QQuickDrawerPrivate() : edge(Qt::LeftEdge), offset(0), position(0), dragMargin(QGuiApplication::styleHints()->startDragDistance()) { + setEdge(Qt::LeftEdge); } qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const @@ -161,6 +210,27 @@ void QQuickDrawerPrivate::reposition() popupItem->setY(window->height() - position * popupItem->height()); break; } + + QQuickPopupPrivate::reposition(); +} + +void QQuickDrawerPrivate::resizeOverlay() +{ + if (!dimmer || !window) + return; + + QRectF geometry(0, 0, window->width(), window->height()); + + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + geometry.setY(popupItem->y()); + geometry.setHeight(popupItem->height()); + } else { + geometry.setX(popupItem->x()); + geometry.setWidth(popupItem->width()); + } + + dimmer->setPosition(geometry.topLeft()); + dimmer->setSize(geometry.size()); } static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int threshold = -1) @@ -309,6 +379,12 @@ bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *ev Q_Q(QQuickDrawer); Q_UNUSED(item); + // Don't react to synthesized mouse move events at INF,INF coordinates. + // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover + // on touch release (QTBUG-55995). + if (qIsInf(event->screenPos().x()) || qIsInf(event->screenPos().y())) + return true; + const QPointF movePoint = event->windowPos(); if (grabMouse(event)) { @@ -374,6 +450,22 @@ bool QQuickDrawerPrivate::prepareExitTransition() return QQuickPopupPrivate::prepareExitTransition(); } +void QQuickDrawerPrivate::setEdge(Qt::Edge e) +{ + edge = e; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + allowVerticalMove = true; + allowVerticalResize = true; + allowHorizontalMove = false; + allowHorizontalResize = false; + } else { + allowVerticalMove = false; + allowVerticalResize = false; + allowHorizontalMove = true; + allowHorizontalResize = true; + } +} + QQuickDrawer::QQuickDrawer(QObject *parent) : QQuickPopup(*(new QQuickDrawerPrivate), parent) { @@ -406,7 +498,7 @@ void QQuickDrawer::setEdge(Qt::Edge edge) if (d->edge == edge) return; - d->edge = edge; + d->setEdge(edge); if (isComponentComplete()) d->reposition(); emit edgeChanged(); @@ -531,4 +623,11 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) } } +void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickDrawer); + QQuickPopup::geometryChanged(newGeometry, oldGeometry); + d->resizeOverlay(); +} + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index e694e27b..bada1344 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -87,6 +87,8 @@ protected: void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + private: Q_DISABLE_COPY(QQuickDrawer) Q_DECLARE_PRIVATE(QQuickDrawer) diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index f14c36dd..b7555c3e 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -68,6 +68,7 @@ public: qreal positionAt(const QPointF &point) const; void reposition() override; + void resizeOverlay() override; bool startDrag(QQuickWindow *window, QMouseEvent *event); bool grabMouse(QMouseEvent *event); @@ -80,6 +81,8 @@ public: bool prepareEnterTransition() override; bool prepareExitTransition() override; + void setEdge(Qt::Edge edge); + Qt::Edge edge; qreal offset; qreal position; diff --git a/src/quicktemplates2/qquickframe.cpp b/src/quicktemplates2/qquickframe.cpp index 2f2a5497..bcc387c1 100644 --- a/src/quicktemplates2/qquickframe.cpp +++ b/src/quicktemplates2/qquickframe.cpp @@ -54,8 +54,8 @@ QT_BEGIN_NAMESPACE or a \l ColumnLayout. Items declared as children of a Frame are automatically parented to the - Frame's contentItem. Items created dynamically need to be explicitly - parented to the contentItem. + Frame's \l {Control::}{contentItem}. Items created dynamically need to be + explicitly parented to the contentItem. If only a single item is used within a Frame, it will resize to fit the implicit size of its contained item. This makes it particularly suitable diff --git a/src/quicktemplates2/qquickgroupbox.cpp b/src/quicktemplates2/qquickgroupbox.cpp index 45fe406f..27c325ef 100644 --- a/src/quicktemplates2/qquickgroupbox.cpp +++ b/src/quicktemplates2/qquickgroupbox.cpp @@ -48,10 +48,10 @@ QT_BEGIN_NAMESPACE \inqmlmodule QtQuick.Controls \since 5.7 \ingroup qtquickcontrols2-containers - \brief A frame with a logical group of controls. + \brief A logical group of controls within a titled visual frame. GroupBox is used to layout a logical group of controls together, within - a titled visual frame. GroupBox does not provide a layout of its own, but + a \l {title}{titled} visual frame. GroupBox does not provide a layout of its own, but requires you to position its contents, for instance by creating a \l RowLayout or a \l ColumnLayout. @@ -75,8 +75,8 @@ QT_BEGIN_NAMESPACE \image qtquickcontrols2-groupbox-checkable.png It is a common pattern to enable or disable the groupbox's children when - its checkbox is toggled on or off, but it is the application that decides - on the behavior of the groupbox. + its checkbox is toggled on or off, but it is up to the application to decide + on the behavior of the checkbox. \snippet qtquickcontrols2-groupbox-checkable.qml 1 @@ -101,6 +101,9 @@ QQuickGroupBox::QQuickGroupBox(QQuickItem *parent) : \qmlproperty string QtQuick.Controls::GroupBox::title This property holds the title. + + The title is typically displayed above the groupbox to + summarize its contents. */ QString QQuickGroupBox::title() const { diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp index b8522668..bf37f801 100644 --- a/src/quicktemplates2/qquickmenu.cpp +++ b/src/quicktemplates2/qquickmenu.cpp @@ -89,6 +89,15 @@ QT_BEGIN_NAMESPACE } \endcode + Typically, menu items are statically declared as children of the menu, but + Menu also provides API to \l {addItem}{add}, \l {insertItem}{insert}, + \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The + items in a menu can be accessed using \l itemAt() or + \l {Popup::}{contentChildren}. + + Although \l {MenuItem}{MenuItems} are most commonly used with Menu, it can + contain any type of item. + \sa {Customizing Menu}, {Menu Controls}, {Popup Controls} */ @@ -350,7 +359,7 @@ void QQuickMenu::moveItem(int from, int to) /*! \qmlmethod void QtQuick.Controls::Menu::removeItem(int index) - Removes an item at \a index. + Removes the item at \a index. \note The ownership of the item is transferred to the caller. */ @@ -372,8 +381,20 @@ void QQuickMenu::removeItem(int index) This property holds the model used to display menu items. - By default, the model is an \l ObjectModel, in order to allow declaring - menu items as children of the menu. + The content model is provided for visualization purposes. It can be assigned + as a model to a content item that presents the contents of the menu. + + \code + Menu { + id: menu + contentItem: ListView { + model: menu.contentModel + } + } + \endcode + + The model allows menu items to be statically declared as children of the + menu. */ QVariant QQuickMenu::contentModel() const { @@ -387,7 +408,14 @@ QVariant QQuickMenu::contentModel() const This property holds the list of content data. - \sa Item::data + The list contains all objects that have been declared in QML as children + of the menu, and also items that have been dynamically added or + inserted using the \l addItem() and \l insertItem() methods, respectively. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. It is not re-ordered when items are inserted or moved. + + \sa Item::data, contentChildren */ QQmlListProperty<QObject> QQuickMenu::contentData() { @@ -402,9 +430,11 @@ QQmlListProperty<QObject> QQuickMenu::contentData() /*! \qmlproperty string QtQuick.Controls::Menu::title - Title for the menu as a submenu or in a menubar. + This property holds the title for the menu. - Its value defaults to an empty string. + The title of a menu is often displayed in the text of a menu item when the + menu is a submenu, and in the text of a tool button when it is in a + menubar. */ QString QQuickMenu::title() const { diff --git a/src/quicktemplates2/qquickmenuitem.cpp b/src/quicktemplates2/qquickmenuitem.cpp index 35942f92..902889b0 100644 --- a/src/quicktemplates2/qquickmenuitem.cpp +++ b/src/quicktemplates2/qquickmenuitem.cpp @@ -52,26 +52,30 @@ QT_BEGIN_NAMESPACE \brief A menu item within a Menu. MenuItem is a convenience type that implements the AbstractButton API, - providing an easy way to respond to menu items being clicked, for example. + providing a familiar way to respond to menu items being \l triggered, for + example. \code Button { id: fileButton text: "File" onClicked: menu.open() - } - Menu { - id: menu - anchor.target: fileButton - MenuItem { - text: "New..." - } - MenuItem { - text: "Open..." - } - MenuItem { - text: "Save" + Menu { + id: menu + + MenuItem { + text: "New..." + onTriggered: document.reset() + } + MenuItem { + text: "Open..." + onTriggered: openDialog.open() + } + MenuItem { + text: "Save" + onTriggered: saveDialog.open() + } } } \endcode @@ -110,6 +114,11 @@ QQuickMenuItem::QQuickMenuItem(QQuickItem *parent) : \qmlproperty bool QtQuick.Controls::MenuItem::checkable This property holds whether the menu item is checkable. + + A checkable menu item toggles between checked (on) and unchecked (off) when + the user clicks on it or interacts with it via the keyboard. + + \sa {AbstractButton::}{checked} */ void QQuickMenuItem::checkableChange() diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index ccae4da6..9bdc9135 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -113,7 +113,7 @@ void QQuickOverlayPrivate::createOverlay(QQuickPopup *popup) QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); if (!p->dimmer) p->dimmer = createDimmer(popup->isModal() ? modal : modeless, popup, q); - resizeOverlay(popup); + p->resizeOverlay(); } void QQuickOverlayPrivate::destroyOverlay(QQuickPopup *popup) @@ -126,16 +126,6 @@ void QQuickOverlayPrivate::destroyOverlay(QQuickPopup *popup) } } -void QQuickOverlayPrivate::resizeOverlay(QQuickPopup *popup) -{ - Q_Q(QQuickOverlay); - QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); - if (p->dimmer) { - p->dimmer->setWidth(q->width()); - p->dimmer->setHeight(q->height()); - } -} - void QQuickOverlayPrivate::toggleOverlay() { Q_Q(QQuickOverlay); @@ -320,7 +310,7 @@ void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &old Q_D(QQuickOverlay); QQuickItem::geometryChanged(newGeometry, oldGeometry); for (QQuickPopup *popup : qAsConst(d->allPopups)) - d->resizeOverlay(popup); + QQuickPopupPrivate::get(popup)->resizeOverlay(); } void QQuickOverlay::mousePressEvent(QMouseEvent *event) @@ -404,7 +394,11 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) switch (event->type()) { case QEvent::MouseButtonPress: emit pressed(); - return popup->overlayEvent(item, event); + if (popup->overlayEvent(item, event)) { + d->mouseGrabberPopup = popup; + return true; + } + break; case QEvent::MouseMove: return popup->overlayEvent(item, event); case QEvent::MouseButtonRelease: diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index db005555..ef142f1b 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -78,7 +78,6 @@ public: void createOverlay(QQuickPopup *popup); void destroyOverlay(QQuickPopup *popup); - void resizeOverlay(QQuickPopup *popup); void toggleOverlay(); QVector<QQuickPopup *> stackingOrderPopups() const; diff --git a/src/quicktemplates2/qquickpage.cpp b/src/quicktemplates2/qquickpage.cpp index 4304c4fb..54e79fbe 100644 --- a/src/quicktemplates2/qquickpage.cpp +++ b/src/quicktemplates2/qquickpage.cpp @@ -104,6 +104,37 @@ QQuickPage::QQuickPage(QQuickItem *parent) : \qmlproperty string QtQuick.Controls::Page::title This property holds the page title. + + The title is often displayed at the top of a page to give + the user context about the page they are viewing. + + \code + ApplicationWindow { + visible: true + width: 400 + height: 400 + + header: Label { + text: view.currentItem.title + horizontalAlignment: Text.AlignHCenter + } + + SwipeView { + id: view + anchors.fill: parent + + Page { + title: qsTr("Home") + } + Page { + title: qsTr("Discover") + } + Page { + title: qsTr("Activity") + } + } + } + \endcode */ QString QQuickPage::title() const @@ -183,7 +214,13 @@ void QQuickPage::setFooter(QQuickItem *footer) This property holds the list of content data. - \sa Item::data + The list contains all objects that have been declared in QML as children + of the container. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. + + \sa Item::data, contentChildren */ QQmlListProperty<QObject> QQuickPage::contentData() { @@ -200,7 +237,13 @@ QQmlListProperty<QObject> QQuickPage::contentData() This property holds the list of content children. - \sa Item::children + The list contains all items that have been declared in QML as children + of the page. + + \note Unlike \c contentData, \c contentChildren does not include non-visual + QML objects. + + \sa Item::children, contentData */ QQmlListProperty<QQuickItem> QQuickPage::contentChildren() { diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 4143ee3f..1a5310bf 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -125,12 +125,18 @@ QQuickPopupPrivate::QQuickPopupPrivate() , hasDim(false) , visible(false) , complete(false) + , hasWidth(false) + , hasHeight(false) , hasTopMargin(false) , hasLeftMargin(false) , hasRightMargin(false) , hasBottomMargin(false) , allowVerticalFlip(false) , allowHorizontalFlip(false) + , allowVerticalMove(true) + , allowHorizontalMove(true) + , allowVerticalResize(true) + , allowHorizontalResize(true) , hadActiveFocusBeforeExitTransition(false) , x(0) , y(0) @@ -587,13 +593,19 @@ void QQuickPopupPrivate::reposition() bool widthAdjusted = false; bool heightAdjusted = false; - QRectF rect(x, y, iw > 0 ? iw : w, ih > 0 ? ih : h); + QRectF rect(allowHorizontalMove ? x : popupItem->x(), + allowVerticalMove ? y : popupItem->y(), + !hasWidth && iw > 0 ? iw : w, + !hasHeight && ih > 0 ? ih : h); if (parentItem) { rect = parentItem->mapRectToScene(rect); if (window) { const QMarginsF margins = getMargins(); - const QRectF bounds = QRectF(0, 0, window->width(), window->height()).marginsRemoved(margins); + const QRectF bounds(qMax<qreal>(0.0, margins.left()), + qMax<qreal>(0.0, margins.top()), + window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()), + window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom())); // if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right) if (allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) { @@ -610,31 +622,39 @@ void QQuickPopupPrivate::reposition() } // push inside the margins if specified - if (margins.top() >= 0 && rect.top() < bounds.top()) - rect.moveTop(margins.top()); - if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) - rect.moveBottom(bounds.bottom()); - if (margins.left() >= 0 && rect.left() < bounds.left()) - rect.moveLeft(margins.left()); - if (margins.right() >= 0 && rect.right() > bounds.right()) - rect.moveRight(bounds.right()); + if (allowVerticalMove) { + if (margins.top() >= 0 && rect.top() < bounds.top()) + rect.moveTop(margins.top()); + if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) + rect.moveBottom(bounds.bottom()); + } + if (allowHorizontalMove) { + if (margins.left() >= 0 && rect.left() < bounds.left()) + rect.moveLeft(margins.left()); + if (margins.right() >= 0 && rect.right() > bounds.right()) + rect.moveRight(bounds.right()); + } if (iw > 0 && (rect.left() < bounds.left() || rect.right() > bounds.right())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (left vs. right) fits larger part of the popup - if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) - rect.moveLeft(bounds.left()); - else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) - rect.moveRight(bounds.right()); + if (allowHorizontalMove && allowHorizontalFlip) { + if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) + rect.moveLeft(bounds.left()); + else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) + rect.moveRight(bounds.right()); + } // as a last resort, adjust the width to fit the window - if (rect.left() < bounds.left()) { - rect.setLeft(bounds.left()); - widthAdjusted = true; - } - if (rect.right() > bounds.right()) { - rect.setRight(bounds.right()); - widthAdjusted = true; + if (allowHorizontalResize) { + if (rect.left() < bounds.left()) { + rect.setLeft(bounds.left()); + widthAdjusted = true; + } + if (rect.right() > bounds.right()) { + rect.setRight(bounds.right()); + widthAdjusted = true; + } } } else if (iw > 0 && rect.left() >= bounds.left() && rect.right() <= bounds.right() && iw != w) { @@ -646,19 +666,23 @@ void QQuickPopupPrivate::reposition() if (ih > 0 && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (above vs. below) fits larger part of the popup - if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) - rect.moveTop(bounds.top()); - else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) - rect.moveBottom(bounds.bottom()); + if (allowVerticalMove && allowVerticalFlip) { + if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) + rect.moveTop(bounds.top()); + else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) + rect.moveBottom(bounds.bottom()); + } // as a last resort, adjust the height to fit the window - if (rect.top() < bounds.top()) { - rect.setTop(bounds.top()); - heightAdjusted = true; - } - if (rect.bottom() > bounds.bottom()) { - rect.setBottom(bounds.bottom()); - heightAdjusted = true; + if (allowVerticalResize) { + if (rect.top() < bounds.top()) { + rect.setTop(bounds.top()); + heightAdjusted = true; + } + if (rect.bottom() > bounds.bottom()) { + rect.setBottom(bounds.bottom()); + heightAdjusted = true; + } } } else if (ih > 0 && rect.top() >= bounds.top() && rect.bottom() <= bounds.bottom() && ih != h) { @@ -681,12 +705,22 @@ void QQuickPopupPrivate::reposition() emit q->yChanged(); } - if (widthAdjusted && rect.width() > 0) + if (!hasWidth && widthAdjusted && rect.width() > 0) popupItem->setWidth(rect.width()); - if (heightAdjusted && rect.height() > 0) + if (!hasHeight && heightAdjusted && rect.height() > 0) popupItem->setHeight(rect.height()); } +void QQuickPopupPrivate::resizeOverlay() +{ + if (!dimmer) + return; + + qreal w = window ? window->width() : 0; + qreal h = window ? window->height() : 0; + dimmer->setSize(QSizeF(w, h)); +} + void QQuickPopupPositioner::removeAncestorListeners(QQuickItem *item) { if (item == m_parentItem) @@ -907,13 +941,20 @@ qreal QQuickPopup::width() const void QQuickPopup::setWidth(qreal width) { Q_D(QQuickPopup); + d->hasWidth = true; d->popupItem->setWidth(width); } void QQuickPopup::resetWidth() { Q_D(QQuickPopup); + if (!d->hasWidth) + return; + + d->hasWidth = false; d->popupItem->resetWidth(); + if (d->popupItem->isVisible()) + d->reposition(); } /*! @@ -930,13 +971,20 @@ qreal QQuickPopup::height() const void QQuickPopup::setHeight(qreal height) { Q_D(QQuickPopup); + d->hasHeight = true; d->popupItem->setHeight(height); } void QQuickPopup::resetHeight() { Q_D(QQuickPopup); + if (!d->hasHeight) + return; + + d->hasHeight = false; d->popupItem->resetHeight(); + if (d->popupItem->isVisible()) + d->reposition(); } /*! diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 93039287..4ad8d171 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -157,6 +157,7 @@ public: void init(); bool tryClose(QQuickItem *item, QMouseEvent *event); virtual void reposition(); + virtual void resizeOverlay(); virtual bool prepareEnterTransition(); virtual bool prepareExitTransition(); @@ -183,12 +184,18 @@ public: bool hasDim; bool visible; bool complete; + bool hasWidth; + bool hasHeight; bool hasTopMargin; bool hasLeftMargin; bool hasRightMargin; bool hasBottomMargin; bool allowVerticalFlip; bool allowHorizontalFlip; + bool allowVerticalMove; + bool allowHorizontalMove; + bool allowVerticalResize; + bool allowHorizontalResize; bool hadActiveFocusBeforeExitTransition; qreal x; qreal y; diff --git a/src/quicktemplates2/qquickswitch.cpp b/src/quicktemplates2/qquickswitch.cpp index 37ae53f0..a7d17e86 100644 --- a/src/quicktemplates2/qquickswitch.cpp +++ b/src/quicktemplates2/qquickswitch.cpp @@ -90,103 +90,28 @@ class QQuickSwitchPrivate : public QQuickAbstractButtonPrivate public: QQuickSwitchPrivate() : position(0) { } - void updatePosition(); - qreal positionAt(const QPoint &point) const; - - bool handleMousePressEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseUngrabEvent(QQuickItem *child); + qreal positionAt(const QPointF &point) const; qreal position; - QPoint pressPoint; }; -void QQuickSwitchPrivate::updatePosition() -{ - Q_Q(QQuickSwitch); - q->setPosition(checked ? 1.0 : 0.0); -} - -qreal QQuickSwitchPrivate::positionAt(const QPoint &point) const +qreal QQuickSwitchPrivate::positionAt(const QPointF &point) const { Q_Q(const QQuickSwitch); - qreal pos = point.x() / indicator->width(); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); if (q->isMirrored()) return 1.0 - pos; return pos; } -bool QQuickSwitchPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - Q_UNUSED(child); - if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) - q->forceActiveFocus(Qt::MouseFocusReason); - - pressPoint = event->pos(); - q->setPressed(true); - emit q->pressed(); - event->accept(); - return true; -} - -bool QQuickSwitchPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - if (!child->keepMouseGrab()) - child->setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().x() - pressPoint.x(), Qt::XAxis, event)); - if (child->keepMouseGrab()) { - q->setPosition(positionAt(event->pos())); - event->accept(); - } - return true; -} - -bool QQuickSwitchPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) - q->forceActiveFocus(Qt::MouseFocusReason); - - pressPoint = QPoint(); - q->setPressed(false); - if (child->keepMouseGrab()) { - bool wasChecked = checked; - q->setChecked(position > 0.5); - q->setPosition(checked ? 1.0 : 0.0); - child->setKeepMouseGrab(false); - if (wasChecked != checked) { - emit q->released(); - emit q->clicked(); - } - event->accept(); - } else { - q->toggle(); - emit q->released(); - emit q->clicked(); - event->accept(); - } - return true; -} - -bool QQuickSwitchPrivate::handleMouseUngrabEvent(QQuickItem *child) -{ - Q_Q(QQuickSwitch); - Q_UNUSED(child); - pressPoint = QPoint(); - q->setChecked(position > 0.5); - q->setPosition(checked ? 1.0 : 0.0); - q->setPressed(false); - return true; -} - QQuickSwitch::QQuickSwitch(QQuickItem *parent) : QQuickAbstractButton(*(new QQuickSwitchPrivate), parent) { + Q_D(QQuickSwitch); + d->keepPressed = true; setCheckable(true); - setFiltersChildMouseEvents(true); - QObjectPrivate::connect(this, &QQuickAbstractButton::checkedChanged, d_func(), &QQuickSwitchPrivate::updatePosition); } /*! @@ -227,30 +152,56 @@ qreal QQuickSwitch::visualPosition() const return d->position; } +void QQuickSwitch::mousePressEvent(QMouseEvent *event) +{ + QQuickAbstractButton::mousePressEvent(event); +} + +void QQuickSwitch::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSwitch); + QQuickAbstractButton::mouseMoveEvent(event); + + const QPointF movePoint = event->localPos(); + if (!keepMouseGrab()) { + // don't start dragging the handle unless the initial press was at the indicator, + // or the drag has reached the indicator area. this prevents unnatural jumps when + // dragging far outside the indicator. + const qreal pressPos = d->positionAt(d->pressPoint); + const qreal movePos = d->positionAt(movePoint); + if ((pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0)) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event)); + } + + if (keepMouseGrab()) + setPosition(d->positionAt(movePoint)); +} + +void QQuickSwitch::mouseReleaseEvent(QMouseEvent *event) +{ + QQuickAbstractButton::mouseReleaseEvent(event); + setKeepMouseGrab(false); +} + void QQuickSwitch::mirrorChange() { QQuickAbstractButton::mirrorChange(); emit visualPositionChanged(); } -bool QQuickSwitch::childMouseEventFilter(QQuickItem *child, QEvent *event) +void QQuickSwitch::nextCheckState() { Q_D(QQuickSwitch); - if (child == indicator()) { - switch (event->type()) { - case QEvent::MouseButtonPress: - return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::MouseMove: - return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::MouseButtonRelease: - return d->handleMouseReleaseEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::UngrabMouse: - return d->handleMouseUngrabEvent(child); - default: - return false; - } - } - return false; + if (keepMouseGrab()) + setChecked(d->position > 0.5); + else + QQuickAbstractButton::nextCheckState(); +} + +void QQuickSwitch::checkStateSet() +{ + Q_D(QQuickSwitch); + setPosition(d->checked ? 1.0 : 0.0); } QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswitch_p.h b/src/quicktemplates2/qquickswitch_p.h index fa92e9f3..27a065b4 100644 --- a/src/quicktemplates2/qquickswitch_p.h +++ b/src/quicktemplates2/qquickswitch_p.h @@ -73,8 +73,14 @@ Q_SIGNALS: void visualPositionChanged(); protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mirrorChange() override; - bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + + void nextCheckState() override; + void checkStateSet() override; private: Q_DISABLE_COPY(QQuickSwitch) diff --git a/src/quicktemplates2/qquickswitchdelegate.cpp b/src/quicktemplates2/qquickswitchdelegate.cpp index fbdae418..62b677e5 100644 --- a/src/quicktemplates2/qquickswitchdelegate.cpp +++ b/src/quicktemplates2/qquickswitchdelegate.cpp @@ -80,22 +80,17 @@ public: { } - void updatePosition(); - qreal positionAt(const QPoint &point) const; + qreal positionAt(const QPointF &point) const; qreal position; }; -void QQuickSwitchDelegatePrivate::updatePosition() -{ - Q_Q(QQuickSwitchDelegate); - q->setPosition(checked ? 1.0 : 0.0); -} - -qreal QQuickSwitchDelegatePrivate::positionAt(const QPoint &point) const +qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const { Q_Q(const QQuickSwitchDelegate); - qreal pos = point.x() / indicator->width(); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); if (q->isMirrored()) return 1.0 - pos; return pos; @@ -104,9 +99,9 @@ qreal QQuickSwitchDelegatePrivate::positionAt(const QPoint &point) const QQuickSwitchDelegate::QQuickSwitchDelegate(QQuickItem *parent) : QQuickItemDelegate(*(new QQuickSwitchDelegatePrivate), parent) { + Q_D(QQuickSwitchDelegate); + d->keepPressed = true; setCheckable(true); - - QObjectPrivate::connect(this, &QQuickAbstractButton::checkedChanged, d_func(), &QQuickSwitchDelegatePrivate::updatePosition); } /*! @@ -147,6 +142,37 @@ qreal QQuickSwitchDelegate::visualPosition() const return d->position; } +void QQuickSwitchDelegate::mousePressEvent(QMouseEvent *event) +{ + QQuickItemDelegate::mousePressEvent(event); +} + +void QQuickSwitchDelegate::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSwitchDelegate); + QQuickItemDelegate::mouseMoveEvent(event); + + const QPointF movePoint = event->localPos(); + if (!keepMouseGrab()) { + // don't start dragging the handle unless the initial press was at the indicator, + // or the drag has reached the indicator area. this prevents unnatural jumps when + // dragging far outside the indicator. + const qreal pressPos = d->positionAt(d->pressPoint); + const qreal movePos = d->positionAt(movePoint); + if ((pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0)) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event)); + } + + if (keepMouseGrab()) + setPosition(d->positionAt(movePoint)); +} + +void QQuickSwitchDelegate::mouseReleaseEvent(QMouseEvent *event) +{ + QQuickItemDelegate::mouseReleaseEvent(event); + setKeepMouseGrab(false); +} + QFont QQuickSwitchDelegate::defaultFont() const { return QQuickControlPrivate::themeFont(QPlatformTheme::ListViewFont); @@ -158,4 +184,19 @@ void QQuickSwitchDelegate::mirrorChange() emit visualPositionChanged(); } +void QQuickSwitchDelegate::nextCheckState() +{ + Q_D(QQuickSwitchDelegate); + if (keepMouseGrab()) + setChecked(d->position > 0.5); + else + QQuickItemDelegate::nextCheckState(); +} + +void QQuickSwitchDelegate::checkStateSet() +{ + Q_D(QQuickSwitchDelegate); + setPosition(d->checked ? 1.0 : 0.0); +} + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswitchdelegate_p.h b/src/quicktemplates2/qquickswitchdelegate_p.h index 5126f643..c0cc21ac 100644 --- a/src/quicktemplates2/qquickswitchdelegate_p.h +++ b/src/quicktemplates2/qquickswitchdelegate_p.h @@ -73,9 +73,16 @@ Q_SIGNALS: void visualPositionChanged(); protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + QFont defaultFont() const override; void mirrorChange() override; + void nextCheckState() override; + void checkStateSet() override; + private: Q_DISABLE_COPY(QQuickSwitchDelegate) Q_DECLARE_PRIVATE(QQuickSwitchDelegate) |