diff options
Diffstat (limited to 'src/quicktemplates2')
114 files changed, 30113 insertions, 0 deletions
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp new file mode 100644 index 00000000..75854d04 --- /dev/null +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -0,0 +1,640 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickabstractbutton_p.h" +#include "qquickabstractbutton_p_p.h" +#include "qquickbuttongroup_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/qguiapplication.h> +#include <QtQuick/private/qquickevents_p_p.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +// copied from qabstractbutton.cpp +static const int AUTO_REPEAT_DELAY = 300; +static const int AUTO_REPEAT_INTERVAL = 100; + +/*! + \qmltype AbstractButton + \inherits Control + \instantiates QQuickAbstractButton + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Abstract base type providing functionality common to buttons. + + AbstractButton provides the interface for controls with button-like + behavior; for example, push buttons and checkable controls like + radio buttons and check boxes. As an abstract control, it has no delegate + implementations, leaving them to the types that derive from it. + + \sa ButtonGroup, {Button Controls} +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::pressed() + + This signal is emitted when the button is interactively pressed by the user. +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::released() + + This signal is emitted when the button is interactively released by the user. +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::canceled() + + This signal is emitted when the button loses mouse grab + while being pressed, or when it would emit the \l released + signal but the mouse cursor is not inside the button. +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::clicked() + + This signal is emitted when the button is interactively clicked by the user. +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::pressAndHold() + + This signal is emitted when the button is interactively pressed and held down by the user. +*/ + +/*! + \qmlsignal QtQuick.Controls::AbstractButton::doubleClicked() + + This signal is emitted when the button is interactively double clicked by the user. +*/ + +QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() : + 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) +{ +} + +bool QQuickAbstractButtonPrivate::isPressAndHoldConnected() +{ + Q_Q(QQuickAbstractButton); + IS_SIGNAL_CONNECTED(q, QQuickAbstractButton, pressAndHold, ()); +} + +void QQuickAbstractButtonPrivate::startPressAndHold() +{ + Q_Q(QQuickAbstractButton); + wasHeld = false; + stopPressAndHold(); + if (isPressAndHoldConnected()) + holdTimer = q->startTimer(QGuiApplication::styleHints()->mousePressAndHoldInterval()); +} + +void QQuickAbstractButtonPrivate::stopPressAndHold() +{ + Q_Q(QQuickAbstractButton); + if (holdTimer > 0) { + q->killTimer(holdTimer); + holdTimer = 0; + } +} + +void QQuickAbstractButtonPrivate::startRepeatDelay() +{ + Q_Q(QQuickAbstractButton); + stopPressRepeat(); + delayTimer = q->startTimer(AUTO_REPEAT_DELAY); +} + +void QQuickAbstractButtonPrivate::startPressRepeat() +{ + Q_Q(QQuickAbstractButton); + stopPressRepeat(); + repeatTimer = q->startTimer(AUTO_REPEAT_INTERVAL); +} + +void QQuickAbstractButtonPrivate::stopPressRepeat() +{ + Q_Q(QQuickAbstractButton); + if (delayTimer > 0) { + q->killTimer(delayTimer); + delayTimer = 0; + } + if (repeatTimer > 0) { + q->killTimer(repeatTimer); + repeatTimer = 0; + } +} + +QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const +{ + Q_Q(const QQuickAbstractButton); + if (group) + return qobject_cast<QQuickAbstractButton *>(group->checkedButton()); + + const QList<QQuickAbstractButton *> buttons = findExclusiveButtons(); + // TODO: A singular QRadioButton can be unchecked, which seems logical, + // because there's nothing to be exclusive with. However, a RadioButton + // from QtQuick.Controls 1.x can never be unchecked, which is the behavior + // that QQuickRadioButton adopted. Uncommenting the following count check + // gives the QRadioButton behavior. Notice that tst_radiobutton.qml needs + // to be updated. + if (!autoExclusive /*|| buttons.count() == 1*/) + return nullptr; + + for (QQuickAbstractButton *button : buttons) { + if (button->isChecked() && button != q) + return button; + } + return checked ? const_cast<QQuickAbstractButton *>(q) : nullptr; +} + +QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons() const +{ + QList<QQuickAbstractButton *> buttons; + if (group) { + QQmlListProperty<QQuickAbstractButton> groupButtons = group->buttons(); + int count = groupButtons.count(&groupButtons); + for (int i = 0; i < count; ++i) { + QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(groupButtons.at(&groupButtons, i)); + if (button) + buttons += button; + } + } else if (parentItem) { + const auto childItems = parentItem->childItems(); + for (QQuickItem *child : childItems) { + QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(child); + if (button && button->autoExclusive() && !QQuickAbstractButtonPrivate::get(button)->group) + buttons += button; + } + } + return buttons; +} + +QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent) : + QQuickControl(*(new QQuickAbstractButtonPrivate), parent) +{ + setActiveFocusOnTab(true); + setFocusPolicy(Qt::StrongFocus); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent) : + QQuickControl(dd, parent) +{ + setActiveFocusOnTab(true); + setFocusPolicy(Qt::StrongFocus); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QQuickAbstractButton::~QQuickAbstractButton() +{ + Q_D(QQuickAbstractButton); + if (d->group) + d->group->removeButton(this); +} + +/*! + \qmlproperty string QtQuick.Controls::AbstractButton::text + + This property holds a textual description of the button. + + \note The text is used for accessibility purposes, so it makes sense to + set a textual description even if the content item is an image. + + \sa {Control::contentItem}{contentItem} +*/ +QString QQuickAbstractButton::text() const +{ + Q_D(const QQuickAbstractButton); + return d->text; +} + +void QQuickAbstractButton::setText(const QString &text) +{ + Q_D(QQuickAbstractButton); + if (d->text == text) + return; + + d->text = text; + setAccessibleName(text); + emit textChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::AbstractButton::down + + This property holds whether the button is visually down. + + Unless explicitly set, this property follows the value of \l pressed. To + return to the default value, set this property to \c undefined. + + \sa pressed +*/ +bool QQuickAbstractButton::isDown() const +{ + Q_D(const QQuickAbstractButton); + return d->down; +} + +void QQuickAbstractButton::setDown(bool down) +{ + Q_D(QQuickAbstractButton); + d->explicitDown = true; + + if (d->down == down) + return; + + d->down = down; + emit downChanged(); +} + +void QQuickAbstractButton::resetDown() +{ + Q_D(QQuickAbstractButton); + if (!d->explicitDown) + return; + + setDown(d->pressed); + d->explicitDown = false; +} + +/*! + \qmlproperty bool QtQuick.Controls::AbstractButton::pressed + \readonly + + This property holds whether the button is physically pressed. A button can + be pressed by either touch or key events. + + \sa down +*/ +bool QQuickAbstractButton::isPressed() const +{ + Q_D(const QQuickAbstractButton); + return d->pressed; +} + +void QQuickAbstractButton::setPressed(bool isPressed) +{ + Q_D(QQuickAbstractButton); + if (d->pressed == isPressed) + return; + + d->pressed = isPressed; + setAccessibleProperty("pressed", isPressed); + emit pressedChanged(); + + if (!d->explicitDown) { + setDown(d->pressed); + d->explicitDown = false; + } +} + +/*! + \qmlproperty bool QtQuick.Controls::AbstractButton::checked + + This property holds whether the button is checked. +*/ +bool QQuickAbstractButton::isChecked() const +{ + Q_D(const QQuickAbstractButton); + return d->checked; +} + +void QQuickAbstractButton::setChecked(bool checked) +{ + Q_D(QQuickAbstractButton); + if (d->checked == checked) + return; + + if (checked && !d->checkable) + setCheckable(true); + + d->checked = checked; + setAccessibleProperty("checked", checked); + checkStateSet(); + emit checkedChanged(); +} + +// We define these in QQuickAbstractButton without exposing checkable as a +// property, and instead expose it as a property in QQuickButton. +// QQuickRadioButton, QQuickSwitch and QQuickCheckBox are checkable by default, +// but if we removed the checkable code from here, they'd each have to +// duplicate it. +bool QQuickAbstractButton::isCheckable() const +{ + Q_D(const QQuickAbstractButton); + return d->checkable; +} + +void QQuickAbstractButton::setCheckable(bool checkable) +{ + Q_D(QQuickAbstractButton); + if (d->checkable == checkable) + return; + + d->checkable = checkable; + setAccessibleProperty("checkable", checkable); + checkableChange(); +} + +/*! + \qmlproperty bool QtQuick.Controls::AbstractButton::autoExclusive + + This property holds whether auto-exclusivity is enabled. + + If auto-exclusivity is enabled, checkable buttons that belong to the same + parent item behave as if they were part of the same ButtonGroup. Only + one button can be checked at any time; checking another button automatically + unchecks the previously checked one. + + \note The property has no effect on buttons that belong to a ButtonGroup. + + RadioButton and TabButton are auto-exclusive by default. +*/ +bool QQuickAbstractButton::autoExclusive() const +{ + Q_D(const QQuickAbstractButton); + return d->autoExclusive; +} + +void QQuickAbstractButton::setAutoExclusive(bool exclusive) +{ + Q_D(QQuickAbstractButton); + if (d->autoExclusive == exclusive) + return; + + d->autoExclusive = exclusive; + emit autoExclusiveChanged(); +} + +bool QQuickAbstractButton::autoRepeat() const +{ + Q_D(const QQuickAbstractButton); + return d->autoRepeat; +} + +void QQuickAbstractButton::setAutoRepeat(bool repeat) +{ + Q_D(QQuickAbstractButton); + if (d->autoRepeat == repeat) + return; + + d->stopPressRepeat(); + d->autoRepeat = repeat; + autoRepeatChange(); +} + +/*! + \qmlproperty Item QtQuick.Controls::AbstractButton::indicator + + This property holds the indicator item. +*/ +QQuickItem *QQuickAbstractButton::indicator() const +{ + Q_D(const QQuickAbstractButton); + return d->indicator; +} + +void QQuickAbstractButton::setIndicator(QQuickItem *indicator) +{ + Q_D(QQuickAbstractButton); + if (d->indicator == indicator) + return; + + d->deleteDelegate(d->indicator); + d->indicator = indicator; + if (indicator) { + if (!indicator->parentItem()) + indicator->setParentItem(this); + indicator->setAcceptedMouseButtons(Qt::LeftButton); + } + emit indicatorChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::AbstractButton::toggle() + + Toggles the checked state of the button. +*/ +void QQuickAbstractButton::toggle() +{ + Q_D(QQuickAbstractButton); + setChecked(!d->checked); +} + +void QQuickAbstractButton::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::focusOutEvent(event); + if (d->pressed) { + setPressed(false); + emit canceled(); + } +} + +void QQuickAbstractButton::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::keyPressEvent(event); + if (event->key() == Qt::Key_Space) { + setPressed(true); + d->pressPoint = QPoint(qRound(width() / 2), qRound(height() / 2)); + + if (d->autoRepeat) { + d->startRepeatDelay(); + d->repeatButton = Qt::NoButton; + } + + emit pressed(); + } +} + +void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::keyReleaseEvent(event); + if (event->key() == Qt::Key_Space) { + setPressed(false); + + nextCheckState(); + emit released(); + emit clicked(); + + if (d->autoRepeat) + d->stopPressRepeat(); + } +} + +void QQuickAbstractButton::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::mousePressEvent(event); + setPressed(true); + d->pressPoint = event->pos(); + + emit pressed(); + + if (d->autoRepeat) { + d->startRepeatDelay(); + d->repeatButton = event->button(); + } else if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) { + d->startPressAndHold(); + } else { + d->stopPressAndHold(); + } +} + +void QQuickAbstractButton::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::mouseMoveEvent(event); + setPressed(d->keepPressed || contains(event->pos())); + + if (!d->pressed && d->autoRepeat) + d->stopPressRepeat(); + else if (d->holdTimer > 0 && (!d->pressed || QLineF(d->pressPoint, event->localPos()).length() > QGuiApplication::styleHints()->startDragDistance())) + d->stopPressAndHold(); +} + +void QQuickAbstractButton::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::mouseReleaseEvent(event); + bool wasPressed = d->pressed; + setPressed(false); + + if (d->keepPressed || contains(event->pos())) + nextCheckState(); + + if (wasPressed) { + emit released(); + if (!d->wasHeld) + emit clicked(); + } else { + emit canceled(); + } + + if (d->autoRepeat) + d->stopPressRepeat(); + else + d->stopPressAndHold(); +} + +void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event) +{ + QQuickControl::mouseDoubleClickEvent(event); + emit doubleClicked(); +} + +void QQuickAbstractButton::mouseUngrabEvent() +{ + Q_D(QQuickAbstractButton); + QQuickControl::mouseUngrabEvent(); + if (d->pressed) { + setPressed(false); + d->stopPressRepeat(); + d->stopPressAndHold(); + emit canceled(); + } +} + +void QQuickAbstractButton::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickAbstractButton); + QQuickControl::timerEvent(event); + if (event->timerId() == d->holdTimer) { + d->stopPressAndHold(); + d->wasHeld = true; + emit pressAndHold(); + } else if (event->timerId() == d->delayTimer) { + d->startPressRepeat(); + } else if (event->timerId() == d->repeatTimer) { + emit released(); + emit clicked(); + emit pressed(); + } +} + +void QQuickAbstractButton::checkStateSet() +{ + Q_D(QQuickAbstractButton); + if (d->checked) { + QQuickAbstractButton *button = d->findCheckedButton(); + if (button && button != this) + button->setChecked(false); + } +} + +void QQuickAbstractButton::nextCheckState() +{ + Q_D(QQuickAbstractButton); + if (d->checkable && (!d->checked || d->findCheckedButton() != this)) + setChecked(!d->checked); +} + +void QQuickAbstractButton::checkableChange() +{ +} + +void QQuickAbstractButton::autoRepeatChange() +{ +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickAbstractButton::accessibilityActiveChanged(bool active) +{ + QQuickControl::accessibilityActiveChanged(active); + + Q_D(QQuickAbstractButton); + if (active) { + setAccessibleName(d->text); + setAccessibleProperty("pressed", d->pressed); + setAccessibleProperty("checked", d->checked); + setAccessibleProperty("checkable", d->checkable); + } +} + +QAccessible::Role QQuickAbstractButton::accessibleRole() const +{ + return QAccessible::Button; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickabstractbutton_p.h b/src/quicktemplates2/qquickabstractbutton_p.h new file mode 100644 index 00000000..779739de --- /dev/null +++ b/src/quicktemplates2/qquickabstractbutton_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKABSTRACTBUTTON_P_H +#define QQUICKABSTRACTBUTTON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickAbstractButtonPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAbstractButton : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL) + Q_PROPERTY(bool down READ isDown WRITE setDown NOTIFY downChanged RESET resetDown FINAL) + Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY checkedChanged FINAL) + Q_PROPERTY(bool autoExclusive READ autoExclusive WRITE setAutoExclusive NOTIFY autoExclusiveChanged FINAL) + Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL) + +public: + explicit QQuickAbstractButton(QQuickItem *parent = nullptr); + ~QQuickAbstractButton(); + + QString text() const; + void setText(const QString &text); + + bool isDown() const; + void setDown(bool down); + void resetDown(); + + bool isPressed() const; + void setPressed(bool pressed); + + bool isChecked() const; + void setChecked(bool checked); + + bool isCheckable() const; + void setCheckable(bool checkable); + + bool autoExclusive() const; + void setAutoExclusive(bool exclusive); + + bool autoRepeat() const; + void setAutoRepeat(bool repeat); + + QQuickItem *indicator() const; + void setIndicator(QQuickItem *indicator); + +public Q_SLOTS: + void toggle(); + +Q_SIGNALS: + void pressed(); + void released(); + void canceled(); + void clicked(); + void pressAndHold(); + void doubleClicked(); + void textChanged(); + void downChanged(); + void pressedChanged(); + void checkedChanged(); + void autoExclusiveChanged(); + void indicatorChanged(); + +protected: + QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent); + + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void timerEvent(QTimerEvent *event) override; + + virtual void checkStateSet(); + virtual void nextCheckState(); + + virtual void checkableChange(); + virtual void autoRepeatChange(); + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickAbstractButton) + Q_DECLARE_PRIVATE(QQuickAbstractButton) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickAbstractButton) + +#endif // QQUICKABSTRACTBUTTON_P_H diff --git a/src/quicktemplates2/qquickabstractbutton_p_p.h b/src/quicktemplates2/qquickabstractbutton_p_p.h new file mode 100644 index 00000000..8138a61d --- /dev/null +++ b/src/quicktemplates2/qquickabstractbutton_p_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKABSTRACTBUTTON_P_P_H +#define QQUICKABSTRACTBUTTON_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickButtonGroup; + +class QQuickAbstractButtonPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickAbstractButton) + +public: + QQuickAbstractButtonPrivate(); + + static QQuickAbstractButtonPrivate *get(QQuickAbstractButton *button) + { + return button->d_func(); + } + + bool isPressAndHoldConnected(); + void startPressAndHold(); + void stopPressAndHold(); + + void startRepeatDelay(); + void startPressRepeat(); + void stopPressRepeat(); + + QQuickAbstractButton *findCheckedButton() const; + QList<QQuickAbstractButton *> findExclusiveButtons() const; + + QString text; + bool down; + bool explicitDown; + bool pressed; + bool keepPressed; + bool checked; + bool checkable; + bool autoExclusive; + bool autoRepeat; + bool wasHeld; + int holdTimer; + int delayTimer; + int repeatTimer; + QPointF pressPoint; + Qt::MouseButton repeatButton; + QQuickItem *indicator; + QQuickButtonGroup *group; +}; + +QT_END_NAMESPACE + +#endif // QQUICKABSTRACTBUTTON_P_P_H diff --git a/src/quicktemplates2/qquickapplicationwindow.cpp b/src/quicktemplates2/qquickapplicationwindow.cpp new file mode 100644 index 00000000..3a454353 --- /dev/null +++ b/src/quicktemplates2/qquickapplicationwindow.cpp @@ -0,0 +1,871 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickapplicationwindow_p.h" +#include "qquickoverlay_p.h" +#include "qquickpopup_p.h" +#include "qquickcontrol_p_p.h" +#include "qquicktextarea_p.h" +#include "qquicktextfield_p.h" +#include "qquicktoolbar_p.h" +#include "qquicktabbar_p.h" + +#include <QtCore/private/qobject_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ApplicationWindow + \inherits Window + \instantiates QQuickApplicationWindow + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Styled top-level window with support for a header and footer. + + ApplicationWindow is a \l Window which makes it convenient to add + a \l header and \l footer item to the window. + + You can declare ApplicationWindow as the root item of your application, + and run it by using \l QQmlApplicationEngine. In this way you can control + the window's properties, appearance and layout from QML. + + \image qtquickcontrols2-applicationwindow-wireframe.png + + \qml + import QtQuick.Controls 2.0 + + ApplicationWindow { + visible: true + + header: ToolBar { + // ... + } + + footer: TabBar { + // ... + } + + StackView { + anchors.fill: parent + } + } + \endqml + + ApplicationWindow supports popups via its \l overlay property, which + ensures that popups are displayed above other content and that the + background is dimmed when a \l {Popup::}{modal} or \l {Popup::dim} + {dimmed} popup is visible. + + \note By default, an ApplicationWindow is not visible. + + \section2 Attached ApplicationWindow Properties + + Due to how \l {Scope and Naming Resolution} works in QML, it is possible + to reference the \c id of the application root element anywhere in its + child QML objects. Even though this approach is fine for many applications + and use cases, for a generic QML component it may not be acceptable as it + creates a dependency to the surrounding environment. + + ApplicationWindow provides a set of attached properties that can be used + to access the window and its building blocks from places where no direct + access to the window is available, without creating a dependency to a + certain window \c id. A QML component that uses the ApplicationWindow + attached properties works in any window regardless of its \c id. The + following example uses the attached \c overlay property to position the + popup to the center of the window, despite the position of the button + that opens the popup. + + \code + Button { + onClicked: popup.open() + + Popup { + id: popup + + parent: ApplicationWindow.overlay + + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: 100 + height: 100 + } + } + \endcode + + \sa {Customizing ApplicationWindow}, Page, {Container Controls} +*/ + +class QQuickApplicationWindowPrivate : public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickApplicationWindow) + +public: + QQuickApplicationWindowPrivate() + : complete(false) + , background(nullptr) + , contentItem(nullptr) + , header(nullptr) + , footer(nullptr) + , overlay(nullptr) + , activeFocusControl(nullptr) + { } + + static QQuickApplicationWindowPrivate *get(QQuickApplicationWindow *window) + { + return window->d_func(); + } + + void relayout(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) override; + void itemVisibilityChanged(QQuickItem *item) override; + void itemImplicitWidthChanged(QQuickItem *item) override; + void itemImplicitHeightChanged(QQuickItem *item) override; + + void updateFont(const QFont &f); + inline void setFont_helper(const QFont &f) { + if (font.resolve() == f.resolve() && font == f) + return; + updateFont(f); + } + void resolveFont(); + + void _q_updateActiveFocus(); + void setActiveFocusControl(QQuickItem *item); + + bool complete; + QQuickItem *background; + QQuickItem *contentItem; + QQuickItem *header; + QQuickItem *footer; + QQuickOverlay *overlay; + QFont font; + QLocale locale; + QQuickItem *activeFocusControl; + QQuickApplicationWindow *q_ptr; +}; + +void QQuickApplicationWindowPrivate::relayout() +{ + Q_Q(QQuickApplicationWindow); + QQuickItem *content = q->contentItem(); + qreal hh = header && header->isVisible() ? header->height() : 0; + qreal fh = footer && footer->isVisible() ? footer->height() : 0; + + content->setY(hh); + content->setWidth(q->width()); + content->setHeight(q->height() - hh - fh); + + if (header) { + header->setY(-hh); + QQuickItemPrivate *p = QQuickItemPrivate::get(header); + if (!p->widthValid) { + header->setWidth(q->width()); + p->widthValid = false; + } + } + + if (footer) { + footer->setY(content->height()); + QQuickItemPrivate *p = QQuickItemPrivate::get(footer); + if (!p->widthValid) { + footer->setWidth(q->width()); + p->widthValid = false; + } + } + + if (background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(background); + if (!p->widthValid && qFuzzyIsNull(background->x())) { + background->setWidth(q->width()); + p->widthValid = false; + } + if (!p->heightValid && qFuzzyIsNull(background->y())) { + background->setHeight(q->height()); + p->heightValid = false; + } + } +} + +void QQuickApplicationWindowPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) +{ + Q_UNUSED(item) + Q_UNUSED(newRect) + Q_UNUSED(oldRect) + relayout(); +} + +void QQuickApplicationWindowPrivate::itemVisibilityChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +void QQuickApplicationWindowPrivate::itemImplicitWidthChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +void QQuickApplicationWindowPrivate::itemImplicitHeightChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +void QQuickApplicationWindowPrivate::_q_updateActiveFocus() +{ + Q_Q(QQuickApplicationWindow); + QQuickItem *item = q->activeFocusItem(); + while (item) { + QQuickControl *control = qobject_cast<QQuickControl *>(item); + if (control) { + setActiveFocusControl(control); + break; + } + QQuickTextField *textField = qobject_cast<QQuickTextField *>(item); + if (textField) { + setActiveFocusControl(textField); + break; + } + QQuickTextArea *textArea = qobject_cast<QQuickTextArea *>(item); + if (textArea) { + setActiveFocusControl(textArea); + break; + } + item = item->parentItem(); + } +} + +void QQuickApplicationWindowPrivate::setActiveFocusControl(QQuickItem *control) +{ + Q_Q(QQuickApplicationWindow); + if (activeFocusControl != control) { + activeFocusControl = control; + emit q->activeFocusControlChanged(); + } +} + +QQuickApplicationWindow::QQuickApplicationWindow(QWindow *parent) : + QQuickWindowQmlImpl(parent), d_ptr(new QQuickApplicationWindowPrivate) +{ + d_ptr->q_ptr = this; + connect(this, SIGNAL(activeFocusItemChanged()), this, SLOT(_q_updateActiveFocus())); +} + +QQuickApplicationWindow::~QQuickApplicationWindow() +{ + Q_D(QQuickApplicationWindow); + if (d->header) + QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + if (d->footer) + QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + d_ptr.reset(); // QTBUG-52731 +} + +/*! + \qmlproperty Item QtQuick.Controls::ApplicationWindow::background + + This property holds the background item. + + The background item is stacked under the \l {contentItem}{content item}, + but above the \l {Window::color}{background color} of the window. + + The background item is useful for images and gradients, for example, + but the \l {Window::}{color} property is preferable for solid colors, + as it doesn't need to create an item. + + \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. + + \sa {Customizing ApplicationWindow}, contentItem, header, footer, overlay +*/ +QQuickItem *QQuickApplicationWindow::background() const +{ + Q_D(const QQuickApplicationWindow); + return d->background; +} + +void QQuickApplicationWindow::setBackground(QQuickItem *background) +{ + Q_D(QQuickApplicationWindow); + if (d->background == background) + return; + + delete d->background; + d->background = background; + if (background) { + background->setParentItem(QQuickWindow::contentItem()); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + if (isComponentComplete()) + d->relayout(); + } + emit backgroundChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::ApplicationWindow::header + + This property holds the window header item. The header item is positioned to + the top, and resized to the width of the window. The default value is \c null. + + \code + ApplicationWindow { + header: TabBar { + // ... + } + } + \endcode + + \note Assigning a ToolBar or TabBar as a window header sets the respective + \l ToolBar::position or \l TabBar::position property automatically to \c Header. + + \sa footer, Page::header +*/ +QQuickItem *QQuickApplicationWindow::header() const +{ + Q_D(const QQuickApplicationWindow); + return d->header; +} + +void QQuickApplicationWindow::setHeader(QQuickItem *header) +{ + Q_D(QQuickApplicationWindow); + if (d->header == header) + return; + + if (d->header) { + QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + d->header->setParentItem(nullptr); + } + d->header = header; + if (header) { + header->setParentItem(contentItem()); + QQuickItemPrivate *p = QQuickItemPrivate::get(header); + p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + if (qFuzzyIsNull(header->z())) + header->setZ(1); + if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(header)) + toolBar->setPosition(QQuickToolBar::Header); + else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(header)) + tabBar->setPosition(QQuickTabBar::Header); + } + if (isComponentComplete()) + d->relayout(); + emit headerChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::ApplicationWindow::footer + + This property holds the window footer item. The footer item is positioned to + the bottom, and resized to the width of the window. The default value is \c null. + + \code + ApplicationWindow { + footer: ToolBar { + // ... + } + } + \endcode + + \note Assigning a ToolBar or TabBar as a window footer sets the respective + \l ToolBar::position or \l TabBar::position property automatically to \c Footer. + + \sa header, Page::footer +*/ +QQuickItem *QQuickApplicationWindow::footer() const +{ + Q_D(const QQuickApplicationWindow); + return d->footer; +} + +void QQuickApplicationWindow::setFooter(QQuickItem *footer) +{ + Q_D(QQuickApplicationWindow); + if (d->footer == footer) + return; + + if (d->footer) { + QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + d->footer->setParentItem(nullptr); + } + d->footer = footer; + if (footer) { + footer->setParentItem(contentItem()); + QQuickItemPrivate *p = QQuickItemPrivate::get(footer); + p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + if (qFuzzyIsNull(footer->z())) + footer->setZ(1); + if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(footer)) + toolBar->setPosition(QQuickToolBar::Footer); + else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(footer)) + tabBar->setPosition(QQuickTabBar::Footer); + } + if (isComponentComplete()) + d->relayout(); + emit footerChanged(); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::ApplicationWindow::contentData + \default + + This default property holds the list of all objects declared as children of + the window. + + The data property allows you to freely mix visual children, resources and + other windows in an ApplicationWindow. + + If you assign an Item to the contentData list, it becomes a child of the + window's contentItem, so that it appears inside the window. The item's + parent will be the window's \l contentItem. + + It should not generally be necessary to refer to the contentData property, + as it is the default property for ApplicationWindow and thus all child + items are automatically assigned to this property. + + \sa contentItem +*/ +QQmlListProperty<QObject> QQuickApplicationWindow::contentData() +{ + return QQuickItemPrivate::get(contentItem())->data(); +} + +/*! + \qmlproperty Item QtQuick.Controls::ApplicationWindow::contentItem + \readonly + + This property holds the window content item. + + The content item is stacked above the \l background item, and under the + \l header, \l footer, and \l overlay items. + + \sa background, header, footer, overlay +*/ +QQuickItem *QQuickApplicationWindow::contentItem() const +{ + QQuickApplicationWindowPrivate *d = const_cast<QQuickApplicationWindowPrivate *>(d_func()); + if (!d->contentItem) { + d->contentItem = new QQuickItem(QQuickWindow::contentItem()); + d->contentItem->setFlag(QQuickItem::ItemIsFocusScope); + d->contentItem->setFocus(true); + d->relayout(); + } + return d->contentItem; +} + +/*! + \qmlproperty Control QtQuick.Controls::ApplicationWindow::activeFocusControl + \readonly + + This property holds the control that currently has active focus, or \c null if there is + no control with active focus. + + 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 activeFocusControl to the SpinBox itself. + + \sa Window::activeFocusItem +*/ +QQuickItem *QQuickApplicationWindow::activeFocusControl() const +{ + Q_D(const QQuickApplicationWindow); + return d->activeFocusControl; +} + +/*! + \qmlpropertygroup QtQuick.Controls::ApplicationWindow::overlay + \qmlproperty Item QtQuick.Controls::ApplicationWindow::overlay + \qmlproperty Component QtQuick.Controls::ApplicationWindow::overlay.modal + \qmlproperty Component QtQuick.Controls::ApplicationWindow::overlay.modeless + + This property holds the window overlay item. Popups are automatically + reparented to the overlay. + + \table + \header + \li Property + \li Description + \row + \li overlay.modal + \li This property holds a component to use as a visual item that implements + background dimming for modal popups. It is created for and stacked below + visible modal popups. + \row + \li overlay.modeless + \li This property holds a component to use as a visual item that implements + background dimming for modeless popups. It is created for and stacked below + visible dimming popups. + \row + \li overlay.pressed() + \li This signal is emitted when the overlay is pressed by the user while + a popup is visible. + \row + \li overlay.released() + \li This signal is emitted when the overlay is released by the user while + a modal popup is visible. + \endtable + + \sa Popup::modal, Popup::dim +*/ +QQuickOverlay *QQuickApplicationWindow::overlay() const +{ + QQuickApplicationWindowPrivate *d = const_cast<QQuickApplicationWindowPrivate *>(d_func()); + if (!d) // being deleted + return nullptr; + + if (!d->overlay) { + d->overlay = new QQuickOverlay(QQuickWindow::contentItem()); + d->overlay->stackAfter(QQuickApplicationWindow::contentItem()); + } + return d->overlay; +} + +/*! + \qmlproperty font QtQuick.Controls::ApplicationWindow::font + + This property holds the font currently set for the window. + + The default font depends on the system environment. QGuiApplication maintains a system/theme + font which serves as a default for all application windows. You can also set the default font + for windows by passing a custom font to QGuiApplication::setFont(), before loading any QML. + Finally, the font is matched against Qt's font database to find the best match. + + ApplicationWindow propagates explicit font properties to child controls. If you change a specific + property on the window's font, that property propagates to all child controls in the window, + overriding any system defaults for that property. + + \sa Control::font +*/ +QFont QQuickApplicationWindow::font() const +{ + Q_D(const QQuickApplicationWindow); + return d->font; +} + +void QQuickApplicationWindow::setFont(const QFont &font) +{ + Q_D(QQuickApplicationWindow); + if (d->font.resolve() == font.resolve() && d->font == font) + return; + + QFont resolvedFont = font.resolve(QQuickControlPrivate::themeFont(QPlatformTheme::SystemFont)); + d->setFont_helper(resolvedFont); +} + +void QQuickApplicationWindow::resetFont() +{ + setFont(QFont()); +} + +void QQuickApplicationWindowPrivate::resolveFont() +{ + QFont resolvedFont = font.resolve(QQuickControlPrivate::themeFont(QPlatformTheme::SystemFont)); + setFont_helper(resolvedFont); +} + +void QQuickApplicationWindowPrivate::updateFont(const QFont &f) +{ + Q_Q(QQuickApplicationWindow); + const bool changed = font != f; + font = f; + + QQuickControlPrivate::updateFontRecur(q->QQuickWindow::contentItem(), f); + + // TODO: internal QQuickPopupManager that provides reliable access to all QQuickPopup instances + const QList<QQuickPopup *> popups = q->findChildren<QQuickPopup *>(); + for (QQuickPopup *popup : popups) + QQuickControlPrivate::get(static_cast<QQuickControl *>(popup->popupItem()))->inheritFont(f); + + if (changed) + emit q->fontChanged(); +} + +QLocale QQuickApplicationWindow::locale() const +{ + Q_D(const QQuickApplicationWindow); + return d->locale; +} + +void QQuickApplicationWindow::setLocale(const QLocale &locale) +{ + Q_D(QQuickApplicationWindow); + if (d->locale == locale) + return; + + d->locale = locale; + QQuickControlPrivate::updateLocaleRecur(QQuickWindow::contentItem(), locale); + + // TODO: internal QQuickPopupManager that provides reliable access to all QQuickPopup instances + const QList<QQuickPopup *> popups = QQuickWindow::contentItem()->findChildren<QQuickPopup *>(); + for (QQuickPopup *popup : popups) + QQuickControlPrivate::get(static_cast<QQuickControl *>(popup->popupItem()))->updateLocale(locale, false); // explicit=false + + emit localeChanged(); +} + +void QQuickApplicationWindow::resetLocale() +{ + setLocale(QLocale()); +} + +QQuickApplicationWindowAttached *QQuickApplicationWindow::qmlAttachedProperties(QObject *object) +{ + return new QQuickApplicationWindowAttached(object); +} + +bool QQuickApplicationWindow::isComponentComplete() const +{ + Q_D(const QQuickApplicationWindow); + return d->complete; +} + +void QQuickApplicationWindow::classBegin() +{ + Q_D(QQuickApplicationWindow); + QQuickWindowQmlImpl::classBegin(); + d->resolveFont(); +} + +void QQuickApplicationWindow::componentComplete() +{ + Q_D(QQuickApplicationWindow); + d->complete = true; + QQuickWindowQmlImpl::componentComplete(); +} + +void QQuickApplicationWindow::resizeEvent(QResizeEvent *event) +{ + Q_D(QQuickApplicationWindow); + QQuickWindowQmlImpl::resizeEvent(event); + d->relayout(); +} + +class QQuickApplicationWindowAttachedPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickApplicationWindowAttached) + +public: + QQuickApplicationWindowAttachedPrivate() : window(nullptr) { } + + void windowChange(QQuickWindow *wnd); + + QQuickWindow *window; +}; + +void QQuickApplicationWindowAttachedPrivate::windowChange(QQuickWindow *wnd) +{ + Q_Q(QQuickApplicationWindowAttached); + if (window == wnd) + return; + + 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); + } + + 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) + : QObject(*(new QQuickApplicationWindowAttachedPrivate), parent) +{ + Q_D(QQuickApplicationWindowAttached); + if (QQuickItem *item = qobject_cast<QQuickItem *>(parent)) { + d->windowChange(item->window()); + QObjectPrivate::connect(item, &QQuickItem::windowChanged, d, &QQuickApplicationWindowAttachedPrivate::windowChange); + if (!d->window) { + QQuickItem *p = item; + while (p) { + if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(p->parent())) { + d->windowChange(popup->window()); + QObjectPrivate::connect(popup, &QQuickPopup::windowChanged, d, &QQuickApplicationWindowAttachedPrivate::windowChange); + } + p = p->parentItem(); + } + } + } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent)) { + d->windowChange(popup->window()); + QObjectPrivate::connect(popup, &QQuickPopup::windowChanged, d, &QQuickApplicationWindowAttachedPrivate::windowChange); + } +} + +/*! + \qmlattachedproperty ApplicationWindow QtQuick.Controls::ApplicationWindow::window + \readonly + + This attached property holds the application window. The property can be attached + to any item. The value is \c null if the item is not in an ApplicationWindow. + + \sa {Attached ApplicationWindow Properties} +*/ +QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const +{ + Q_D(const QQuickApplicationWindowAttached); + return qobject_cast<QQuickApplicationWindow *>(d->window); +} + +/*! + \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::contentItem + \readonly + + This attached property holds the window content item. The property can be attached + to any item. The value is \c null if the item is not in an ApplicationWindow. + + \sa {Attached ApplicationWindow Properties} +*/ +QQuickItem *QQuickApplicationWindowAttached::contentItem() const +{ + Q_D(const QQuickApplicationWindowAttached); + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->contentItem(); + return nullptr; +} + +/*! + \qmlattachedproperty Control QtQuick.Controls::ApplicationWindow::activeFocusControl + \readonly + + This attached property holds the control that currently has active focus, or \c null + if there is no control with active focus. The property can be attached to any item. + The value is \c null if the item is not in an ApplicationWindow, or the window has + no active focus. + + \sa Window::activeFocusItem, {Attached ApplicationWindow Properties} +*/ +QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const +{ + Q_D(const QQuickApplicationWindowAttached); + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->activeFocusControl(); + return nullptr; +} + +/*! + \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::header + \readonly + + This attached property holds the window header item. The property can be attached + to any item. The value is \c null if the item is not in an ApplicationWindow, or + the window has no header item. + + \sa {Attached ApplicationWindow Properties} +*/ +QQuickItem *QQuickApplicationWindowAttached::header() const +{ + Q_D(const QQuickApplicationWindowAttached); + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->header(); + return nullptr; +} + +/*! + \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::footer + \readonly + + This attached property holds the window footer item. The property can be attached + to any item. The value is \c null if the item is not in an ApplicationWindow, or + the window has no footer item. + + \sa {Attached ApplicationWindow Properties} +*/ +QQuickItem *QQuickApplicationWindowAttached::footer() const +{ + Q_D(const QQuickApplicationWindowAttached); + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->footer(); + return nullptr; +} + +/*! + \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::overlay + \readonly + + This attached property holds the window overlay item. The property can be attached + to any item. The value is \c null if the item is not in an ApplicationWindow. + + \sa {Attached ApplicationWindow Properties} +*/ +QQuickOverlay *QQuickApplicationWindowAttached::overlay() const +{ + Q_D(const QQuickApplicationWindowAttached); + return QQuickOverlay::overlay(d->window); +} + +QT_END_NAMESPACE + +#include "moc_qquickapplicationwindow_p.cpp" diff --git a/src/quicktemplates2/qquickapplicationwindow_p.h b/src/quicktemplates2/qquickapplicationwindow_p.h new file mode 100644 index 00000000..820c6d23 --- /dev/null +++ b/src/quicktemplates2/qquickapplicationwindow_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKAPPLICATIONWINDOW_P_H +#define QQUICKAPPLICATIONWINDOW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquickwindowmodule_p.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> +#include <QtGui/qfont.h> +#include <QtCore/qlocale.h> + +QT_BEGIN_NAMESPACE + +class QQuickOverlay; +class QQuickApplicationWindowPrivate; +class QQuickApplicationWindowAttached; +class QQuickApplicationWindowAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindow : public QQuickWindowQmlImpl +{ + Q_OBJECT + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT FINAL) + Q_PROPERTY(QQmlListProperty<QObject> data READ contentData FINAL) + Q_PROPERTY(QQuickItem *activeFocusControl READ activeFocusControl NOTIFY activeFocusControlChanged FINAL) + Q_PROPERTY(QQuickItem *header READ header WRITE setHeader NOTIFY headerChanged FINAL) + Q_PROPERTY(QQuickItem *footer READ footer WRITE setFooter NOTIFY footerChanged FINAL) + Q_PROPERTY(QQuickOverlay *overlay READ overlay CONSTANT FINAL) + Q_PROPERTY(QFont font READ font WRITE setFont RESET resetFont NOTIFY fontChanged FINAL) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL) + Q_CLASSINFO("DefaultProperty", "data") + +public: + explicit QQuickApplicationWindow(QWindow *parent = nullptr); + ~QQuickApplicationWindow(); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + + QQuickItem *contentItem() const; + QQmlListProperty<QObject> contentData(); + + QQuickItem *activeFocusControl() const; + + QQuickItem *header() const; + void setHeader(QQuickItem *header); + + QQuickItem *footer() const; + void setFooter(QQuickItem *footer); + + QQuickOverlay *overlay() const; + + QFont font() const; + void setFont(const QFont &font); + void resetFont(); + + QLocale locale() const; + void setLocale(const QLocale &locale); + void resetLocale(); + + static QQuickApplicationWindowAttached *qmlAttachedProperties(QObject *object); + +Q_SIGNALS: + void backgroundChanged(); + void activeFocusControlChanged(); + void headerChanged(); + void footerChanged(); + void fontChanged(); + void localeChanged(); + +protected: + bool isComponentComplete() const; + void classBegin() override; + void componentComplete() override; + void resizeEvent(QResizeEvent *event) override; + +private: + Q_DISABLE_COPY(QQuickApplicationWindow) + Q_DECLARE_PRIVATE(QQuickApplicationWindow) + Q_PRIVATE_SLOT(d_func(), void _q_updateActiveFocus()) + QScopedPointer<QQuickApplicationWindowPrivate> d_ptr; +}; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindowAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickApplicationWindow *window READ window NOTIFY windowChanged FINAL) + Q_PROPERTY(QQuickItem *contentItem READ contentItem NOTIFY contentItemChanged FINAL) + Q_PROPERTY(QQuickItem *activeFocusControl READ activeFocusControl NOTIFY activeFocusControlChanged FINAL) + Q_PROPERTY(QQuickItem *header READ header NOTIFY headerChanged FINAL) + Q_PROPERTY(QQuickItem *footer READ footer NOTIFY footerChanged FINAL) + Q_PROPERTY(QQuickOverlay *overlay READ overlay NOTIFY overlayChanged FINAL) + +public: + explicit QQuickApplicationWindowAttached(QObject *parent = nullptr); + + QQuickApplicationWindow *window() const; + QQuickItem *contentItem() const; + QQuickItem *activeFocusControl() const; + QQuickItem *header() const; + QQuickItem *footer() const; + QQuickOverlay *overlay() const; + +Q_SIGNALS: + void windowChanged(); + void contentItemChanged(); + void activeFocusControlChanged(); + void headerChanged(); + void footerChanged(); + void overlayChanged(); + +private: + Q_DISABLE_COPY(QQuickApplicationWindowAttached) + Q_DECLARE_PRIVATE(QQuickApplicationWindowAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickApplicationWindow) +QML_DECLARE_TYPEINFO(QQuickApplicationWindow, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKAPPLICATIONWINDOW_P_H diff --git a/src/quicktemplates2/qquickbusyindicator.cpp b/src/quicktemplates2/qquickbusyindicator.cpp new file mode 100644 index 00000000..2cd3f62d --- /dev/null +++ b/src/quicktemplates2/qquickbusyindicator.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickbusyindicator_p.h" +#include "qquickcontrol_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype BusyIndicator + \inherits Control + \instantiates QQuickBusyIndicator + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-indicators + \brief Indicates background activity, for example, while content is being loaded. + + \image qtquickcontrols2-busyindicator.gif + + The busy indicator should be used to indicate activity while content is + being loaded or the UI is blocked waiting for a resource to become available. + + The following snippet shows how to use the BusyIndicator: + + \qml + BusyIndicator { + running: image.status === Image.Loading + } + \endqml + + BusyIndicator is similar to an indeterminate \l ProgressBar. Both can be + used to indicate background activity. The main difference is visual, and + that ProgressBar can also present a concrete amount of progress (when it + can be determined). Due to the visual difference, busy indicators and + indeterminate progress bars fit different places in user interfaces. + Typical places for a busy indicator: + \list + \li in the corner of a \l ToolBar + \li as an overlay on top of a \l Page + \li on the side of an \l ItemDelegate + \endlist + + \sa {Customizing BusyIndicator}, {Indicator Controls}, ProgressBar +*/ + +class QQuickBusyIndicatorPrivate : public QQuickControlPrivate +{ +public: + QQuickBusyIndicatorPrivate() : running(true) { } + + bool running; +}; + +QQuickBusyIndicator::QQuickBusyIndicator(QQuickItem *parent) : + QQuickControl(*(new QQuickBusyIndicatorPrivate), parent) +{ +} + +/*! + \qmlproperty bool QtQuick.Controls::BusyIndicator::running + + This property holds whether the busy indicator is currently indicating + activity. + + \note The indicator is only visible when this property is set to \c true. + + The default value is \c true. + +*/ +bool QQuickBusyIndicator::isRunning() const +{ + Q_D(const QQuickBusyIndicator); + return d->running; +} + +void QQuickBusyIndicator::setRunning(bool running) +{ + Q_D(QQuickBusyIndicator); + if (d->running == running) + return; + + d->running = running; + emit runningChanged(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickBusyIndicator::accessibleRole() const +{ + return QAccessible::Indicator; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickbusyindicator_p.h b/src/quicktemplates2/qquickbusyindicator_p.h new file mode 100644 index 00000000..4da2c152 --- /dev/null +++ b/src/quicktemplates2/qquickbusyindicator_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKBUSYINDICATOR_P_H +#define QQUICKBUSYINDICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickBusyIndicatorPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickBusyIndicator : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged FINAL) + +public: + explicit QQuickBusyIndicator(QQuickItem *parent = nullptr); + + bool isRunning() const; + void setRunning(bool running); + +Q_SIGNALS: + void runningChanged(); + +protected: +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickBusyIndicator) + Q_DECLARE_PRIVATE(QQuickBusyIndicator) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickBusyIndicator) + +#endif // QQUICKBUSYINDICATOR_P_H diff --git a/src/quicktemplates2/qquickbutton.cpp b/src/quicktemplates2/qquickbutton.cpp new file mode 100644 index 00000000..ca6d4551 --- /dev/null +++ b/src/quicktemplates2/qquickbutton.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickbutton_p.h" +#include "qquickabstractbutton_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Button + \inherits AbstractButton + \instantiates QQuickButton + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Push-button that can be clicked to perform a command or answer a question. + + \image qtquickcontrols2-button.gif + + Button presents a push-button control that can be pushed or clicked by + the user. Buttons are normally used to perform an action, or to answer + a question. Typical buttons are \e OK, \e Apply, \e Cancel, \e Close, + \e Yes, \e No, and \e Help. + + A button emits the signal \l {AbstractButton::}{clicked()} when it is activated by the user. + Connect to this signal to perform the button's action. Buttons also + provide the signals \l {AbstractButton::}{canceled()}, \l {AbstractButton::}{doubleClicked()}, \l {AbstractButton::}{pressed()}, + \l {AbstractButton::}{released()} and \l {AbstractButton::}{pressAndHold()} for long presses. + + See the snippet below on how to connect to the button's signals. + + \code + RowLayout { + Button { + text: "Ok" + onClicked: model.submit() + } + Button { + text: "Cancel" + onClicked: model.revert() + } + } + \endcode + + \sa {Customizing Button}, {Button Controls} +*/ + +class QQuickButtonPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickButton) + +public: + QQuickButtonPrivate(); + + bool flat; + bool highlighted; +}; + +QQuickButtonPrivate::QQuickButtonPrivate() : + flat(false), highlighted(false) +{ +} + +QQuickButton::QQuickButton(QQuickItem *parent) : + QQuickAbstractButton(*(new QQuickButtonPrivate), 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() +{ + emit checkableChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Button::autoRepeat + + 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. +*/ + +void QQuickButton::autoRepeatChange() +{ + emit autoRepeatChanged(); +} + +QFont QQuickButton::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::PushButtonFont); +} + +/*! + \qmlproperty bool QtQuick.Controls::Button::highlighted + + This property holds whether the button is highlighted. + + \image qtquickcontrols2-button-highlighted.gif + + A button can be highlighted in order to draw the user's attention towards + it. It has no effect on keyboard interaction. + + The default value is \c false. +*/ +bool QQuickButton::isHighlighted() const +{ + Q_D(const QQuickButton); + return d->highlighted; +} + +void QQuickButton::setHighlighted(bool highlighted) +{ + Q_D(QQuickButton); + if (highlighted == d->highlighted) + return; + + d->highlighted = highlighted; + emit highlightedChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Button::flat + + This property holds whether the button is flat. + + \image qtquickcontrols2-button-flat.gif + + A flat button typically does not draw a background unless it is pressed or checked. + + The default value is \c false. +*/ +bool QQuickButton::isFlat() const +{ + Q_D(const QQuickButton); + return d->flat; +} + +void QQuickButton::setFlat(bool flat) +{ + Q_D(QQuickButton); + if (flat == d->flat) + return; + + d->flat = flat; + emit flatChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickbutton_p.h b/src/quicktemplates2/qquickbutton_p.h new file mode 100644 index 00000000..fafce150 --- /dev/null +++ b/src/quicktemplates2/qquickbutton_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKBUTTON_P_H +#define QQUICKBUTTON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickButtonPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButton : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged FINAL) + Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY autoRepeatChanged FINAL) + Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL) + Q_PROPERTY(bool flat READ isFlat WRITE setFlat NOTIFY flatChanged FINAL) + +public: + explicit QQuickButton(QQuickItem *parent = nullptr); + + bool isHighlighted() const; + void setHighlighted(bool highlighted); + + bool isFlat() const; + void setFlat(bool flat); + +Q_SIGNALS: + void checkableChanged(); + void autoRepeatChanged(); + void highlightedChanged(); + void flatChanged(); + +protected: + void checkableChange() override; + void autoRepeatChange() override; + + QFont defaultFont() const override; + +private: + Q_DISABLE_COPY(QQuickButton) + Q_DECLARE_PRIVATE(QQuickButton) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickButton) + +#endif // QQUICKBUTTON_P_H diff --git a/src/quicktemplates2/qquickbuttongroup.cpp b/src/quicktemplates2/qquickbuttongroup.cpp new file mode 100644 index 00000000..6ed75a2b --- /dev/null +++ b/src/quicktemplates2/qquickbuttongroup.cpp @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickbuttongroup_p.h" + +#include <QtCore/private/qobject_p.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qvariant.h> +#include <QtQml/qqmlinfo.h> + +#include "qquickabstractbutton_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ButtonGroup + \inherits QtObject + \instantiates QQuickButtonGroup + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup utilities + \brief Mutually-exclusive group of checkable buttons. + + ButtonGroup is a non-visual, mutually exclusive group of buttons. + It is used with controls such as RadioButton, where only one of the options + can be selected at a time. + + The most straight-forward way to use ButtonGroup is to assign + a list of buttons. For example, the list of children of a + \l{Item Positioners}{positioner} or a \l{Qt Quick Layouts}{layout} + that manages a group of mutually exclusive buttons. + + \code + ButtonGroup { + buttons: column.children + } + + Column { + id: column + + RadioButton { + checked: true + text: qsTr("DAB") + } + + RadioButton { + text: qsTr("FM") + } + + RadioButton { + text: qsTr("AM") + } + } + \endcode + + Mutually exclusive buttons do not always share the same parent item, + or the parent layout may sometimes contain items that should not be + included in the button group. Such cases are best handled using + the \l group attached property. + + \code + ButtonGroup { id: radioGroup } + + Column { + Label { + text: qsTr("Radio:") + } + + RadioButton { + checked: true + text: qsTr("DAB") + ButtonGroup.group: radioGroup + } + + RadioButton { + text: qsTr("FM") + ButtonGroup.group: radioGroup + } + + RadioButton { + text: qsTr("AM") + ButtonGroup.group: radioGroup + } + } + \endcode + + More advanced use cases can be handled using the \c addButton() and + \c removeButton() methods. + + \sa RadioButton, {Button Controls} +*/ + +class QQuickButtonGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickButtonGroup) + +public: + QQuickButtonGroupPrivate() : checkedButton(nullptr) { } + + void clear(); + void _q_updateCurrent(); + + static void buttons_append(QQmlListProperty<QQuickAbstractButton> *prop, QQuickAbstractButton *obj); + static int buttons_count(QQmlListProperty<QQuickAbstractButton> *prop); + static QQuickAbstractButton *buttons_at(QQmlListProperty<QQuickAbstractButton> *prop, int index); + static void buttons_clear(QQmlListProperty<QQuickAbstractButton> *prop); + + QQuickAbstractButton *checkedButton; + QVector<QQuickAbstractButton*> buttons; +}; + +void QQuickButtonGroupPrivate::clear() +{ + for (QQuickAbstractButton *button : qAsConst(buttons)) { + QQuickAbstractButtonPrivate::get(button)->group = nullptr; + QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, this, &QQuickButtonGroupPrivate::_q_updateCurrent); + } + buttons.clear(); +} + +void QQuickButtonGroupPrivate::_q_updateCurrent() +{ + Q_Q(QQuickButtonGroup); + QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender()); + if (button && button->isChecked()) + q->setCheckedButton(button); + else if (!buttons.contains(checkedButton)) + q->setCheckedButton(nullptr); +} + +void QQuickButtonGroupPrivate::buttons_append(QQmlListProperty<QQuickAbstractButton> *prop, QQuickAbstractButton *obj) +{ + QQuickButtonGroup *q = static_cast<QQuickButtonGroup *>(prop->object); + q->addButton(obj); +} + +int QQuickButtonGroupPrivate::buttons_count(QQmlListProperty<QQuickAbstractButton> *prop) +{ + QQuickButtonGroupPrivate *p = static_cast<QQuickButtonGroupPrivate *>(prop->data); + return p->buttons.count(); +} + +QQuickAbstractButton *QQuickButtonGroupPrivate::buttons_at(QQmlListProperty<QQuickAbstractButton> *prop, int index) +{ + QQuickButtonGroupPrivate *p = static_cast<QQuickButtonGroupPrivate *>(prop->data); + return p->buttons.value(index); +} + +void QQuickButtonGroupPrivate::buttons_clear(QQmlListProperty<QQuickAbstractButton> *prop) +{ + QQuickButtonGroupPrivate *p = static_cast<QQuickButtonGroupPrivate *>(prop->data); + if (!p->buttons.isEmpty()) { + p->clear(); + QQuickButtonGroup *q = static_cast<QQuickButtonGroup *>(prop->object); + // QTBUG-52358: don't clear the checked button immediately + QMetaObject::invokeMethod(q, "_q_updateCurrent", Qt::QueuedConnection); + emit q->buttonsChanged(); + } +} + +QQuickButtonGroup::QQuickButtonGroup(QObject *parent) + : QObject(*(new QQuickButtonGroupPrivate), parent) +{ +} + +QQuickButtonGroup::~QQuickButtonGroup() +{ + Q_D(QQuickButtonGroup); + d->clear(); +} + +QQuickButtonGroupAttached *QQuickButtonGroup::qmlAttachedProperties(QObject *object) +{ + return new QQuickButtonGroupAttached(object); +} + +/*! + \qmlproperty AbstractButton QtQuick.Controls::ButtonGroup::checkedButton + + This property holds the currently selected button, or \c null if there is none. + + By default, it is the first checked button added to the button group. +*/ +QQuickAbstractButton *QQuickButtonGroup::checkedButton() const +{ + Q_D(const QQuickButtonGroup); + return d->checkedButton; +} + +void QQuickButtonGroup::setCheckedButton(QQuickAbstractButton *checkedButton) +{ + Q_D(QQuickButtonGroup); + if (d->checkedButton == checkedButton) + return; + + if (d->checkedButton) + d->checkedButton->setChecked(false); + d->checkedButton = checkedButton; + if (checkedButton) + checkedButton->setChecked(true); + emit checkedButtonChanged(); +} + +/*! + \qmlproperty list<AbstractButton> QtQuick.Controls::ButtonGroup::buttons + \default + + This property holds the list of buttons. + + \code + ButtonGroup { + buttons: column.children + } + + Column { + id: column + + RadioButton { + checked: true + text: qsTr("Option A") + } + + RadioButton { + text: qsTr("Option B") + } + } + \endcode + + \sa group +*/ +QQmlListProperty<QQuickAbstractButton> QQuickButtonGroup::buttons() +{ + Q_D(QQuickButtonGroup); + return QQmlListProperty<QQuickAbstractButton>(this, d, + QQuickButtonGroupPrivate::buttons_append, + QQuickButtonGroupPrivate::buttons_count, + QQuickButtonGroupPrivate::buttons_at, + QQuickButtonGroupPrivate::buttons_clear); +} + +/*! + \qmlmethod void QtQuick.Controls::ButtonGroup::addButton(AbstractButton button) + + Adds a \a button to the button group. + + \note Manually adding objects to a button group is typically unnecessary. + The \l buttons property and the \l group attached property provide a + convenient and declarative syntax. + + \sa buttons, group +*/ +void QQuickButtonGroup::addButton(QQuickAbstractButton *button) +{ + Q_D(QQuickButtonGroup); + if (!button || d->buttons.contains(button)) + return; + + QQuickAbstractButtonPrivate::get(button)->group = this; + QObjectPrivate::connect(button, &QQuickAbstractButton::checkedChanged, d, &QQuickButtonGroupPrivate::_q_updateCurrent); + + if (button->isChecked()) + setCheckedButton(button); + + d->buttons.append(button); + emit buttonsChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::ButtonGroup::removeButton(AbstractButton button) + + Removes a \a button from the button group. + + \note Manually removing objects from a button group is typically unnecessary. + The \l buttons property and the \l group attached property provide a + convenient and declarative syntax. + + \sa buttons, group +*/ +void QQuickButtonGroup::removeButton(QQuickAbstractButton *button) +{ + Q_D(QQuickButtonGroup); + if (!button || !d->buttons.contains(button)) + return; + + QQuickAbstractButtonPrivate::get(button)->group = nullptr; + QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, d, &QQuickButtonGroupPrivate::_q_updateCurrent); + + if (d->checkedButton == button) + setCheckedButton(nullptr); + + d->buttons.removeOne(button); + emit buttonsChanged(); +} + +class QQuickButtonGroupAttachedPrivate : public QObjectPrivate +{ +public: + QQuickButtonGroupAttachedPrivate() : group(nullptr) { } + + QQuickButtonGroup *group; +}; + +QQuickButtonGroupAttached::QQuickButtonGroupAttached(QObject *parent) : + QObject(*(new QQuickButtonGroupAttachedPrivate), parent) +{ +} + +/*! + \qmlattachedproperty ButtonGroup QtQuick.Controls::ButtonGroup::group + + This property attaches a button to a button group. + + \code + ButtonGroup { id: group } + + RadioButton { + checked: true + text: qsTr("Option A") + ButtonGroup.group: group + } + + RadioButton { + text: qsTr("Option B") + ButtonGroup.group: group + } + \endcode + + \sa buttons +*/ +QQuickButtonGroup *QQuickButtonGroupAttached::group() const +{ + Q_D(const QQuickButtonGroupAttached); + return d->group; +} + +void QQuickButtonGroupAttached::setGroup(QQuickButtonGroup *group) +{ + Q_D(QQuickButtonGroupAttached); + if (d->group == group) + return; + + if (d->group) + d->group->removeButton(qobject_cast<QQuickAbstractButton*>(parent())); + d->group = group; + if (group) + group->addButton(qobject_cast<QQuickAbstractButton*>(parent())); + emit groupChanged(); +} + +QT_END_NAMESPACE + +#include "moc_qquickbuttongroup_p.cpp" diff --git a/src/quicktemplates2/qquickbuttongroup_p.h b/src/quicktemplates2/qquickbuttongroup_p.h new file mode 100644 index 00000000..baf4e13f --- /dev/null +++ b/src/quicktemplates2/qquickbuttongroup_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKBUTTONGROUP_P_H +#define QQUICKBUTTONGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class QQuickAbstractButton; +class QQuickButtonGroupPrivate; +class QQuickButtonGroupAttached; +class QQuickButtonGroupAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickAbstractButton *checkedButton READ checkedButton WRITE setCheckedButton NOTIFY checkedButtonChanged) + Q_PROPERTY(QQmlListProperty<QQuickAbstractButton> buttons READ buttons NOTIFY buttonsChanged FINAL) + +public: + explicit QQuickButtonGroup(QObject *parent = nullptr); + ~QQuickButtonGroup(); + + static QQuickButtonGroupAttached *qmlAttachedProperties(QObject *object); + + QQuickAbstractButton *checkedButton() const; + void setCheckedButton(QQuickAbstractButton *checkedButton); + + QQmlListProperty<QQuickAbstractButton> buttons(); + +public Q_SLOTS: + void addButton(QQuickAbstractButton *button); + void removeButton(QQuickAbstractButton *button); + +Q_SIGNALS: + void checkedButtonChanged(); + void buttonsChanged(); + +private: + Q_DISABLE_COPY(QQuickButtonGroup) + Q_DECLARE_PRIVATE(QQuickButtonGroup) + + Q_PRIVATE_SLOT(d_func(), void _q_updateCurrent()) +}; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroupAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickButtonGroup *group READ group WRITE setGroup NOTIFY groupChanged FINAL) + +public: + explicit QQuickButtonGroupAttached(QObject *parent = nullptr); + + QQuickButtonGroup *group() const; + void setGroup(QQuickButtonGroup *group); + +Q_SIGNALS: + void groupChanged(); + +private: + Q_DISABLE_COPY(QQuickButtonGroupAttached) + Q_DECLARE_PRIVATE(QQuickButtonGroupAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickButtonGroup) +QML_DECLARE_TYPEINFO(QQuickButtonGroup, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKBUTTONGROUP_P_H diff --git a/src/quicktemplates2/qquickcheckbox.cpp b/src/quicktemplates2/qquickcheckbox.cpp new file mode 100644 index 00000000..fd5ab6ba --- /dev/null +++ b/src/quicktemplates2/qquickcheckbox.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcheckbox_p.h" +#include "qquickabstractbutton_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype CheckBox + \inherits AbstractButton + \instantiates QQuickCheckBox + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Check button that can be toggled on or off. + + \image qtquickcontrols2-checkbox.gif + + 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. For larger sets of options, such as those + in a list, consider using \l CheckDelegate instead. + + 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 + \l tristate property. This state indicates that the regular checked/unchecked + state can not be determined; generally because of other states that affect + the checkbox. This state is useful when several child nodes are selected + in a treeview, for example. + + \code + ColumnLayout { + CheckBox { + checked: true + text: qsTr("First") + } + CheckBox { + text: qsTr("Second") + } + CheckBox { + checked: true + text: qsTr("Third") + } + } + \endcode + + \sa {Customizing CheckBox}, {Button Controls} +*/ + +class QQuickCheckBoxPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickCheckBox) + +public: + QQuickCheckBoxPrivate() + : tristate(false), checkState(Qt::Unchecked) + { + } + + bool tristate; + Qt::CheckState checkState; +}; + +QQuickCheckBox::QQuickCheckBox(QQuickItem *parent) : + QQuickAbstractButton(*(new QQuickCheckBoxPrivate), parent) +{ + setCheckable(true); +} + +/*! + \qmlproperty bool QtQuick.Controls::CheckBox::tristate + + This property holds whether the checkbox is a tri-state checkbox. + + In the animation below, the first checkbox is tri-state: + + \image qtquickcontrols2-checkbox-tristate.gif + + The default is \c false, i.e., the checkbox has only two states. +*/ +bool QQuickCheckBox::isTristate() const +{ + Q_D(const QQuickCheckBox); + return d->tristate; +} + +void QQuickCheckBox::setTristate(bool tristate) +{ + Q_D(QQuickCheckBox); + if (d->tristate == tristate) + return; + + d->tristate = tristate; + emit tristateChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::CheckBox::checkState + + This property holds the check state of the checkbox. + + Available states: + \value Qt.Unchecked The checkbox is unchecked. + \value Qt.PartiallyChecked The checkbox is partially checked. This state is only used when \l tristate is enabled. + \value Qt.Checked The checkbox is checked. + + \sa tristate, {AbstractButton::checked}{checked} +*/ +Qt::CheckState QQuickCheckBox::checkState() const +{ + Q_D(const QQuickCheckBox); + return d->checkState; +} + +void QQuickCheckBox::setCheckState(Qt::CheckState state) +{ + Q_D(QQuickCheckBox); + if (d->checkState == state) + return; + + if (!d->tristate && state == Qt::PartiallyChecked) + setTristate(true); + + bool wasChecked = isChecked(); + d->checked = state != Qt::Unchecked; + d->checkState = state; + emit checkStateChanged(); + if (d->checked != wasChecked) + emit checkedChanged(); +} + +QFont QQuickCheckBox::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::CheckBoxFont); +} + +void QQuickCheckBox::checkStateSet() +{ + setCheckState(isChecked() ? Qt::Checked : Qt::Unchecked); +} + +void QQuickCheckBox::nextCheckState() +{ + Q_D(QQuickCheckBox); + if (d->tristate) + setCheckState(static_cast<Qt::CheckState>((d->checkState + 1) % 3)); + else + QQuickAbstractButton::nextCheckState(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickCheckBox::accessibleRole() const +{ + return QAccessible::CheckBox; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickcheckbox_p.h b/src/quicktemplates2/qquickcheckbox_p.h new file mode 100644 index 00000000..bee3bae4 --- /dev/null +++ b/src/quicktemplates2/qquickcheckbox_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCHECKBOX_P_H +#define QQUICKCHECKBOX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickCheckBoxPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckBox : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL) + Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL) + +public: + explicit QQuickCheckBox(QQuickItem *parent = nullptr); + + bool isTristate() const; + void setTristate(bool tristate); + + Qt::CheckState checkState() const; + void setCheckState(Qt::CheckState state); + +Q_SIGNALS: + void tristateChanged(); + void checkStateChanged(); + +protected: + QFont defaultFont() const override; + + void checkStateSet() override; + void nextCheckState() override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickCheckBox) + Q_DECLARE_PRIVATE(QQuickCheckBox) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickCheckBox) + +#endif // QQUICKCHECKBOX_P_H diff --git a/src/quicktemplates2/qquickcheckdelegate.cpp b/src/quicktemplates2/qquickcheckdelegate.cpp new file mode 100644 index 00000000..51bc8501 --- /dev/null +++ b/src/quicktemplates2/qquickcheckdelegate.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcheckdelegate_p.h" +#include "qquickitemdelegate_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype CheckDelegate + \inherits ItemDelegate + \instantiates QQuickCheckDelegate + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-delegates + \brief Item delegate with a check indicator that can be toggled on or off. + + \image qtquickcontrols2-checkdelegate.gif + + 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 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: + partially checked. The partially checked state can be enabled using the + \l tristate property. This state indicates that the regular checked/unchecked + state can not be determined; generally because of other states that affect + the check delegate. This state is useful when several child nodes are selected + in a treeview, for example. + + \code + ListView { + model: ["Option 1", "Option 2", "Option 3"] + delegate: CheckDelegate { + text: modelData + } + } + \endcode + + \sa {Customizing CheckDelegate}, {Delegate Controls}, CheckBox +*/ + +class QQuickCheckDelegatePrivate : public QQuickItemDelegatePrivate +{ + Q_DECLARE_PUBLIC(QQuickCheckDelegate) + +public: + QQuickCheckDelegatePrivate() + : tristate(false), checkState(Qt::Unchecked) + { + } + + bool tristate; + Qt::CheckState checkState; +}; + +QQuickCheckDelegate::QQuickCheckDelegate(QQuickItem *parent) : + QQuickItemDelegate(*(new QQuickCheckDelegatePrivate), parent) +{ + setCheckable(true); +} + +/*! + \qmlproperty bool QtQuick.Controls::CheckDelegate::tristate + + This property determines whether the check delegate has three states. + + In the animation below, the first checkdelegate is tri-state: + + \image qtquickcontrols2-checkdelegate-tristate.gif + + The default is \c false, i.e., the delegate has only two states. +*/ +bool QQuickCheckDelegate::isTristate() const +{ + Q_D(const QQuickCheckDelegate); + return d->tristate; +} + +void QQuickCheckDelegate::setTristate(bool tristate) +{ + Q_D(QQuickCheckDelegate); + if (d->tristate == tristate) + return; + + d->tristate = tristate; + emit tristateChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::CheckDelegate::checkState + + This property determines the check state of the check delegate. + + Available states: + \value Qt.Unchecked The delegate is unchecked. + \value Qt.PartiallyChecked The delegate is partially checked. This state is only used when \l tristate is enabled. + \value Qt.Checked The delegate is checked. + + \sa tristate, {AbstractButton::checked}{checked} +*/ +Qt::CheckState QQuickCheckDelegate::checkState() const +{ + Q_D(const QQuickCheckDelegate); + return d->checkState; +} + +void QQuickCheckDelegate::setCheckState(Qt::CheckState state) +{ + Q_D(QQuickCheckDelegate); + if (d->checkState == state) + return; + + if (!d->tristate && state == Qt::PartiallyChecked) + setTristate(true); + + bool wasChecked = isChecked(); + d->checked = state != Qt::Unchecked; + d->checkState = state; + emit checkStateChanged(); + if (d->checked != wasChecked) + emit checkedChanged(); +} + +QFont QQuickCheckDelegate::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ListViewFont); +} + +void QQuickCheckDelegate::checkStateSet() +{ + setCheckState(isChecked() ? Qt::Checked : Qt::Unchecked); +} + +void QQuickCheckDelegate::nextCheckState() +{ + Q_D(QQuickCheckDelegate); + if (d->tristate) + setCheckState(static_cast<Qt::CheckState>((d->checkState + 1) % 3)); + else + QQuickItemDelegate::nextCheckState(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickCheckDelegate::accessibleRole() const +{ + return QAccessible::CheckBox; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickcheckdelegate_p.h b/src/quicktemplates2/qquickcheckdelegate_p.h new file mode 100644 index 00000000..d0509512 --- /dev/null +++ b/src/quicktemplates2/qquickcheckdelegate_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCHECKDELEGATE_P_H +#define QQUICKCHECKDELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickitemdelegate_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickCheckDelegatePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckDelegate : public QQuickItemDelegate +{ + Q_OBJECT + Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL) + Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL) + +public: + explicit QQuickCheckDelegate(QQuickItem *parent = nullptr); + + bool isTristate() const; + void setTristate(bool tristate); + + Qt::CheckState checkState() const; + void setCheckState(Qt::CheckState state); + +Q_SIGNALS: + void tristateChanged(); + void checkStateChanged(); + +protected: + QFont defaultFont() const override; + + void checkStateSet() override; + void nextCheckState() override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickCheckDelegate) + Q_DECLARE_PRIVATE(QQuickCheckDelegate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickCheckDelegate) + +#endif // QQUICKCHECKDELEGATE_P_H diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp new file mode 100644 index 00000000..01f0f699 --- /dev/null +++ b/src/quicktemplates2/qquickcombobox.cpp @@ -0,0 +1,1037 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcombobox_p.h" +#include "qquickcontrol_p_p.h" +#include "qquickabstractbutton_p.h" +#include "qquickpopup_p_p.h" + +#include <QtCore/qregexp.h> +#include <QtCore/qabstractitemmodel.h> +#include <QtGui/qpa/qplatformtheme.h> +#include <QtQml/qjsvalue.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtQuick/private/qquickevents_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ComboBox + \inherits Control + \instantiates QQuickComboBox + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Combined button and popup list for selecting options. + + \image qtquickcontrols2-combobox.gif + + ComboBox is a combined button and popup list. It provides a means of + presenting a list of options to the user in a way that takes up the + minimum amount of screen space. + + ComboBox is populated with a data model. The data model is commonly + a JavaScript array, a \l ListModel or an integer, but other types + of \l {qml-data-models}{data models} are also supported. + + \code + ComboBox { + model: ["First", "Second", "Third"] + } + \endcode + + \section1 ComboBox Model Roles + + ComboBox is able to visualize standard \l {qml-data-models}{data models} + that provide the \c modelData role: + \list + \li models that have only one role + \li models that do not have named roles (JavaScript array, integer) + \endlist + + When using models that have multiple named roles, ComboBox must be configured + to use a specific \l {textRole}{text role} for its \l {displayText}{display text} + and \l delegate instances. + + \code + ComboBox { + textRole: "key" + model: ListModel { + ListElement { key: "First"; value: 123 } + ListElement { key: "Second"; value: 456 } + ListElement { key: "Third"; value: 789 } + } + } + \endcode + + \note If ComboBox is assigned a data model that has multiple named roles, but + \l textRole is not defined, ComboBox is unable to visualize it and throws a + \c {ReferenceError: modelData is not defined}. + + \sa {Customizing ComboBox}, {Input Controls} +*/ + +/*! + \qmlsignal void QtQuick.Controls::ComboBox::activated(int index) + + This signal is emitted when the item at \a index is activated by the user. + + An item is activated when it is selected while the popup is open, + causing the popup to close (and \l currentIndex to change), + or while the popup is closed and the combo box is navigated via + keyboard, causing the \l currentIndex to change. + The \l currentIndex property is set to \a index. + + \sa currentIndex +*/ + +/*! + \qmlsignal void QtQuick.Controls::ComboBox::highlighted(int index) + + This signal is emitted when the item at \a index in the popup list is highlighted by the user. + + The highlighted signal is only emitted when the popup is open and an item + is highlighted, but not necessarily \l activated. + + \sa highlightedIndex +*/ + +namespace { + enum Activation { NoActivate, Activate }; + enum Highlighting { NoHighlight, Highlight }; +} + +class QQuickComboBoxDelegateModel : public QQmlDelegateModel +{ +public: + explicit QQuickComboBoxDelegateModel(QQuickComboBox *combo); + QString stringValue(int index, const QString &role) override; + +private: + QQuickComboBox *combo; +}; + +QQuickComboBoxDelegateModel::QQuickComboBoxDelegateModel(QQuickComboBox *combo) : + QQmlDelegateModel(qmlContext(combo), combo), combo(combo) +{ +} + +QString QQuickComboBoxDelegateModel::stringValue(int index, const QString &role) +{ + QVariant model = combo->model(); + if (model.userType() == QMetaType::QVariantList) { + QVariant object = model.toList().value(index); + if (object.userType() == QMetaType::QVariantMap) { + const QVariantMap data = object.toMap(); + if (data.count() == 1 && role == QLatin1String("modelData")) + return data.first().toString(); + return data.value(role).toString(); + } + } + return QQmlDelegateModel::stringValue(index, role); +} + +class QQuickComboBoxPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickComboBox) + +public: + QQuickComboBoxPrivate() : pressed(false), ownModel(false), hasDisplayText(false), hasCurrentIndex(false), + highlightedIndex(-1), currentIndex(-1), delegateModel(nullptr), + delegate(nullptr), indicator(nullptr), popup(nullptr) { } + + bool isPopupVisible() const; + void showPopup(); + void hidePopup(bool accept); + void togglePopup(bool accept); + + void itemClicked(); + + void createdItem(int index, QObject *object); + void countChanged(); + void updateCurrentText(); + void incrementCurrentIndex(); + void decrementCurrentIndex(); + void setCurrentIndex(int index, Activation activate); + void updateHighlightedIndex(); + void setHighlightedIndex(int index, Highlighting highlight); + + void keySearch(const QString &text); + int match(int start, const QString &text, Qt::MatchFlags flags) const; + + void createDelegateModel(); + + bool pressed; + bool ownModel; + bool hasDisplayText; + bool hasCurrentIndex; + int highlightedIndex; + int currentIndex; + QVariant model; + QString textRole; + QString currentText; + QString displayText; + QQuickItem *pressedItem; + QQmlInstanceModel *delegateModel; + QQmlComponent *delegate; + QQuickItem *indicator; + QQuickPopup *popup; +}; + +bool QQuickComboBoxPrivate::isPopupVisible() const +{ + return popup && popup->isVisible(); +} + +void QQuickComboBoxPrivate::showPopup() +{ + if (popup && !popup->isVisible()) + popup->open(); +} + +void QQuickComboBoxPrivate::hidePopup(bool accept) +{ + Q_Q(QQuickComboBox); + if (accept) { + q->setCurrentIndex(highlightedIndex); + emit q->activated(currentIndex); + } + if (popup && popup->isVisible()) + popup->close(); +} + +void QQuickComboBoxPrivate::togglePopup(bool accept) +{ + if (!popup) + return; + + if (popup->isVisible()) + hidePopup(accept); + else + showPopup(); +} + +void QQuickComboBoxPrivate::itemClicked() +{ + Q_Q(QQuickComboBox); + int index = delegateModel->indexOf(q->sender(), nullptr); + if (index != -1) { + setHighlightedIndex(index, Highlight); + hidePopup(true); + } +} + +void QQuickComboBoxPrivate::createdItem(int index, QObject *object) +{ + QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object); + if (button) { + button->setFocusPolicy(Qt::NoFocus); + connect(button, &QQuickAbstractButton::clicked, this, &QQuickComboBoxPrivate::itemClicked); + } + + if (index == currentIndex) + updateCurrentText(); +} + +void QQuickComboBoxPrivate::countChanged() +{ + Q_Q(QQuickComboBox); + if (q->count() == 0) + q->setCurrentIndex(-1); + emit q->countChanged(); +} + +void QQuickComboBoxPrivate::updateCurrentText() +{ + Q_Q(QQuickComboBox); + QString text = q->textAt(currentIndex); + if (currentText != text) { + currentText = text; + if (!hasDisplayText) + q->setAccessibleName(text); + emit q->currentTextChanged(); + } + if (!hasDisplayText && displayText != text) { + displayText = text; + emit q->displayTextChanged(); + } +} + +void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate) +{ + Q_Q(QQuickComboBox); + if (currentIndex == index) + return; + + currentIndex = index; + emit q->currentIndexChanged(); + + if (componentComplete) + updateCurrentText(); + + if (activate) + emit q->activated(index); +} + +void QQuickComboBoxPrivate::incrementCurrentIndex() +{ + Q_Q(QQuickComboBox); + if (isPopupVisible()) { + if (highlightedIndex < q->count() - 1) + setHighlightedIndex(highlightedIndex + 1, Highlight); + } else { + if (currentIndex < q->count() - 1) + setCurrentIndex(currentIndex + 1, Activate); + } +} + +void QQuickComboBoxPrivate::decrementCurrentIndex() +{ + if (isPopupVisible()) { + if (highlightedIndex > 0) + setHighlightedIndex(highlightedIndex - 1, Highlight); + } else { + if (currentIndex > 0) + setCurrentIndex(currentIndex - 1, Activate); + } +} + +void QQuickComboBoxPrivate::updateHighlightedIndex() +{ + setHighlightedIndex(popup->isVisible() ? currentIndex : -1, NoHighlight); +} + +void QQuickComboBoxPrivate::setHighlightedIndex(int index, Highlighting highlight) +{ + Q_Q(QQuickComboBox); + if (highlightedIndex == index) + return; + + highlightedIndex = index; + emit q->highlightedIndexChanged(); + + if (highlight) + emit q->highlighted(index); +} + +void QQuickComboBoxPrivate::keySearch(const QString &text) +{ + int index = match(currentIndex + 1, text, Qt::MatchStartsWith | Qt::MatchWrap); + if (index != -1) + setCurrentIndex(index, Activate); +} + +int QQuickComboBoxPrivate::match(int start, const QString &text, Qt::MatchFlags flags) const +{ + Q_Q(const QQuickComboBox); + uint matchType = flags & 0x0F; + bool wrap = flags & Qt::MatchWrap; + Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int from = start; + int to = q->count(); + + // iterates twice if wrapping + for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { + for (int idx = from; idx < to; ++idx) { + QString t = q->textAt(idx); + switch (matchType) { + case Qt::MatchExactly: + if (t == text) + return idx; + break; + case Qt::MatchRegExp: + if (QRegExp(text, cs).exactMatch(t)) + return idx; + break; + case Qt::MatchWildcard: + if (QRegExp(text, cs, QRegExp::Wildcard).exactMatch(t)) + return idx; + break; + case Qt::MatchStartsWith: + if (t.startsWith(text, cs)) + return idx; + break; + case Qt::MatchEndsWith: + if (t.endsWith(text, cs)) + return idx; + break; + case Qt::MatchFixedString: + if (t.compare(text, cs) == 0) + return idx; + break; + case Qt::MatchContains: + default: + if (t.contains(text, cs)) + return idx; + break; + } + } + // prepare for the next iteration + from = 0; + to = start; + } + return -1; +} + +void QQuickComboBoxPrivate::createDelegateModel() +{ + Q_Q(QQuickComboBox); + bool ownedOldModel = ownModel; + QQmlInstanceModel* oldModel = delegateModel; + if (oldModel) { + disconnect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged); + disconnect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::updateCurrentText); + disconnect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem); + } + + ownModel = false; + delegateModel = model.value<QQmlInstanceModel *>(); + + if (!delegateModel && model.isValid()) { + QQmlDelegateModel *dataModel = new QQuickComboBoxDelegateModel(q); + dataModel->setModel(model); + dataModel->setDelegate(delegate); + if (q->isComponentComplete()) + dataModel->componentComplete(); + + ownModel = true; + delegateModel = dataModel; + } + + if (delegateModel) { + connect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged); + connect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::updateCurrentText); + connect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem); + } + + emit q->delegateModelChanged(); + + if (ownedOldModel) + delete oldModel; +} + +QQuickComboBox::QQuickComboBox(QQuickItem *parent) : + QQuickControl(*(new QQuickComboBoxPrivate), parent) +{ + setFocusPolicy(Qt::StrongFocus); + setFlag(QQuickItem::ItemIsFocusScope); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QQuickComboBox::~QQuickComboBox() +{ + Q_D(QQuickComboBox); + delete d->popup; + d->popup = nullptr; +} + +/*! + \readonly + \qmlproperty int QtQuick.Controls::ComboBox::count + + This property holds the number of items in the combo box. +*/ +int QQuickComboBox::count() const +{ + Q_D(const QQuickComboBox); + return d->delegateModel ? d->delegateModel->count() : 0; +} + +/*! + \qmlproperty model QtQuick.Controls::ComboBox::model + + This property holds the model providing data for the combo box. + + \code + ComboBox { + textRole: "key" + model: ListModel { + ListElement { key: "First"; value: 123 } + ListElement { key: "Second"; value: 456 } + ListElement { key: "Third"; value: 789 } + } + } + \endcode + + \sa textRole, {qml-data-models}{Data Models} +*/ +QVariant QQuickComboBox::model() const +{ + Q_D(const QQuickComboBox); + return d->model; +} + +void QQuickComboBox::setModel(const QVariant& m) +{ + Q_D(QQuickComboBox); + QVariant model = m; + if (model.userType() == qMetaTypeId<QJSValue>()) + model = model.value<QJSValue>().toVariant(); + + if (d->model == model) + return; + + if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model)) + QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText); + if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(model)) + QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText); + + d->model = model; + d->createDelegateModel(); + if (isComponentComplete()) { + setCurrentIndex(count() > 0 ? 0 : -1); + d->updateCurrentText(); + } + emit modelChanged(); +} + +/*! + \internal + \qmlproperty model QtQuick.Controls::ComboBox::delegateModel + + This property holds the model providing delegate instances for the combo box. +*/ +QQmlInstanceModel *QQuickComboBox::delegateModel() const +{ + Q_D(const QQuickComboBox); + return d->delegateModel; +} + +/*! + \qmlproperty bool QtQuick.Controls::ComboBox::pressed + + This property holds whether the combo box button is pressed. +*/ +bool QQuickComboBox::isPressed() const +{ + Q_D(const QQuickComboBox); + return d->pressed; +} + +void QQuickComboBox::setPressed(bool pressed) +{ + Q_D(QQuickComboBox); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + emit pressedChanged(); +} + +/*! + \readonly + \qmlproperty int QtQuick.Controls::ComboBox::highlightedIndex + + This property holds the index of the highlighted item in the combo box popup list. + + When a highlighted item is activated, the popup is closed, \l currentIndex + is set to \c highlightedIndex, and the value of this property is reset to + \c -1, as there is no longer a highlighted item. + + \sa highlighted(), currentIndex +*/ +int QQuickComboBox::highlightedIndex() const +{ + Q_D(const QQuickComboBox); + return d->highlightedIndex; +} + +/*! + \qmlproperty int QtQuick.Controls::ComboBox::currentIndex + + This property holds the index of the current item in the combo box. + + \sa activated(), currentText, highlightedIndex +*/ +int QQuickComboBox::currentIndex() const +{ + Q_D(const QQuickComboBox); + return d->currentIndex; +} + +void QQuickComboBox::setCurrentIndex(int index) +{ + Q_D(QQuickComboBox); + d->hasCurrentIndex = true; + d->setCurrentIndex(index, NoActivate); +} + +/*! + \readonly + \qmlproperty string QtQuick.Controls::ComboBox::currentText + + This property holds the text of the current item in the combo box. + + \sa currentIndex, displayText, textRole +*/ +QString QQuickComboBox::currentText() const +{ + Q_D(const QQuickComboBox); + return d->currentText; +} + +/*! + \qmlproperty string QtQuick.Controls::ComboBox::displayText + + This property holds the text that is displayed on the combo box button. + + By default, the display text presents the current selection. That is, + it follows the text of the current item. However, the default display + text can be overridden with a custom value. + + \code + ComboBox { + currentIndex: 1 + displayText: "Size: " + currentText + model: ["S", "M", "L"] + } + \endcode + + \sa currentText, textRole +*/ +QString QQuickComboBox::displayText() const +{ + Q_D(const QQuickComboBox); + return d->displayText; +} + +void QQuickComboBox::setDisplayText(const QString &text) +{ + Q_D(QQuickComboBox); + d->hasDisplayText = true; + if (d->displayText == text) + return; + + d->displayText = text; + setAccessibleName(text); + emit displayTextChanged(); +} + +void QQuickComboBox::resetDisplayText() +{ + Q_D(QQuickComboBox); + if (!d->hasDisplayText) + return; + + d->hasDisplayText = false; + d->updateCurrentText(); +} + +/*! + \qmlproperty string QtQuick.Controls::ComboBox::textRole + + This property holds the model role used for populating the combo box. + + When the model has multiple roles, \c textRole can be set to determine + which role should be displayed. + + \sa model, currentText, displayText, {ComboBox Model Roles} +*/ +QString QQuickComboBox::textRole() const +{ + Q_D(const QQuickComboBox); + return d->textRole; +} + +void QQuickComboBox::setTextRole(const QString &role) +{ + Q_D(QQuickComboBox); + if (d->textRole == role) + return; + + d->textRole = role; + if (isComponentComplete()) + d->updateCurrentText(); + emit textRoleChanged(); +} + +/*! + \qmlproperty Component QtQuick.Controls::ComboBox::delegate + + This property holds a delegate that presents an item in the combo box popup. + + It is recommended to use \l ItemDelegate (or any other \l AbstractButton + derivatives) as the delegate. This ensures that the interaction works as + expected, and the popup will automatically close when appropriate. When + other types are used as the delegate, the popup must be closed manually. + For example, if \l MouseArea is used: + + \code + delegate: Rectangle { + // ... + MouseArea { + // ... + onClicked: comboBox.popup.close() + } + } + \endcode + + \sa ItemDelegate, {Customizing ComboBox} +*/ +QQmlComponent *QQuickComboBox::delegate() const +{ + Q_D(const QQuickComboBox); + return d->delegate; +} + +void QQuickComboBox::setDelegate(QQmlComponent* delegate) +{ + Q_D(QQuickComboBox); + if (d->delegate == delegate) + return; + + delete d->delegate; + d->delegate = delegate; + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->delegateModel); + if (delegateModel) + delegateModel->setDelegate(d->delegate); + emit delegateChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::ComboBox::indicator + + This property holds the drop indicator item. + + \sa {Customizing ComboBox} +*/ +QQuickItem *QQuickComboBox::indicator() const +{ + Q_D(const QQuickComboBox); + return d->indicator; +} + +void QQuickComboBox::setIndicator(QQuickItem *indicator) +{ + Q_D(QQuickComboBox); + if (d->indicator == indicator) + return; + + d->deleteDelegate(d->indicator); + d->indicator = indicator; + if (indicator) { + if (!indicator->parentItem()) + indicator->setParentItem(this); + } + emit indicatorChanged(); +} + +/*! + \qmlproperty Popup QtQuick.Controls::ComboBox::popup + + This property holds the popup. + + The popup can be opened or closed manually, if necessary: + + \code + onSpecialEvent: comboBox.popup.close() + \endcode + + \sa {Customizing ComboBox} +*/ +QQuickPopup *QQuickComboBox::popup() const +{ + Q_D(const QQuickComboBox); + return d->popup; +} + +void QQuickComboBox::setPopup(QQuickPopup *popup) +{ + Q_D(QQuickComboBox); + if (d->popup == popup) + return; + + if (d->popup) + QObjectPrivate::disconnect(d->popup, &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::updateHighlightedIndex); + d->deleteDelegate(d->popup); + if (popup) { + QQuickPopupPrivate::get(popup)->allowVerticalFlip = true; + popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent); + QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::updateHighlightedIndex); + } + d->popup = popup; + emit popupChanged(); +} + +/*! + \qmlmethod string QtQuick.Controls::ComboBox::textAt(int index) + + Returns the text for the specified \a index, or an empty string + if the index is out of bounds. + + \sa textRole +*/ +QString QQuickComboBox::textAt(int index) const +{ + Q_D(const QQuickComboBox); + if (!d->delegateModel || index < 0 || index >= d->delegateModel->count()) + return QString(); + + QString text; + QObject *object = d->delegateModel->object(index); + if (object) { + text = d->delegateModel->stringValue(index, d->textRole.isEmpty() ? QStringLiteral("modelData") : d->textRole); + d->delegateModel->release(object); + } + return text; +} + +/*! + \qmlmethod int QtQuick.Controls::ComboBox::find(string text, flags = Qt.MatchExactly) + + Returns the index of the specified \a text, or \c -1 if no match is found. + + The way the search is performed is defined by the specified match \a flags. By default, + combo box performs case sensitive exact matching (\c Qt.MatchExactly). All other match + types are case-insensitive unless the \c Qt.MatchCaseSensitive flag is also specified. + + \value Qt.MatchExactly The search term matches exactly (default). + \value Qt.MatchRegExp The search term matches as a regular expression. + \value Qt.MatchWildcard The search term matches using wildcards. + \value Qt.MatchFixedString The search term matches as a fixed string. + \value Qt.MatchStartsWith The search term matches the start of the item. + \value Qt.MatchEndsWidth The search term matches the end of the item. + \value Qt.MatchContains The search term is contained in the item. + \value Qt.MatchCaseSensitive The search is case sensitive. + + \sa textRole +*/ +int QQuickComboBox::find(const QString &text, Qt::MatchFlags flags) const +{ + Q_D(const QQuickComboBox); + return d->match(0, text, flags); +} + +/*! + \qmlmethod void QtQuick.Controls::ComboBox::incrementCurrentIndex() + + Increments the current index of the combo box, or the highlighted + index if the popup list is visible. + + \sa currentIndex, highlightedIndex +*/ +void QQuickComboBox::incrementCurrentIndex() +{ + Q_D(QQuickComboBox); + d->incrementCurrentIndex(); +} + +/*! + \qmlmethod void QtQuick.Controls::ComboBox::decrementCurrentIndex() + + Decrements the current index of the combo box, or the highlighted + index if the popup list is visible. + + \sa currentIndex, highlightedIndex +*/ +void QQuickComboBox::decrementCurrentIndex() +{ + Q_D(QQuickComboBox); + d->decrementCurrentIndex(); +} + +void QQuickComboBox::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickComboBox); + QQuickControl::focusOutEvent(event); + d->hidePopup(false); + setPressed(false); +} + +void QQuickComboBox::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickComboBox); + QQuickControl::keyPressEvent(event); + + switch (event->key()) { + case Qt::Key_Escape: + case Qt::Key_Back: + if (d->isPopupVisible()) + event->accept(); + break; + case Qt::Key_Space: + if (!event->isAutoRepeat()) + setPressed(true); + event->accept(); + break; + case Qt::Key_Enter: + case Qt::Key_Return: + if (d->isPopupVisible()) + setPressed(true); + event->accept(); + break; + case Qt::Key_Up: + d->decrementCurrentIndex(); + event->accept(); + break; + case Qt::Key_Down: + d->incrementCurrentIndex(); + event->accept(); + break; + case Qt::Key_Home: + if (d->isPopupVisible()) + d->setHighlightedIndex(0, Highlight); + else + d->setCurrentIndex(0, Activate); + event->accept(); + break; + case Qt::Key_End: + if (d->isPopupVisible()) + d->setHighlightedIndex(count() - 1, Highlight); + else + d->setCurrentIndex(count() - 1, Activate); + event->accept(); + break; + default: + if (!event->text().isEmpty()) + d->keySearch(event->text()); + else + event->ignore(); + break; + } +} + +void QQuickComboBox::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickComboBox); + QQuickControl::keyReleaseEvent(event); + if (!d->popup || event->isAutoRepeat()) + return; + + switch (event->key()) { + case Qt::Key_Space: + d->togglePopup(true); + setPressed(false); + event->accept(); + break; + case Qt::Key_Enter: + case Qt::Key_Return: + d->hidePopup(d->isPopupVisible()); + setPressed(false); + event->accept(); + break; + case Qt::Key_Escape: + case Qt::Key_Back: + d->hidePopup(false); + setPressed(false); + event->accept(); + break; + default: + break; + } +} + +void QQuickComboBox::mousePressEvent(QMouseEvent *event) +{ + QQuickControl::mousePressEvent(event); + setPressed(true); +} + +void QQuickComboBox::mouseMoveEvent(QMouseEvent* event) +{ + QQuickControl::mouseMoveEvent(event); + setPressed(contains(event->pos())); +} + +void QQuickComboBox::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickComboBox); + QQuickControl::mouseReleaseEvent(event); + if (d->pressed) { + setPressed(false); + d->togglePopup(false); + } +} + +void QQuickComboBox::mouseUngrabEvent() +{ + QQuickControl::mouseUngrabEvent(); + setPressed(false); +} + +void QQuickComboBox::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickComboBox); + QQuickControl::wheelEvent(event); + if (d->wheelEnabled && !d->isPopupVisible()) { + const int oldIndex = d->currentIndex; + if (event->angleDelta().y() > 0) + d->decrementCurrentIndex(); + else + d->incrementCurrentIndex(); + event->setAccepted(d->currentIndex != oldIndex); + } +} + +void QQuickComboBox::componentComplete() +{ + Q_D(QQuickComboBox); + QQuickControl::componentComplete(); + + if (d->delegateModel && d->ownModel) + static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete(); + + if (count() > 0) { + if (!d->hasCurrentIndex && d->currentIndex == -1) + setCurrentIndex(0); + else + d->updateCurrentText(); + } +} + +QFont QQuickComboBox::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ComboMenuItemFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickComboBox::accessibleRole() const +{ + return QAccessible::ComboBox; +} + +void QQuickComboBox::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickComboBox); + QQuickControl::accessibilityActiveChanged(active); + + if (active) + setAccessibleName(d->hasDisplayText ? d->displayText : d->currentText); +} +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickcombobox_p.h b/src/quicktemplates2/qquickcombobox_p.h new file mode 100644 index 00000000..4cbdd77b --- /dev/null +++ b/src/quicktemplates2/qquickcombobox_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCOMBOBOX_P_H +#define QQUICKCOMBOBOX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickPopup; +class QQmlInstanceModel; +class QQuickComboBoxPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged FINAL) + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged FINAL) + Q_PROPERTY(QQmlInstanceModel *delegateModel READ delegateModel NOTIFY delegateModelChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(int highlightedIndex READ highlightedIndex NOTIFY highlightedIndexChanged FINAL) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL) + Q_PROPERTY(QString currentText READ currentText NOTIFY currentTextChanged FINAL) + Q_PROPERTY(QString displayText READ displayText WRITE setDisplayText RESET resetDisplayText NOTIFY displayTextChanged FINAL) + Q_PROPERTY(QString textRole READ textRole WRITE setTextRole NOTIFY textRoleChanged FINAL) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL) + Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL) + Q_PROPERTY(QQuickPopup *popup READ popup WRITE setPopup NOTIFY popupChanged FINAL) + +public: + explicit QQuickComboBox(QQuickItem *parent = nullptr); + ~QQuickComboBox(); + + int count() const; + + QVariant model() const; + void setModel(const QVariant &model); + QQmlInstanceModel *delegateModel() const; + + bool isPressed() const; + void setPressed(bool pressed); + + int highlightedIndex() const; + + int currentIndex() const; + void setCurrentIndex(int index); + + QString currentText() const; + + QString displayText() const; + void setDisplayText(const QString &text); + void resetDisplayText(); + + QString textRole() const; + void setTextRole(const QString &role); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *delegate); + + QQuickItem *indicator() const; + void setIndicator(QQuickItem *indicator); + + QQuickPopup *popup() const; + void setPopup(QQuickPopup *popup); + + Q_INVOKABLE QString textAt(int index) const; + Q_INVOKABLE int find(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly) const; + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void countChanged(); + void modelChanged(); + void delegateModelChanged(); + void pressedChanged(); + void highlightedIndexChanged(); + void currentIndexChanged(); + void currentTextChanged(); + void displayTextChanged(); + void textRoleChanged(); + void delegateChanged(); + void indicatorChanged(); + void popupChanged(); + + void activated(int index); + void highlighted(int index); + +protected: + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void wheelEvent(QWheelEvent *event) override; + + void componentComplete() override; + + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; + void accessibilityActiveChanged(bool active) override; +#endif + +private: + Q_DISABLE_COPY(QQuickComboBox) + Q_DECLARE_PRIVATE(QQuickComboBox) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickComboBox) + +#endif // QQUICKCOMBOBOX_P_H diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp new file mode 100644 index 00000000..c95cfc75 --- /dev/null +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -0,0 +1,649 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontainer_p.h" +#include "qquickcontainer_p_p.h" + +#include <QtQuick/private/qquickflickable_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Container + \inherits Control + \instantiates QQuickContainer + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Abstract base type providing functionality common to containers. + + Container is the base type of container-like user interface controls that + allow dynamic insertion and removal of items. + + \section2 Using Containers + + 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. + + 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 + read-only \l currentItem property. + + The following example illustrates dynamic insertion of items to a \l TabBar, + which is one of the concrete implementations of Container. + + \code + Row { + TabBar { + id: tabBar + + currentIndex: 0 + width: parent.width - addButton.width + + TabButton { text: "TabButton" } + } + + Component { + id: tabButton + TabButton { text: "TabButton" } + } + + Button { + id: addButton + text: "+" + flat: true + onClicked: { + tabBar.addItem(tabButton.createObject(tabBar)) + console.log("added:", tabBar.itemAt(tabBar.count - 1)) + } + } + } + \endcode + + \section2 Implementing Containers + + Container does not provide any default visualization. It is used to implement + such containers as \l SwipeView and \l TabBar. When implementing a custom + container, the most important part of the API is \l contentModel, which provides + the contained items in a way that it can be used as a delegate model for item + views and repeaters. + + \code + Container { + id: container + + contentItem: ListView { + model: container.contentModel + snapMode: ListView.SnapOneItem + orientation: ListView.Horizontal + } + + Text { + text: "Page 1" + width: container.width + height: container.height + } + + Text { + text: "Page 2" + width: container.width + height: container.height + } + } + \endcode + + Notice how the sizes of the page items are set by hand. This is because the + example uses a plain Container, which does not make any assumptions on the + visual layout. It is typically not necessary to specify sizes for items in + concrete Container implementations, such as \l SwipeView and \l TabBar. + + \sa {Container Controls} +*/ + +static QQuickItem *effectiveContentItem(QQuickItem *item) +{ + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(item); + if (flickable) + return flickable->contentItem(); + return item; +} + +QQuickContainerPrivate::QQuickContainerPrivate() : contentModel(nullptr), currentIndex(-1), updatingCurrent(false), + changeTypes(Destroyed | Parent | SiblingOrder) +{ +} + +void QQuickContainerPrivate::init() +{ + Q_Q(QQuickContainer); + contentModel = new QQmlObjectModel(q); + QObject::connect(contentModel, &QQmlObjectModel::countChanged, q, &QQuickContainer::countChanged); + QObject::connect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged); +} + +void QQuickContainerPrivate::cleanup() +{ + Q_Q(QQuickContainer); + // ensure correct destruction order (QTBUG-46798) + const int count = contentModel->count(); + for (int i = 0; i < count; ++i) { + QQuickItem *item = itemAt(i); + if (item) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes); + } + + if (contentItem) { + QQuickItem *focusItem = QQuickItemPrivate::get(contentItem)->subFocusItem; + if (focusItem && window) + QQuickWindowPrivate::get(window)->clearFocusInScope(contentItem, focusItem, Qt::OtherFocusReason); + + q->contentItemChange(nullptr, contentItem); + delete contentItem; + } + + QObject::disconnect(contentModel, &QQmlObjectModel::countChanged, q, &QQuickContainer::countChanged); + QObject::disconnect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged); + delete contentModel; +} + +QQuickItem *QQuickContainerPrivate::itemAt(int index) const +{ + return qobject_cast<QQuickItem *>(contentModel->get(index)); +} + +void QQuickContainerPrivate::insertItem(int index, QQuickItem *item) +{ + Q_Q(QQuickContainer); + if (!q->isContent(item)) + return; + contentData.append(item); + + updatingCurrent = true; + + item->setParentItem(effectiveContentItem(contentItem)); + QQuickItemPrivate::get(item)->addItemChangeListener(this, changeTypes); + contentModel->insert(index, item); + + q->itemAdded(index, item); + + if (contentModel->count() == 1 && currentIndex == -1) + q->setCurrentIndex(index); + + updatingCurrent = false; +} + +void QQuickContainerPrivate::moveItem(int from, int to) +{ + Q_Q(QQuickContainer); + int oldCurrent = currentIndex; + contentModel->move(from, to); + + updatingCurrent = true; + + if (from == oldCurrent) + q->setCurrentIndex(to); + else if (from < oldCurrent && to >= oldCurrent) + q->setCurrentIndex(oldCurrent - 1); + else if (from > oldCurrent && to <= oldCurrent) + q->setCurrentIndex(oldCurrent + 1); + + updatingCurrent = false; +} + +void QQuickContainerPrivate::removeItem(int index, QQuickItem *item) +{ + Q_Q(QQuickContainer); + if (!q->isContent(item)) + return; + contentData.removeOne(item); + + updatingCurrent = true; + + bool currentChanged = false; + if (index == currentIndex) { + q->setCurrentIndex(currentIndex - 1); + } else if (index < currentIndex) { + --currentIndex; + currentChanged = true; + } + + QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes); + item->setParentItem(nullptr); + contentModel->remove(index); + + q->itemRemoved(index, item); + + if (currentChanged) + emit q->currentIndexChanged(); + + updatingCurrent = false; +} + +void QQuickContainerPrivate::_q_currentIndexChanged() +{ + Q_Q(QQuickContainer); + if (!updatingCurrent) + q->setCurrentIndex(contentItem ? contentItem->property("currentIndex").toInt() : -1); +} + +void QQuickContainerPrivate::itemChildAdded(QQuickItem *, QQuickItem *child) +{ + // add dynamically reparented items (eg. by a Repeater) + if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child)) + insertItem(contentModel->count(), child); +} + +void QQuickContainerPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) +{ + // remove dynamically unparented items (eg. by a Repeater) + if (!parent) + removeItem(contentModel->indexOf(item, nullptr), item); +} + +void QQuickContainerPrivate::itemSiblingOrderChanged(QQuickItem *) +{ + // reorder the restacked items (eg. by a Repeater) + Q_Q(QQuickContainer); + QList<QQuickItem *> siblings = effectiveContentItem(contentItem)->childItems(); + for (int i = 0; i < siblings.count(); ++i) { + QQuickItem* sibling = siblings.at(i); + int index = contentModel->indexOf(sibling, nullptr); + q->moveItem(index, i); + } +} + +void QQuickContainerPrivate::itemDestroyed(QQuickItem *item) +{ + int index = contentModel->indexOf(item, nullptr); + if (index != -1) + removeItem(index, item); +} + +void QQuickContainerPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); + QQuickItem *item = qobject_cast<QQuickItem *>(obj); + if (item) { + if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) + item->setParentItem(effectiveContentItem(p->contentItem)); + else if (p->contentModel->indexOf(item, nullptr) == -1) + q->addItem(item); + } else { + p->contentData.append(obj); + } +} + +int QQuickContainerPrivate::contentData_count(QQmlListProperty<QObject> *prop) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + return p->contentData.count(); +} + +QObject *QQuickContainerPrivate::contentData_at(QQmlListProperty<QObject> *prop, int index) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + return p->contentData.value(index); +} + +void QQuickContainerPrivate::contentData_clear(QQmlListProperty<QObject> *prop) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + p->contentData.clear(); +} + +void QQuickContainerPrivate::contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *item) +{ + QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); + q->addItem(item); +} + +int QQuickContainerPrivate::contentChildren_count(QQmlListProperty<QQuickItem> *prop) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + return p->contentModel->count(); +} + +QQuickItem *QQuickContainerPrivate::contentChildren_at(QQmlListProperty<QQuickItem> *prop, int index) +{ + QQuickContainer *q = static_cast<QQuickContainer *>(prop->object); + return q->itemAt(index); +} + +void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty<QQuickItem> *prop) +{ + QQuickContainerPrivate *p = static_cast<QQuickContainerPrivate *>(prop->data); + p->contentModel->clear(); +} + +QQuickContainer::QQuickContainer(QQuickItem *parent) : + QQuickControl(*(new QQuickContainerPrivate), parent) +{ + Q_D(QQuickContainer); + d->init(); +} + +QQuickContainer::QQuickContainer(QQuickContainerPrivate &dd, QQuickItem *parent) : + QQuickControl(dd, parent) +{ + Q_D(QQuickContainer); + d->init(); +} + +QQuickContainer::~QQuickContainer() +{ + Q_D(QQuickContainer); + d->cleanup(); +} + +/*! + \qmlproperty int QtQuick.Controls::Container::count + \readonly + + This property holds the number of items. +*/ +int QQuickContainer::count() const +{ + Q_D(const QQuickContainer); + return d->contentModel->count(); +} + +/*! + \qmlmethod Item QtQuick.Controls::Container::itemAt(int index) + + Returns the item at \a index, or \c null if it does not exist. +*/ +QQuickItem *QQuickContainer::itemAt(int index) const +{ + Q_D(const QQuickContainer); + return d->itemAt(index); +} + +/*! + \qmlmethod void QtQuick.Controls::Container::addItem(Item item) + + Adds an \a item. +*/ +void QQuickContainer::addItem(QQuickItem *item) +{ + Q_D(QQuickContainer); + insertItem(d->contentModel->count(), item); +} + +/*! + \qmlmethod void QtQuick.Controls::Container::insertItem(int index, Item item) + + Inserts an \a item at \a index. +*/ +void QQuickContainer::insertItem(int index, QQuickItem *item) +{ + Q_D(QQuickContainer); + if (!item) + return; + const int count = d->contentModel->count(); + if (index < 0 || index > count) + index = count; + + int oldIndex = d->contentModel->indexOf(item, nullptr); + if (oldIndex != -1) { + if (oldIndex < index) + --index; + if (oldIndex != index) + d->moveItem(oldIndex, index); + } else { + d->insertItem(index, item); + } +} + +/*! + \qmlmethod void QtQuick.Controls::Container::moveItem(int from, int to) + + Moves an item \a from one index \a to another. +*/ +void QQuickContainer::moveItem(int from, int to) +{ + Q_D(QQuickContainer); + const int count = d->contentModel->count(); + if (from < 0 || from > count - 1) + return; + if (to < 0 || to > count - 1) + to = count - 1; + + if (from != to) + d->moveItem(from, to); +} + +/*! + \qmlmethod void QtQuick.Controls::Container::removeItem(int index) + + Removes an item at \a index. + + \note The ownership of the item is transferred to the caller. +*/ +void QQuickContainer::removeItem(int index) +{ + Q_D(QQuickContainer); + const int count = d->contentModel->count(); + if (index < 0 || index >= count) + return; + + QQuickItem *item = itemAt(index); + if (item) + d->removeItem(index, item); +} + +/*! + \qmlproperty model QtQuick.Controls::Container::contentModel + \readonly + + This property holds the content model of items. + + 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 container. + + \code + Container { + id: container + contentItem: ListView { + model: container.contentModel + } + } + \endcode + + \sa contentData, contentChildren +*/ +QVariant QQuickContainer::contentModel() const +{ + Q_D(const QQuickContainer); + return QVariant::fromValue(d->contentModel); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::Container::contentData + \default + + This property holds the list of content data. + + The list contains all objects that have been declared in QML as children + of the container, 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> QQuickContainer::contentData() +{ + Q_D(QQuickContainer); + return QQmlListProperty<QObject>(this, d, + QQuickContainerPrivate::contentData_append, + QQuickContainerPrivate::contentData_count, + QQuickContainerPrivate::contentData_at, + QQuickContainerPrivate::contentData_clear); +} + +/*! + \qmlproperty list<Item> QtQuick.Controls::Container::contentChildren + + This property holds the list of content children. + + The list contains all items that have been declared in QML as children + of the container, and also items that have been dynamically added or + inserted using the \l addItem() and \l insertItem() methods, respectively. + + \note Unlike \c contentData, \c contentChildren does not include non-visual + QML objects. It is re-ordered when items are inserted or moved. + + \sa Item::children, contentData +*/ +QQmlListProperty<QQuickItem> QQuickContainer::contentChildren() +{ + Q_D(QQuickContainer); + return QQmlListProperty<QQuickItem>(this, d, + QQuickContainerPrivate::contentChildren_append, + QQuickContainerPrivate::contentChildren_count, + QQuickContainerPrivate::contentChildren_at, + QQuickContainerPrivate::contentChildren_clear); +} + +/*! + \qmlproperty int QtQuick.Controls::Container::currentIndex + + This property holds the index of the current item. + + \sa currentItem +*/ +int QQuickContainer::currentIndex() const +{ + Q_D(const QQuickContainer); + return d->currentIndex; +} + +void QQuickContainer::setCurrentIndex(int index) +{ + Q_D(QQuickContainer); + if (d->currentIndex == index) + return; + + d->currentIndex = index; + emit currentIndexChanged(); + emit currentItemChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Container::currentItem + \readonly + + This property holds the current item. + + \sa currentIndex +*/ +QQuickItem *QQuickContainer::currentItem() const +{ + Q_D(const QQuickContainer); + return itemAt(d->currentIndex); +} + +void QQuickContainer::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickContainer); + QQuickControl::itemChange(change, data); + if (change == QQuickItem::ItemChildAddedChange && isComponentComplete() && data.item != d->background && data.item != d->contentItem) { + if (!QQuickItemPrivate::get(data.item)->isTransparentForPositioner() && d->contentModel->indexOf(data.item, nullptr) == -1) + addItem(data.item); + } +} + +void QQuickContainer::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickContainer); + QQuickControl::contentItemChange(newItem, oldItem); + + static const int slotIndex = metaObject()->indexOfSlot("_q_currentIndexChanged()"); + + if (oldItem) { + QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children); + QQuickItem *oldContentItem = effectiveContentItem(oldItem); + if (oldContentItem != oldItem) + QQuickItemPrivate::get(oldContentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children); + + int signalIndex = oldItem->metaObject()->indexOfSignal("currentIndexChanged()"); + if (signalIndex != -1) + QMetaObject::disconnect(oldItem, signalIndex, this, slotIndex); + } + + if (newItem) { + QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children); + QQuickItem *newContentItem = effectiveContentItem(newItem); + if (newContentItem != newItem) + QQuickItemPrivate::get(newContentItem)->addItemChangeListener(d, QQuickItemPrivate::Children); + + int signalIndex = newItem->metaObject()->indexOfSignal("currentIndexChanged()"); + if (signalIndex != -1) + QMetaObject::connect(newItem, signalIndex, this, slotIndex); + } +} + +bool QQuickContainer::isContent(QQuickItem *item) const +{ + // If the item has a QML context associated to it (it was created in QML), + // we add it to the content model. Otherwise, it's probably the default + // highlight item that is always created by the item views, which we need + // to exclude. + // + // TODO: Find a better way to identify/exclude the highlight item... + return qmlContext(item); +} + +void QQuickContainer::itemAdded(int index, QQuickItem *item) +{ + Q_UNUSED(index); + Q_UNUSED(item); +} + +void QQuickContainer::itemRemoved(int index, QQuickItem *item) +{ + Q_UNUSED(index); + Q_UNUSED(item); +} + +QT_END_NAMESPACE + +#include "moc_qquickcontainer_p.cpp" diff --git a/src/quicktemplates2/qquickcontainer_p.h b/src/quicktemplates2/qquickcontainer_p.h new file mode 100644 index 00000000..2ed30b77 --- /dev/null +++ b/src/quicktemplates2/qquickcontainer_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTAINER_P_H +#define QQUICKCONTAINER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +class QQuickContainerPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainer : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged FINAL) + Q_PROPERTY(QVariant contentModel READ contentModel CONSTANT FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickContainer(QQuickItem *parent = nullptr); + ~QQuickContainer(); + + int count() const; + Q_INVOKABLE QQuickItem *itemAt(int index) const; + Q_INVOKABLE void addItem(QQuickItem *item); + Q_INVOKABLE void insertItem(int index, QQuickItem *item); + Q_INVOKABLE void moveItem(int from, int to); + Q_INVOKABLE void removeItem(int index); + + QVariant contentModel() const; + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + + int currentIndex() const; + QQuickItem *currentItem() const; + +public Q_SLOTS: + void setCurrentIndex(int index); + +Q_SIGNALS: + void countChanged(); + void contentChildrenChanged(); + void currentIndexChanged(); + void currentItemChanged(); + +protected: + QQuickContainer(QQuickContainerPrivate &dd, QQuickItem *parent); + + void itemChange(ItemChange change, const ItemChangeData &data) override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + + virtual bool isContent(QQuickItem *item) const; + virtual void itemAdded(int index, QQuickItem *item); + virtual void itemRemoved(int index, QQuickItem *item); + +private: + Q_DISABLE_COPY(QQuickContainer) + Q_DECLARE_PRIVATE(QQuickContainer) + Q_PRIVATE_SLOT(d_func(), void _q_currentIndexChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickContainer) + +#endif // QQUICKCONTAINER_P_H diff --git a/src/quicktemplates2/qquickcontainer_p_p.h b/src/quicktemplates2/qquickcontainer_p_p.h new file mode 100644 index 00000000..3b60ad8c --- /dev/null +++ b/src/quicktemplates2/qquickcontainer_p_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTAINER_P_P_H +#define QQUICKCONTAINER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQml/private/qqmlobjectmodel_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainerPrivate : public QQuickControlPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickContainer) + +public: + QQuickContainerPrivate(); + + void init(); + void cleanup(); + + QQuickItem *itemAt(int index) const; + void insertItem(int index, QQuickItem *item); + void moveItem(int from, int to); + void removeItem(int index, QQuickItem *item); + + void _q_currentIndexChanged(); + + void itemChildAdded(QQuickItem *item, QQuickItem *child) override; + void itemSiblingOrderChanged(QQuickItem *item) override; + void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; + void itemDestroyed(QQuickItem *item) override; + + static void contentData_append(QQmlListProperty<QObject> *prop, QObject *obj); + static int contentData_count(QQmlListProperty<QObject> *prop); + static QObject *contentData_at(QQmlListProperty<QObject> *prop, int index); + static void contentData_clear(QQmlListProperty<QObject> *prop); + + static void contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *obj); + static int contentChildren_count(QQmlListProperty<QQuickItem> *prop); + static QQuickItem *contentChildren_at(QQmlListProperty<QQuickItem> *prop, int index); + static void contentChildren_clear(QQmlListProperty<QQuickItem> *prop); + + QObjectList contentData; + QQmlObjectModel *contentModel; + int currentIndex; + bool updatingCurrent; + QQuickItemPrivate::ChangeTypes changeTypes; +}; + +QT_END_NAMESPACE + +#endif // QQUICKCONTAINER_P_P_H diff --git a/src/quicktemplates2/qquickcontrol.cpp b/src/quicktemplates2/qquickcontrol.cpp new file mode 100644 index 00000000..cb891ee9 --- /dev/null +++ b/src/quicktemplates2/qquickcontrol.cpp @@ -0,0 +1,1226 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontrol_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/qguiapplication.h> +#include "qquicklabel_p.h" +#include "qquicklabel_p_p.h" +#include "qquicktextarea_p.h" +#include "qquicktextarea_p_p.h" +#include "qquicktextfield_p.h" +#include "qquicktextfield_p_p.h" +#include "qquickpopup_p.h" +#include "qquickpopup_p_p.h" +#include "qquickapplicationwindow_p.h" + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformtheme.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtQuick/private/qquickaccessibleattached_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Control + \inherits Item + \instantiates QQuickControl + \inqmlmodule QtQuick.Controls + \since 5.7 + \brief Abstract base type providing functionality common to all controls. + + Control is the base type of user interface controls. It receives input + 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 + \l ApplicationWindow, items underneath it will not get mouse or touch + events. + + \sa ApplicationWindow, Container +*/ + +static bool isKeyFocusReason(Qt::FocusReason reason) +{ + return reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason || reason == Qt::ShortcutFocusReason; +} + +QQuickControlPrivate::ExtraData::ExtraData() +{ +} + +QQuickControlPrivate::QQuickControlPrivate() : + hasTopPadding(false), hasLeftPadding(false), hasRightPadding(false), hasBottomPadding(false), hasLocale(false), hovered(false), wheelEnabled(false), + padding(0), topPadding(0), leftPadding(0), rightPadding(0), bottomPadding(0), spacing(0), + focusPolicy(Qt::NoFocus), focusReason(Qt::OtherFocusReason), + background(nullptr), contentItem(nullptr), accessibleAttached(nullptr) +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installActivationObserver(this); +#endif +} + +QQuickControlPrivate::~QQuickControlPrivate() +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::removeActivationObserver(this); +#endif +} + +void QQuickControlPrivate::mirrorChange() +{ + Q_Q(QQuickControl); + if (locale.textDirection() == Qt::LeftToRight) + q->mirrorChange(); +} + +void QQuickControlPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickControl); + qreal oldPadding = q->topPadding(); + topPadding = value; + hasTopPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->topPaddingChanged(); + emit q->availableHeightChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, oldPadding, rightPadding, bottomPadding)); + } +} + +void QQuickControlPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickControl); + qreal oldPadding = q->leftPadding(); + leftPadding = value; + hasLeftPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->leftPaddingChanged(); + emit q->availableWidthChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(oldPadding, topPadding, rightPadding, bottomPadding)); + } +} + +void QQuickControlPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickControl); + qreal oldPadding = q->rightPadding(); + rightPadding = value; + hasRightPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->rightPaddingChanged(); + emit q->availableWidthChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, topPadding, oldPadding, bottomPadding)); + } +} + +void QQuickControlPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickControl); + qreal oldPadding = q->bottomPadding(); + bottomPadding = value; + hasBottomPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->bottomPaddingChanged(); + emit q->availableHeightChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, topPadding, rightPadding, oldPadding)); + } +} + +void QQuickControlPrivate::resizeBackground() +{ + Q_Q(QQuickControl); + if (background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(background); + if (!p->widthValid && qFuzzyIsNull(background->x())) { + background->setWidth(q->width()); + p->widthValid = false; + } + if (!p->heightValid && qFuzzyIsNull(background->y())) { + background->setHeight(q->height()); + p->heightValid = false; + } + } +} + +void QQuickControlPrivate::resizeContent() +{ + Q_Q(QQuickControl); + if (contentItem) { + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight())); + } +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickControlPrivate::accessibilityActiveChanged(bool active) +{ + Q_Q(QQuickControl); + return q->accessibilityActiveChanged(active); +} + +QAccessible::Role QQuickControlPrivate::accessibleRole() const +{ + Q_Q(const QQuickControl); + return q->accessibleRole(); +} + +QAccessible::Role QQuickControl::accessibleRole() const +{ + return QAccessible::NoRole; +} + +void QQuickControl::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickControl); + if (d->accessibleAttached || !active) + return; + + d->accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, true)); + + // QQuickControl relies on the existence of a QQuickAccessibleAttached object. + // However, qmlAttachedPropertiesObject(create=true) creates an instance only + // for items that have been created by a QML engine. Therefore we create the + // object by hand for items created in C++ (QQuickPopupItem, for instance). + if (!d->accessibleAttached) + d->accessibleAttached = new QQuickAccessibleAttached(this); + + d->accessibleAttached->setRole(accessibleRole()); +} +#endif + +/*! + \internal + + Returns the font that the control w inherits from its ancestors and + QGuiApplication::font. +*/ +QFont QQuickControlPrivate::parentFont(const QQuickItem *item) +{ + QQuickItem *p = item->parentItem(); + while (p) { + if (QQuickControl *control = qobject_cast<QQuickControl *>(p)) + return control->font(); + else if (QQuickLabel *label = qobject_cast<QQuickLabel *>(p)) + return label->font(); + else if (QQuickTextField *textField = qobject_cast<QQuickTextField *>(p)) + return textField->font(); + else if (QQuickTextArea *textArea = qobject_cast<QQuickTextArea *>(p)) + return textArea->font(); + + p = p->parentItem(); + } + + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(item->window())) + return window->font(); + + return themeFont(QPlatformTheme::SystemFont); +} + +QFont QQuickControlPrivate::themeFont(QPlatformTheme::Font type) +{ + if (QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + if (const QFont *font = theme->font(type)) { + QFont f = *font; + if (type == QPlatformTheme::SystemFont) + f.resolve(0); + return f; + } + } + + return QFont(); +} + +/*! + \internal + + Determine which font is implicitly imposed on this control by its ancestors + and QGuiApplication::font, resolve this against its own font (attributes from + the implicit font are copied over). Then propagate this font to this + control's children. +*/ +void QQuickControlPrivate::resolveFont() +{ + Q_Q(QQuickControl); + inheritFont(parentFont(q)); +} + +void QQuickControlPrivate::inheritFont(const QFont &f) +{ + Q_Q(QQuickControl); + QFont parentFont = extra.isAllocated() ? extra->font.resolve(f) : f; + parentFont.resolve(extra.isAllocated() ? extra->font.resolve() | f.resolve() : f.resolve()); + + const QFont defaultFont = q->defaultFont(); + const QFont resolvedFont = parentFont.resolve(defaultFont); + + setFont_helper(resolvedFont); +} + +/*! + \internal + + Assign \a font to this control, and propagate it to all children. +*/ +void QQuickControlPrivate::updateFont(const QFont &f) +{ + Q_Q(QQuickControl); + QFont old = resolvedFont; + resolvedFont = f; + + if (old != f) + q->fontChange(f, old); + + QQuickControlPrivate::updateFontRecur(q, f); + + if (old != f) + emit q->fontChanged(); +} + +void QQuickControlPrivate::updateFontRecur(QQuickItem *item, const QFont &f) +{ + const auto childItems = item->childItems(); + for (QQuickItem *child : childItems) { + if (QQuickControl *control = qobject_cast<QQuickControl *>(child)) + QQuickControlPrivate::get(control)->inheritFont(f); + else if (QQuickLabel *label = qobject_cast<QQuickLabel *>(child)) + QQuickLabelPrivate::get(label)->inheritFont(f); + else if (QQuickTextArea *textArea = qobject_cast<QQuickTextArea *>(child)) + QQuickTextAreaPrivate::get(textArea)->inheritFont(f); + else if (QQuickTextField *textField = qobject_cast<QQuickTextField *>(child)) + QQuickTextFieldPrivate::get(textField)->inheritFont(f); + else + QQuickControlPrivate::updateFontRecur(child, f); + } +} + +QString QQuickControl::accessibleName() const +{ +#ifndef QT_NO_ACCESSIBILITY + Q_D(const QQuickControl); + if (d->accessibleAttached) + return d->accessibleAttached->name(); +#endif + return QString(); +} + +void QQuickControl::setAccessibleName(const QString &name) +{ +#ifndef QT_NO_ACCESSIBILITY + Q_D(QQuickControl); + if (d->accessibleAttached) + d->accessibleAttached->setName(name); +#else + Q_UNUSED(name) +#endif +} + +QVariant QQuickControl::accessibleProperty(const char *propertyName) +{ +#ifndef QT_NO_ACCESSIBILITY + Q_D(QQuickControl); + if (d->accessibleAttached) + return QQuickAccessibleAttached::property(this, propertyName); +#endif + Q_UNUSED(propertyName) + return QVariant(); +} + +bool QQuickControl::setAccessibleProperty(const char *propertyName, const QVariant &value) +{ +#ifndef QT_NO_ACCESSIBILITY + Q_D(QQuickControl); + if (d->accessibleAttached) + return QQuickAccessibleAttached::setProperty(this, propertyName, value); +#endif + Q_UNUSED(propertyName) + Q_UNUSED(value) + return false; +} + +QQuickControl::QQuickControl(QQuickItem *parent) : + QQuickItem(*(new QQuickControlPrivate), parent) +{ +} + +QQuickControl::QQuickControl(QQuickControlPrivate &dd, QQuickItem *parent) : + QQuickItem(dd, parent) +{ +} + +void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + Q_D(QQuickControl); + QQuickItem::itemChange(change, value); + switch (change) { + case ItemVisibleHasChanged: + if (!value.boolValue) + setHovered(false); + break; + case ItemParentHasChanged: + if (value.item) { + d->resolveFont(); + if (!d->hasLocale) + d->updateLocale(QQuickControlPrivate::calcLocale(d->parentItem), false); // explicit=false + } + break; + case ItemActiveFocusHasChanged: + if (isKeyFocusReason(d->focusReason)) + emit visualFocusChanged(); + break; + default: + break; + } +} + +/*! + \qmlproperty font QtQuick.Controls::Control::font + + This property holds the font currently set for the control. + + This property describes the control's requested font. The font is used by the control's + style when rendering standard components, and is available as a means to ensure that custom + controls can maintain consistency with the native platform's native look and feel. It's common + that different platforms, or different styles, define different fonts for an application. + + The default font depends on the system environment. ApplicationWindow maintains a system/theme + font which serves as a default for all controls. There may also be special font defaults for + certain types of controls. You can also set the default font for controls by passing a custom + font to QGuiApplication::setFont(), before loading the QML. Finally, the font is matched + against Qt's font database to find the best match. + + 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 +{ + Q_D(const QQuickControl); + return d->resolvedFont; +} + +void QQuickControl::setFont(const QFont &font) +{ + Q_D(QQuickControl); + if (d->extra.value().font.resolve() == font.resolve() && d->extra.value().font == font) + return; + + d->extra.value().font = font; + d->resolveFont(); +} + +void QQuickControl::resetFont() +{ + setFont(QFont()); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::availableWidth + \readonly + + This property holds the width available to the \l contentItem after + deducting horizontal padding from the \l {Item::}{width} of the control. + + \sa {Control Layout}, padding, leftPadding, rightPadding +*/ +qreal QQuickControl::availableWidth() const +{ + return qMax<qreal>(0.0, width() - leftPadding() - rightPadding()); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::availableHeight + \readonly + + This property holds the height available to the \l contentItem after + deducting vertical padding from the \l {Item::}{height} of the control. + + \sa {Control Layout}, padding, topPadding, bottomPadding +*/ +qreal QQuickControl::availableHeight() const +{ + return qMax<qreal>(0.0, height() - topPadding() - bottomPadding()); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::padding + + This property holds the default padding. + + 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 +{ + Q_D(const QQuickControl); + return d->padding; +} + +void QQuickControl::setPadding(qreal padding) +{ + Q_D(QQuickControl); + if (qFuzzyCompare(d->padding, padding)) + return; + QMarginsF oldPadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); + d->padding = padding; + emit paddingChanged(); + QMarginsF newPadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); + if (!qFuzzyCompare(newPadding.top(), oldPadding.top())) + emit topPaddingChanged(); + if (!qFuzzyCompare(newPadding.left(), oldPadding.left())) + emit leftPaddingChanged(); + if (!qFuzzyCompare(newPadding.right(), oldPadding.right())) + emit rightPaddingChanged(); + if (!qFuzzyCompare(newPadding.bottom(), oldPadding.bottom())) + emit bottomPaddingChanged(); + if (!qFuzzyCompare(newPadding.top(), oldPadding.top()) || !qFuzzyCompare(newPadding.bottom(), oldPadding.bottom())) + emit availableHeightChanged(); + if (!qFuzzyCompare(newPadding.left(), oldPadding.left()) || !qFuzzyCompare(newPadding.right(), oldPadding.right())) + emit availableWidthChanged(); + paddingChange(newPadding, oldPadding); +} + +void QQuickControl::resetPadding() +{ + setPadding(0); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::topPadding + + This property holds the top padding. + + \sa {Control Layout}, padding, bottomPadding, availableHeight +*/ +qreal QQuickControl::topPadding() const +{ + Q_D(const QQuickControl); + if (d->hasTopPadding) + return d->topPadding; + return d->padding; +} + +void QQuickControl::setTopPadding(qreal padding) +{ + Q_D(QQuickControl); + d->setTopPadding(padding); +} + +void QQuickControl::resetTopPadding() +{ + Q_D(QQuickControl); + d->setTopPadding(0, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::leftPadding + + This property holds the left padding. + + \sa {Control Layout}, padding, rightPadding, availableWidth +*/ +qreal QQuickControl::leftPadding() const +{ + Q_D(const QQuickControl); + if (d->hasLeftPadding) + return d->leftPadding; + return d->padding; +} + +void QQuickControl::setLeftPadding(qreal padding) +{ + Q_D(QQuickControl); + d->setLeftPadding(padding); +} + +void QQuickControl::resetLeftPadding() +{ + Q_D(QQuickControl); + d->setLeftPadding(0, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::rightPadding + + This property holds the right padding. + + \sa {Control Layout}, padding, leftPadding, availableWidth +*/ +qreal QQuickControl::rightPadding() const +{ + Q_D(const QQuickControl); + if (d->hasRightPadding) + return d->rightPadding; + return d->padding; +} + +void QQuickControl::setRightPadding(qreal padding) +{ + Q_D(QQuickControl); + d->setRightPadding(padding); +} + +void QQuickControl::resetRightPadding() +{ + Q_D(QQuickControl); + d->setRightPadding(0, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Control::bottomPadding + + This property holds the bottom padding. + + \sa {Control Layout}, padding, topPadding, availableHeight +*/ +qreal QQuickControl::bottomPadding() const +{ + Q_D(const QQuickControl); + if (d->hasBottomPadding) + return d->bottomPadding; + return d->padding; +} + +void QQuickControl::setBottomPadding(qreal padding) +{ + Q_D(QQuickControl); + d->setBottomPadding(padding); +} + +void QQuickControl::resetBottomPadding() +{ + Q_D(QQuickControl); + d->setBottomPadding(0, true); +} + +/*! + \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 +{ + Q_D(const QQuickControl); + return d->spacing; +} + +void QQuickControl::setSpacing(qreal spacing) +{ + Q_D(QQuickControl); + if (!qFuzzyCompare(d->spacing, spacing)) { + d->spacing = spacing; + emit spacingChanged(); + } +} + +void QQuickControl::resetSpacing() +{ + setSpacing(0); +} + +/*! + \qmlproperty Locale QtQuick.Controls::Control::locale + + This property holds the locale of the control. + It contains locale specific properties for formatting data and numbers. + Unless a special locale has been set, this is either the parent's locale + or the default locale. + + Control propagates explicit locale properties from parent to children. + If you change a specific property on a control's locale, that property + propagates to all of the control's children, overriding any system defaults + for that property. + + \sa mirrored, {LayoutMirroring}{LayoutMirroring} +*/ +QLocale QQuickControl::locale() const +{ + Q_D(const QQuickControl); + return d->locale; +} + +void QQuickControl::setLocale(const QLocale &locale) +{ + Q_D(QQuickControl); + if (d->hasLocale && d->locale == locale) + return; + + d->updateLocale(locale, true); // explicit=true +} + +void QQuickControl::resetLocale() +{ + Q_D(QQuickControl); + if (!d->hasLocale) + return; + + d->hasLocale = false; + d->updateLocale(QQuickControlPrivate::calcLocale(d->parentItem), false); // explicit=false +} + +QLocale QQuickControlPrivate::calcLocale(const QQuickItem *item) +{ + const QQuickItem *p = item; + while (p) { + if (const QQuickControl *control = qobject_cast<const QQuickControl *>(p)) + return control->locale(); + + QVariant v = p->property("locale"); + if (v.isValid() && v.userType() == QMetaType::QLocale) + return v.toLocale(); + + p = p->parentItem(); + } + + if (item) { + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(item->window())) + return window->locale(); + } + + return QLocale(); +} + +/* + Deletes "delegate" if Component.completed() has been emitted, + otherwise stores it in pendingDeletions. +*/ +void QQuickControlPrivate::deleteDelegate(QObject *delegate) +{ + if (componentComplete) + delete delegate; + else + extra.value().pendingDeletions.append(delegate); +} + +void QQuickControlPrivate::updateLocale(const QLocale &l, bool e) +{ + Q_Q(QQuickControl); + if (!e && hasLocale) + return; + + QLocale old = q->locale(); + hasLocale = e; + if (old != l) { + bool wasMirrored = q->isMirrored(); + q->localeChange(l, old); + locale = l; + QQuickControlPrivate::updateLocaleRecur(q, l); + emit q->localeChanged(); + if (wasMirrored != q->isMirrored()) + q->mirrorChange(); + } +} + +void QQuickControlPrivate::updateLocaleRecur(QQuickItem *item, const QLocale &l) +{ + const auto childItems = item->childItems(); + for (QQuickItem *child : childItems) { + if (QQuickControl *control = qobject_cast<QQuickControl *>(child)) + QQuickControlPrivate::get(control)->updateLocale(l, false); + else + updateLocaleRecur(child, l); + } +} + +/*! + \qmlproperty bool QtQuick.Controls::Control::mirrored + \readonly + + 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; that is, when using a + right-to-left locale or when \l {LayoutMirroring::enabled}{LayoutMirroring.enabled} + is \c true. + + \sa locale, {LayoutMirroring}{LayoutMirroring}, {Right-to-left User Interfaces} +*/ +bool QQuickControl::isMirrored() const +{ + Q_D(const QQuickControl); + return d->isMirrored() || d->locale.textDirection() == Qt::RightToLeft; +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Control::focusPolicy + + This property determines the way the control accepts focus. + + \value Qt.TabFocus The control accepts focus by tabbing. + \value Qt.ClickFocus The control accepts focus by clicking. + \value Qt.StrongFocus The control accepts focus by both tabbing and clicking. + \value Qt.WheelFocus The control accepts focus by tabbing, clicking, and using the mouse wheel. + \value Qt.NoFocus The control does not accept focus. +*/ +Qt::FocusPolicy QQuickControl::focusPolicy() const +{ + Q_D(const QQuickControl); + uint policy = d->focusPolicy; + if (activeFocusOnTab()) + policy |= Qt::TabFocus; + return static_cast<Qt::FocusPolicy>(policy); +} + +void QQuickControl::setFocusPolicy(Qt::FocusPolicy policy) +{ + Q_D(QQuickControl); + if (d->focusPolicy == policy) + return; + + d->focusPolicy = policy; + setActiveFocusOnTab(policy & Qt::TabFocus); + emit focusPolicyChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Control::focusReason + \readonly + + \include qquickcontrol-focusreason.qdocinc + + \sa visualFocus +*/ +Qt::FocusReason QQuickControl::focusReason() const +{ + Q_D(const QQuickControl); + return d->focusReason; +} + +void QQuickControl::setFocusReason(Qt::FocusReason reason) +{ + Q_D(QQuickControl); + if (d->focusReason == reason) + return; + + Qt::FocusReason oldReason = d->focusReason; + d->focusReason = reason; + emit focusReasonChanged(); + if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason)) + emit visualFocusChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Control::visualFocus + \readonly + + This property holds whether the control has visual focus. This property + is \c true when the control has active focus and the focus reason is either + \c Qt.TabFocusReason, \c Qt.BacktabFocusReason, or \c Qt.ShortcutFocusReason. + + In general, for visualizing key focus, this property is preferred over + \l Item::activeFocus. This ensures that key focus is only visualized when + interacting with keys - not when interacting via touch or mouse. + + \sa focusReason, Item::activeFocus +*/ +bool QQuickControl::hasVisualFocus() const +{ + Q_D(const QQuickControl); + return d->activeFocus && isKeyFocusReason(d->focusReason); +} + +/*! + \qmlproperty bool QtQuick.Controls::Control::hovered + \readonly + + This property holds whether the control is hovered. + + \sa hoverEnabled +*/ +bool QQuickControl::isHovered() const +{ + Q_D(const QQuickControl); + return d->hovered; +} + +void QQuickControl::setHovered(bool hovered) +{ + Q_D(QQuickControl); + if (hovered == d->hovered) + return; + + d->hovered = hovered; + emit hoveredChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Control::hoverEnabled + + This property determines whether the control accepts hover events. The default value is \c false. + + \sa hovered +*/ +bool QQuickControl::isHoverEnabled() const +{ + Q_D(const QQuickControl); + return d->hoverEnabled; +} + +void QQuickControl::setHoverEnabled(bool enabled) +{ + Q_D(QQuickControl); + if (enabled == d->hoverEnabled) + return; + + setAcceptHoverEvents(enabled); + emit hoverEnabledChanged(); +} + +/*! + \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 +{ + Q_D(const QQuickControl); + return d->wheelEnabled; +} + +void QQuickControl::setWheelEnabled(bool enabled) +{ + Q_D(QQuickControl); + if (d->wheelEnabled == enabled) + return; + + d->wheelEnabled = enabled; + emit wheelEnabledChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Control::background + + 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 + + \input qquickcontrol-background.qdocinc notes + + \sa {Control Layout} +*/ +QQuickItem *QQuickControl::background() const +{ + Q_D(const QQuickControl); + return d->background; +} + +void QQuickControl::setBackground(QQuickItem *background) +{ + Q_D(QQuickControl); + if (d->background == background) + return; + + d->deleteDelegate(d->background); + d->background = background; + if (background) { + background->setParentItem(this); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + if (isComponentComplete()) + d->resizeBackground(); + } + emit backgroundChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Control::contentItem + + This property holds the visual content item. + + \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 +{ + Q_D(const QQuickControl); + return d->contentItem; +} + +void QQuickControl::setContentItem(QQuickItem *item) +{ + Q_D(QQuickControl); + if (d->contentItem == item) + return; + + contentItemChange(item, d->contentItem); + d->deleteDelegate(d->contentItem); + d->contentItem = item; + if (item) { + if (!item->parentItem()) + item->setParentItem(this); + if (isComponentComplete()) + d->resizeContent(); + } + emit contentItemChanged(); +} + +void QQuickControl::classBegin() +{ + Q_D(QQuickControl); + QQuickItem::classBegin(); + d->resolveFont(); +} + +void QQuickControl::componentComplete() +{ + Q_D(QQuickControl); + QQuickItem::componentComplete(); + if (!d->hasLocale) + d->locale = QQuickControlPrivate::calcLocale(d->parentItem); +#ifndef QT_NO_ACCESSIBILITY + if (!d->accessibleAttached && QAccessible::isActive()) + accessibilityActiveChanged(true); +#endif + + if (d->extra.isAllocated()) { + qDeleteAll(d->extra.value().pendingDeletions); + d->extra.value().pendingDeletions.clear(); + } +} + +QFont QQuickControl::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::SystemFont); +} + +void QQuickControl::focusInEvent(QFocusEvent *event) +{ + QQuickItem::focusInEvent(event); + setFocusReason(event->reason()); +} + +void QQuickControl::focusOutEvent(QFocusEvent *event) +{ + QQuickItem::focusOutEvent(event); + setFocusReason(event->reason()); +} + +void QQuickControl::hoverEnterEvent(QHoverEvent *event) +{ + Q_D(QQuickControl); + setHovered(d->hoverEnabled); + event->setAccepted(d->hoverEnabled); +} + +void QQuickControl::hoverMoveEvent(QHoverEvent *event) +{ + Q_D(QQuickControl); + setHovered(d->hoverEnabled && contains(event->pos())); + event->setAccepted(d->hoverEnabled); +} + +void QQuickControl::hoverLeaveEvent(QHoverEvent *event) +{ + Q_D(QQuickControl); + setHovered(false); + event->setAccepted(d->hoverEnabled); +} + +void QQuickControl::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickControl); + if ((d->focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) + forceActiveFocus(Qt::MouseFocusReason); + + event->accept(); +} + +void QQuickControl::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickControl); + setHovered(d->hoverEnabled && contains(event->pos())); + event->accept(); +} + +void QQuickControl::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickControl); + if ((d->focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) + forceActiveFocus(Qt::MouseFocusReason); + + event->accept(); +} + +void QQuickControl::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickControl); + if ((d->focusPolicy & Qt::WheelFocus) == Qt::WheelFocus) + forceActiveFocus(Qt::MouseFocusReason); + + event->setAccepted(d->wheelEnabled); +} + +void QQuickControl::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickControl); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + d->resizeBackground(); + d->resizeContent(); + if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) + emit availableWidthChanged(); + if (!qFuzzyCompare(newGeometry.height(), oldGeometry.height())) + emit availableHeightChanged(); +} + +void QQuickControl::fontChange(const QFont &newFont, const QFont &oldFont) +{ + Q_UNUSED(newFont); + Q_UNUSED(oldFont); +} + +void QQuickControl::mirrorChange() +{ + emit mirroredChanged(); +} + +void QQuickControl::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) +{ + Q_D(QQuickControl); + Q_UNUSED(newPadding); + Q_UNUSED(oldPadding); + d->resizeContent(); +} + +void QQuickControl::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_UNUSED(newItem); + Q_UNUSED(oldItem); +} + +void QQuickControl::localeChange(const QLocale &newLocale, const QLocale &oldLocale) +{ + Q_UNUSED(newLocale); + Q_UNUSED(oldLocale); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickcontrol_p.h b/src/quicktemplates2/qquickcontrol_p.h new file mode 100644 index 00000000..d9827017 --- /dev/null +++ b/src/quicktemplates2/qquickcontrol_p.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTROL_P_H +#define QQUICKCONTROL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qlocale.h> +#include <QtQuick/qquickitem.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickControlPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControl : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QFont font READ font WRITE setFont RESET resetFont NOTIFY fontChanged FINAL) + Q_PROPERTY(qreal availableWidth READ availableWidth NOTIFY availableWidthChanged FINAL) + Q_PROPERTY(qreal availableHeight READ availableHeight NOTIFY availableHeightChanged FINAL) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged FINAL) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged FINAL) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged FINAL) + Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged FINAL) + Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged FINAL) + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET resetSpacing NOTIFY spacingChanged FINAL) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL) + Q_PROPERTY(bool mirrored READ isMirrored NOTIFY mirroredChanged FINAL) + Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL) + Q_PROPERTY(Qt::FocusReason focusReason READ focusReason WRITE setFocusReason NOTIFY focusReasonChanged FINAL) + Q_PROPERTY(bool visualFocus READ hasVisualFocus NOTIFY visualFocusChanged FINAL) + Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL) + Q_PROPERTY(bool hoverEnabled READ isHoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged FINAL) + Q_PROPERTY(bool wheelEnabled READ isWheelEnabled WRITE setWheelEnabled NOTIFY wheelEnabledChanged FINAL) + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged FINAL) + +public: + explicit QQuickControl(QQuickItem *parent = nullptr); + + QFont font() const; + void setFont(const QFont &font); + void resetFont(); + + qreal availableWidth() const; + qreal availableHeight() const; + + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + + qreal spacing() const; + void setSpacing(qreal spacing); + void resetSpacing(); + + QLocale locale() const; + void setLocale(const QLocale &locale); + void resetLocale(); + + bool isMirrored() const; + + Qt::FocusPolicy focusPolicy() const; + void setFocusPolicy(Qt::FocusPolicy policy); + + Qt::FocusReason focusReason() const; + void setFocusReason(Qt::FocusReason reason); + + bool hasVisualFocus() const; + + bool isHovered() const; + void setHovered(bool hovered); + + bool isHoverEnabled() const; + void setHoverEnabled(bool enabled); + + bool isWheelEnabled() const; + void setWheelEnabled(bool enabled); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + + QQuickItem *contentItem() const; + void setContentItem(QQuickItem *item); + +Q_SIGNALS: + void fontChanged(); + void availableWidthChanged(); + void availableHeightChanged(); + void paddingChanged(); + void topPaddingChanged(); + void leftPaddingChanged(); + void rightPaddingChanged(); + void bottomPaddingChanged(); + void spacingChanged(); + void localeChanged(); + void mirroredChanged(); + void focusPolicyChanged(); + void focusReasonChanged(); + void visualFocusChanged(); + void hoveredChanged(); + void hoverEnabledChanged(); + void wheelEnabledChanged(); + void backgroundChanged(); + void contentItemChanged(); + +protected: + virtual QFont defaultFont() const; + + QQuickControl(QQuickControlPrivate &dd, QQuickItem *parent); + + void classBegin() override; + void componentComplete() override; + + void itemChange(ItemChange change, const ItemChangeData &value) override; + + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void hoverEnterEvent(QHoverEvent *event) override; + void hoverMoveEvent(QHoverEvent *event) override; + void hoverLeaveEvent(QHoverEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + + virtual void fontChange(const QFont &newFont, const QFont &oldFont); + virtual void mirrorChange(); + virtual void paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding); + virtual void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem); + virtual void localeChange(const QLocale &newLocale, const QLocale &oldLocale); + +#ifndef QT_NO_ACCESSIBILITY + virtual void accessibilityActiveChanged(bool active); + virtual QAccessible::Role accessibleRole() const; +#endif + + // helper functions which avoid to check QT_NO_ACCESSIBILITY + QString accessibleName() const; + void setAccessibleName(const QString &name); + + QVariant accessibleProperty(const char *propertyName); + bool setAccessibleProperty(const char *propertyName, const QVariant &value); + +private: + Q_DISABLE_COPY(QQuickControl) + Q_DECLARE_PRIVATE(QQuickControl) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickControl) + +#endif // QQUICKCONTROL_P_H diff --git a/src/quicktemplates2/qquickcontrol_p_p.h b/src/quicktemplates2/qquickcontrol_p_p.h new file mode 100644 index 00000000..64f29986 --- /dev/null +++ b/src/quicktemplates2/qquickcontrol_p_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTROL_P_P_H +#define QQUICKCONTROL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickcontrol_p.h" + +#include <QtQuick/private/qquickitem_p.h> +#include <QtQml/private/qlazilyallocated_p.h> +#include <qpa/qplatformtheme.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQuickAccessibleAttached; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControlPrivate : public QQuickItemPrivate +#ifndef QT_NO_ACCESSIBILITY + , public QAccessible::ActivationObserver +#endif +{ + Q_DECLARE_PUBLIC(QQuickControl) + +public: + QQuickControlPrivate(); + virtual ~QQuickControlPrivate(); + + static QQuickControlPrivate *get(QQuickControl *control) + { + return control->d_func(); + } + + void mirrorChange() override; + + void setTopPadding(qreal value, bool reset = false); + void setLeftPadding(qreal value, bool reset = false); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); + + void resizeBackground(); + virtual void resizeContent(); + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + + void updateFont(const QFont &f); + static void updateFontRecur(QQuickItem *item, const QFont &f); + inline void setFont_helper(const QFont &f) { + if (resolvedFont.resolve() == f.resolve() && resolvedFont == f) + return; + updateFont(f); + } + virtual void resolveFont(); + void inheritFont(const QFont &f); + static QFont parentFont(const QQuickItem *item); + static QFont themeFont(QPlatformTheme::Font type); + + void updateLocale(const QLocale &l, bool e); + static void updateLocaleRecur(QQuickItem *item, const QLocale &l); + static QLocale calcLocale(const QQuickItem *item); + + void deleteDelegate(QObject *object); + + struct ExtraData { + ExtraData(); + QFont font; + // This list contains the default delegates which were + // replaced with custom ones via declarative assignments + // before Component.completed() was emitted. See QTBUG-50992. + QVector<QObject*> pendingDeletions; + }; + QLazilyAllocated<ExtraData> extra; + + QFont resolvedFont; + bool hasTopPadding; + bool hasLeftPadding; + bool hasRightPadding; + bool hasBottomPadding; + bool hasLocale; + bool hovered; + bool wheelEnabled; + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + qreal spacing; + QLocale locale; + Qt::FocusPolicy focusPolicy; + Qt::FocusReason focusReason; + QQuickItem *background; + QQuickItem *contentItem; + QQuickAccessibleAttached *accessibleAttached; +}; + +QT_END_NAMESPACE + +#endif // QQUICKCONTROL_P_P_H diff --git a/src/quicktemplates2/qquickdial.cpp b/src/quicktemplates2/qquickdial.cpp new file mode 100644 index 00000000..79aff781 --- /dev/null +++ b/src/quicktemplates2/qquickdial.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdial_p.h" + +#include <QtCore/qmath.h> +#include <QtQuick/private/qquickflickable_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Dial + \inherits Control + \instantiates QQuickDial + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Circular dial that is rotated to set a value. + + The Dial is similar to a traditional dial knob that is found on devices + such as stereos or industrial equipment. It allows the user to specify a + value within a range. + + \image qtquickcontrols2-dial-no-wrap.gif + + The value of the dial is set with the \l value property. The range is + set with the \l from and \l to properties. To enable or disable wrapping, + use the \l wrap property. + + The dial can be manipulated with a keyboard. It supports the following + actions: + + \table + \header \li \b {Action} \li \b {Key} + \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Left + \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Down + \row \li Set \l value to \l from \li \c Qt.Key_Home + \row \li Increase \l value by \l stepSize \li \c Qt.Key_Right + \row \li Increase \l value by \l stepSize \li \c Qt.Key_Up + \row \li Set \l value to \l to \li \c Qt.Key_End + \endtable + + \sa {Customizing Dial}, {Input Controls} +*/ + +static const qreal startAngleRadians = (M_PI * 2.0) * (4.0 / 6.0); +static const qreal startAngle = -140; +static const qreal endAngleRadians = (M_PI * 2.0) * (5.0 / 6.0); +static const qreal endAngle = 140; + +class QQuickDialPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickDial) + +public: + QQuickDialPrivate() : + from(0), + to(1), + value(0), + position(0), + angle(startAngle), + stepSize(0), + pressed(false), + snapMode(QQuickDial::NoSnap), + wrap(false), + handle(nullptr) + { + } + + qreal valueAt(qreal position) const; + qreal snapPosition(qreal position) const; + qreal positionAt(const QPoint &point) const; + void setPosition(qreal position); + void updatePosition(); + bool isLargeChange(const QPoint &eventPos, qreal proposedPosition) const; + + qreal from; + qreal to; + qreal value; + qreal position; + qreal angle; + qreal stepSize; + bool pressed; + QPoint pressPoint; + QQuickDial::SnapMode snapMode; + bool wrap; + QQuickItem *handle; +}; + +qreal QQuickDialPrivate::valueAt(qreal position) const +{ + return from + (to - from) * position; +} + +qreal QQuickDialPrivate::snapPosition(qreal position) const +{ + const qreal range = to - from; + if (qFuzzyIsNull(range)) + return position; + + const qreal effectiveStep = stepSize / range; + if (qFuzzyIsNull(effectiveStep)) + return position; + + return qRound(position / effectiveStep) * effectiveStep; +} + +qreal QQuickDialPrivate::positionAt(const QPoint &point) const +{ + qreal yy = height / 2.0 - point.y(); + qreal xx = point.x() - width / 2.0; + qreal angle = (xx || yy) ? atan2(yy, xx) : 0; + + if (angle < M_PI / -2) + angle = angle + M_PI * 2; + + qreal normalizedAngle = (startAngleRadians - angle) / endAngleRadians; + return normalizedAngle; +} + +void QQuickDialPrivate::setPosition(qreal pos) +{ + Q_Q(QQuickDial); + pos = qBound<qreal>(0.0, pos, 1.0); + if (qFuzzyCompare(position, pos)) + return; + + position = pos; + + angle = startAngle + position * qAbs(endAngle - startAngle); + + emit q->positionChanged(); + emit q->angleChanged(); +} + +void QQuickDialPrivate::updatePosition() +{ + qreal pos = 0; + if (!qFuzzyCompare(from, to)) + pos = (value - from) / (to - from); + setPosition(pos); +} + +bool QQuickDialPrivate::isLargeChange(const QPoint &eventPos, qreal proposedPosition) const +{ + return qAbs(proposedPosition - position) >= 0.5 && eventPos.y() >= height / 2; +} + +QQuickDial::QQuickDial(QQuickItem *parent) : + QQuickControl(*(new QQuickDialPrivate), parent) +{ + setActiveFocusOnTab(true); + setAcceptedMouseButtons(Qt::LeftButton); +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::from + + This property holds the starting value for the range. The default value is \c 0.0. + + \sa to, value +*/ +qreal QQuickDial::from() const +{ + Q_D(const QQuickDial); + return d->from; +} + +void QQuickDial::setFrom(qreal from) +{ + Q_D(QQuickDial); + if (qFuzzyCompare(d->from, from)) + return; + + d->from = from; + emit fromChanged(); + if (isComponentComplete()) { + setValue(d->value); + d->updatePosition(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::to + + This property holds the end value for the range. The default value is + \c 1.0. + + \sa from, value +*/ +qreal QQuickDial::to() const +{ + Q_D(const QQuickDial); + return d->to; +} + +void QQuickDial::setTo(qreal to) +{ + Q_D(QQuickDial); + if (qFuzzyCompare(d->to, to)) + return; + + d->to = to; + emit toChanged(); + if (isComponentComplete()) { + setValue(d->value); + d->updatePosition(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::value + + This property holds the value in the range \c from - \c to. The default + value is \c 0.0. + + Unlike the \l position property, the \c value is not updated while the + handle is dragged. The value is updated after the value has been chosen + and the dial has been released. + + \sa position +*/ +qreal QQuickDial::value() const +{ + Q_D(const QQuickDial); + return d->value; +} + +void QQuickDial::setValue(qreal value) +{ + Q_D(QQuickDial); + if (isComponentComplete()) + value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to); + + if (qFuzzyCompare(d->value, value)) + return; + + d->value = value; + d->updatePosition(); + emit valueChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::position + \readonly + + This property holds the logical position of the handle. + + The position is expressed as a fraction of the control's angle range (the + range within which the handle can be moved) in the range \c {0.0 - 1.0}. + Unlike the \l value property, the \c position is continuously updated while + the handle is dragged. + + \sa value, angle +*/ +qreal QQuickDial::position() const +{ + Q_D(const QQuickDial); + return d->position; +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::angle + \readonly + + This property holds the angle of the handle. + + 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 +{ + Q_D(const QQuickDial); + return d->angle; +} + +/*! + \qmlproperty real QtQuick.Controls::Dial::stepSize + + 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() +*/ +qreal QQuickDial::stepSize() const +{ + Q_D(const QQuickDial); + return d->stepSize; +} + +void QQuickDial::setStepSize(qreal step) +{ + Q_D(QQuickDial); + if (qFuzzyCompare(d->stepSize, step)) + return; + + d->stepSize = step; + emit stepSizeChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Dial::snapMode + + This property holds the snap mode. + + The snap mode works with the \l stepSize to allow the handle to snap to + certain points along the dial. + + Possible values: + \value Dial.NoSnap The dial does not snap (default). + \value Dial.SnapAlways The dial snaps while the handle is dragged. + \value Dial.SnapOnRelease The dial does not snap while being dragged, but only after the handle is released. + + \sa stepSize +*/ +QQuickDial::SnapMode QQuickDial::snapMode() const +{ + Q_D(const QQuickDial); + return d->snapMode; +} + +void QQuickDial::setSnapMode(SnapMode mode) +{ + Q_D(QQuickDial); + if (d->snapMode == mode) + return; + + d->snapMode = mode; + emit snapModeChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Dial::wrap + + This property holds whether the dial wraps when dragged. + + For example, when this property is set to \c true, dragging the dial past + the \l to position will result in the handle being positioned at the + \l from position, and vice versa: + + \image qtquickcontrols2-dial-wrap.gif + + When this property is \c false, it's not possible to drag the dial across + the from and to values. + + \image qtquickcontrols2-dial-no-wrap.gif + + The default value is \c false. +*/ +bool QQuickDial::wrap() const +{ + Q_D(const QQuickDial); + return d->wrap; +} + +void QQuickDial::setWrap(bool wrap) +{ + Q_D(QQuickDial); + if (d->wrap == wrap) + return; + + d->wrap = wrap; + emit wrapChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Dial::pressed + + This property holds whether the dial is pressed. + + The dial will be pressed when either the mouse is pressed over it, or a key + such as \c Qt.Key_Left is held down. If you'd prefer not to have the dial + be pressed upon key presses (due to styling reasons, for example), you can + use the \l {Keys}{Keys attached property}: + + \code + Dial { + Keys.onLeftPressed: {} + } + \endcode + + This will result in pressed only being \c true upon mouse presses. +*/ +bool QQuickDial::isPressed() const +{ + Q_D(const QQuickDial); + return d->pressed; +} + +void QQuickDial::setPressed(bool pressed) +{ + Q_D(QQuickDial); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + setAccessibleProperty("pressed", pressed); + emit pressedChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::Dial::increase() + + Increases the value by \l stepSize, or \c 0.1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickDial::increase() +{ + Q_D(QQuickDial); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + setValue(d->value + step); +} + +/*! + \qmlmethod void QtQuick.Controls::Dial::decrease() + + Decreases the value by \l stepSize, or \c 0.1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickDial::decrease() +{ + Q_D(QQuickDial); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + setValue(d->value - step); +} + +/*! + \qmlproperty Item QtQuick.Controls::Dial::handle + + This property holds the handle of the dial. + + The handle acts as a visual indicator of the position of the dial. + + \sa {Customizing Dial} +*/ +QQuickItem *QQuickDial::handle() const +{ + Q_D(const QQuickDial); + return d->handle; +} + +void QQuickDial::setHandle(QQuickItem *handle) +{ + Q_D(QQuickDial); + if (handle == d->handle) + return; + + d->deleteDelegate(d->handle); + d->handle = handle; + if (d->handle && !d->handle->parentItem()) + d->handle->setParentItem(this); + emit handleChanged(); +} + +void QQuickDial::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickDial); + switch (event->key()) { + case Qt::Key_Left: + case Qt::Key_Down: + setPressed(true); + if (isMirrored()) + increase(); + else + decrease(); + break; + + case Qt::Key_Right: + case Qt::Key_Up: + setPressed(true); + if (isMirrored()) + decrease(); + else + increase(); + break; + + case Qt::Key_Home: + setPressed(true); + setValue(isMirrored() ? d->to : d->from); + break; + + case Qt::Key_End: + setPressed(true); + setValue(isMirrored() ? d->from : d->to); + break; + + default: + event->ignore(); + QQuickControl::keyPressEvent(event); + break; + } +} + +void QQuickDial::keyReleaseEvent(QKeyEvent *event) +{ + QQuickControl::keyReleaseEvent(event); + setPressed(false); +} + +void QQuickDial::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickDial); + QQuickControl::mousePressEvent(event); + d->pressPoint = event->pos(); + setPressed(true); +} + +void QQuickDial::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickDial); + QQuickControl::mouseMoveEvent(event); + if (!keepMouseGrab()) { + bool overXDragThreshold = QQuickWindowPrivate::dragOverThreshold(event->pos().x() - d->pressPoint.x(), Qt::XAxis, event); + setKeepMouseGrab(overXDragThreshold); + + if (!overXDragThreshold) { + bool overYDragThreshold = QQuickWindowPrivate::dragOverThreshold(event->pos().y() - d->pressPoint.y(), Qt::YAxis, event); + setKeepMouseGrab(overYDragThreshold); + } + } + if (keepMouseGrab()) { + qreal pos = d->positionAt(event->pos()); + if (d->snapMode == SnapAlways) + pos = d->snapPosition(pos); + + if (d->wrap || (!d->wrap && !d->isLargeChange(event->pos(), pos))) + d->setPosition(pos); + } +} + +void QQuickDial::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickDial); + QQuickControl::mouseReleaseEvent(event); + + if (keepMouseGrab()) { + qreal pos = d->positionAt(event->pos()); + if (d->snapMode != NoSnap) + pos = d->snapPosition(pos); + + if (d->wrap || (!d->wrap && !d->isLargeChange(event->pos(), pos))) + setValue(d->valueAt(pos)); + + setKeepMouseGrab(false); + } + + setPressed(false); + d->pressPoint = QPoint(); +} + +void QQuickDial::mouseUngrabEvent() +{ + Q_D(QQuickDial); + QQuickControl::mouseUngrabEvent(); + d->pressPoint = QPoint(); + setPressed(false); +} + +void QQuickDial::mirrorChange() +{ + QQuickControl::mirrorChange(); + emit angleChanged(); +} + +void QQuickDial::componentComplete() +{ + Q_D(QQuickDial); + QQuickControl::componentComplete(); + setValue(d->value); + d->updatePosition(); +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickDial::accessibilityActiveChanged(bool active) +{ + QQuickControl::accessibilityActiveChanged(active); + + Q_D(QQuickDial); + if (active) + setAccessibleProperty("pressed", d->pressed); +} + +QAccessible::Role QQuickDial::accessibleRole() const +{ + return QAccessible::Dial; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickdial_p.h b/src/quicktemplates2/qquickdial_p.h new file mode 100644 index 00000000..2d87d6d9 --- /dev/null +++ b/src/quicktemplates2/qquickdial_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKDIAL_P_H +#define QQUICKDIAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvariant.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickDialAttached; +class QQuickDialPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDial : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL) + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged FINAL) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged FINAL) + Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL) + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL) + Q_PROPERTY(bool wrap READ wrap WRITE setWrap NOTIFY wrapChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(QQuickItem *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL) + +public: + explicit QQuickDial(QQuickItem *parent = nullptr); + + qreal from() const; + void setFrom(qreal from); + + qreal to() const; + void setTo(qreal to); + + qreal value() const; + void setValue(qreal value); + + qreal position() const; + + qreal angle() const; + + qreal stepSize() const; + void setStepSize(qreal step); + + enum SnapMode { + NoSnap, + SnapAlways, + SnapOnRelease + }; + Q_ENUM(SnapMode) + + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + bool wrap() const; + void setWrap(bool wrap); + + bool isPressed() const; + void setPressed(bool pressed); + + QQuickItem *handle() const; + void setHandle(QQuickItem *handle); + +public Q_SLOTS: + void increase(); + void decrease(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void valueChanged(); + void positionChanged(); + void angleChanged(); + void stepSizeChanged(); + void snapModeChanged(); + void wrapChanged(); + void pressedChanged(); + void handleChanged(); + +protected: + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void mirrorChange() override; + void componentComplete() override; + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickDial) + Q_DECLARE_PRIVATE(QQuickDial) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDial) + +#endif // QQUICKDIAL_P_H diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp new file mode 100644 index 00000000..1dae911e --- /dev/null +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -0,0 +1,634 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdrawer_p.h" +#include "qquickdrawer_p_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/private/qquickanimation_p.h> +#include <QtQuick/private/qquicktransition_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Drawer + \inherits Popup + \instantiates QQuickDrawer + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-navigation + \ingroup qtquickcontrols2-popups + \brief Side panel that can be opened and closed using a swipe gesture. + + Drawer provides a swipe-based side panel, similar to those often used in + touch interfaces to provide a central location for navigation. + + \image qtquickcontrols2-drawer.gif + + Drawer can be positioned at any of the four edges of the content item. + The drawer above is positioned against the left edge of the window. The + drawer is then opened by \e "dragging" it out from the left edge of the + window. + + \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: + + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + width: 200 + height: 228 + visible: true + + Drawer { + id: drawer + width: 0.66 * window.width + height: window.height + } + + Label { + id: content + + text: "Aa" + font.pixelSize: 96 + anchors.fill: parent + verticalAlignment: Label.AlignVCenter + horizontalAlignment: Label.AlignHCenter + + transform: Translate { + x: drawer.position * content.width * 0.33 + } + } + } + \endcode + + If you would like the application's contents to stay where they are when + the drawer is opened, don't apply a translation. + + \note On some platforms, certain edges may be reserved for system + gestures and therefore cannot be used with Drawer. For example, the + top and bottom edges may be reserved for system notifications and + control centers on Android and iOS. + + \sa SwipeView, {Customizing Drawer}, {Navigation Controls}, {Popup Controls} +*/ + +QQuickDrawerPrivate::QQuickDrawerPrivate() + : edge(Qt::LeftEdge), offset(0), position(0), + dragMargin(QGuiApplication::styleHints()->startDragDistance()) +{ + setEdge(Qt::LeftEdge); +} + +qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const +{ + Q_Q(const QQuickDrawer); + QQuickWindow *window = q->window(); + if (!window) + return 0; + + switch (edge) { + case Qt::TopEdge: + return point.y() / q->height(); + case Qt::LeftEdge: + return point.x() / q->width(); + case Qt::RightEdge: + return (window->width() - point.x()) / q->width(); + case Qt::BottomEdge: + return (window->height() - point.y()) / q->height(); + default: + return 0; + } +} + +void QQuickDrawerPrivate::reposition() +{ + Q_Q(QQuickDrawer); + QQuickWindow *window = q->window(); + if (!window) + return; + + switch (edge) { + case Qt::LeftEdge: + popupItem->setX((position - 1.0) * popupItem->width()); + break; + case Qt::RightEdge: + popupItem->setX(window->width() - position * popupItem->width()); + break; + case Qt::TopEdge: + popupItem->setY((position - 1.0) * popupItem->height()); + break; + case Qt::BottomEdge: + 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) +{ + return QQuickWindowPrivate::dragOverThreshold(d, axis, event, threshold); +} + +bool QQuickDrawerPrivate::startDrag(QQuickWindow *window, QMouseEvent *event) +{ + if (!window || dragMargin < 0.0 || qFuzzyIsNull(dragMargin)) + return false; + + bool drag = false; + switch (edge) { + case Qt::LeftEdge: + drag = !dragOverThreshold(event->windowPos().x(), Qt::XAxis, event, dragMargin); + break; + case Qt::RightEdge: + drag = !dragOverThreshold(window->width() - event->windowPos().x(), Qt::XAxis, event, dragMargin); + break; + case Qt::TopEdge: + drag = !dragOverThreshold(event->windowPos().y(), Qt::YAxis, event, dragMargin); + break; + case Qt::BottomEdge: + drag = !dragOverThreshold(window->height() - event->windowPos().y(), Qt::YAxis, event, dragMargin); + break; + default: + break; + } + + if (drag) { + prepareEnterTransition(); + reposition(); + handleMousePressEvent(window->contentItem(), event); + } + + return drag; +} + +bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) +{ + Q_Q(QQuickDrawer); + if (!window || popupItem->keepMouseGrab()) + return false; + + const QPointF movePoint = event->windowPos(); + + // Flickable uses a hard-coded threshold of 15 for flicking, and + // QStyleHints::startDragDistance for dragging. Drawer uses a bit + // larger threshold to avoid being too eager to steal touch (QTBUG-50045) + const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + bool overThreshold = false; + if (position > 0 || dragMargin > 0) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold); + else + overThreshold = dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold); + } + + // Don't be too eager to steal presses outside the drawer (QTBUG-53929) + if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !popupItem->contains(popupItem->mapFromScene(movePoint))) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin; + else + overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; + } + + return overThreshold; +} + +static const qreal openCloseVelocityThreshold = 300; + +bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) +{ + bool wasGrabbed = popupItem->keepMouseGrab(); + if (wasGrabbed) { + const QPointF releasePoint = event->windowPos(); + velocityCalculator.stopMeasuring(releasePoint, event->timestamp()); + + qreal velocity = 0; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + velocity = velocityCalculator.velocity().x(); + else + velocity = velocityCalculator.velocity().y(); + + // the velocity is calculated so that swipes from left to right + // and top to bottom have positive velocity, and swipes from right + // to left and bottom to top have negative velocity. + // + // - top/left edge: positive velocity opens, negative velocity closes + // - bottom/right edge: negative velocity opens, positive velocity closes + // + // => invert the velocity for bottom and right edges, for the threshold comparison below + if (edge == Qt::RightEdge || edge == Qt::BottomEdge) + velocity = -velocity; + + if (position > 0.7 || velocity > openCloseVelocityThreshold) { + transitionManager.transitionEnter(); + } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { + transitionManager.transitionExit(); + } else { + switch (edge) { + case Qt::LeftEdge: + if (releasePoint.x() - pressPoint.x() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::RightEdge: + if (releasePoint.x() - pressPoint.x() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::TopEdge: + if (releasePoint.y() - pressPoint.y() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::BottomEdge: + if (releasePoint.y() - pressPoint.y() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + } + } + } + return wasGrabbed; +} + +bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + offset = 0; + pressPoint = event->windowPos(); + velocityCalculator.startMeasuring(pressPoint, event->timestamp()); + + // don't block press events + // a) outside a non-modal drawer, + // b) to drawer children, or + // c) outside a modal drawer's background dimming + event->setAccepted(modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(pressPoint)))); + return event->isAccepted(); +} + +bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +{ + 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)) { + QQuickItem *grabber = window->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + popupItem->grabMouse(); + popupItem->setKeepMouseGrab(true); + offset = qMin<qreal>(0.0, positionAt(movePoint) - position); + } + } + + if (popupItem->keepMouseGrab()) + q->setPosition(positionAt(movePoint) - offset); + event->accept(); + + return popupItem->keepMouseGrab(); +} + +bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_UNUSED(item); + + const bool wasGrabbed = ungrabMouse(event); + + popupItem->setKeepMouseGrab(false); + pressPoint = QPoint(); + event->accept(); + + return wasGrabbed; +} + +static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) +{ + QList<QQuickStateAction> actions; + if (!transition || !QQuickPopupPrivate::get(drawer)->window || !transition->enabled()) + return actions; + + qmlExecuteDeferred(transition); + + QQmlProperty defaultTarget(drawer, QLatin1String("position")); + QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); + int count = animations.count(&animations); + for (int i = 0; i < count; ++i) { + QQuickAbstractAnimation *anim = animations.at(&animations, i); + anim->setDefaultTarget(defaultTarget); + } + + actions << QQuickStateAction(drawer, QLatin1String("position"), to); + return actions; +} + +bool QQuickDrawerPrivate::prepareEnterTransition() +{ + Q_Q(QQuickDrawer); + enterActions = prepareTransition(q, enter, 1.0); + return QQuickPopupPrivate::prepareEnterTransition(); +} + +bool QQuickDrawerPrivate::prepareExitTransition() +{ + Q_Q(QQuickDrawer); + exitActions = prepareTransition(q, exit, 0.0); + 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) +{ + setFocus(true); + setModal(true); + setFiltersChildMouseEvents(true); + setClosePolicy(CloseOnEscape | CloseOnReleaseOutside); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Drawer::edge + + This property holds the edge of the window at which the drawer will + open from. The acceptable values are: + + \value Qt.TopEdge The top edge of the window. + \value Qt.LeftEdge The left edge of the window (default). + \value Qt.RightEdge The right edge of the window. + \value Qt.BottomEdge The bottom edge of the window. +*/ +Qt::Edge QQuickDrawer::edge() const +{ + Q_D(const QQuickDrawer); + return d->edge; +} + +void QQuickDrawer::setEdge(Qt::Edge edge) +{ + Q_D(QQuickDrawer); + if (d->edge == edge) + return; + + d->setEdge(edge); + if (isComponentComplete()) + d->reposition(); + emit edgeChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Drawer::position + + This property holds the position of the drawer relative to its final + destination. That is, the position will be \c 0.0 when the drawer + is fully closed, and \c 1.0 when fully open. +*/ +qreal QQuickDrawer::position() const +{ + Q_D(const QQuickDrawer); + return d->position; +} + +void QQuickDrawer::setPosition(qreal position) +{ + Q_D(QQuickDrawer); + position = qBound<qreal>(0.0, position, 1.0); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + if (isComponentComplete()) + d->reposition(); + if (d->dimmer) + d->dimmer->setOpacity(position); + emit positionChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Drawer::dragMargin + + This property holds the distance from the screen edge within which + drag actions will open the drawer. Setting the value to \c 0 or less + prevents opening the drawer by dragging. + + The default value is \c Qt.styleHints.startDragDistance. +*/ +qreal QQuickDrawer::dragMargin() const +{ + Q_D(const QQuickDrawer); + return d->dragMargin; +} + +void QQuickDrawer::setDragMargin(qreal margin) +{ + Q_D(QQuickDrawer); + if (qFuzzyCompare(d->dragMargin, margin)) + return; + + d->dragMargin = margin; + emit dragMarginChanged(); +} + +void QQuickDrawer::resetDragMargin() +{ + setDragMargin(QGuiApplication::styleHints()->startDragDistance()); +} + +bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) +{ + Q_D(QQuickDrawer); + 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)); + default: + return false; + } +} + +void QQuickDrawer::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickDrawer); + QQuickPopup::mousePressEvent(event); + d->handleMousePressEvent(d->popupItem, event); +} + +void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickDrawer); + QQuickPopup::mouseMoveEvent(event); + d->handleMouseMoveEvent(d->popupItem, event); +} + +void QQuickDrawer::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickDrawer); + QQuickPopup::mouseReleaseEvent(event); + d->handleMouseReleaseEvent(d->popupItem, event); +} + +void QQuickDrawer::mouseUngrabEvent() +{ + Q_D(QQuickDrawer); + QQuickPopup::mouseUngrabEvent(); + d->pressPoint = QPoint(); + d->velocityCalculator.reset(); +} + +bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) +{ + Q_D(QQuickDrawer); + switch (event->type()) { + case QEvent::MouseButtonPress: + d->tryClose(item, static_cast<QMouseEvent *>(event)); + return d->handleMousePressEvent(item, static_cast<QMouseEvent *>(event)); + case QEvent::MouseMove: + return d->handleMouseMoveEvent(item, static_cast<QMouseEvent *>(event)); + case QEvent::MouseButtonRelease: + d->tryClose(item, static_cast<QMouseEvent *>(event)); + return d->handleMouseReleaseEvent(item, static_cast<QMouseEvent *>(event)); + default: + return QQuickPopup::overlayEvent(item, 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 new file mode 100644 index 00000000..bada1344 --- /dev/null +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKDRAWER_P_H +#define QQUICKDRAWER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickpopup_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickDrawerPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDrawer : public QQuickPopup +{ + Q_OBJECT + Q_PROPERTY(Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged FINAL) + Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin RESET resetDragMargin NOTIFY dragMarginChanged FINAL) + +public: + explicit QQuickDrawer(QObject *parent = nullptr); + + Qt::Edge edge() const; + void setEdge(Qt::Edge edge); + + qreal position() const; + void setPosition(qreal position); + + qreal dragMargin() const; + void setDragMargin(qreal margin); + void resetDragMargin(); + +Q_SIGNALS: + void edgeChanged(); + void positionChanged(); + void dragMarginChanged(); + +protected: + bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + 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) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDrawer) + +#endif // QQUICKDRAWER_P_H diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h new file mode 100644 index 00000000..b7555c3e --- /dev/null +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKDRAWER_P_P_H +#define QQUICKDRAWER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdrawer_p.h" +#include "qquickpopup_p_p.h" +#include "qquickvelocitycalculator_p_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickDrawerPrivate : public QQuickPopupPrivate +{ + Q_DECLARE_PUBLIC(QQuickDrawer) + +public: + QQuickDrawerPrivate(); + + static QQuickDrawerPrivate *get(QQuickDrawer *drawer) + { + return drawer->d_func(); + } + + qreal positionAt(const QPointF &point) const; + void reposition() override; + void resizeOverlay() override; + + bool startDrag(QQuickWindow *window, QMouseEvent *event); + bool grabMouse(QMouseEvent *event); + bool ungrabMouse(QMouseEvent *event); + + bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); + + bool prepareEnterTransition() override; + bool prepareExitTransition() override; + + void setEdge(Qt::Edge edge); + + Qt::Edge edge; + qreal offset; + qreal position; + qreal dragMargin; + QPointF pressPoint; + QQuickVelocityCalculator velocityCalculator; +}; + +QT_END_NAMESPACE + +#endif // QQUICKDRAWER_P_P_H diff --git a/src/quicktemplates2/qquickframe.cpp b/src/quicktemplates2/qquickframe.cpp new file mode 100644 index 00000000..9deef6be --- /dev/null +++ b/src/quicktemplates2/qquickframe.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickframe_p.h" +#include "qquickframe_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Frame + \inherits Pane + \instantiates QQuickFrame + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Visual frame for a logical group of controls. + + Frame is used to layout a logical group of controls together within a + visual frame. Frame 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. + + Items declared as children of a Frame are automatically parented to the + 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 + for use together with layouts. + + \image qtquickcontrols2-frame.png + + \snippet qtquickcontrols2-frame.qml 1 + + \sa {Customizing Frame}, {Container Controls} +*/ + +QQuickFrame::QQuickFrame(QQuickItem *parent) : + QQuickPane(*(new QQuickFramePrivate), parent) +{ +} + +QQuickFrame::QQuickFrame(QQuickFramePrivate &dd, QQuickItem *parent) : + QQuickPane(dd, parent) +{ +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickFrame::accessibleRole() const +{ + return QAccessible::Border; +} +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickframe_p.h b/src/quicktemplates2/qquickframe_p.h new file mode 100644 index 00000000..093da248 --- /dev/null +++ b/src/quicktemplates2/qquickframe_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFRAME_P_H +#define QQUICKFRAME_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickpane_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickFramePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFrame : public QQuickPane +{ + Q_OBJECT + +public: + explicit QQuickFrame(QQuickItem *parent = nullptr); + +protected: + QQuickFrame(QQuickFramePrivate &dd, QQuickItem *parent); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickFrame) + Q_DECLARE_PRIVATE(QQuickFrame) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFrame) + +#endif // QQUICKFRAME_P_H diff --git a/src/quicktemplates2/qquickframe_p_p.h b/src/quicktemplates2/qquickframe_p_p.h new file mode 100644 index 00000000..6939be6c --- /dev/null +++ b/src/quicktemplates2/qquickframe_p_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFRAME_P_P_H +#define QQUICKFRAME_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickpane_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickFrame; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFramePrivate : public QQuickPanePrivate +{ +}; + +QT_END_NAMESPACE + +#endif // QQUICKFRAME_P_P_H diff --git a/src/quicktemplates2/qquickgroupbox.cpp b/src/quicktemplates2/qquickgroupbox.cpp new file mode 100644 index 00000000..dd6d1883 --- /dev/null +++ b/src/quicktemplates2/qquickgroupbox.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickgroupbox_p.h" +#include "qquickframe_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype GroupBox + \inherits Frame + \instantiates QQuickGroupBox + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Visual frame and title for a logical group of controls. + + GroupBox is used to layout a logical group of controls together, within + 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. + + Items declared as children of a GroupBox are automatically parented to the + GroupBox's \l {Control::}{contentItem}. Items created dynamically need to be + explicitly parented to the contentItem. + + If only a single item is used within a GroupBox, it will resize to fit the + implicit size of its contained item. This makes it particularly suitable + for use together with layouts. + + \image qtquickcontrols2-groupbox.png + + \snippet qtquickcontrols2-groupbox.qml 1 + + \section2 Checkable GroupBox + + Even though GroupBox has no built-in check box, it is straightforward + to create a checkable GroupBox by pairing it with a CheckBox. + + \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 up to the application to decide + on the behavior of the checkbox. + + \snippet qtquickcontrols2-groupbox-checkable.qml 1 + + \sa CheckBox, {Customizing GroupBox}, {Container Controls} +*/ + +class QQuickGroupBoxPrivate : public QQuickFramePrivate +{ +public: + QQuickGroupBoxPrivate() : label(nullptr) { } + + QString title; + QQuickItem *label; +}; + +QQuickGroupBox::QQuickGroupBox(QQuickItem *parent) : + QQuickFrame(*(new QQuickGroupBoxPrivate), 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 +{ + Q_D(const QQuickGroupBox); + return d->title; +} + +void QQuickGroupBox::setTitle(const QString &title) +{ + Q_D(QQuickGroupBox); + if (d->title == title) + return; + + d->title = title; + setAccessibleName(title); + emit titleChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::GroupBox::label + + This property holds the label item that visualizes \l title. + + \sa {Customizing GroupBox} +*/ +QQuickItem *QQuickGroupBox::label() const +{ + Q_D(const QQuickGroupBox); + return d->label; +} + +void QQuickGroupBox::setLabel(QQuickItem *label) +{ + Q_D(QQuickGroupBox); + if (d->label == label) + return; + + d->deleteDelegate(d->label); + d->label = label; + if (label && !label->parentItem()) + label->setParentItem(this); + emit labelChanged(); +} + +QFont QQuickGroupBox::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::GroupBoxTitleFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickGroupBox::accessibleRole() const +{ + return QAccessible::Grouping; +} + +void QQuickGroupBox::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickGroupBox); + QQuickFrame::accessibilityActiveChanged(active); + + if (active) + setAccessibleName(d->title); +} +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickgroupbox_p.h b/src/quicktemplates2/qquickgroupbox_p.h new file mode 100644 index 00000000..98f6ad06 --- /dev/null +++ b/src/quicktemplates2/qquickgroupbox_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGROUPBOX_P_H +#define QQUICKGROUPBOX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickframe_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickGroupBoxPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickGroupBox : public QQuickFrame +{ + Q_OBJECT + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL) + Q_PROPERTY(QQuickItem *label READ label WRITE setLabel NOTIFY labelChanged FINAL) + +public: + explicit QQuickGroupBox(QQuickItem *parent = nullptr); + + QString title() const; + void setTitle(const QString &title); + + QQuickItem *label() const; + void setLabel(QQuickItem *label); + +Q_SIGNALS: + void titleChanged(); + void labelChanged(); + +protected: + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; + void accessibilityActiveChanged(bool active) override; +#endif + +private: + Q_DISABLE_COPY(QQuickGroupBox) + Q_DECLARE_PRIVATE(QQuickGroupBox) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickGroupBox) + +#endif // QQUICKGROUPBOX_P_H diff --git a/src/quicktemplates2/qquickitemdelegate.cpp b/src/quicktemplates2/qquickitemdelegate.cpp new file mode 100644 index 00000000..411cb224 --- /dev/null +++ b/src/quicktemplates2/qquickitemdelegate.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitemdelegate_p.h" +#include "qquickitemdelegate_p_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ItemDelegate + \inherits AbstractButton + \instantiates QQuickItemDelegate + \inqmlmodule QtQuick.Controls + \since 5.7 + \brief Basic item delegate that can be used in various views and controls. + + \image qtquickcontrols2-itemdelegate.gif + + ItemDelegate presents a standard view item. It can be used as a delegate + in various views and controls, such as \l ListView and \l ComboBox. + + ItemDelegate inherits its API from AbstractButton. For instance, you can set + \l {AbstractButton::text}{text}, and react to \l {AbstractButton::clicked}{clicks} + using the AbstractButton API. + + \snippet qtquickcontrols2-itemdelegate.qml 1 + + \sa {Customizing ItemDelegate}, {Delegate Controls} +*/ + +QQuickItemDelegatePrivate::QQuickItemDelegatePrivate() : + highlighted(false) +{ +} + +QQuickItemDelegate::QQuickItemDelegate(QQuickItem *parent) : + QQuickAbstractButton(*(new QQuickItemDelegatePrivate), parent) +{ + setFocusPolicy(Qt::NoFocus); +} + +QQuickItemDelegate::QQuickItemDelegate(QQuickItemDelegatePrivate &dd, QQuickItem *parent) : + QQuickAbstractButton(dd, parent) +{ + setFocusPolicy(Qt::NoFocus); +} + +/*! + \qmlproperty bool QtQuick.Controls::ItemDelegate::highlighted + + This property holds whether the delegate is highlighted. + + A delegate can be highlighted in order to draw the user's attention towards + it. It has no effect on keyboard interaction. For example, you can + highlight the current item in a ListView using the following code: + + \code + ListView { + id: listView + model: 10 + delegate: ItemDelegate { + text: modelData + highlighted: ListView.isCurrentItem + onClicked: listView.currentIndex = index + } + } + \endcode + + The default value is \c false. +*/ +bool QQuickItemDelegate::isHighlighted() const +{ + Q_D(const QQuickItemDelegate); + return d->highlighted; +} + +void QQuickItemDelegate::setHighlighted(bool highlighted) +{ + Q_D(QQuickItemDelegate); + if (highlighted == d->highlighted) + return; + + d->highlighted = highlighted; + emit highlightedChanged(); +} + +QFont QQuickItemDelegate::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ItemViewFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickItemDelegate::accessibleRole() const +{ + return QAccessible::ListItem; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickitemdelegate_p.h b/src/quicktemplates2/qquickitemdelegate_p.h new file mode 100644 index 00000000..429b0ca9 --- /dev/null +++ b/src/quicktemplates2/qquickitemdelegate_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMDELEGATE_P_H +#define QQUICKITEMDELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickItemDelegatePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegate : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL) + +public: + explicit QQuickItemDelegate(QQuickItem *parent = nullptr); + + bool isHighlighted() const; + void setHighlighted(bool highlighted); + +Q_SIGNALS: + void highlightedChanged(); + +protected: + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +protected: + QQuickItemDelegate(QQuickItemDelegatePrivate &dd, QQuickItem *parent); + +private: + Q_DISABLE_COPY(QQuickItemDelegate) + Q_DECLARE_PRIVATE(QQuickItemDelegate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickItemDelegate) + +#endif // QQUICKITEMDELEGATE_P_H diff --git a/src/quicktemplates2/qquickitemdelegate_p_p.h b/src/quicktemplates2/qquickitemdelegate_p_p.h new file mode 100644 index 00000000..c69f3d46 --- /dev/null +++ b/src/quicktemplates2/qquickitemdelegate_p_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMDELEGATE_P_P_H +#define QQUICKITEMDELEGATE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickItemDelegate) + +public: + QQuickItemDelegatePrivate(); + + bool highlighted; +}; + +QT_END_NAMESPACE + +#endif // QQUICKITEMDELEGATE_P_P_H diff --git a/src/quicktemplates2/qquicklabel.cpp b/src/quicktemplates2/qquicklabel.cpp new file mode 100644 index 00000000..ebd1755a --- /dev/null +++ b/src/quicktemplates2/qquicklabel.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklabel_p.h" +#include "qquicklabel_p_p.h" +#include "qquickcontrol_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuick/private/qquickclipnode_p.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtQuick/private/qquickaccessibleattached_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Label + \inherits Text + \instantiates QQuickLabel + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup text + \brief Styled text label with inherited font. + + Label extends \l Text with styling and \l {Control::font}{font} + inheritance. The default colors and font are style specific. Label + can also have a visual \l background item. + + \image qtquickcontrols2-label.png + + \snippet qtquickcontrols2-label.qml 1 + + You can use the properties of \l Text to change the appearance of the text as desired: + + \qml + Label { + text: "Hello world" + font.pixelSize: 22 + font.italic: true + } + \endqml + + \sa {Customizing Label} +*/ + +QQuickLabel::QQuickLabel(QQuickItem *parent) : + QQuickText(*(new QQuickLabelPrivate), parent) +{ + Q_D(QQuickLabel); + QObjectPrivate::connect(this, &QQuickText::textChanged, + d, &QQuickLabelPrivate::_q_textChanged); +} + +QQuickLabel::~QQuickLabel() +{ +} + +QQuickLabelPrivate::QQuickLabelPrivate() + : background(nullptr), accessibleAttached(nullptr) +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installActivationObserver(this); +#endif +} + +QQuickLabelPrivate::~QQuickLabelPrivate() +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::removeActivationObserver(this); +#endif +} + +/*! + \internal + + Determine which font is implicitly imposed on this control by its ancestors + and QGuiApplication::font, resolve this against its own font (attributes from + the implicit font are copied over). Then propagate this font to this + control's children. +*/ +void QQuickLabelPrivate::resolveFont() +{ + Q_Q(QQuickLabel); + inheritFont(QQuickControlPrivate::parentFont(q)); +} + +void QQuickLabelPrivate::inheritFont(const QFont &f) +{ + Q_Q(QQuickLabel); + QFont parentFont = font.resolve(f); + parentFont.resolve(font.resolve() | f.resolve()); + + const QFont defaultFont = QQuickControlPrivate::themeFont(QPlatformTheme::LabelFont); + const QFont resolvedFont = parentFont.resolve(defaultFont); + + const bool changed = resolvedFont != sourceFont; + q->QQuickText::setFont(resolvedFont); + if (changed) + emit q->fontChanged(); +} + +void QQuickLabelPrivate::_q_textChanged(const QString &text) +{ +#ifndef QT_NO_ACCESSIBILITY + if (accessibleAttached) + accessibleAttached->setName(text); +#else + Q_UNUSED(text) +#endif +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickLabelPrivate::accessibilityActiveChanged(bool active) +{ + if (accessibleAttached || !active) + return; + + Q_Q(QQuickLabel); + accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true)); + if (accessibleAttached) { + accessibleAttached->setRole(accessibleRole()); + accessibleAttached->setName(text); + } else { + qWarning() << "QQuickLabel: " << q << " QQuickAccessibleAttached object creation failed!"; + } +} + +QAccessible::Role QQuickLabelPrivate::accessibleRole() const +{ + return QAccessible::StaticText; +} +#endif + +/* + Deletes "delegate" if Component.completed() has been emitted, + otherwise stores it in pendingDeletions. +*/ +void QQuickLabelPrivate::deleteDelegate(QObject *delegate) +{ + if (componentComplete) + delete delegate; + else + pendingDeletions.append(delegate); +} + +QFont QQuickLabel::font() const +{ + return QQuickText::font(); +} + +void QQuickLabel::setFont(const QFont &font) +{ + Q_D(QQuickLabel); + if (d->font.resolve() == font.resolve() && d->font == font) + return; + + d->font = font; + d->resolveFont(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Label::background + + This property holds the background item. + + \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. + + \sa {Customizing Label} +*/ +QQuickItem *QQuickLabel::background() const +{ + Q_D(const QQuickLabel); + return d->background; +} + +void QQuickLabel::setBackground(QQuickItem *background) +{ + Q_D(QQuickLabel); + if (d->background == background) + return; + + d->deleteDelegate(d->background); + d->background = background; + if (background) { + background->setParentItem(this); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + } + emit backgroundChanged(); +} + +void QQuickLabel::classBegin() +{ + Q_D(QQuickLabel); + QQuickText::classBegin(); + d->resolveFont(); +} + +void QQuickLabel::componentComplete() +{ + Q_D(QQuickLabel); + QQuickText::componentComplete(); +#ifndef QT_NO_ACCESSIBILITY + if (!d->accessibleAttached && QAccessible::isActive()) + d->accessibilityActiveChanged(true); +#endif + + qDeleteAll(d->pendingDeletions); + d->pendingDeletions.clear(); +} + +void QQuickLabel::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + Q_D(QQuickLabel); + QQuickText::itemChange(change, value); + if (change == ItemParentHasChanged && value.item) + d->resolveFont(); +} + +void QQuickLabel::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickLabel); + QQuickText::geometryChanged(newGeometry, oldGeometry); + if (d->background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->background); + if (!p->widthValid) { + d->background->setWidth(newGeometry.width()); + p->widthValid = false; + } + if (!p->heightValid) { + d->background->setHeight(newGeometry.height()); + p->heightValid = false; + } + } +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicklabel_p.h b/src/quicktemplates2/qquicklabel_p.h new file mode 100644 index 00000000..c4484d26 --- /dev/null +++ b/src/quicktemplates2/qquicklabel_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLABEL_P_H +#define QQUICKLABEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickLabelPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickLabel : public QQuickText +{ + Q_OBJECT + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + +public: + explicit QQuickLabel(QQuickItem *parent = nullptr); + ~QQuickLabel(); + + QFont font() const; + void setFont(const QFont &font); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + +Q_SIGNALS: + void fontChanged(); + void backgroundChanged(); + +protected: + void classBegin() override; + void componentComplete() override; + + void itemChange(ItemChange change, const ItemChangeData &value) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private: + Q_DISABLE_COPY(QQuickLabel) + Q_DECLARE_PRIVATE(QQuickLabel) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickLabel) + +#endif // QQUICKLABEL_P_H diff --git a/src/quicktemplates2/qquicklabel_p_p.h b/src/quicktemplates2/qquicklabel_p_p.h new file mode 100644 index 00000000..f5185768 --- /dev/null +++ b/src/quicktemplates2/qquicklabel_p_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLABEL_P_P_H +#define QQUICKLABEL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktext_p_p.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQuickAccessibleAttached; + +class QQuickLabelPrivate : public QQuickTextPrivate +#ifndef QT_NO_ACCESSIBILITY + , public QAccessible::ActivationObserver +#endif +{ + Q_DECLARE_PUBLIC(QQuickLabel) + +public: + QQuickLabelPrivate(); + ~QQuickLabelPrivate(); + + static QQuickLabelPrivate *get(QQuickLabel *item) { + return static_cast<QQuickLabelPrivate *>(QObjectPrivate::get(item)); } + + void resizeBackground(); + void resolveFont(); + void inheritFont(const QFont &f); + + void _q_textChanged(const QString &text); + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + + void deleteDelegate(QObject *object); + + QFont font; + QQuickItem *background; + QQuickAccessibleAttached *accessibleAttached; + // This list contains the default delegates which were + // replaced with custom ones via declarative assignments + // before Component.completed() was emitted. See QTBUG-50992. + QVector<QObject *> pendingDeletions; +}; + +QT_END_NAMESPACE + +#endif // QQUICKLABEL_P_P_H diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp new file mode 100644 index 00000000..2203e574 --- /dev/null +++ b/src/quicktemplates2/qquickmenu.cpp @@ -0,0 +1,532 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmenu_p.h" +#include "qquickmenu_p_p.h" +#include "qquickmenuitem_p.h" + +#include <QtGui/qevent.h> +#include <QtQml/private/qqmlobjectmodel_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuick/private/qquickitemview_p.h> +#include <QtQuick/private/qquickevents_p_p.h> +#include <QtQuick/private/qquickwindow_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Menu + \inherits Popup + \instantiates QQuickMenu + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-menus + \ingroup qtquickcontrols2-popups + \brief Menu popup that can be used as a context menu or popup menu. + + \image qtquickcontrols2-menu.png + + Menu has two main use cases: + \list + \li Context menus; for example, a menu that is shown after right clicking + \li Popup menus; for example, a menu that is shown after clicking a button + \endlist + + \code + Button { + id: fileButton + text: "File" + onClicked: menu.open() + + Menu { + id: menu + y: fileButton.height + + MenuItem { + text: "New..." + } + MenuItem { + text: "Open..." + } + MenuItem { + text: "Save" + } + } + } + \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} +*/ + +QQuickMenuPrivate::QQuickMenuPrivate() : + contentItem(nullptr), + contentModel(nullptr) +{ + Q_Q(QQuickMenu); + contentModel = new QQmlObjectModel(q); +} + +QQuickItem *QQuickMenuPrivate::itemAt(int index) const +{ + return qobject_cast<QQuickItem *>(contentModel->get(index)); +} + +void QQuickMenuPrivate::insertItem(int index, QQuickItem *item) +{ + contentData.append(item); + item->setParentItem(contentItem); + if (qobject_cast<QQuickItemView *>(contentItem)) + QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262 + if (complete) + resizeItem(item); + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent); + contentModel->insert(index, item); + + QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item); + if (menuItem) { + Q_Q(QQuickMenu); + QObjectPrivate::connect(menuItem, &QQuickMenuItem::pressed, this, &QQuickMenuPrivate::onItemPressed); + QObject::connect(menuItem, &QQuickMenuItem::triggered, q, &QQuickPopup::close); + QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged); + } +} + +void QQuickMenuPrivate::moveItem(int from, int to) +{ + contentModel->move(from, to); +} + +void QQuickMenuPrivate::removeItem(int index, QQuickItem *item) +{ + contentData.removeOne(item); + + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent); + item->setParentItem(nullptr); + contentModel->remove(index); + + QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item); + if (menuItem) { + Q_Q(QQuickMenu); + QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::pressed, this, &QQuickMenuPrivate::onItemPressed); + QObject::disconnect(menuItem, &QQuickMenuItem::triggered, q, &QQuickPopup::close); + QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged); + } +} + +void QQuickMenuPrivate::resizeItem(QQuickItem *item) +{ + if (!item || !contentItem) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + if (!p->widthValid) { + item->setWidth(contentItem->width()); + p->widthValid = false; + } +} + +void QQuickMenuPrivate::resizeItems() +{ + if (!contentModel) + return; + + for (int i = 0; i < contentModel->count(); ++i) + resizeItem(itemAt(i)); +} + +void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child) +{ + // add dynamically reparented items (eg. by a Repeater) + if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child)) + insertItem(contentModel->count(), child); +} + +void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) +{ + // remove dynamically unparented items (eg. by a Repeater) + if (!parent) + removeItem(contentModel->indexOf(item, nullptr), item); +} + +void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *) +{ + // reorder the restacked items (eg. by a Repeater) + Q_Q(QQuickMenu); + QList<QQuickItem *> siblings = contentItem->childItems(); + for (int i = 0; i < siblings.count(); ++i) { + QQuickItem* sibling = siblings.at(i); + int index = contentModel->indexOf(sibling, nullptr); + q->moveItem(index, i); + } +} + +void QQuickMenuPrivate::itemDestroyed(QQuickItem *item) +{ + QQuickPopupPrivate::itemDestroyed(item); + int index = contentModel->indexOf(item, nullptr); + if (index != -1) + removeItem(index, item); +} + +void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +{ + if (complete) + resizeItems(); +} + +void QQuickMenuPrivate::onItemPressed() +{ + Q_Q(QQuickMenu); + QQuickItem *item = qobject_cast<QQuickItem*>(q->sender()); + if (item) + item->forceActiveFocus(); +} + +void QQuickMenuPrivate::onItemActiveFocusChanged() +{ + Q_Q(QQuickMenu); + QQuickItem *item = qobject_cast<QQuickItem*>(q->sender()); + if (!item->hasActiveFocus()) + return; + + int indexOfItem = contentModel->indexOf(item, nullptr); + setCurrentIndex(indexOfItem); +} + +int QQuickMenuPrivate::currentIndex() const +{ + QVariant index = contentItem->property("currentIndex"); + if (!index.isValid()) + return -1; + return index.toInt(); +} + +void QQuickMenuPrivate::setCurrentIndex(int index) +{ + contentItem->setProperty("currentIndex", index); +} + +void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj) +{ + QQuickMenuPrivate *p = static_cast<QQuickMenuPrivate *>(prop->data); + QQuickMenu *q = static_cast<QQuickMenu *>(prop->object); + QQuickItem *item = qobject_cast<QQuickItem *>(obj); + if (item) { + if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) { + QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder); + item->setParentItem(p->contentItem); + } else if (p->contentModel->indexOf(item, nullptr) == -1) { + q->addItem(item); + } + } else { + p->contentData.append(obj); + } +} + +int QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop) +{ + QQuickMenuPrivate *p = static_cast<QQuickMenuPrivate *>(prop->data); + return p->contentData.count(); +} + +QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, int index) +{ + QQuickMenuPrivate *p = static_cast<QQuickMenuPrivate *>(prop->data); + return p->contentData.value(index); +} + +void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop) +{ + QQuickMenuPrivate *p = static_cast<QQuickMenuPrivate *>(prop->data); + p->contentData.clear(); +} + +QQuickMenu::QQuickMenu(QObject *parent) : + QQuickPopup(*(new QQuickMenuPrivate), parent) +{ + setFocus(true); + setClosePolicy(CloseOnEscape | CloseOnPressOutside | CloseOnReleaseOutside); +} + +/*! + \qmlmethod Item QtQuick.Controls::Menu::itemAt(int index) + + Returns the item at \a index, or \c null if it does not exist. +*/ +QQuickItem *QQuickMenu::itemAt(int index) const +{ + Q_D(const QQuickMenu); + return d->itemAt(index); +} + +/*! + \qmlmethod void QtQuick.Controls::Menu::addItem(Item item) + + Adds \a item to the end of the list of items. +*/ +void QQuickMenu::addItem(QQuickItem *item) +{ + Q_D(QQuickMenu); + insertItem(d->contentModel->count(), item); +} + +/*! + \qmlmethod void QtQuick.Controls::Menu::insertItem(int index, Item item) + + Inserts \a item at \a index. +*/ +void QQuickMenu::insertItem(int index, QQuickItem *item) +{ + Q_D(QQuickMenu); + if (!item) + return; + const int count = d->contentModel->count(); + if (index < 0 || index > count) + index = count; + + int oldIndex = d->contentModel->indexOf(item, nullptr); + if (oldIndex != -1) { + if (oldIndex < index) + --index; + if (oldIndex != index) + d->moveItem(oldIndex, index); + } else { + d->insertItem(index, item); + } +} + +/*! + \qmlmethod void QtQuick.Controls::Menu::moveItem(int from, int to) + + Moves an item \a from one index \a to another. +*/ +void QQuickMenu::moveItem(int from, int to) +{ + Q_D(QQuickMenu); + const int count = d->contentModel->count(); + if (from < 0 || from > count - 1) + return; + if (to < 0 || to > count - 1) + to = count - 1; + + if (from != to) + d->moveItem(from, to); +} + +/*! + \qmlmethod void QtQuick.Controls::Menu::removeItem(int index) + + Removes the item at \a index. + + \note The ownership of the item is transferred to the caller. +*/ +void QQuickMenu::removeItem(int index) +{ + Q_D(QQuickMenu); + const int count = d->contentModel->count(); + if (index < 0 || index >= count) + return; + + QQuickItem *item = itemAt(index); + if (item) + d->removeItem(index, item); +} + +/*! + \qmlproperty model QtQuick.Controls::Menu::contentModel + \readonly + + This property holds the model used to display menu items. + + 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 +{ + Q_D(const QQuickMenu); + return QVariant::fromValue(d->contentModel); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::Menu::contentData + \default + + This property holds the list of content 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, {Popup::}{contentChildren} +*/ +QQmlListProperty<QObject> QQuickMenu::contentData() +{ + Q_D(QQuickMenu); + return QQmlListProperty<QObject>(this, d, + QQuickMenuPrivate::contentData_append, + QQuickMenuPrivate::contentData_count, + QQuickMenuPrivate::contentData_at, + QQuickMenuPrivate::contentData_clear); +} + +/*! + \qmlproperty string QtQuick.Controls::Menu::title + + This property holds the title for the menu. + + 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 +{ + Q_D(const QQuickMenu); + return d->title; +} + +void QQuickMenu::setTitle(QString &title) +{ + Q_D(QQuickMenu); + if (title == d->title) + return; + d->title = title; + emit titleChanged(); +} + +void QQuickMenu::componentComplete() +{ + Q_D(QQuickMenu); + QQuickPopup::componentComplete(); + d->resizeItems(); +} + +void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickMenu); + Q_UNUSED(oldItem); + QQuickPopup::contentItemChange(newItem, oldItem); + d->contentItem = newItem; +} + +void QQuickMenu::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + Q_D(QQuickMenu); + QQuickPopup::itemChange(change, data); + + if (change == QQuickItem::ItemVisibleHasChanged) { + if (!data.boolValue) { + // Ensure that when the menu isn't visible, there's no current item + // the next time it's opened. + QQuickItem *focusItem = QQuickItemPrivate::get(d->contentItem)->subFocusItem; + if (focusItem) { + QQuickWindow *window = QQuickPopup::window(); + if (window) + QQuickWindowPrivate::get(window)->clearFocusInScope(d->contentItem, focusItem, Qt::OtherFocusReason); + } + d->setCurrentIndex(-1); + } + } +} + +void QQuickMenu::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickMenu); + QQuickPopup::keyReleaseEvent(event); + if (d->contentModel->count() == 0) + return; + + // QTBUG-17051 + // Work around the fact that ListView has no way of distinguishing between + // mouse and keyboard interaction, thanks to the "interactive" bool in Flickable. + // What we actually want is to have a way to always allow keyboard interaction but + // only allow flicking with the mouse when there are too many menu items to be + // shown at once. + switch (event->key()) { + case Qt::Key_Up: + if (d->contentItem->metaObject()->indexOfMethod("decrementCurrentIndex()") != -1) + QMetaObject::invokeMethod(d->contentItem, "decrementCurrentIndex"); + break; + + case Qt::Key_Down: + if (d->contentItem->metaObject()->indexOfMethod("incrementCurrentIndex()") != -1) + QMetaObject::invokeMethod(d->contentItem, "incrementCurrentIndex"); + break; + + default: + break; + } + + int index = d->currentIndex(); + QQuickItem *item = itemAt(index); + if (item) + item->forceActiveFocus(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickMenu::accessibleRole() const +{ + return QAccessible::PopupMenu; +} +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#include "moc_qquickmenu_p.cpp" diff --git a/src/quicktemplates2/qquickmenu_p.h b/src/quicktemplates2/qquickmenu_p.h new file mode 100644 index 00000000..3a815b41 --- /dev/null +++ b/src/quicktemplates2/qquickmenu_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENU_P_H +#define QQUICKMENU_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/qqmllist.h> +#include <QtQml/qqml.h> + +#include "qquickpopup_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickMenuItem; +class QQuickMenuPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenu : public QQuickPopup +{ + Q_OBJECT + Q_PROPERTY(QVariant contentModel READ contentModel CONSTANT FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickMenu(QObject *parent = nullptr); + + Q_INVOKABLE QQuickItem *itemAt(int index) const; + Q_INVOKABLE void addItem(QQuickItem *item); + Q_INVOKABLE void insertItem(int index, QQuickItem *item); + Q_INVOKABLE void moveItem(int from, int to); + Q_INVOKABLE void removeItem(int index); + + QVariant contentModel() const; + QQmlListProperty<QObject> contentData(); + + QString title() const; + void setTitle(QString &title); + +protected: + void componentComplete() override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override; + void keyReleaseEvent(QKeyEvent *event) override; + +Q_SIGNALS: + void titleChanged(); + +protected: +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif // QT_NO_ACCESSIBILITY + +private: + Q_DISABLE_COPY(QQuickMenu) + Q_DECLARE_PRIVATE(QQuickMenu) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMenu) + +#endif // QQUICKMENU_P_H diff --git a/src/quicktemplates2/qquickmenu_p_p.h b/src/quicktemplates2/qquickmenu_p_p.h new file mode 100644 index 00000000..c27e6631 --- /dev/null +++ b/src/quicktemplates2/qquickmenu_p_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENU_P_P_H +#define QQUICKMENU_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvector.h> +#include <QtCore/qpointer.h> + +#include <QtQuickTemplates2/private/qquickpopup_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlObjectModel; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuPrivate : public QQuickPopupPrivate +{ + Q_DECLARE_PUBLIC(QQuickMenu) + +public: + QQuickMenuPrivate(); + + QQuickItem *itemAt(int index) const; + void insertItem(int index, QQuickItem *item); + void moveItem(int from, int to); + void removeItem(int index, QQuickItem *item); + + void resizeItem(QQuickItem *item); + void resizeItems(); + + void itemChildAdded(QQuickItem *item, QQuickItem *child) override; + void itemSiblingOrderChanged(QQuickItem *item) override; + void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; + void itemDestroyed(QQuickItem *item) override; + void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) override; + + void onItemPressed(); + void onItemActiveFocusChanged(); + + int currentIndex() const; + void setCurrentIndex(int index); + + static void contentData_append(QQmlListProperty<QObject> *prop, QObject *obj); + static int contentData_count(QQmlListProperty<QObject> *prop); + static QObject *contentData_at(QQmlListProperty<QObject> *prop, int index); + static void contentData_clear(QQmlListProperty<QObject> *prop); + + QQuickItem *contentItem; // TODO: cleanup + QVector<QObject *> contentData; + QQmlObjectModel *contentModel; + QString title; +}; + +QT_END_NAMESPACE + +#endif // QQUICKMENU_P_P_H + diff --git a/src/quicktemplates2/qquickmenuitem.cpp b/src/quicktemplates2/qquickmenuitem.cpp new file mode 100644 index 00000000..564d3f38 --- /dev/null +++ b/src/quicktemplates2/qquickmenuitem.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmenuitem_p.h" +#include "qquickabstractbutton_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> +#include <QtQuick/private/qquickevents_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MenuItem + \inherits AbstractButton + \instantiates QQuickMenuItem + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-menus + \brief Presents an item within a Menu. + + MenuItem is a convenience type that implements the AbstractButton API, + 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 + + MenuItem { + text: "New..." + onTriggered: document.reset() + } + MenuItem { + text: "Open..." + onTriggered: openDialog.open() + } + MenuItem { + text: "Save" + onTriggered: saveDialog.open() + } + } + } + \endcode + + \sa {Customizing MenuItem}, {Menu Controls} +*/ + +class QQuickMenuItemPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickMenuItem) + +public: + QQuickMenuItemPrivate(); + + bool highlighted; +}; + +QQuickMenuItemPrivate::QQuickMenuItemPrivate() : + highlighted(false) +{ +} + +/*! + \qmlsignal void QtQuick.Controls::MenuItem::triggered() + + This signal is emitted when the menu item is triggered by the user. +*/ + +QQuickMenuItem::QQuickMenuItem(QQuickItem *parent) : + QQuickAbstractButton(*(new QQuickMenuItemPrivate), parent) +{ + connect(this, &QQuickAbstractButton::clicked, this, &QQuickMenuItem::triggered); +} + +/*! + \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() +{ + emit checkableChanged(); +} + +QFont QQuickMenuItem::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::MenuItemFont); +} + +/*! + \qmlproperty bool QtQuick.Controls::MenuItem::highlighted + + This property holds whether the menu item is highlighted. + + A menu item can be highlighted in order to draw the user's attention + towards it. It has no effect on keyboard interaction. + + The default value is \c false. +*/ +bool QQuickMenuItem::isHighlighted() const +{ + Q_D(const QQuickMenuItem); + return d->highlighted; +} + +void QQuickMenuItem::setHighlighted(bool highlighted) +{ + Q_D(QQuickMenuItem); + if (highlighted == d->highlighted) + return; + + d->highlighted = highlighted; + emit highlightedChanged(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickMenuItem::accessibleRole() const +{ + return QAccessible::MenuItem; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickmenuitem_p.h b/src/quicktemplates2/qquickmenuitem_p.h new file mode 100644 index 00000000..6c717e13 --- /dev/null +++ b/src/quicktemplates2/qquickmenuitem_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENUITEM_P_H +#define QQUICKMENUITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickMenuItemPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuItem : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged FINAL) + Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL) + +public: + explicit QQuickMenuItem(QQuickItem *parent = nullptr); + + bool isHighlighted() const; + void setHighlighted(bool highlighted); + +Q_SIGNALS: + void checkableChanged(); + void triggered(); + void highlightedChanged(); + +protected: + void checkableChange() override; + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickMenuItem) + Q_DECLARE_PRIVATE(QQuickMenuItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMenuItem) + +#endif // QQUICKMENUITEM_P_H diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp new file mode 100644 index 00000000..4a95244f --- /dev/null +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickoverlay_p.h" +#include "qquickoverlay_p_p.h" +#include "qquickpopup_p_p.h" +#include "qquickdrawer_p_p.h" +#include "qquickapplicationwindow_p.h" +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmlproperty.h> +#include <QtQml/qqmlcomponent.h> +#include <algorithm> + +QT_BEGIN_NAMESPACE + +void QQuickOverlayPrivate::popupAboutToShow() +{ + Q_Q(QQuickOverlay); + QQuickPopup *popup = qobject_cast<QQuickPopup *>(q->sender()); + if (!popup || !popup->dim()) + return; + + // use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors + QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); + if (p->dimmer) + QQmlProperty::write(p->dimmer, QStringLiteral("opacity"), 1.0); +} + +void QQuickOverlayPrivate::popupAboutToHide() +{ + Q_Q(QQuickOverlay); + QQuickPopup *popup = qobject_cast<QQuickPopup *>(q->sender()); + if (!popup || !popup->dim()) + return; + + // use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors + QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); + if (p->dimmer) + QQmlProperty::write(p->dimmer, QStringLiteral("opacity"), 0.0); +} + +static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQuickItem *parent) +{ + QQuickItem *item = nullptr; + if (component) { + QQmlContext *creationContext = component->creationContext(); + if (!creationContext) + creationContext = qmlContext(parent); + QQmlContext *context = new QQmlContext(creationContext); + context->setContextObject(popup); + item = qobject_cast<QQuickItem*>(component->beginCreate(context)); + } + + // when there is no overlay component available (with plain QQuickWindow), + // use a plain QQuickItem as a fallback to block hover events + if (!item && popup->isModal()) + item = new QQuickItem; + + if (item) { + item->setOpacity(popup->isVisible() ? 1.0 : 0.0); + item->setParentItem(parent); + item->stackBefore(popup->popupItem()); + item->setZ(popup->z()); + if (popup->isModal()) { + item->setAcceptedMouseButtons(Qt::AllButtons); + // TODO: switch to QStyleHints::useHoverEffects in Qt 5.8 + item->setAcceptHoverEvents(true); + // item->setAcceptHoverEvents(QGuiApplication::styleHints()->useHoverEffects()); + // connect(QGuiApplication::styleHints(), &QStyleHints::useHoverEffectsChanged, item, &QQuickItem::setAcceptHoverEvents); + } + if (component) + component->completeCreate(); + } + return item; +} + +void QQuickOverlayPrivate::createOverlay(QQuickPopup *popup) +{ + Q_Q(QQuickOverlay); + QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); + if (!p->dimmer) + p->dimmer = createDimmer(popup->isModal() ? modal : modeless, popup, q); + p->resizeOverlay(); +} + +void QQuickOverlayPrivate::destroyOverlay(QQuickPopup *popup) +{ + QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); + if (p->dimmer) { + p->dimmer->setParentItem(nullptr); + p->dimmer->deleteLater(); + p->dimmer = nullptr; + } +} + +void QQuickOverlayPrivate::toggleOverlay() +{ + Q_Q(QQuickOverlay); + QQuickPopup *popup = qobject_cast<QQuickPopup *>(q->sender()); + if (!popup) + return; + + destroyOverlay(popup); + if (popup->dim()) + createOverlay(popup); +} + +QVector<QQuickPopup *> QQuickOverlayPrivate::stackingOrderPopups() const +{ + const QList<QQuickItem *> children = paintOrderChildItems(); + + QVector<QQuickPopup *> popups; + popups.reserve(children.count()); + + for (auto it = children.crbegin(), end = children.crend(); it != end; ++it) { + QQuickPopup *popup = qobject_cast<QQuickPopup *>((*it)->parent()); + if (popup) + popups += popup; + } + + return popups; +} + +QVector<QQuickDrawer *> QQuickOverlayPrivate::stackingOrderDrawers() const +{ + QVector<QQuickDrawer *> sorted(allDrawers); + std::sort(sorted.begin(), sorted.end(), [](const QQuickDrawer *one, const QQuickDrawer *another) { + return one->z() > another->z(); + }); + return sorted; +} + +void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &) +{ + Q_Q(QQuickOverlay); + q->setSize(newGeometry.size()); +} + +QQuickOverlayPrivate::QQuickOverlayPrivate() : + modal(nullptr), + modeless(nullptr) +{ +} + +void QQuickOverlayPrivate::addPopup(QQuickPopup *popup) +{ + Q_Q(QQuickOverlay); + allPopups += popup; + if (QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup)) { + allDrawers += drawer; + q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); + } +} + +void QQuickOverlayPrivate::removePopup(QQuickPopup *popup) +{ + Q_Q(QQuickOverlay); + allPopups.removeOne(popup); + if (allDrawers.removeOne(static_cast<QQuickDrawer *>(popup))) + q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); +} + +void QQuickOverlayPrivate::setMouseGrabberPopup(QQuickPopup *popup) +{ + if (popup && !popup->isVisible()) + popup = nullptr; + mouseGrabberPopup = popup; +} + +QQuickOverlay::QQuickOverlay(QQuickItem *parent) + : QQuickItem(*(new QQuickOverlayPrivate), parent) +{ + Q_D(QQuickOverlay); + setZ(1000001); // DefaultWindowDecoration+1 + setAcceptedMouseButtons(Qt::AllButtons); + setFiltersChildMouseEvents(true); + setVisible(false); + + if (parent) { + setSize(QSizeF(parent->width(), parent->height())); + QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Geometry); + } +} + +QQuickOverlay::~QQuickOverlay() +{ + Q_D(QQuickOverlay); + if (QQuickItem *parent = parentItem()) + QQuickItemPrivate::get(parent)->removeItemChangeListener(d, QQuickItemPrivate::Geometry); +} + +QQmlComponent *QQuickOverlay::modal() const +{ + Q_D(const QQuickOverlay); + return d->modal; +} + +void QQuickOverlay::setModal(QQmlComponent *modal) +{ + Q_D(QQuickOverlay); + if (d->modal == modal) + return; + + delete d->modal; + d->modal = modal; + emit modalChanged(); +} + +QQmlComponent *QQuickOverlay::modeless() const +{ + Q_D(const QQuickOverlay); + return d->modeless; +} + +void QQuickOverlay::setModeless(QQmlComponent *modeless) +{ + Q_D(QQuickOverlay); + if (d->modeless == modeless) + return; + + delete d->modeless; + d->modeless = modeless; + emit modelessChanged(); +} + +QQuickOverlay *QQuickOverlay::overlay(QQuickWindow *window) +{ + if (!window) + return nullptr; + + QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow *>(window); + if (applicationWindow) + return applicationWindow->overlay(); + + const char *name = "_q_QQuickOverlay"; + QQuickOverlay *overlay = window->property(name).value<QQuickOverlay *>(); + if (!overlay) { + QQuickItem *content = window->contentItem(); + // Do not re-create the overlay if the window is being destroyed + // and thus, its content item no longer has a window associated. + if (content->window()) { + overlay = new QQuickOverlay(window->contentItem()); + window->setProperty(name, QVariant::fromValue(overlay)); + } + } + return overlay; +} + +void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickOverlay); + QQuickItem::itemChange(change, data); + + QQuickPopup *popup = nullptr; + if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { + popup = qobject_cast<QQuickPopup *>(data.item->parent()); + setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); + } + if (!popup) + return; + + if (change == ItemChildAddedChange) { + if (popup->dim()) + d->createOverlay(popup); + QObjectPrivate::connect(popup, &QQuickPopup::dimChanged, d, &QQuickOverlayPrivate::toggleOverlay); + QObjectPrivate::connect(popup, &QQuickPopup::modalChanged, d, &QQuickOverlayPrivate::toggleOverlay); + if (!qobject_cast<QQuickDrawer *>(popup)) { + QObjectPrivate::connect(popup, &QQuickPopup::aboutToShow, d, &QQuickOverlayPrivate::popupAboutToShow); + QObjectPrivate::connect(popup, &QQuickPopup::aboutToHide, d, &QQuickOverlayPrivate::popupAboutToHide); + } + } else if (change == ItemChildRemovedChange) { + d->destroyOverlay(popup); + QObjectPrivate::disconnect(popup, &QQuickPopup::dimChanged, d, &QQuickOverlayPrivate::toggleOverlay); + QObjectPrivate::disconnect(popup, &QQuickPopup::modalChanged, d, &QQuickOverlayPrivate::toggleOverlay); + if (!qobject_cast<QQuickDrawer *>(popup)) { + QObjectPrivate::disconnect(popup, &QQuickPopup::aboutToShow, d, &QQuickOverlayPrivate::popupAboutToShow); + QObjectPrivate::disconnect(popup, &QQuickPopup::aboutToHide, d, &QQuickOverlayPrivate::popupAboutToHide); + } + } +} + +void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickOverlay); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + for (QQuickPopup *popup : qAsConst(d->allPopups)) + QQuickPopupPrivate::get(popup)->resizeOverlay(); +} + +void QQuickOverlay::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickOverlay); + emit pressed(); + + if (!d->allDrawers.isEmpty()) { + // the overlay background was pressed, so there are no modal popups open. + // test if the press point lands on any drawer's drag margin + + const QVector<QQuickDrawer *> drawers = d->stackingOrderDrawers(); + for (QQuickDrawer *drawer : drawers) { + QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); + if (p->startDrag(window(), event)) { + d->setMouseGrabberPopup(drawer); + return; + } + } + } + + if (!d->mouseGrabberPopup) { + const auto popups = d->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(this, event)) { + d->setMouseGrabberPopup(popup); + return; + } + } + } + + event->ignore(); +} + +void QQuickOverlay::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickOverlay); + if (d->mouseGrabberPopup) + d->mouseGrabberPopup->overlayEvent(this, event); +} + +void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickOverlay); + emit released(); + + if (d->mouseGrabberPopup) { + d->mouseGrabberPopup->overlayEvent(this, event); + d->setMouseGrabberPopup(nullptr); + } else { + const auto popups = d->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(this, event)) + break; + } + } +} + +void QQuickOverlay::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickOverlay); + if (d->mouseGrabberPopup) { + d->mouseGrabberPopup->overlayEvent(this, event); + return; + } else { + const auto popups = d->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(this, event)) + return; + } + } + event->ignore(); +} + +bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + Q_D(QQuickOverlay); + const auto popups = d->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); + + // Stop filtering overlay events when reaching a popup item or an item + // that is inside the popup. Let the popup content handle its events. + if (item == p->popupItem || p->popupItem->isAncestorOf(item)) + break; + + // Let the popup try closing itself when pressing or releasing over its + // background dimming OR over another popup underneath, in case the popup + // does not have background dimming. + if (item == p->dimmer || !p->popupItem->isAncestorOf(item)) { + switch (event->type()) { + case QEvent::MouseButtonPress: + emit pressed(); + if (popup->overlayEvent(item, event)) { + d->setMouseGrabberPopup(popup); + return true; + } + break; + case QEvent::MouseMove: + return popup->overlayEvent(item, event); + case QEvent::MouseButtonRelease: + emit released(); + d->setMouseGrabberPopup(nullptr); + return popup->overlayEvent(item, event); + default: + break; + } + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickoverlay_p.h b/src/quicktemplates2/qquickoverlay_p.h new file mode 100644 index 00000000..af16900f --- /dev/null +++ b/src/quicktemplates2/qquickoverlay_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKOVERLAY_P_H +#define QQUICKOVERLAY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/qquickitem.h> +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponent; +class QQuickOverlayPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlay : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QQmlComponent *modal READ modal WRITE setModal NOTIFY modalChanged FINAL) + Q_PROPERTY(QQmlComponent *modeless READ modeless WRITE setModeless NOTIFY modelessChanged FINAL) + +public: + explicit QQuickOverlay(QQuickItem *parent = nullptr); + ~QQuickOverlay(); + + QQmlComponent *modal() const; + void setModal(QQmlComponent *modal); + + QQmlComponent *modeless() const; + void setModeless(QQmlComponent *modeless); + + static QQuickOverlay *overlay(QQuickWindow *window); + +Q_SIGNALS: + void modalChanged(); + void modelessChanged(); + void pressed(); + void released(); + +protected: + void itemChange(ItemChange change, const ItemChangeData &data) override; + void geometryChanged(const QRectF &oldGeometry, const QRectF &newGeometry) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; + +private: + Q_DISABLE_COPY(QQuickOverlay) + Q_DECLARE_PRIVATE(QQuickOverlay) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickOverlay) + +#endif // QQUICKOVERLAY_P_H diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h new file mode 100644 index 00000000..6e72b39a --- /dev/null +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKOVERLAY_P_P_H +#define QQUICKOVERLAY_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickoverlay_p.h" + +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickPopup; +class QQuickDrawer; + +class QQuickOverlayPrivate : public QQuickItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickOverlay) + +public: + QQuickOverlayPrivate(); + + static QQuickOverlayPrivate *get(QQuickOverlay *overlay) + { + return overlay->d_func(); + } + + void addPopup(QQuickPopup *popup); + void removePopup(QQuickPopup *popup); + void setMouseGrabberPopup(QQuickPopup *popup); + + void popupAboutToShow(); + void popupAboutToHide(); + + void createOverlay(QQuickPopup *popup); + void destroyOverlay(QQuickPopup *popup); + void toggleOverlay(); + + QVector<QQuickPopup *> stackingOrderPopups() const; + QVector<QQuickDrawer *> stackingOrderDrawers() const; + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + + QQmlComponent *modal; + QQmlComponent *modeless; + QVector<QQuickPopup *> allPopups; + QVector<QQuickDrawer *> allDrawers; + QPointer<QQuickPopup> mouseGrabberPopup; +}; + +QT_END_NAMESPACE + +#endif // QQUICKOVERLAY_P_P_H diff --git a/src/quicktemplates2/qquickpage.cpp b/src/quicktemplates2/qquickpage.cpp new file mode 100644 index 00000000..c71ecd42 --- /dev/null +++ b/src/quicktemplates2/qquickpage.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpage_p.h" +#include "qquickcontrol_p_p.h" +#include "qquicktoolbar_p.h" +#include "qquicktabbar_p.h" + +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Page + \inherits Control + \instantiates QQuickPage + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Styled page control with support for a header and footer. + + Page is a container control which makes it convenient to add + a \l header and \l footer item to a page. + + \image qtquickcontrols2-page-wireframe.png + + The following example snippet illustrates how to use a page-specific + toolbar header and an application-wide tabbar footer. + + \qml + import QtQuick.Controls 2.0 + + ApplicationWindow { + visible: true + + StackView { + anchors.fill: parent + + initialItem: Page { + header: ToolBar { + // ... + } + } + } + + footer: TabBar { + // ... + } + } + \endqml + + \sa ApplicationWindow, {Container Controls} +*/ + +class QQuickPagePrivate : public QQuickControlPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickPage) + +public: + QQuickPagePrivate(); + + void relayout(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) override; + void itemVisibilityChanged(QQuickItem *item) override; + void itemImplicitWidthChanged(QQuickItem *item) override; + void itemImplicitHeightChanged(QQuickItem *item) override; + + QString title; + QQuickItem *header; + QQuickItem *footer; +}; + +QQuickPagePrivate::QQuickPagePrivate() : header(nullptr), footer(nullptr) +{ +} + +void QQuickPagePrivate::relayout() +{ + Q_Q(QQuickPage); + QQuickItem *content = q->contentItem(); + const qreal hh = header && header->isVisible() ? header->height() : 0; + const qreal fh = footer && footer->isVisible() ? footer->height() : 0; + + content->setY(hh + q->topPadding()); + content->setX(q->leftPadding()); + content->setWidth(q->availableWidth()); + content->setHeight(q->availableHeight() - hh - fh); + + if (header) + header->setWidth(q->width()); + + if (footer) { + footer->setY(q->height() - fh); + footer->setWidth(q->width()); + } +} + +void QQuickPagePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) +{ + Q_UNUSED(item) + Q_UNUSED(newRect) + Q_UNUSED(oldRect) + relayout(); +} + +void QQuickPagePrivate::itemVisibilityChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +void QQuickPagePrivate::itemImplicitWidthChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +void QQuickPagePrivate::itemImplicitHeightChanged(QQuickItem *item) +{ + Q_UNUSED(item); + relayout(); +} + +QQuickPage::QQuickPage(QQuickItem *parent) : + QQuickControl(*(new QQuickPagePrivate), parent) +{ + setFlag(ItemIsFocusScope); + setAcceptedMouseButtons(Qt::AllButtons); +} + +/*! + \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 +{ + return d_func()->title; +} + +void QQuickPage::setTitle(const QString &title) +{ + Q_D(QQuickPage); + if (d->title == title) + return; + + d->title = title; + setAccessibleName(title); + emit titleChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Page::header + + This property holds the page header item. The header item is positioned to + the top, and resized to the width of the page. The default value is \c null. + + \note Assigning a ToolBar or TabBar as a page header sets the respective + \l ToolBar::position or \l TabBar::position property automatically to \c Header. + + \sa footer, ApplicationWindow::header +*/ +QQuickItem *QQuickPage::header() const +{ + Q_D(const QQuickPage); + return d->header; +} + +void QQuickPage::setHeader(QQuickItem *header) +{ + Q_D(QQuickPage); + if (d->header == header) + return; + + if (d->header) { + QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + d->header->setParentItem(nullptr); + } + d->header = header; + if (header) { + header->setParentItem(this); + QQuickItemPrivate *p = QQuickItemPrivate::get(header); + p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + if (qFuzzyIsNull(header->z())) + header->setZ(1); + if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(header)) + toolBar->setPosition(QQuickToolBar::Header); + else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(header)) + tabBar->setPosition(QQuickTabBar::Header); + } + if (isComponentComplete()) + d->relayout(); + emit headerChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Page::footer + + This property holds the page footer item. The footer item is positioned to + the bottom, and resized to the width of the page. The default value is \c null. + + \note Assigning a ToolBar or TabBar as a page footer sets the respective + \l ToolBar::position or \l TabBar::position property automatically to \c Footer. + + \sa header, ApplicationWindow::footer +*/ +QQuickItem *QQuickPage::footer() const +{ + Q_D(const QQuickPage); + return d->footer; +} + +void QQuickPage::setFooter(QQuickItem *footer) +{ + Q_D(QQuickPage); + if (d->footer == footer) + return; + + if (d->footer) { + QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + d->footer->setParentItem(nullptr); + } + d->footer = footer; + if (footer) { + footer->setParentItem(this); + QQuickItemPrivate *p = QQuickItemPrivate::get(footer); + p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | + QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight); + if (qFuzzyIsNull(footer->z())) + footer->setZ(1); + if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(footer)) + toolBar->setPosition(QQuickToolBar::Footer); + else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(footer)) + tabBar->setPosition(QQuickTabBar::Footer); + } + if (isComponentComplete()) + d->relayout(); + emit footerChanged(); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::Page::contentData + \default + + This property holds the list of content 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() +{ + Q_D(QQuickPage); + return QQmlListProperty<QObject>(d->contentItem, nullptr, + QQuickItemPrivate::data_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + QQuickItemPrivate::data_clear); +} + +/*! + \qmlproperty list<Item> QtQuick.Controls::Page::contentChildren + + This property holds the list of content 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() +{ + Q_D(QQuickPage); + return QQmlListProperty<QQuickItem>(d->contentItem, nullptr, + QQuickItemPrivate::children_append, + QQuickItemPrivate::children_count, + QQuickItemPrivate::children_at, + QQuickItemPrivate::children_clear); +} + +void QQuickPage::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + QQuickControl::contentItemChange(newItem, oldItem); + if (oldItem) + disconnect(oldItem, &QQuickItem::childrenChanged, this, &QQuickPage::contentChildrenChanged); + if (newItem) + connect(newItem, &QQuickItem::childrenChanged, this, &QQuickPage::contentChildrenChanged); + emit contentChildrenChanged(); +} + +void QQuickPage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPage); + QQuickControl::geometryChanged(newGeometry, oldGeometry); + d->relayout(); +} + +void QQuickPage::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) +{ + Q_D(QQuickPage); + QQuickControl::paddingChange(newPadding, oldPadding); + d->relayout(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickPage::accessibleRole() const +{ + return QAccessible::PageTab; +} + +void QQuickPage::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickPage); + QQuickControl::accessibilityActiveChanged(active); + + if (active) + setAccessibleName(d->title); +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickpage_p.h b/src/quicktemplates2/qquickpage_p.h new file mode 100644 index 00000000..64f7602f --- /dev/null +++ b/src/quicktemplates2/qquickpage_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPAGE_P_H +#define QQUICKPAGE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +class QQuickPagePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPage : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL) + Q_PROPERTY(QQuickItem *header READ header WRITE setHeader NOTIFY headerChanged FINAL) + Q_PROPERTY(QQuickItem *footer READ footer WRITE setFooter NOTIFY footerChanged FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickPage(QQuickItem *parent = nullptr); + + QString title() const; + void setTitle(const QString &title); + + QQuickItem *header() const; + void setHeader(QQuickItem *header); + + QQuickItem *footer() const; + void setFooter(QQuickItem *footer); + + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + +Q_SIGNALS: + void titleChanged(); + void headerChanged(); + void footerChanged(); + void contentChildrenChanged(); + +protected: + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; + void accessibilityActiveChanged(bool active) override; +#endif + +private: + Q_DISABLE_COPY(QQuickPage) + Q_DECLARE_PRIVATE(QQuickPage) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPage) + +#endif // QQUICKPAGE_P_H diff --git a/src/quicktemplates2/qquickpageindicator.cpp b/src/quicktemplates2/qquickpageindicator.cpp new file mode 100644 index 00000000..0c1ed616 --- /dev/null +++ b/src/quicktemplates2/qquickpageindicator.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpageindicator_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtCore/qmath.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PageIndicator + \inherits Control + \instantiates QQuickPageIndicator + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-indicators + \brief Indicates the currently active page. + + PageIndicator is used to indicate the currently active page + in a container of multiple pages. PageIndicator consists of + delegate items that present pages. + + \image qtquickcontrols2-pageindicator.png + + \code + Column { + StackLayout { + id: stackLayout + + Page { + // ... + } + Page { + // ... + } + Page { + // ... + } + } + + PageIndicator { + currentIndex: stackLayout.currentIndex + count: stackLayout.count + } + } + \endcode + + \sa SwipeView, {Customizing PageIndicator}, {Indicator Controls} +*/ + +class QQuickPageIndicatorPrivate : public QQuickControlPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickPageIndicator) + +public: + QQuickPageIndicatorPrivate() : count(0), currentIndex(0), + interactive(false), delegate(nullptr), pressedItem(nullptr) + { + } + + QQuickItem *itemAt(const QPoint &pos) const; + void updatePressed(bool pressed, const QPoint &pos = QPoint()); + void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value); + + void itemChildAdded(QQuickItem *, QQuickItem *child); + + int count; + int currentIndex; + bool interactive; + QQmlComponent *delegate; + QQuickItem *pressedItem; +}; + +QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPoint &pos) const +{ + Q_Q(const QQuickPageIndicator); + if (!contentItem || !q->contains(pos)) + return nullptr; + + QPointF contentPos = q->mapToItem(contentItem, pos); + QQuickItem *item = contentItem->childAt(contentPos.x(), contentPos.y()); + while (item && item->parentItem() != contentItem) + item = item->parentItem(); + if (item && !QQuickItemPrivate::get(item)->isTransparentForPositioner()) + return item; + + // find the nearest + qreal distance = qInf(); + QQuickItem *nearest = nullptr; + const auto childItems = contentItem->childItems(); + for (QQuickItem *child : childItems) { + if (QQuickItemPrivate::get(child)->isTransparentForPositioner()) + continue; + + QPointF center = child->boundingRect().center(); + QPointF pt = contentItem->mapToItem(child, contentPos); + + qreal len = QLineF(center, pt).length(); + if (len < distance) { + distance = len; + nearest = child; + } + } + return nearest; +} + +void QQuickPageIndicatorPrivate::updatePressed(bool pressed, const QPoint &pos) +{ + QQuickItem *prevItem = pressedItem; + pressedItem = pressed ? itemAt(pos) : nullptr; + if (prevItem != pressedItem) { + setContextProperty(prevItem, QStringLiteral("pressed"), false); + setContextProperty(pressedItem, QStringLiteral("pressed"), pressed); + } +} + +void QQuickPageIndicatorPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value) +{ + QQmlContext *context = qmlContext(item); + if (context && context->isValid()) { + context = context->parentContext(); + if (context && context->isValid()) + context->setContextProperty(name, value); + } +} + +void QQuickPageIndicatorPrivate::itemChildAdded(QQuickItem *, QQuickItem *child) +{ + if (!QQuickItemPrivate::get(child)->isTransparentForPositioner()) + setContextProperty(child, QStringLiteral("pressed"), false); +} + +QQuickPageIndicator::QQuickPageIndicator(QQuickItem *parent) : + QQuickControl(*(new QQuickPageIndicatorPrivate), parent) +{ +} + +/*! + \qmlproperty int QtQuick.Controls::PageIndicator::count + + This property holds the number of pages. +*/ +int QQuickPageIndicator::count() const +{ + Q_D(const QQuickPageIndicator); + return d->count; +} + +void QQuickPageIndicator::setCount(int count) +{ + Q_D(QQuickPageIndicator); + if (d->count == count) + return; + + d->count = count; + emit countChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::PageIndicator::currentIndex + + This property holds the index of the current page. +*/ +int QQuickPageIndicator::currentIndex() const +{ + Q_D(const QQuickPageIndicator); + return d->currentIndex; +} + +void QQuickPageIndicator::setCurrentIndex(int index) +{ + Q_D(QQuickPageIndicator); + if (d->currentIndex == index) + return; + + d->currentIndex = index; + emit currentIndexChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::PageIndicator::interactive + + This property holds whether the control is interactive. An interactive page indicator + reacts to presses and automatically changes the \l {currentIndex}{current index} + appropriately. + + \snippet qtquickcontrols2-pageindicator-interactive.qml 1 + + \note Page indicators are typically quite small (in order to avoid + distracting the user from the actual content of the user interface). They + can be hard to click, and might not be easily recognized as interactive by + the user. For these reasons, they are best used to complement primary + methods of navigation (such as \l SwipeView), not replace them. + + The default value is \c false. +*/ +bool QQuickPageIndicator::isInteractive() const +{ + Q_D(const QQuickPageIndicator); + return d->interactive; +} + +void QQuickPageIndicator::setInteractive(bool interactive) +{ + Q_D(QQuickPageIndicator); + if (d->interactive == interactive) + return; + + d->interactive = interactive; + setAcceptedMouseButtons(interactive ? Qt::LeftButton : Qt::NoButton); + emit interactiveChanged(); +} + +/*! + \qmlproperty Component QtQuick.Controls::PageIndicator::delegate + + This property holds a delegate that presents a page. + + The following properties are available in the context of each delegate: + \table + \row \li \b index : int \li The index of the item + \row \li \b pressed : bool \li Whether the item is pressed + \endtable +*/ +QQmlComponent *QQuickPageIndicator::delegate() const +{ + Q_D(const QQuickPageIndicator); + return d->delegate; +} + +void QQuickPageIndicator::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQuickPageIndicator); + if (d->delegate == delegate) + return; + + d->delegate = delegate; + emit delegateChanged(); +} + +void QQuickPageIndicator::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickPageIndicator); + QQuickControl::contentItemChange(newItem, oldItem); + if (oldItem) + QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children); + if (newItem) + QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children); +} + +void QQuickPageIndicator::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickPageIndicator); + if (d->interactive) { + d->updatePressed(true, event->pos()); + event->accept(); + } +} + +void QQuickPageIndicator::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickPageIndicator); + if (d->interactive) { + d->updatePressed(true, event->pos()); + event->accept(); + } +} + +void QQuickPageIndicator::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickPageIndicator); + if (d->interactive) { + if (d->pressedItem) + setCurrentIndex(d->contentItem->childItems().indexOf(d->pressedItem)); + d->updatePressed(false); + event->accept(); + } +} + +void QQuickPageIndicator::mouseUngrabEvent() +{ + Q_D(QQuickPageIndicator); + if (d->interactive) + d->updatePressed(false); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickPageIndicator::accessibleRole() const +{ + return QAccessible::Indicator; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickpageindicator_p.h b/src/quicktemplates2/qquickpageindicator_p.h new file mode 100644 index 00000000..b5833835 --- /dev/null +++ b/src/quicktemplates2/qquickpageindicator_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPAGEINDICATOR_P_H +#define QQUICKPAGEINDICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponent; +class QQuickPageIndicatorPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPageIndicator : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged FINAL) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL) + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged FINAL) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL) + +public: + explicit QQuickPageIndicator(QQuickItem *parent = nullptr); + + int count() const; + void setCount(int count); + + int currentIndex() const; + void setCurrentIndex(int index); + + bool isInteractive() const; + void setInteractive(bool interactive); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *delegate); + +Q_SIGNALS: + void countChanged(); + void currentIndexChanged(); + void interactiveChanged(); + void delegateChanged(); + +protected: + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickPageIndicator) + Q_DECLARE_PRIVATE(QQuickPageIndicator) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPageIndicator) + +#endif // QQUICKPAGEINDICATOR_P_H diff --git a/src/quicktemplates2/qquickpane.cpp b/src/quicktemplates2/qquickpane.cpp new file mode 100644 index 00000000..9f9c139b --- /dev/null +++ b/src/quicktemplates2/qquickpane.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpane_p.h" +#include "qquickpane_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Pane + \inherits Control + \instantiates QQuickPane + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Provides a background matching with the application style and theme. + + Pane provides a background color that matches with the application style + and theme. Pane 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. + + Items declared as children of a Pane are automatically parented to the + Pane's \l {Control::}{contentItem}. Items created dynamically need to be + explicitly parented to the contentItem. + + \section1 Content Sizing + + If only a single item is used within a Pane, it will resize to fit the + implicit size of its contained item. This makes it particularly suitable + for use together with layouts. + + \image qtquickcontrols2-pane.png + + \snippet qtquickcontrols2-pane.qml 1 + + Sometimes there might be two items within the pane: + + \code + Pane { + SwipeView { + // ... + } + PageIndicator { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + } + \endcode + + In this case, Pane cannot calculate a sensible implicit size. Since we're + anchoring the \l PageIndicator over the \l SwipeView, we can simply set the + content size to the view's implicit size: + + \code + Pane { + contentWidth: view.implicitWidth + contentHeight: view.implicitHeight + + SwipeView { + id: view + // ... + } + PageIndicator { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + } + \endcode + + \sa {Customizing Pane}, {Container Controls} +*/ + +QQuickPanePrivate::QQuickPanePrivate() : contentWidth(0), contentHeight(0) +{ +} + +QQuickPane::QQuickPane(QQuickItem *parent) : + QQuickControl(*(new QQuickPanePrivate), parent) +{ + setFlag(QQuickItem::ItemIsFocusScope); + setAcceptedMouseButtons(Qt::AllButtons); +} + +QQuickPane::QQuickPane(QQuickPanePrivate &dd, QQuickItem *parent) : + QQuickControl(dd, parent) +{ + setFlag(QQuickItem::ItemIsFocusScope); + setAcceptedMouseButtons(Qt::AllButtons); +} + +/*! + \qmlproperty real QtQuick.Controls::Pane::contentWidth + + This property holds the content width. It is used for calculating the total + implicit width of the pane. + + For more information, see \l {Content Sizing}. + + \sa contentHeight +*/ +qreal QQuickPane::contentWidth() const +{ + Q_D(const QQuickPane); + return d->contentWidth; +} + +void QQuickPane::setContentWidth(qreal width) +{ + Q_D(QQuickPane); + if (qFuzzyCompare(d->contentWidth, width)) + return; + + d->contentWidth = width; + emit contentWidthChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Pane::contentHeight + + This property holds the content height. It is used for calculating the total + implicit height of the pane. + + For more information, see \l {Content Sizing}. + + \sa contentWidth +*/ +qreal QQuickPane::contentHeight() const +{ + Q_D(const QQuickPane); + return d->contentHeight; +} + +void QQuickPane::setContentHeight(qreal height) +{ + Q_D(QQuickPane); + if (qFuzzyCompare(d->contentHeight, height)) + return; + + d->contentHeight = height; + emit contentHeightChanged(); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::Pane::contentData + \default + + This property holds the list of content data. + + The list contains all objects that have been declared in QML as children + of the pane. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. + + \sa Item::data, contentChildren +*/ +QQmlListProperty<QObject> QQuickPane::contentData() +{ + Q_D(QQuickPane); + return QQmlListProperty<QObject>(d->contentItem, nullptr, + QQuickItemPrivate::data_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + QQuickItemPrivate::data_clear); +} + +/*! + \qmlproperty list<Item> QtQuick.Controls::Pane::contentChildren + + This property holds the list of content children. + + The list contains all items that have been declared in QML as children + of the pane. + + \note Unlike \c contentData, \c contentChildren does not include non-visual + QML objects. + + \sa Item::children, contentData +*/ +QQmlListProperty<QQuickItem> QQuickPane::contentChildren() +{ + Q_D(QQuickPane); + return QQmlListProperty<QQuickItem>(d->contentItem, nullptr, + QQuickItemPrivate::children_append, + QQuickItemPrivate::children_count, + QQuickItemPrivate::children_at, + QQuickItemPrivate::children_clear); +} + +void QQuickPane::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + QQuickControl::contentItemChange(newItem, oldItem); + if (oldItem) + disconnect(oldItem, &QQuickItem::childrenChanged, this, &QQuickPane::contentChildrenChanged); + if (newItem) + connect(newItem, &QQuickItem::childrenChanged, this, &QQuickPane::contentChildrenChanged); + emit contentChildrenChanged(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickPane::accessibleRole() const +{ + return QAccessible::Pane; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickpane_p.h b/src/quicktemplates2/qquickpane_p.h new file mode 100644 index 00000000..95834bdc --- /dev/null +++ b/src/quicktemplates2/qquickpane_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPANE_P_H +#define QQUICKPANE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +class QQuickPanePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPane : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged FINAL) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickPane(QQuickItem *parent = nullptr); + + qreal contentWidth() const; + void setContentWidth(qreal width); + + qreal contentHeight() const; + void setContentHeight(qreal height); + + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + +Q_SIGNALS: + void contentWidthChanged(); + void contentHeightChanged(); + void contentChildrenChanged(); + +protected: + QQuickPane(QQuickPanePrivate &dd, QQuickItem *parent); + + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickPane) + Q_DECLARE_PRIVATE(QQuickPane) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPane) + +#endif // QQUICKPANE_P_H diff --git a/src/quicktemplates2/qquickpane_p_p.h b/src/quicktemplates2/qquickpane_p_p.h new file mode 100644 index 00000000..acc0d0ab --- /dev/null +++ b/src/quicktemplates2/qquickpane_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPANE_P_P_H +#define QQUICKPANE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickPane; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPanePrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickPane) + +public: + QQuickPanePrivate(); + + qreal contentWidth; + qreal contentHeight; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPANE_P_P_H diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp new file mode 100644 index 00000000..bdcfe6ca --- /dev/null +++ b/src/quicktemplates2/qquickpopup.cpp @@ -0,0 +1,2398 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpopup_p.h" +#include "qquickpopup_p_p.h" +#include "qquickapplicationwindow_p.h" +#include "qquickshortcutcontext_p_p.h" +#include "qquickoverlay_p_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/private/qshortcutmap_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQml/qqmlinfo.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquicktransition_p.h> +#include <QtQuick/private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Popup + \inherits QtObject + \instantiates QQuickPopup + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-popups + \brief Base type of popup-like user interface controls. + + Popup is the base type of popup-like user interface controls. It can be + used with \l Window or \l ApplicationWindow. + + \qml + import QtQuick.Window 2.2 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + width: 400 + height: 400 + visible: true + + Button { + text: "Open" + onClicked: popup.open() + } + + Popup { + id: popup + x: 100 + y: 100 + width: 200 + height: 300 + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + } + } + \endqml + + In order to ensure that a popup is displayed above other items in the + scene, it is recommended to use ApplicationWindow. ApplicationWindow also + provides background dimming effects. + + Popup 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. + + Items declared as children of a Popup are automatically parented to the + Popups's \l contentItem. Items created dynamically need to be explicitly + parented to the contentItem. + + \section1 Popup Layout + + The following diagram illustrates the layout of a typical popup: + + \image qtquickcontrols2-popup.png + + The \l implicitWidth and \l implicitHeight of a popup are typically based + on the implicit sizes of the background and the content item plus any + \l padding. These properties determine how large the popup will be when no + explicit \l width or \l height is specified. + + The \l background item fills the entire width and height of the popup, + unless an explicit size has been given for it. + + The geometry of the \l contentItem is determined by the \l padding. + + \section1 Popup Sizing + + If only a single item is used within a Popup, it will resize to fit the + implicit size of its contained item. This makes it particularly suitable + for use together with layouts. + + \code + Popup { + ColumnLayout { + anchors.fill: parent + CheckBox { text: qsTr("E-mail") } + CheckBox { text: qsTr("Calendar") } + CheckBox { text: qsTr("Contacts") } + } + } + \endcode + + Sometimes there might be two items within the popup: + + \code + Popup { + SwipeView { + // ... + } + PageIndicator { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + } + \endcode + + In this case, Popup cannot calculate a sensible implicit size. Since we're + anchoring the \l PageIndicator over the \l SwipeView, we can simply set the + content size to the view's implicit size: + + \code + Popup { + contentWidth: view.implicitWidth + contentHeight: view.implicitHeight + + SwipeView { + id: view + // ... + } + PageIndicator { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + } + \endcode + + \sa {Popup Controls}, {Customizing Popup}, ApplicationWindow +*/ + +/*! + \qmlsignal void QtQuick.Controls::Popup::opened() + + This signal is emitted when the popup is opened. + + \sa closed +*/ + +/*! + \qmlsignal void QtQuick.Controls::Popup::closed() + + This signal is emitted when the popup is closed. + + \sa opened +*/ + +static const QQuickItemPrivate::ChangeTypes AncestorChangeTypes = QQuickItemPrivate::Geometry + | QQuickItemPrivate::Parent + | QQuickItemPrivate::Children; + +static const QQuickItemPrivate::ChangeTypes ItemChangeTypes = QQuickItemPrivate::Geometry + | QQuickItemPrivate::Parent; + +QQuickPopupPrivate::QQuickPopupPrivate() + : QObjectPrivate() + , focus(false) + , modal(false) + , dim(false) + , hasDim(false) + , visible(false) + , complete(false) + , positioning(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) + , effectiveX(0) + , effectiveY(0) + , margins(-1) + , topMargin(0) + , leftMargin(0) + , rightMargin(0) + , bottomMargin(0) + , contentWidth(0) + , contentHeight(0) + , transitionState(QQuickPopupPrivate::NoTransition) + , closePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutside) + , parentItem(nullptr) + , dimmer(nullptr) + , window(nullptr) + , enter(nullptr) + , exit(nullptr) + , popupItem(nullptr) + , positioner(this) + , transitionManager(this) +{ +} + +QQuickPopupPrivate::~QQuickPopupPrivate() +{ +} + +void QQuickPopupPrivate::init() +{ + Q_Q(QQuickPopup); + popupItem = new QQuickPopupItem(q); + q->setParentItem(qobject_cast<QQuickItem *>(parent)); + QObject::connect(popupItem, &QQuickControl::paddingChanged, q, &QQuickPopup::paddingChanged); +} + +bool QQuickPopupPrivate::tryClose(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickPopup); + const bool isPress = event->type() == QEvent::MouseButtonPress; + const bool onOutside = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutside : QQuickPopup::CloseOnReleaseOutside); + const bool onOutsideParent = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutsideParent : QQuickPopup::CloseOnReleaseOutsideParent); + if (onOutside || onOutsideParent) { + if (!popupItem->contains(item->mapToItem(popupItem, event->pos()))) { + if (!onOutsideParent || !parentItem || !parentItem->contains(item->mapToItem(parentItem, event->pos()))) { + q->close(); + return true; + } + } + } + return false; +} + +bool QQuickPopupPrivate::prepareEnterTransition() +{ + Q_Q(QQuickPopup); + if (!window) { + qmlInfo(q) << "cannot find any window to open popup in."; + return false; + } + + if (transitionState == EnterTransition && transitionManager.isRunning()) + return false; + + if (transitionState != EnterTransition) { + popupItem->setParentItem(QQuickOverlay::overlay(window)); + emit q->aboutToShow(); + visible = true; + transitionState = EnterTransition; + popupItem->setVisible(true); + positioner.setParentItem(parentItem); + emit q->visibleChanged(); + } + return true; +} + +bool QQuickPopupPrivate::prepareExitTransition() +{ + Q_Q(QQuickPopup); + if (transitionState == ExitTransition && transitionManager.isRunning()) + return false; + + if (transitionState != ExitTransition) { + if (focus) { + // The setFocus(false) call below removes any active focus before we're + // able to check it in finalizeExitTransition. + hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus(); + popupItem->setFocus(false); + } + transitionState = ExitTransition; + emit q->aboutToHide(); + } + return true; +} + +void QQuickPopupPrivate::finalizeEnterTransition() +{ + Q_Q(QQuickPopup); + if (focus) + popupItem->setFocus(true); + transitionState = NoTransition; + emit q->opened(); +} + +void QQuickPopupPrivate::finalizeExitTransition() +{ + Q_Q(QQuickPopup); + positioner.setParentItem(nullptr); + popupItem->setParentItem(nullptr); + popupItem->setVisible(false); + + if (hadActiveFocusBeforeExitTransition && window) { + if (!qobject_cast<QQuickPopupItem *>(window->activeFocusItem())) { + QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window); + if (applicationWindow) + applicationWindow->contentItem()->setFocus(true); + else + window->contentItem()->setFocus(true); + } + } + + visible = false; + transitionState = NoTransition; + hadActiveFocusBeforeExitTransition = false; + emit q->visibleChanged(); + emit q->closed(); +} + +QMarginsF QQuickPopupPrivate::getMargins() const +{ + Q_Q(const QQuickPopup); + return QMarginsF(q->leftMargin(), q->topMargin(), q->rightMargin(), q->bottomMargin()); +} + +void QQuickPopupPrivate::setTopMargin(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldMargin = q->topMargin(); + topMargin = value; + hasTopMargin = !reset; + if ((!reset && !qFuzzyCompare(oldMargin, value)) || (reset && !qFuzzyCompare(oldMargin, margins))) { + emit q->topMarginChanged(); + q->marginsChange(QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin), + QMarginsF(leftMargin, oldMargin, rightMargin, bottomMargin)); + } +} + +void QQuickPopupPrivate::setLeftMargin(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldMargin = q->leftMargin(); + leftMargin = value; + hasLeftMargin = !reset; + if ((!reset && !qFuzzyCompare(oldMargin, value)) || (reset && !qFuzzyCompare(oldMargin, margins))) { + emit q->leftMarginChanged(); + q->marginsChange(QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin), + QMarginsF(oldMargin, topMargin, rightMargin, bottomMargin)); + } +} + +void QQuickPopupPrivate::setRightMargin(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldMargin = q->rightMargin(); + rightMargin = value; + hasRightMargin = !reset; + if ((!reset && !qFuzzyCompare(oldMargin, value)) || (reset && !qFuzzyCompare(oldMargin, margins))) { + emit q->rightMarginChanged(); + q->marginsChange(QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin), + QMarginsF(leftMargin, topMargin, oldMargin, bottomMargin)); + } +} + +void QQuickPopupPrivate::setBottomMargin(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldMargin = q->bottomMargin(); + bottomMargin = value; + hasBottomMargin = !reset; + if ((!reset && !qFuzzyCompare(oldMargin, value)) || (reset && !qFuzzyCompare(oldMargin, margins))) { + emit q->bottomMarginChanged(); + q->marginsChange(QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin), + QMarginsF(leftMargin, topMargin, rightMargin, oldMargin)); + } +} + +void QQuickPopupPrivate::setWindow(QQuickWindow *newWindow) +{ + Q_Q(QQuickPopup); + if (window == newWindow) + return; + + if (window) { + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + if (overlay) + QQuickOverlayPrivate::get(overlay)->removePopup(q); + } + + if (newWindow) { + QQuickOverlay *overlay = QQuickOverlay::overlay(newWindow); + if (overlay) + QQuickOverlayPrivate::get(overlay)->addPopup(q); + } + + window = newWindow; + emit q->windowChanged(newWindow); +} + +class QQuickPopupItemPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickPopupItem) + +public: + QQuickPopupItemPrivate(QQuickPopup *popup); + + void implicitWidthChanged() override; + void implicitHeightChanged() override; + + void resolveFont() override; + + int backId; + int escapeId; + QQuickPopup *popup; +}; + +QQuickPopupItemPrivate::QQuickPopupItemPrivate(QQuickPopup *popup) + : backId(0), + escapeId(0), + popup(popup) +{ + isTabFence = true; +} + +void QQuickPopupItemPrivate::implicitWidthChanged() +{ + QQuickControlPrivate::implicitWidthChanged(); + emit popup->implicitWidthChanged(); +} + +void QQuickPopupItemPrivate::implicitHeightChanged() +{ + QQuickControlPrivate::implicitHeightChanged(); + emit popup->implicitHeightChanged(); +} + +void QQuickPopupItemPrivate::resolveFont() +{ + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(popup->window())) + inheritFont(window->font()); +} + +QQuickPopupItem::QQuickPopupItem(QQuickPopup *popup) : + QQuickControl(*(new QQuickPopupItemPrivate(popup)), nullptr) +{ + setParent(popup); + setVisible(false); + setFlag(ItemIsFocusScope); + setAcceptedMouseButtons(Qt::AllButtons); + + // TODO: switch to QStyleHints::useHoverEffects in Qt 5.8 + setAcceptHoverEvents(true); + // setAcceptHoverEvents(QGuiApplication::styleHints()->useHoverEffects()); + // connect(QGuiApplication::styleHints(), &QStyleHints::useHoverEffectsChanged, this, &QQuickItem::setAcceptHoverEvents); +} + +void QQuickPopupItem::updatePolish() +{ + Q_D(QQuickPopupItem); + return QQuickPopupPrivate::get(d->popup)->reposition(); +} + +void QQuickPopupItem::grabShortcut() +{ +#ifndef QT_NO_SHORTCUT + Q_D(QQuickPopupItem); + QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance(); + if (!d->backId) + d->backId = pApp->shortcutMap.addShortcut(this, Qt::Key_Back, Qt::WindowShortcut, QQuickShortcutContext::matcher); + if (!d->escapeId) + d->escapeId = pApp->shortcutMap.addShortcut(this, Qt::Key_Escape, Qt::WindowShortcut, QQuickShortcutContext::matcher); +#endif // QT_NO_SHORTCUT +} + +void QQuickPopupItem::ungrabShortcut() +{ +#ifndef QT_NO_SHORTCUT + Q_D(QQuickPopupItem); + QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance(); + if (d->backId) { + pApp->shortcutMap.removeShortcut(d->backId, this); + d->backId = 0; + } + if (d->escapeId) { + pApp->shortcutMap.removeShortcut(d->escapeId, this); + d->escapeId = 0; + } +#endif // QT_NO_SHORTCUT +} + +bool QQuickPopupItem::event(QEvent *event) +{ + Q_D(QQuickPopupItem); + if (event->type() == QEvent::Shortcut) { + QShortcutEvent *se = static_cast<QShortcutEvent *>(event); + if (se->shortcutId() == d->escapeId || se->shortcutId() == d->backId) { + d->popup->close(); + return true; + } + } + return QQuickItem::event(event); +} + +bool QQuickPopupItem::childMouseEventFilter(QQuickItem *child, QEvent *event) +{ + Q_D(QQuickPopupItem); + return d->popup->childMouseEventFilter(child, event); +} + +void QQuickPopupItem::focusInEvent(QFocusEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->focusInEvent(event); +} + +void QQuickPopupItem::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->focusOutEvent(event); +} + +void QQuickPopupItem::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->keyPressEvent(event); +} + +void QQuickPopupItem::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->keyReleaseEvent(event); +} + +void QQuickPopupItem::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mousePressEvent(event); +} + +void QQuickPopupItem::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseMoveEvent(event); +} + +void QQuickPopupItem::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseReleaseEvent(event); +} + +void QQuickPopupItem::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseDoubleClickEvent(event); +} + +void QQuickPopupItem::mouseUngrabEvent() +{ + Q_D(QQuickPopupItem); + d->popup->mouseUngrabEvent(); +} + +void QQuickPopupItem::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->wheelEvent(event); +} + +void QQuickPopupItem::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickPopupItem); + QQuickControl::contentItemChange(newItem, oldItem); + d->popup->contentItemChange(newItem, oldItem); +} + +void QQuickPopupItem::fontChange(const QFont &newFont, const QFont &oldFont) +{ + Q_D(QQuickPopupItem); + QQuickControl::fontChange(newFont, oldFont); + d->popup->fontChange(newFont, oldFont); +} + +void QQuickPopupItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPopupItem); + QQuickControl::geometryChanged(newGeometry, oldGeometry); + d->popup->geometryChanged(newGeometry, oldGeometry); +} + +void QQuickPopupItem::localeChange(const QLocale &newLocale, const QLocale &oldLocale) +{ + Q_D(QQuickPopupItem); + QQuickControl::localeChange(newLocale, oldLocale); + d->popup->localeChange(newLocale, oldLocale); +} + +void QQuickPopupItem::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickPopupItem); + QQuickControl::itemChange(change, data); + d->popup->itemChange(change, data); +} + +void QQuickPopupItem::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) +{ + Q_D(QQuickPopupItem); + QQuickControl::paddingChange(newPadding, oldPadding); + d->popup->paddingChange(newPadding, oldPadding); +} + +QFont QQuickPopupItem::defaultFont() const +{ + Q_D(const QQuickPopupItem); + return d->popup->defaultFont(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickPopupItem::accessibleRole() const +{ + Q_D(const QQuickPopupItem); + return d->popup->accessibleRole(); +} + +void QQuickPopupItem::accessibilityActiveChanged(bool active) +{ + Q_D(const QQuickPopupItem); + QQuickControl::accessibilityActiveChanged(active); + d->popup->accessibilityActiveChanged(active); +} +#endif // QT_NO_ACCESSIBILITY + +QQuickPopupPositioner::QQuickPopupPositioner(QQuickPopupPrivate *popup) : + m_parentItem(nullptr), + m_popup(popup) +{ +} + +QQuickPopupPositioner::~QQuickPopupPositioner() +{ + if (m_parentItem) { + QQuickItemPrivate::get(m_parentItem)->removeItemChangeListener(this, ItemChangeTypes); + removeAncestorListeners(m_parentItem->parentItem()); + } +} + +QQuickItem *QQuickPopupPositioner::parentItem() const +{ + return m_parentItem; +} + +void QQuickPopupPositioner::setParentItem(QQuickItem *parent) +{ + if (m_parentItem == parent) + return; + + if (m_parentItem) { + QQuickItemPrivate::get(m_parentItem)->removeItemChangeListener(this, ItemChangeTypes); + removeAncestorListeners(m_parentItem->parentItem()); + } + + m_parentItem = parent; + + if (!parent) + return; + + QQuickItemPrivate::get(parent)->addItemChangeListener(this, ItemChangeTypes); + addAncestorListeners(parent->parentItem()); + + if (m_popup->popupItem->isVisible()) + m_popup->reposition(); +} + +void QQuickPopupPositioner::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +{ + if (m_parentItem && m_popup->popupItem->isVisible()) + m_popup->reposition(); +} + +void QQuickPopupPositioner::itemParentChanged(QQuickItem *, QQuickItem *parent) +{ + addAncestorListeners(parent); +} + +void QQuickPopupPositioner::itemChildRemoved(QQuickItem *item, QQuickItem *child) +{ + if (isAncestor(child)) + removeAncestorListeners(item); +} + +void QQuickPopupPrivate::itemDestroyed(QQuickItem *item) +{ + Q_Q(QQuickPopup); + if (item == parentItem) + q->setParentItem(nullptr); +} + +void QQuickPopupPrivate::reposition() +{ + Q_Q(QQuickPopup); + if (!popupItem->isVisible()) + return; + + if (positioning) { + popupItem->polish(); + return; + } + + const qreal w = popupItem->width(); + const qreal h = popupItem->height(); + const qreal iw = popupItem->implicitWidth(); + const qreal ih = popupItem->implicitHeight(); + + bool widthAdjusted = false; + bool heightAdjusted = false; + + 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(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())) { + const QRectF flipped = parentItem->mapRectToScene(QRectF(parentItem->width() - x - rect.width(), y, rect.width(), rect.height())); + if (flipped.intersected(bounds).width() > rect.intersected(bounds).width()) + rect.moveLeft(flipped.left()); + } + + // if the popup doesn't fit vertically inside the window, try flipping it around (above <-> below) + if (allowVerticalFlip && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) { + const QRectF flipped = parentItem->mapRectToScene(QRectF(x, parentItem->height() - y - rect.height(), rect.width(), rect.height())); + if (flipped.intersected(bounds).height() > rect.intersected(bounds).height()) + rect.moveTop(flipped.top()); + } + + // push inside the margins if specified + 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 (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 (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) { + // restore original width + rect.setWidth(iw); + widthAdjusted = true; + } + + 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 (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 (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) { + // restore original height + rect.setHeight(ih); + heightAdjusted = true; + } + } + } + + positioning = true; + + popupItem->setPosition(rect.topLeft()); + + const QPointF effectivePos = parentItem ? parentItem->mapFromScene(rect.topLeft()) : rect.topLeft(); + if (!qFuzzyCompare(effectiveX, effectivePos.x())) { + effectiveX = effectivePos.x(); + emit q->xChanged(); + } + if (!qFuzzyCompare(effectiveY, effectivePos.y())) { + effectiveY = effectivePos.y(); + emit q->yChanged(); + } + + if (!hasWidth && widthAdjusted && rect.width() > 0) + popupItem->setWidth(rect.width()); + if (!hasHeight && heightAdjusted && rect.height() > 0) + popupItem->setHeight(rect.height()); + + positioning = false; +} + +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) + return; + + QQuickItem *p = item; + while (p) { + QQuickItemPrivate::get(p)->removeItemChangeListener(this, AncestorChangeTypes); + p = p->parentItem(); + } +} + +void QQuickPopupPositioner::addAncestorListeners(QQuickItem *item) +{ + if (item == m_parentItem) + return; + + QQuickItem *p = item; + while (p) { + QQuickItemPrivate::get(p)->addItemChangeListener(this, AncestorChangeTypes); + p = p->parentItem(); + } +} + +// TODO: use QQuickItem::isAncestorOf() in dev/5.7 +bool QQuickPopupPositioner::isAncestor(QQuickItem *item) const +{ + if (!m_parentItem) + return false; + + QQuickItem *parent = m_parentItem; + while (parent) { + if (parent == item) + return true; + parent = parent->parentItem(); + } + return false; +} + +QQuickPopupTransitionManager::QQuickPopupTransitionManager(QQuickPopupPrivate *popup) + : QQuickTransitionManager(), popup(popup) +{ +} + +void QQuickPopupTransitionManager::transitionEnter() +{ + if (popup->transitionState == QQuickPopupPrivate::ExitTransition) + cancel(); + + if (!popup->prepareEnterTransition()) + return; + + if (popup->window) + transition(popup->enterActions, popup->enter, popup->q_func()); + else + finished(); +} + +void QQuickPopupTransitionManager::transitionExit() +{ + if (!popup->prepareExitTransition()) + return; + + if (popup->window) + transition(popup->exitActions, popup->exit, popup->q_func()); + else + finished(); +} + +void QQuickPopupTransitionManager::finished() +{ + if (popup->transitionState == QQuickPopupPrivate::EnterTransition) + popup->finalizeEnterTransition(); + else if (popup->transitionState == QQuickPopupPrivate::ExitTransition) + popup->finalizeExitTransition(); +} + +QQuickPopup::QQuickPopup(QObject *parent) + : QObject(*(new QQuickPopupPrivate), parent) +{ + Q_D(QQuickPopup); + d->init(); +} + +QQuickPopup::QQuickPopup(QQuickPopupPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + Q_D(QQuickPopup); + d->init(); +} + +QQuickPopup::~QQuickPopup() +{ + Q_D(QQuickPopup); + setParentItem(nullptr); + d->popupItem->ungrabShortcut(); + delete d->popupItem; +} + +/*! + \qmlmethod void QtQuick.Controls::Popup::open() + + Opens the popup. + + \sa visible +*/ +void QQuickPopup::open() +{ + setVisible(true); +} + +/*! + \qmlmethod void QtQuick.Controls::Popup::close() + + Closes the popup. + + \sa visible +*/ +void QQuickPopup::close() +{ + setVisible(false); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::x + + This property holds the x-coordinate of the popup. + + \sa y, z +*/ +qreal QQuickPopup::x() const +{ + Q_D(const QQuickPopup); + return d->effectiveX; +} + +void QQuickPopup::setX(qreal x) +{ + Q_D(QQuickPopup); + setPosition(QPointF(x, d->y)); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::y + + This property holds the y-coordinate of the popup. + + \sa x, z +*/ +qreal QQuickPopup::y() const +{ + Q_D(const QQuickPopup); + return d->effectiveY; +} + +void QQuickPopup::setY(qreal y) +{ + Q_D(QQuickPopup); + setPosition(QPointF(d->x, y)); +} + +QPointF QQuickPopup::position() const +{ + Q_D(const QQuickPopup); + return QPointF(d->effectiveX, d->effectiveY); +} + +void QQuickPopup::setPosition(const QPointF &pos) +{ + Q_D(QQuickPopup); + const bool xChange = !qFuzzyCompare(d->x, pos.x()); + const bool yChange = !qFuzzyCompare(d->y, pos.y()); + if (!xChange && !yChange) + return; + + d->x = pos.x(); + d->y = pos.y(); + if (d->popupItem->isVisible()) { + d->reposition(); + } else { + if (xChange) + emit xChanged(); + if (yChange) + emit yChanged(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::z + + This property holds the z-value of the popup. Z-value determines + the stacking order of popups. + + If two visible popups have the same z-value, the last one that + was opened will be on top. + + The default z-value is \c 0. + + \sa x, y +*/ +qreal QQuickPopup::z() const +{ + Q_D(const QQuickPopup); + return d->popupItem->z(); +} + +void QQuickPopup::setZ(qreal z) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(z, d->popupItem->z())) + return; + d->popupItem->setZ(z); + emit zChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::width + + This property holds the width of the popup. +*/ +qreal QQuickPopup::width() const +{ + Q_D(const QQuickPopup); + return d->popupItem->width(); +} + +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(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::height + + This property holds the height of the popup. +*/ +qreal QQuickPopup::height() const +{ + Q_D(const QQuickPopup); + return d->popupItem->height(); +} + +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(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::implicitWidth + + This property holds the implicit width of the popup. +*/ +qreal QQuickPopup::implicitWidth() const +{ + Q_D(const QQuickPopup); + return d->popupItem->implicitWidth(); +} + +void QQuickPopup::setImplicitWidth(qreal width) +{ + Q_D(QQuickPopup); + d->popupItem->setImplicitWidth(width); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::implicitHeight + + This property holds the implicit height of the popup. +*/ +qreal QQuickPopup::implicitHeight() const +{ + Q_D(const QQuickPopup); + return d->popupItem->implicitHeight(); +} + +void QQuickPopup::setImplicitHeight(qreal height) +{ + Q_D(QQuickPopup); + d->popupItem->setImplicitHeight(height); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::contentWidth + + This property holds the content width. It is used for calculating the + total implicit width of the Popup. + + For more information, see \l {Popup Sizing}. + + \sa contentHeight +*/ +qreal QQuickPopup::contentWidth() const +{ + Q_D(const QQuickPopup); + return d->contentWidth; +} + +void QQuickPopup::setContentWidth(qreal width) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(d->contentWidth, width)) + return; + + d->contentWidth = width; + emit contentWidthChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::contentHeight + + This property holds the content height. It is used for calculating the + total implicit height of the Popup. + + For more information, see \l {Popup Sizing}. + + \sa contentWidth +*/ +qreal QQuickPopup::contentHeight() const +{ + Q_D(const QQuickPopup); + return d->contentHeight; +} + +void QQuickPopup::setContentHeight(qreal height) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(d->contentHeight, height)) + return; + + d->contentHeight = height; + emit contentHeightChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::availableWidth + \readonly + + This property holds the width available to the \l contentItem after + deducting horizontal padding from the \l {Item::}{width} of the popup. + + \sa padding, leftPadding, rightPadding +*/ +qreal QQuickPopup::availableWidth() const +{ + Q_D(const QQuickPopup); + return d->popupItem->availableWidth(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::availableHeight + \readonly + + This property holds the height available to the \l contentItem after + deducting vertical padding from the \l {Item::}{height} of the popup. + + \sa padding, topPadding, bottomPadding +*/ +qreal QQuickPopup::availableHeight() const +{ + Q_D(const QQuickPopup); + return d->popupItem->availableHeight(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::margins + + This property holds the default margins around the popup. + + A popup with negative margins is not pushed within the bounds + of the enclosing window. The default value is \c -1. + + \sa topMargin, leftMargin, rightMargin, bottomMargin +*/ +qreal QQuickPopup::margins() const +{ + Q_D(const QQuickPopup); + return d->margins; +} + +void QQuickPopup::setMargins(qreal margins) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(d->margins, margins)) + return; + QMarginsF oldMargins(leftMargin(), topMargin(), rightMargin(), bottomMargin()); + d->margins = margins; + emit marginsChanged(); + QMarginsF newMargins(leftMargin(), topMargin(), rightMargin(), bottomMargin()); + if (!qFuzzyCompare(newMargins.top(), oldMargins.top())) + emit topMarginChanged(); + if (!qFuzzyCompare(newMargins.left(), oldMargins.left())) + emit leftMarginChanged(); + if (!qFuzzyCompare(newMargins.right(), oldMargins.right())) + emit rightMarginChanged(); + if (!qFuzzyCompare(newMargins.bottom(), oldMargins.bottom())) + emit bottomMarginChanged(); + marginsChange(newMargins, oldMargins); +} + +void QQuickPopup::resetMargins() +{ + setMargins(-1); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::topMargin + + This property holds the top margin around the popup. + + A popup with a negative top margin is not pushed within the top edge + of the enclosing window. The default value is \c -1. + + \sa margins, bottomMargin +*/ +qreal QQuickPopup::topMargin() const +{ + Q_D(const QQuickPopup); + if (d->hasTopMargin) + return d->topMargin; + return d->margins; +} + +void QQuickPopup::setTopMargin(qreal margin) +{ + Q_D(QQuickPopup); + d->setTopMargin(margin); +} + +void QQuickPopup::resetTopMargin() +{ + Q_D(QQuickPopup); + d->setTopMargin(-1, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::leftMargin + + This property holds the left margin around the popup. + + A popup with a negative left margin is not pushed within the left edge + of the enclosing window. The default value is \c -1. + + \sa margins, rightMargin +*/ +qreal QQuickPopup::leftMargin() const +{ + Q_D(const QQuickPopup); + if (d->hasLeftMargin) + return d->leftMargin; + return d->margins; +} + +void QQuickPopup::setLeftMargin(qreal margin) +{ + Q_D(QQuickPopup); + d->setLeftMargin(margin); +} + +void QQuickPopup::resetLeftMargin() +{ + Q_D(QQuickPopup); + d->setLeftMargin(-1, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::rightMargin + + This property holds the right margin around the popup. + + A popup with a negative right margin is not pushed within the right edge + of the enclosing window. The default value is \c -1. + + \sa margins, leftMargin +*/ +qreal QQuickPopup::rightMargin() const +{ + Q_D(const QQuickPopup); + if (d->hasRightMargin) + return d->rightMargin; + return d->margins; +} + +void QQuickPopup::setRightMargin(qreal margin) +{ + Q_D(QQuickPopup); + d->setRightMargin(margin); +} + +void QQuickPopup::resetRightMargin() +{ + Q_D(QQuickPopup); + d->setRightMargin(-1, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::bottomMargin + + This property holds the bottom margin around the popup. + + A popup with a negative bottom margin is not pushed within the bottom edge + of the enclosing window. The default value is \c -1. + + \sa margins, topMargin +*/ +qreal QQuickPopup::bottomMargin() const +{ + Q_D(const QQuickPopup); + if (d->hasBottomMargin) + return d->bottomMargin; + return d->margins; +} + +void QQuickPopup::setBottomMargin(qreal margin) +{ + Q_D(QQuickPopup); + d->setBottomMargin(margin); +} + +void QQuickPopup::resetBottomMargin() +{ + Q_D(QQuickPopup); + d->setBottomMargin(-1, true); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::padding + + This property holds the default padding. + + \include qquickpopup-padding.qdocinc + + \sa availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding +*/ +qreal QQuickPopup::padding() const +{ + Q_D(const QQuickPopup); + return d->popupItem->padding(); +} + +void QQuickPopup::setPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->popupItem->setPadding(padding); +} + +void QQuickPopup::resetPadding() +{ + Q_D(QQuickPopup); + d->popupItem->resetPadding(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::topPadding + + This property holds the top padding. + + \include qquickpopup-padding.qdocinc + + \sa padding, bottomPadding, availableHeight +*/ +qreal QQuickPopup::topPadding() const +{ + Q_D(const QQuickPopup); + return d->popupItem->topPadding(); +} + +void QQuickPopup::setTopPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->popupItem->setTopPadding(padding); +} + +void QQuickPopup::resetTopPadding() +{ + Q_D(QQuickPopup); + d->popupItem->resetTopPadding(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::leftPadding + + This property holds the left padding. + + \include qquickpopup-padding.qdocinc + + \sa padding, rightPadding, availableWidth +*/ +qreal QQuickPopup::leftPadding() const +{ + Q_D(const QQuickPopup); + return d->popupItem->leftPadding(); +} + +void QQuickPopup::setLeftPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->popupItem->setLeftPadding(padding); +} + +void QQuickPopup::resetLeftPadding() +{ + Q_D(QQuickPopup); + d->popupItem->resetLeftPadding(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::rightPadding + + This property holds the right padding. + + \include qquickpopup-padding.qdocinc + + \sa padding, leftPadding, availableWidth +*/ +qreal QQuickPopup::rightPadding() const +{ + Q_D(const QQuickPopup); + return d->popupItem->rightPadding(); +} + +void QQuickPopup::setRightPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->popupItem->setRightPadding(padding); +} + +void QQuickPopup::resetRightPadding() +{ + Q_D(QQuickPopup); + d->popupItem->resetRightPadding(); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::bottomPadding + + This property holds the bottom padding. + + \include qquickpopup-padding.qdocinc + + \sa padding, topPadding, availableHeight +*/ +qreal QQuickPopup::bottomPadding() const +{ + Q_D(const QQuickPopup); + return d->popupItem->bottomPadding(); +} + +void QQuickPopup::setBottomPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->popupItem->setBottomPadding(padding); +} + +void QQuickPopup::resetBottomPadding() +{ + Q_D(QQuickPopup); + d->popupItem->resetBottomPadding(); +} + +/*! + \qmlproperty Locale QtQuick.Controls::Popup::locale + + This property holds the locale of the popup. + + \sa {LayoutMirroring}{LayoutMirroring} +*/ +QLocale QQuickPopup::locale() const +{ + Q_D(const QQuickPopup); + return d->popupItem->locale(); +} + +void QQuickPopup::setLocale(const QLocale &locale) +{ + Q_D(QQuickPopup); + d->popupItem->setLocale(locale); +} + +void QQuickPopup::resetLocale() +{ + Q_D(QQuickPopup); + d->popupItem->resetLocale(); +} + +/*! + \qmlproperty font QtQuick.Controls::Popup::font + + This property holds the font currently set for the popup. + + Popup propagates explicit font properties to its children. If you change a specific + property on a popup's font, that property propagates to all of the popup's children, + overriding any system defaults for that property. + + \code + Popup { + font.family: "Courier" + + Column { + Label { + text: qsTr("This will use Courier...") + } + + Switch { + text: qsTr("... and so will this") + } + } + } + \endcode + + \sa Control::font, ApplicationWindow::font +*/ +QFont QQuickPopup::font() const +{ + Q_D(const QQuickPopup); + return d->popupItem->font(); +} + +void QQuickPopup::setFont(const QFont &font) +{ + Q_D(QQuickPopup); + d->popupItem->setFont(font); +} + +void QQuickPopup::resetFont() +{ + Q_D(QQuickPopup); + d->popupItem->resetFont(); +} + +QQuickWindow *QQuickPopup::window() const +{ + Q_D(const QQuickPopup); + return d->window; +} + +QQuickItem *QQuickPopup::popupItem() const +{ + Q_D(const QQuickPopup); + return d->popupItem; +} + +/*! + \qmlproperty Item QtQuick.Controls::Popup::parent + + This property holds the parent item. +*/ +QQuickItem *QQuickPopup::parentItem() const +{ + Q_D(const QQuickPopup); + return d->parentItem; +} + +void QQuickPopup::setParentItem(QQuickItem *parent) +{ + Q_D(QQuickPopup); + if (d->parentItem == parent) + return; + + if (d->parentItem) { + QObjectPrivate::disconnect(d->parentItem, &QQuickItem::windowChanged, d, &QQuickPopupPrivate::setWindow); + QQuickItemPrivate::get(d->parentItem)->removeItemChangeListener(d, QQuickItemPrivate::Destroyed); + } + d->parentItem = parent; + if (d->positioner.parentItem()) + d->positioner.setParentItem(parent); + if (parent) { + QObjectPrivate::connect(parent, &QQuickItem::windowChanged, d, &QQuickPopupPrivate::setWindow); + QQuickItemPrivate::get(d->parentItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed); + + QQuickControlPrivate *p = QQuickControlPrivate::get(d->popupItem); + p->resolveFont(); + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(parent->window())) + p->updateLocale(window->locale(), false); // explicit=false + } else { + close(); + } + d->setWindow(parent ? parent->window() : nullptr); + emit parentChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Popup::background + + This property holds the background item. + + \note If the background item has no explicit size specified, it automatically + follows the popup's size. In most cases, there is no need to specify + width or height for a background item. + + \note Most popups use the implicit size of the background item to calculate + the implicit size of the popup 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 {Customizing Popup} +*/ +QQuickItem *QQuickPopup::background() const +{ + Q_D(const QQuickPopup); + return d->popupItem->background(); +} + +void QQuickPopup::setBackground(QQuickItem *background) +{ + Q_D(QQuickPopup); + if (d->popupItem->background() == background) + return; + + d->popupItem->setBackground(background); + emit backgroundChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Popup::contentItem + + This property holds the content item of the popup. + + The content item is the visual implementation of the popup. When the + popup is made visible, the content item is automatically reparented to + the \l {ApplicationWindow::overlay}{overlay item} of its application + window. + + \note The content item is automatically resized to fit within the + \l padding of the popup. + + \note Most popups use the implicit size of the content item to calculate + the implicit size of the popup 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). + + \sa {Customizing Popup} +*/ +QQuickItem *QQuickPopup::contentItem() const +{ + Q_D(const QQuickPopup); + return d->popupItem->contentItem(); +} + +void QQuickPopup::setContentItem(QQuickItem *item) +{ + Q_D(QQuickPopup); + d->popupItem->setContentItem(item); +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::Popup::contentData + \default + + This property holds the list of content data. + + The list contains all objects that have been declared in QML as children + of the popup. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. + + \sa Item::data, contentChildren +*/ +QQmlListProperty<QObject> QQuickPopup::contentData() +{ + Q_D(QQuickPopup); + return QQmlListProperty<QObject>(d->popupItem->contentItem(), nullptr, + QQuickItemPrivate::data_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + QQuickItemPrivate::data_clear); +} + +/*! + \qmlproperty list<Item> QtQuick.Controls::Popup::contentChildren + + This property holds the list of content children. + + The list contains all items that have been declared in QML as children + of the popup. + + \note Unlike \c contentData, \c contentChildren does not include non-visual + QML objects. + + \sa Item::children, contentData +*/ +QQmlListProperty<QQuickItem> QQuickPopup::contentChildren() +{ + Q_D(QQuickPopup); + return QQmlListProperty<QQuickItem>(d->popupItem->contentItem(), nullptr, + QQuickItemPrivate::children_append, + QQuickItemPrivate::children_count, + QQuickItemPrivate::children_at, + QQuickItemPrivate::children_clear); +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::clip + + This property holds whether clipping is enabled. The default value is \c false. +*/ +bool QQuickPopup::clip() const +{ + Q_D(const QQuickPopup); + return d->popupItem->clip(); +} + +void QQuickPopup::setClip(bool clip) +{ + Q_D(QQuickPopup); + if (clip == d->popupItem->clip()) + return; + d->popupItem->setClip(clip); + emit clipChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::focus + + This property holds whether the popup wants focus. + + When the popup actually receives focus, \l activeFocus will be \c true. + For more information, see \l {Keyboard Focus in Qt Quick}. + + The default value is \c false. + + \sa activeFocus +*/ +bool QQuickPopup::hasFocus() const +{ + Q_D(const QQuickPopup); + return d->focus; +} + +void QQuickPopup::setFocus(bool focus) +{ + Q_D(QQuickPopup); + if (d->focus == focus) + return; + d->focus = focus; + emit focusChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::activeFocus + \readonly + + This property holds whether the popup has active focus. + + \sa focus, {Keyboard Focus in Qt Quick} +*/ +bool QQuickPopup::hasActiveFocus() const +{ + Q_D(const QQuickPopup); + return d->popupItem->hasActiveFocus(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::modal + + This property holds whether the popup is modal. The default value is \c false. +*/ +bool QQuickPopup::isModal() const +{ + Q_D(const QQuickPopup); + return d->modal; +} + +void QQuickPopup::setModal(bool modal) +{ + Q_D(QQuickPopup); + if (d->modal == modal) + return; + d->modal = modal; + emit modalChanged(); + + if (!d->hasDim) { + setDim(modal); + d->hasDim = false; + } +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::dim + + This property holds whether the popup dims the background. + + Unless explicitly set, this property follows the value of \l modal. To + return to the default value, set this property to \c undefined. + + \sa modal +*/ +bool QQuickPopup::dim() const +{ + Q_D(const QQuickPopup); + return d->dim; +} + +void QQuickPopup::setDim(bool dim) +{ + Q_D(QQuickPopup); + d->hasDim = true; + + if (d->dim == dim) + return; + + d->dim = dim; + emit dimChanged(); +} + +void QQuickPopup::resetDim() +{ + Q_D(QQuickPopup); + if (!d->hasDim) + return; + + setDim(d->modal); + d->hasDim = false; +} + +/*! + \qmlproperty bool QtQuick.Controls::Popup::visible + + This property holds whether the popup is visible. The default value is \c false. + + \sa open(), close() +*/ +bool QQuickPopup::isVisible() const +{ + Q_D(const QQuickPopup); + return d->visible && d->popupItem->isVisible(); +} + +void QQuickPopup::setVisible(bool visible) +{ + Q_D(QQuickPopup); + if (d->visible == visible && d->transitionState != QQuickPopupPrivate::ExitTransition) + return; + + if (d->complete) { + if (visible) + d->transitionManager.transitionEnter(); + else + d->transitionManager.transitionExit(); + } else { + d->visible = visible; + } +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::opacity + + This property holds the opacity of the popup. Opacity is specified as a number between + \c 0.0 (fully transparent) and \c 1.0 (fully opaque). The default value is \c 1.0. + + \sa visible +*/ +qreal QQuickPopup::opacity() const +{ + Q_D(const QQuickPopup); + return d->popupItem->opacity(); +} + +void QQuickPopup::setOpacity(qreal opacity) +{ + Q_D(QQuickPopup); + d->popupItem->setOpacity(opacity); +} + +/*! + \qmlproperty real QtQuick.Controls::Popup::scale + + This property holds the scale factor of the popup. The default value is \c 1.0. + + A scale of less than \c 1.0 causes the popup to be rendered at a smaller size, + and a scale greater than \c 1.0 renders the popup at a larger size. A negative + scale causes the popup to be mirrored when rendered. +*/ +qreal QQuickPopup::scale() const +{ + Q_D(const QQuickPopup); + return d->popupItem->scale(); +} + +void QQuickPopup::setScale(qreal scale) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(scale, d->popupItem->scale())) + return; + d->popupItem->setScale(scale); + emit scaleChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Popup::closePolicy + + This property determines the circumstances under which the popup closes. + The flags can be combined to allow several ways of closing the popup. + + The available values are: + \value Popup.NoAutoClose The popup will only close when manually instructed to do so. + \value Popup.CloseOnPressOutside The popup will close when the mouse is pressed outside of it. + \value Popup.CloseOnPressOutsideParent The popup will close when the mouse is pressed outside of its parent. + \value Popup.CloseOnReleaseOutside The popup will close when the mouse is released outside of it. + \value Popup.CloseOnReleaseOutsideParent The popup will close when the mouse is released outside of its parent. + \value Popup.CloseOnEscape The popup will close when the escape key is pressed while the popup + has active focus. + + The default value is \c {Popup.CloseOnEscape | Popup.CloseOnPressOutside}. + + \note There is a known limitation that the \c Popup.CloseOnReleaseOutside + and \c Popup.CloseOnReleaseOutsideParent policies only work with + \l modal popups. +*/ +QQuickPopup::ClosePolicy QQuickPopup::closePolicy() const +{ + Q_D(const QQuickPopup); + return d->closePolicy; +} + +void QQuickPopup::setClosePolicy(ClosePolicy policy) +{ + Q_D(QQuickPopup); + if (d->closePolicy == policy) + return; + d->closePolicy = policy; + if (isVisible()) { + if (policy & QQuickPopup::CloseOnEscape) + d->popupItem->grabShortcut(); + else + d->popupItem->ungrabShortcut(); + } + emit closePolicyChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Popup::transformOrigin + + This property holds the origin point for transformations in enter and exit transitions. + + Nine transform origins are available, as shown in the image below. + The default transform origin is \c Popup.Center. + + \image qtquickcontrols2-popup-transformorigin.png + + \sa enter, exit, Item::transformOrigin +*/ +QQuickPopup::TransformOrigin QQuickPopup::transformOrigin() const +{ + Q_D(const QQuickPopup); + return static_cast<TransformOrigin>(d->popupItem->transformOrigin()); +} + +void QQuickPopup::setTransformOrigin(TransformOrigin origin) +{ + Q_D(QQuickPopup); + d->popupItem->setTransformOrigin(static_cast<QQuickItem::TransformOrigin>(origin)); +} + +/*! + \qmlproperty Transition QtQuick.Controls::Popup::enter + + This property holds the transition that is applied to the popup item + when the popup is opened and enters the screen. + + The following example animates the opacity of the popup when it enters + the screen: + \code + Popup { + enter: Transition { + NumberAnimation { property: "opacity"; from: 0.0; to: 1.0 } + } + } + \endcode + + \sa exit +*/ +QQuickTransition *QQuickPopup::enter() const +{ + Q_D(const QQuickPopup); + return d->enter; +} + +void QQuickPopup::setEnter(QQuickTransition *transition) +{ + Q_D(QQuickPopup); + if (d->enter == transition) + return; + d->enter = transition; + emit enterChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::Popup::exit + + This property holds the transition that is applied to the popup item + when the popup is closed and exits the screen. + + The following example animates the opacity of the popup when it exits + the screen: + \code + Popup { + exit: Transition { + NumberAnimation { property: "opacity"; from: 1.0; to: 0.0 } + } + } + \endcode + + \sa enter +*/ +QQuickTransition *QQuickPopup::exit() const +{ + Q_D(const QQuickPopup); + return d->exit; +} + +void QQuickPopup::setExit(QQuickTransition *transition) +{ + Q_D(QQuickPopup); + if (d->exit == transition) + return; + d->exit = transition; + emit exitChanged(); +} + +bool QQuickPopup::filtersChildMouseEvents() const +{ + Q_D(const QQuickPopup); + return d->popupItem->filtersChildMouseEvents(); +} + +void QQuickPopup::setFiltersChildMouseEvents(bool filter) +{ + Q_D(QQuickPopup); + d->popupItem->setFiltersChildMouseEvents(filter); +} + +/*! + \qmlmethod QtQuick.Controls::Popup::forceActiveFocus(reason = Qt.OtherFocusReason) + + Forces active focus on the popup with the given \a reason. + + This method sets focus on the popup and ensures that all ancestor + \l FocusScope objects in the object hierarchy are also given \l focus. + + \sa activeFocus, Qt::FocusReason +*/ +void QQuickPopup::forceActiveFocus(Qt::FocusReason reason) +{ + Q_D(QQuickPopup); + d->popupItem->forceActiveFocus(reason); +} + +void QQuickPopup::classBegin() +{ + Q_D(QQuickPopup); + d->popupItem->classBegin(); +} + +void QQuickPopup::componentComplete() +{ + Q_D(QQuickPopup); + d->complete = true; + if (!parentItem()) + setParentItem(qobject_cast<QQuickItem *>(parent())); + if (d->visible) + d->transitionManager.transitionEnter(); + d->popupItem->componentComplete(); +} + +bool QQuickPopup::isComponentComplete() const +{ + Q_D(const QQuickPopup); + return d->complete; +} + +bool QQuickPopup::childMouseEventFilter(QQuickItem *child, QEvent *event) +{ + Q_UNUSED(child); + Q_UNUSED(event); + return false; +} + +void QQuickPopup::focusInEvent(QFocusEvent *event) +{ + event->accept(); +} + +void QQuickPopup::focusOutEvent(QFocusEvent *event) +{ + event->accept(); +} + +void QQuickPopup::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickPopup); + event->accept(); + + if (hasActiveFocus() && (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab)) + QQuickItemPrivate::focusNextPrev(d->popupItem, event->key() == Qt::Key_Tab); +} + +void QQuickPopup::keyReleaseEvent(QKeyEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mousePressEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseMoveEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseReleaseEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseDoubleClickEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseUngrabEvent() +{ + QQuickOverlay *overlay = QQuickOverlay::overlay(window()); + if (overlay) { + QQuickOverlayPrivate *p = QQuickOverlayPrivate::get(overlay); + if (p->mouseGrabberPopup == this) + p->mouseGrabberPopup = nullptr; + } +} + +bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) +{ + Q_D(QQuickPopup); + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::MouseMove: + case QEvent::Wheel: + if (d->modal) + event->accept(); + return d->modal; + + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + if (d->modal) + event->accept(); + d->tryClose(item, static_cast<QMouseEvent *>(event)); + return d->modal; + + default: + return false; + } +} + +void QQuickPopup::wheelEvent(QWheelEvent *event) +{ + event->accept(); +} + +void QQuickPopup::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_UNUSED(newItem); + Q_UNUSED(oldItem); + emit contentItemChanged(); +} + +void QQuickPopup::fontChange(const QFont &newFont, const QFont &oldFont) +{ + Q_UNUSED(newFont); + Q_UNUSED(oldFont); + emit fontChanged(); +} + +void QQuickPopup::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPopup); + d->reposition(); + if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) { + emit widthChanged(); + emit availableWidthChanged(); + } + if (!qFuzzyCompare(newGeometry.height(), oldGeometry.height())) { + emit heightChanged(); + emit availableHeightChanged(); + } +} + +void QQuickPopup::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + Q_D(QQuickPopup); + + switch (change) { + case QQuickItem::ItemActiveFocusHasChanged: + emit activeFocusChanged(); + break; + case QQuickItem::ItemOpacityHasChanged: + emit opacityChanged(); + break; + case QQuickItem::ItemVisibleHasChanged: + if (isComponentComplete() && d->closePolicy & CloseOnEscape) { + if (data.boolValue) + d->popupItem->grabShortcut(); + else + d->popupItem->ungrabShortcut(); + } + default: + break; + } +} + +void QQuickPopup::localeChange(const QLocale &newLocale, const QLocale &oldLocale) +{ + Q_UNUSED(newLocale); + Q_UNUSED(oldLocale); + emit localeChanged(); +} + +void QQuickPopup::marginsChange(const QMarginsF &newMargins, const QMarginsF &oldMargins) +{ + Q_D(QQuickPopup); + Q_UNUSED(newMargins); + Q_UNUSED(oldMargins); + d->reposition(); +} + +void QQuickPopup::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) +{ + const bool tp = !qFuzzyCompare(newPadding.top(), oldPadding.top()); + const bool lp = !qFuzzyCompare(newPadding.left(), oldPadding.left()); + const bool rp = !qFuzzyCompare(newPadding.right(), oldPadding.right()); + const bool bp = !qFuzzyCompare(newPadding.bottom(), oldPadding.bottom()); + + if (tp) + emit topPaddingChanged(); + if (lp) + emit leftPaddingChanged(); + if (rp) + emit rightPaddingChanged(); + if (bp) + emit bottomPaddingChanged(); + + if (lp || rp) + emit availableWidthChanged(); + if (tp || bp) + emit availableHeightChanged(); +} + +QFont QQuickPopup::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::SystemFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickPopup::accessibleRole() const +{ + return QAccessible::Dialog; +} + +void QQuickPopup::accessibilityActiveChanged(bool active) +{ + Q_UNUSED(active); +} +#endif // QT_NO_ACCESSIBILITY + +QString QQuickPopup::accessibleName() const +{ + Q_D(const QQuickPopup); + return d->popupItem->accessibleName(); +} + +void QQuickPopup::setAccessibleName(const QString &name) +{ + Q_D(QQuickPopup); + d->popupItem->setAccessibleName(name); +} + +QVariant QQuickPopup::accessibleProperty(const char *propertyName) +{ + Q_D(const QQuickPopup); + return d->popupItem->accessibleProperty(propertyName); +} + +bool QQuickPopup::setAccessibleProperty(const char *propertyName, const QVariant &value) +{ + Q_D(QQuickPopup); + return d->popupItem->setAccessibleProperty(propertyName, value); +} + +QT_END_NAMESPACE + +#include "moc_qquickpopup_p.cpp" diff --git a/src/quicktemplates2/qquickpopup_p.h b/src/quicktemplates2/qquickpopup_p.h new file mode 100644 index 00000000..6393dcdc --- /dev/null +++ b/src/quicktemplates2/qquickpopup_p.h @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOPUP_P_H +#define QQUICKPOPUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qmargins.h> +#include <QtGui/qevent.h> +#include <QtCore/qlocale.h> +#include <QtGui/qfont.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> +#include <QtQml/qqml.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqmlparserstatus.h> +#include <QtQuick/qquickitem.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQuickWindow; +class QQuickPopupPrivate; +class QQuickTransition; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopup : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL) + Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL) + Q_PROPERTY(qreal width READ width WRITE setWidth RESET resetWidth NOTIFY widthChanged FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight RESET resetHeight NOTIFY heightChanged FINAL) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged FINAL) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged FINAL) + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged FINAL) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged FINAL) + Q_PROPERTY(qreal availableWidth READ availableWidth NOTIFY availableWidthChanged FINAL) + Q_PROPERTY(qreal availableHeight READ availableHeight NOTIFY availableHeightChanged FINAL) + Q_PROPERTY(qreal margins READ margins WRITE setMargins RESET resetMargins NOTIFY marginsChanged FINAL) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged FINAL) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged FINAL) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged FINAL) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged FINAL) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged FINAL) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged FINAL) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged FINAL) + Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged FINAL) + Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged FINAL) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL) + Q_PROPERTY(QFont font READ font WRITE setFont RESET resetFont NOTIFY fontChanged FINAL) + Q_PROPERTY(QQuickItem *parent READ parentItem WRITE setParentItem NOTIFY parentChanged FINAL) + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) + Q_PROPERTY(bool clip READ clip WRITE setClip NOTIFY clipChanged FINAL) + Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) + Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL) + Q_PROPERTY(bool modal READ isModal WRITE setModal NOTIFY modalChanged FINAL) + Q_PROPERTY(bool dim READ dim WRITE setDim RESET resetDim NOTIFY dimChanged FINAL) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged FINAL) + Q_PROPERTY(ClosePolicy closePolicy READ closePolicy WRITE setClosePolicy NOTIFY closePolicyChanged FINAL) + Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin) + Q_PROPERTY(QQuickTransition *enter READ enter WRITE setEnter NOTIFY enterChanged FINAL) + Q_PROPERTY(QQuickTransition *exit READ exit WRITE setExit NOTIFY exitChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickPopup(QObject *parent = nullptr); + ~QQuickPopup(); + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + + QPointF position() const; + void setPosition(const QPointF &pos); + + qreal z() const; + void setZ(qreal z); + + qreal width() const; + void setWidth(qreal width); + void resetWidth(); + + qreal height() const; + void setHeight(qreal height); + void resetHeight(); + + qreal implicitWidth() const; + void setImplicitWidth(qreal width); + + qreal implicitHeight() const; + void setImplicitHeight(qreal height); + + qreal contentWidth() const; + void setContentWidth(qreal width); + + qreal contentHeight() const; + void setContentHeight(qreal height); + + qreal availableWidth() const; + qreal availableHeight() const; + + qreal margins() const; + void setMargins(qreal margins); + void resetMargins(); + + qreal topMargin() const; + void setTopMargin(qreal margin); + void resetTopMargin(); + + qreal leftMargin() const; + void setLeftMargin(qreal margin); + void resetLeftMargin(); + + qreal rightMargin() const; + void setRightMargin(qreal margin); + void resetRightMargin(); + + qreal bottomMargin() const; + void setBottomMargin(qreal margin); + void resetBottomMargin(); + + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + + QLocale locale() const; + void setLocale(const QLocale &locale); + void resetLocale(); + + QFont font() const; + void setFont(const QFont &font); + void resetFont(); + + QQuickWindow *window() const; + QQuickItem *popupItem() const; + + QQuickItem *parentItem() const; + void setParentItem(QQuickItem *parent); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + + QQuickItem *contentItem() const; + void setContentItem(QQuickItem *item); + + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + + bool clip() const; + void setClip(bool clip); + + bool hasFocus() const; + void setFocus(bool focus); + + bool hasActiveFocus() const; + + bool isModal() const; + void setModal(bool modal); + + bool dim() const; + void setDim(bool dim); + void resetDim(); + + bool isVisible() const; + virtual void setVisible(bool visible); + + qreal opacity() const; + void setOpacity(qreal opacity); + + qreal scale() const; + void setScale(qreal scale); + + enum ClosePolicyFlag { + NoAutoClose = 0x00, + CloseOnPressOutside = 0x01, + CloseOnPressOutsideParent = 0x02, + CloseOnReleaseOutside = 0x04, + CloseOnReleaseOutsideParent = 0x08, + CloseOnEscape = 0x10 + }; + Q_DECLARE_FLAGS(ClosePolicy, ClosePolicyFlag) + Q_FLAG(ClosePolicy) + + ClosePolicy closePolicy() const; + void setClosePolicy(ClosePolicy policy); + + // keep in sync with Item.TransformOrigin + enum TransformOrigin { + TopLeft, Top, TopRight, + Left, Center, Right, + BottomLeft, Bottom, BottomRight + }; + Q_ENUM(TransformOrigin) + + TransformOrigin transformOrigin() const; + void setTransformOrigin(TransformOrigin); + + QQuickTransition *enter() const; + void setEnter(QQuickTransition *transition); + + QQuickTransition *exit() const; + void setExit(QQuickTransition *transition); + + bool filtersChildMouseEvents() const; + void setFiltersChildMouseEvents(bool filter); + + Q_INVOKABLE void forceActiveFocus(Qt::FocusReason reason = Qt::OtherFocusReason); + +public Q_SLOTS: + void open(); + void close(); + +Q_SIGNALS: + void xChanged(); + void yChanged(); + void zChanged(); + void widthChanged(); + void heightChanged(); + void implicitWidthChanged(); + void implicitHeightChanged(); + void contentWidthChanged(); + void contentHeightChanged(); + void availableWidthChanged(); + void availableHeightChanged(); + void marginsChanged(); + void topMarginChanged(); + void leftMarginChanged(); + void rightMarginChanged(); + void bottomMarginChanged(); + void paddingChanged(); + void topPaddingChanged(); + void leftPaddingChanged(); + void rightPaddingChanged(); + void bottomPaddingChanged(); + void fontChanged(); + void localeChanged(); + void parentChanged(); + void backgroundChanged(); + void contentItemChanged(); + void contentChildrenChanged(); + void clipChanged(); + void focusChanged(); + void activeFocusChanged(); + void modalChanged(); + void dimChanged(); + void visibleChanged(); + void opacityChanged(); + void scaleChanged(); + void closePolicyChanged(); + void enterChanged(); + void exitChanged(); + void windowChanged(QQuickWindow *window); + + void aboutToShow(); + void aboutToHide(); + void opened(); + void closed(); + +protected: + QQuickPopup(QQuickPopupPrivate &dd, QObject *parent); + + void classBegin() override; + void componentComplete() override; + bool isComponentComplete() const; + + virtual bool childMouseEventFilter(QQuickItem *child, QEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseUngrabEvent(); + virtual bool overlayEvent(QQuickItem *item, QEvent *event); + virtual void wheelEvent(QWheelEvent *event); + + virtual void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem); + virtual void fontChange(const QFont &newFont, const QFont &oldFont); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void localeChange(const QLocale &newLocale, const QLocale &oldLocale); + virtual void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data); + virtual void marginsChange(const QMarginsF &newMargins, const QMarginsF &oldMargins); + virtual void paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding); + + virtual QFont defaultFont() const; + +#ifndef QT_NO_ACCESSIBILITY + virtual QAccessible::Role accessibleRole() const; + virtual void accessibilityActiveChanged(bool active); +#endif + + QString accessibleName() const; + void setAccessibleName(const QString &name); + + QVariant accessibleProperty(const char *propertyName); + bool setAccessibleProperty(const char *propertyName, const QVariant &value); + +private: + Q_DISABLE_COPY(QQuickPopup) + Q_DECLARE_PRIVATE(QQuickPopup) + friend class QQuickPopupItem; + friend class QQuickOverlay; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPopup::ClosePolicy) + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPopup) + +#endif // QQUICKPOPUP_P_H diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h new file mode 100644 index 00000000..0c12b077 --- /dev/null +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOPUP_P_P_H +#define QQUICKPOPUP_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpopup_p.h" +#include "qquickcontrol_p.h" + +#include <QtCore/private/qobject_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuick/private/qquicktransitionmanager_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickTransition; +class QQuickTransitionManager; +class QQuickPopup; +class QQuickPopupPrivate; +class QQuickPopupItemPrivate; + +class QQuickPopupTransitionManager : public QQuickTransitionManager +{ +public: + QQuickPopupTransitionManager(QQuickPopupPrivate *popup); + + void transitionEnter(); + void transitionExit(); + +protected: + void finished() override; + +private: + QQuickPopupPrivate *popup; +}; + +class QQuickPopupItem : public QQuickControl +{ + Q_OBJECT + +public: + explicit QQuickPopupItem(QQuickPopup *popup); + + void grabShortcut(); + void ungrabShortcut(); + +protected: + void updatePolish() override; + + bool event(QEvent *event) override; + bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void wheelEvent(QWheelEvent *event) override; + + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + void fontChange(const QFont &newFont, const QFont &oldFont) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void localeChange(const QLocale &newLocale, const QLocale &oldLocale) override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + void paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) override; + + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; + void accessibilityActiveChanged(bool active) override; +#endif + +private: + Q_DECLARE_PRIVATE(QQuickPopupItem) + + friend class QQuickPopup; +}; + +class QQuickPopupPositioner : public QQuickItemChangeListener +{ +public: + explicit QQuickPopupPositioner(QQuickPopupPrivate *popup); + ~QQuickPopupPositioner(); + + QQuickItem *parentItem() const; + void setParentItem(QQuickItem *parent); + +protected: + void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) override; + void itemParentChanged(QQuickItem *, QQuickItem *parent) override; + void itemChildRemoved(QQuickItem *, QQuickItem *child) override; + +private: + void removeAncestorListeners(QQuickItem *item); + void addAncestorListeners(QQuickItem *item); + + bool isAncestor(QQuickItem *item) const; + + QQuickItem *m_parentItem; + QQuickPopupPrivate *m_popup; +}; + +class Q_AUTOTEST_EXPORT QQuickPopupPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickPopup) + +public: + QQuickPopupPrivate(); + ~QQuickPopupPrivate(); + + static QQuickPopupPrivate *get(QQuickPopup *popup) + { + return popup->d_func(); + } + + void init(); + bool tryClose(QQuickItem *item, QMouseEvent *event); + virtual void reposition(); + virtual void resizeOverlay(); + + virtual bool prepareEnterTransition(); + virtual bool prepareExitTransition(); + virtual void finalizeEnterTransition(); + virtual void finalizeExitTransition(); + + QMarginsF getMargins() const; + + void setTopMargin(qreal value, bool reset = false); + void setLeftMargin(qreal value, bool reset = false); + void setRightMargin(qreal value, bool reset = false); + void setBottomMargin(qreal value, bool reset = false); + + void setWindow(QQuickWindow *window); + void itemDestroyed(QQuickItem *item) override; + + enum TransitionState { + NoTransition, EnterTransition, ExitTransition + }; + + bool focus; + bool modal; + bool dim; + bool hasDim; + bool visible; + bool complete; + bool positioning; + 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; + qreal effectiveX; + qreal effectiveY; + qreal margins; + qreal topMargin; + qreal leftMargin; + qreal rightMargin; + qreal bottomMargin; + qreal contentWidth; + qreal contentHeight; + TransitionState transitionState; + QQuickPopup::ClosePolicy closePolicy; + QQuickItem *parentItem; + QQuickItem *dimmer; + QQuickWindow *window; + QQuickTransition *enter; + QQuickTransition *exit; + QQuickPopupItem *popupItem; + QQuickPopupPositioner positioner; + QList<QQuickStateAction> enterActions; + QList<QQuickStateAction> exitActions; + QQuickPopupTransitionManager transitionManager; + + friend class QQuickPopupTransitionManager; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPOPUP_P_P_H diff --git a/src/quicktemplates2/qquickpresshandler.cpp b/src/quicktemplates2/qquickpresshandler.cpp new file mode 100644 index 00000000..1e1a17bc --- /dev/null +++ b/src/quicktemplates2/qquickpresshandler.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpresshandler_p_p.h" + +#include <QtCore/private/qobject_p.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qstylehints.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickevents_p_p.h> + +QT_BEGIN_NAMESPACE + +QQuickPressHandler::QQuickPressHandler() + : control(nullptr) + , longPress(false) + , pressAndHoldSignalIndex(-1) + , delayedMousePressEvent(nullptr) +{ } + +void QQuickPressHandler::mousePressEvent(QMouseEvent *event) +{ + longPress = false; + pressPos = event->localPos(); + if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) { + timer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), control); + delayedMousePressEvent = new QMouseEvent(event->type(), event->pos(), event->button(), event->buttons(), event->modifiers()); + } else { + timer.stop(); + } +} + +void QQuickPressHandler::mouseMoveEvent(QMouseEvent *event) +{ + if (qAbs(int(event->localPos().x() - pressPos.x())) > QGuiApplication::styleHints()->startDragDistance()) + timer.stop(); +} + +void QQuickPressHandler::mouseReleaseEvent(QMouseEvent *) +{ + if (!longPress) + timer.stop(); +} + +void QQuickPressHandler::timerEvent(QTimerEvent *) +{ + timer.stop(); + clearDelayedMouseEvent(); + + if (pressAndHoldSignalIndex == -1) + pressAndHoldSignalIndex = control->metaObject()->indexOfSignal("pressAndHold(QQuickMouseEvent*)"); + Q_ASSERT(pressAndHoldSignalIndex != -1); + + longPress = QObjectPrivate::get(control)->isSignalConnected(pressAndHoldSignalIndex); + if (longPress) { + QQuickMouseEvent mev(pressPos.x(), pressPos.y(), Qt::LeftButton, Qt::LeftButton, + QGuiApplication::keyboardModifiers(), false/*isClick*/, true/*wasHeld*/); + mev.setAccepted(true); + // Use fast signal invocation since we already got its index + QQuickMouseEvent *mevPtr = &mev; + void *args[] = { nullptr, &mevPtr }; + QMetaObject::metacall(control, QMetaObject::InvokeMetaMethod, pressAndHoldSignalIndex, args); + if (!mev.isAccepted()) + longPress = false; + } +} + +void QQuickPressHandler::clearDelayedMouseEvent() +{ + if (delayedMousePressEvent) { + delete delayedMousePressEvent; + delayedMousePressEvent = 0; + } +} + +bool QQuickPressHandler::isActive() +{ + return !(timer.isActive() || longPress); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickpresshandler_p_p.h b/src/quicktemplates2/qquickpresshandler_p_p.h new file mode 100644 index 00000000..dec6f202 --- /dev/null +++ b/src/quicktemplates2/qquickpresshandler_p_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPRESSHANDLER_P_P_H +#define QQUICKPRESSHANDLER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qpoint.h> +#include <QtCore/qbasictimer.h> + +QT_BEGIN_NAMESPACE + +class QQuickItem; +class QMouseEvent; +class QTimerEvent; + +struct QQuickPressHandler +{ + QQuickPressHandler(); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void timerEvent(QTimerEvent *event); + + void clearDelayedMouseEvent(); + bool isActive(); + + QQuickItem *control; + QBasicTimer timer; + QPointF pressPos; + bool longPress; + int pressAndHoldSignalIndex; + QMouseEvent *delayedMousePressEvent; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPRESSHANDLER_P_P_H diff --git a/src/quicktemplates2/qquickprogressbar.cpp b/src/quicktemplates2/qquickprogressbar.cpp new file mode 100644 index 00000000..d8ada6f4 --- /dev/null +++ b/src/quicktemplates2/qquickprogressbar.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickprogressbar_p.h" +#include "qquickcontrol_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ProgressBar + \inherits Control + \instantiates QQuickProgressBar + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-indicators + \brief Indicates the progress of an operation. + + \image qtquickcontrols2-progressbar.gif + + ProgressBar indicates the progress of an operation. The value should be updated + regularly. The range is defined by \l from and \l to, which both can contain any value. + + \code + ProgressBar { + value: 0.5 + } + \endcode + + ProgressBar also supports a special \l indeterminate mode, which is useful, + for example, when unable to determine the size of the item being downloaded, + or if the download progress gets interrupted due to a network disconnection. + + \image qtquickcontrols2-progressbar-indeterminate.gif + + \code + ProgressBar { + indeterminate: true + } + \endcode + + The indeterminate mode is similar to a \l BusyIndicator. Both can be used + to indicate background activity. The main difference is visual, and that + ProgressBar can also present a concrete amount of progress (when it can be + determined). Due to the visual difference, indeterminate progress bars and + busy indicators fit different places in user interfaces. Typical places for + an indeterminate progress bar: + \list + \li at the bottom of a \l ToolBar + \li inline within the content of a \l Page + \li in an \l ItemDelegate to show the progress of a particular item + \endlist + + \sa {Customizing ProgressBar}, BusyIndicator, {Indicator Controls} +*/ + +class QQuickProgressBarPrivate : public QQuickControlPrivate +{ +public: + QQuickProgressBarPrivate() : from(0), to(1.0), value(0), indeterminate(false) + { + } + + qreal from; + qreal to; + qreal value; + bool indeterminate; +}; + +QQuickProgressBar::QQuickProgressBar(QQuickItem *parent) : + QQuickControl(*(new QQuickProgressBarPrivate), parent) +{ +} + +/*! + \qmlproperty real QtQuick.Controls::ProgressBar::from + + This property holds the starting value for the progress. The default value is \c 0.0. + + \sa to, value +*/ +qreal QQuickProgressBar::from() const +{ + Q_D(const QQuickProgressBar); + return d->from; +} + +void QQuickProgressBar::setFrom(qreal from) +{ + Q_D(QQuickProgressBar); + if (qFuzzyCompare(d->from, from)) + return; + + d->from = from; + emit fromChanged(); + emit positionChanged(); + emit visualPositionChanged(); + if (isComponentComplete()) + setValue(d->value); +} + +/*! + \qmlproperty real QtQuick.Controls::ProgressBar::to + + This property holds the end value for the progress. The default value is \c 1.0. + + \sa from, value +*/ +qreal QQuickProgressBar::to() const +{ + Q_D(const QQuickProgressBar); + return d->to; +} + +void QQuickProgressBar::setTo(qreal to) +{ + Q_D(QQuickProgressBar); + if (qFuzzyCompare(d->to, to)) + return; + + d->to = to; + emit toChanged(); + emit positionChanged(); + emit visualPositionChanged(); + if (isComponentComplete()) + setValue(d->value); +} + +/*! + \qmlproperty real QtQuick.Controls::ProgressBar::value + + This property holds the progress value. The default value is \c 0.0. + + \sa from, to, position +*/ +qreal QQuickProgressBar::value() const +{ + Q_D(const QQuickProgressBar); + return d->value; +} + +void QQuickProgressBar::setValue(qreal value) +{ + Q_D(QQuickProgressBar); + if (isComponentComplete()) + value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to); + + if (qFuzzyCompare(d->value, value)) + return; + + d->value = value; + emit valueChanged(); + emit positionChanged(); + emit visualPositionChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::ProgressBar::position + \readonly + + This property holds the logical position of the progress. + + The position is expressed as a fraction of the value, in the range + \c {0.0 - 1.0}. For visualizing the progress, the right-to-left + aware \l visualPosition should be used instead. + + \sa value, visualPosition +*/ +qreal QQuickProgressBar::position() const +{ + Q_D(const QQuickProgressBar); + if (qFuzzyCompare(d->from, d->to)) + return 0; + return (d->value - d->from) / (d->to - d->from); +} + +/*! + \qmlproperty real QtQuick.Controls::ProgressBar::visualPosition + \readonly + + This property holds the visual position of the progress. + + The position is expressed as a fraction of the value, in the range \c {0.0 - 1.0}. + When the control is \l {Control::mirrored}{mirrored}, \c visuaPosition is equal + to \c {1.0 - position}. This makes \c visualPosition suitable for visualizing + the progress, taking right-to-left support into account. + + \sa position, value +*/ +qreal QQuickProgressBar::visualPosition() const +{ + if (isMirrored()) + return 1.0 - position(); + return position(); +} + +/*! + \qmlproperty bool QtQuick.Controls::ProgressBar::indeterminate + + This property holds whether the progress bar is in indeterminate mode. + A progress bar in indeterminate mode displays that an operation is in progress, but it + doesn't show how much progress has been made. + + \image qtquickcontrols2-progressbar-indeterminate.gif + +*/ +bool QQuickProgressBar::isIndeterminate() const +{ + Q_D(const QQuickProgressBar); + return d->indeterminate; +} + +void QQuickProgressBar::setIndeterminate(bool indeterminate) +{ + Q_D(QQuickProgressBar); + if (d->indeterminate == indeterminate) + return; + + d->indeterminate = indeterminate; + emit indeterminateChanged(); +} + +void QQuickProgressBar::mirrorChange() +{ + QQuickControl::mirrorChange(); + if (!qFuzzyCompare(position(), qreal(0.5))) + emit visualPositionChanged(); +} + +void QQuickProgressBar::componentComplete() +{ + Q_D(QQuickProgressBar); + QQuickControl::componentComplete(); + setValue(d->value); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickProgressBar::accessibleRole() const +{ + return QAccessible::ProgressBar; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickprogressbar_p.h b/src/quicktemplates2/qquickprogressbar_p.h new file mode 100644 index 00000000..82adee77 --- /dev/null +++ b/src/quicktemplates2/qquickprogressbar_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPROGRESSBAR_P_H +#define QQUICKPROGRESSBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickProgressBarPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickProgressBar : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL) + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged FINAL) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL) + Q_PROPERTY(bool indeterminate READ isIndeterminate WRITE setIndeterminate NOTIFY indeterminateChanged FINAL) + +public: + explicit QQuickProgressBar(QQuickItem *parent = nullptr); + + qreal from() const; + void setFrom(qreal from); + + qreal to() const; + void setTo(qreal to); + + qreal value() const; + void setValue(qreal value); + + qreal position() const; + qreal visualPosition() const; + + bool isIndeterminate() const; + void setIndeterminate(bool indeterminate); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void valueChanged(); + void positionChanged(); + void visualPositionChanged(); + void indeterminateChanged(); + +protected: + void mirrorChange() override; + void componentComplete() override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickProgressBar) + Q_DECLARE_PRIVATE(QQuickProgressBar) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickProgressBar) + +#endif // QQUICKPROGRESSBAR_P_H diff --git a/src/quicktemplates2/qquickradiobutton.cpp b/src/quicktemplates2/qquickradiobutton.cpp new file mode 100644 index 00000000..acddb50d --- /dev/null +++ b/src/quicktemplates2/qquickradiobutton.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickradiobutton_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RadioButton + \inherits AbstractButton + \instantiates QQuickRadioButton + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Exclusive radio button that can be toggled on or off. + + \image qtquickcontrols2-radiobutton.gif + + RadioButton presents an option button that can be toggled on (checked) or + off (unchecked). Radio buttons are typically used to select one option + from a set of options. + + RadioButton inherits its API from \l 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 radio button can be set with the + \l {AbstractButton::}{checked} property. + + Radio buttons are \l {AbstractButton::autoExclusive}{auto-exclusive} + by default. Only one button can be checked at any time amongst radio + buttons that belong to the same parent item; checking another button + automatically unchecks the previously checked one. For radio buttons + that do not share a common parent, ButtonGroup can be used to manage + exclusivity. + + \l RadioDelegate is similar to RadioButton, except that it is typically + used in views. + + \code + ColumnLayout { + RadioButton { + checked: true + text: qsTr("First") + } + RadioButton { + text: qsTr("Second") + } + RadioButton { + text: qsTr("Third") + } + } + \endcode + + \sa ButtonGroup, {Customizing RadioButton}, {Button Controls}, RadioDelegate +*/ + +QQuickRadioButton::QQuickRadioButton(QQuickItem *parent) : + QQuickAbstractButton(parent) +{ + setCheckable(true); + setAutoExclusive(true); +} + +QFont QQuickRadioButton::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::RadioButtonFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickRadioButton::accessibleRole() const +{ + return QAccessible::RadioButton; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickradiobutton_p.h b/src/quicktemplates2/qquickradiobutton_p.h new file mode 100644 index 00000000..c9d9d8ea --- /dev/null +++ b/src/quicktemplates2/qquickradiobutton_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRADIOBUTTON_P_H +#define QQUICKRADIOBUTTON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioButton : public QQuickAbstractButton +{ + Q_OBJECT + +public: + explicit QQuickRadioButton(QQuickItem *parent = nullptr); + +protected: + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRadioButton) + +#endif // QQUICKRADIOBUTTON_P_H diff --git a/src/quicktemplates2/qquickradiodelegate.cpp b/src/quicktemplates2/qquickradiodelegate.cpp new file mode 100644 index 00000000..4580dcf2 --- /dev/null +++ b/src/quicktemplates2/qquickradiodelegate.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickradiodelegate_p.h" +#include "qquickabstractbutton_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RadioDelegate + \inherits ItemDelegate + \instantiates QQuickRadioDelegate + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-delegates + \brief Exclusive item delegate with a radio indicator that can be toggled on or off. + + \image qtquickcontrols2-radiodelegate.gif + + RadioDelegate presents an item delegate that can be toggled on (checked) or + off (unchecked). Radio delegates are typically used to select one option + from a set of options. + + RadioDelegate 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 radio delegate can be set with the + \l {AbstractButton::}{checked} property. + + Radio delegates are \l {AbstractButton::autoExclusive}{auto-exclusive} + by default. Only one delegate can be checked at any time amongst radio + delegates that belong to the same parent item; checking another delegate + automatically unchecks the previously checked one. For radio delegates + that do not share a common parent, ButtonGroup can be used to manage + exclusivity. + + \l RadioButton is similar to RadioDelegate, except that it is typically + not used in views, but rather when there are only a few options, and often + with the requirement that each button is uniquely identifiable. + + \code + ButtonGroup { + id: buttonGroup + } + + ListView { + model: ["Option 1", "Option 2", "Option 3"] + delegate: RadioDelegate { + text: modelData + checked: index == 0 + ButtonGroup.group: buttonGroup + } + } + \endcode + + \sa {Customizing RadioDelegate}, {Delegate Controls}, RadioButton +*/ + +QQuickRadioDelegate::QQuickRadioDelegate(QQuickItem *parent) : + QQuickItemDelegate(parent) +{ + setCheckable(true); + setAutoExclusive(true); +} + +QFont QQuickRadioDelegate::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ListViewFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickRadioDelegate::accessibleRole() const +{ + return QAccessible::RadioButton; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickradiodelegate_p.h b/src/quicktemplates2/qquickradiodelegate_p.h new file mode 100644 index 00000000..3f231fd7 --- /dev/null +++ b/src/quicktemplates2/qquickradiodelegate_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRADIODELEGATE_P_H +#define QQUICKRADIODELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickitemdelegate_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickRadioDelegatePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioDelegate : public QQuickItemDelegate +{ + Q_OBJECT + +public: + explicit QQuickRadioDelegate(QQuickItem *parent = nullptr); + +protected: + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRadioDelegate) + +#endif // QQUICKRADIODELEGATE_P_H diff --git a/src/quicktemplates2/qquickrangeslider.cpp b/src/quicktemplates2/qquickrangeslider.cpp new file mode 100644 index 00000000..26a5b010 --- /dev/null +++ b/src/quicktemplates2/qquickrangeslider.cpp @@ -0,0 +1,932 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickrangeslider_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtCore/qscopedpointer.h> +#include <QtQuick/private/qquickwindow_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RangeSlider + \inherits Control + \instantiates QQuickRangeSlider + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Used to select a range of values by sliding two handles along a track. + + \image qtquickcontrols2-rangeslider.gif + + RangeSlider is used to select a range specified by two values, by sliding + each handle along a track. + + In the example below, custom \l from and \l to values are set, and the + initial positions of the \l first and \l second handles are set: + + \code + RangeSlider { + from: 1 + to: 100 + first.value: 25 + second.value: 75 + } + \endcode + + The \l {first.position} and \l {second.position} properties are expressed as + fractions of the control's size, in the range \c {0.0 - 1.0}. + The \l {first.visualPosition} and \l {second.visualPosition} properties are + the same, except that they are reversed in a + \l {Right-to-left User Interfaces}{right-to-left} application. + The \c visualPosition is useful for positioning the handles when styling + RangeSlider. In the example above, \l {first.visualPosition} will be \c 0.24 + in a left-to-right application, and \c 0.76 in a right-to-left application. + + \sa {Customizing RangeSlider}, {Input Controls} +*/ + +class QQuickRangeSliderNodePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickRangeSliderNode) +public: + QQuickRangeSliderNodePrivate(qreal value, QQuickRangeSlider *slider) : + value(value), + isPendingValue(false), + pendingValue(0), + position(0), + handle(nullptr), + slider(slider), + pressed(false) + { + } + + bool isFirst() const; + + void setPosition(qreal position, bool ignoreOtherPosition = false); + void updatePosition(bool ignoreOtherPosition = false); + + static QQuickRangeSliderNodePrivate *get(QQuickRangeSliderNode *node); + +private: + friend class QQuickRangeSlider; + + qreal value; + bool isPendingValue; + qreal pendingValue; + qreal position; + QQuickItem *handle; + QQuickRangeSlider *slider; + bool pressed; +}; + +bool QQuickRangeSliderNodePrivate::isFirst() const +{ + return this == get(slider->first()); +} + +void QQuickRangeSliderNodePrivate::setPosition(qreal position, bool ignoreOtherPosition) +{ + Q_Q(QQuickRangeSliderNode); + + const qreal min = isFirst() || ignoreOtherPosition ? 0.0 : qMax<qreal>(0.0, slider->first()->position()); + const qreal max = !isFirst() || ignoreOtherPosition ? 1.0 : qMin<qreal>(1.0, slider->second()->position()); + position = qBound(min, position, max); + if (!qFuzzyCompare(this->position, position)) { + this->position = position; + emit q->positionChanged(); + emit q->visualPositionChanged(); + } +} + +void QQuickRangeSliderNodePrivate::updatePosition(bool ignoreOtherPosition) +{ + qreal pos = 0; + if (!qFuzzyCompare(slider->from(), slider->to())) + pos = (value - slider->from()) / (slider->to() - slider->from()); + setPosition(pos, ignoreOtherPosition); +} + +QQuickRangeSliderNodePrivate *QQuickRangeSliderNodePrivate::get(QQuickRangeSliderNode *node) +{ + return node->d_func(); +} + +QQuickRangeSliderNode::QQuickRangeSliderNode(qreal value, QQuickRangeSlider *slider) : + QObject(*(new QQuickRangeSliderNodePrivate(value, slider)), slider) +{ +} + +QQuickRangeSliderNode::~QQuickRangeSliderNode() +{ +} + +qreal QQuickRangeSliderNode::value() const +{ + Q_D(const QQuickRangeSliderNode); + return d->value; +} + +void QQuickRangeSliderNode::setValue(qreal value) +{ + Q_D(QQuickRangeSliderNode); + if (!d->slider->isComponentComplete()) { + d->pendingValue = value; + d->isPendingValue = true; + return; + } + + // First, restrict the first value to be within to and from. + const qreal smaller = qMin(d->slider->to(), d->slider->from()); + const qreal larger = qMax(d->slider->to(), d->slider->from()); + value = qBound(smaller, value, larger); + + // Then, ensure that it doesn't go past the other value, + // a check that depends on whether or not the range is inverted. + const bool invertedRange = d->slider->from() > d->slider->to(); + if (d->isFirst()) { + if (invertedRange) { + if (value < d->slider->second()->value()) + value = d->slider->second()->value(); + } else { + if (value > d->slider->second()->value()) + value = d->slider->second()->value(); + } + } else { + if (invertedRange) { + if (value > d->slider->first()->value()) + value = d->slider->first()->value(); + } else { + if (value < d->slider->first()->value()) + value = d->slider->first()->value(); + } + } + + if (!qFuzzyCompare(d->value, value)) { + d->value = value; + d->updatePosition(); + emit valueChanged(); + } +} + +qreal QQuickRangeSliderNode::position() const +{ + Q_D(const QQuickRangeSliderNode); + return d->position; +} + +qreal QQuickRangeSliderNode::visualPosition() const +{ + Q_D(const QQuickRangeSliderNode); + if (d->slider->orientation() == Qt::Vertical || d->slider->isMirrored()) + return 1.0 - d->position; + return d->position; +} + +QQuickItem *QQuickRangeSliderNode::handle() const +{ + Q_D(const QQuickRangeSliderNode); + return d->handle; +} + +void QQuickRangeSliderNode::setHandle(QQuickItem *handle) +{ + Q_D(QQuickRangeSliderNode); + if (d->handle == handle) + return; + + QQuickControlPrivate::get(d->slider)->deleteDelegate(d->handle); + d->handle = handle; + if (handle) { + if (!handle->parentItem()) + handle->setParentItem(d->slider); + + QQuickItem *firstHandle = d->slider->first()->handle(); + QQuickItem *secondHandle = d->slider->second()->handle(); + if (firstHandle && secondHandle) { + // The order of property assignments in QML is undefined, + // but we need the first handle to be before the second due + // to focus order constraints, so check for that here. + const QList<QQuickItem *> childItems = d->slider->childItems(); + const int firstIndex = childItems.indexOf(firstHandle); + const int secondIndex = childItems.indexOf(secondHandle); + if (firstIndex != -1 && secondIndex != -1 && firstIndex > secondIndex) { + firstHandle->stackBefore(secondHandle); + // Ensure we have some way of knowing which handle is above + // the other when it comes to mouse presses, and also that + // they are rendered in the correct order. + secondHandle->setZ(secondHandle->z() + 1); + } + } + + handle->setActiveFocusOnTab(true); + } + emit handleChanged(); +} + +bool QQuickRangeSliderNode::isPressed() const +{ + Q_D(const QQuickRangeSliderNode); + return d->pressed; +} + +void QQuickRangeSliderNode::setPressed(bool pressed) +{ + Q_D(QQuickRangeSliderNode); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + d->slider->setAccessibleProperty("pressed", pressed || d->slider->second()->isPressed()); + emit pressedChanged(); +} + +void QQuickRangeSliderNode::increase() +{ + Q_D(QQuickRangeSliderNode); + qreal step = qFuzzyIsNull(d->slider->stepSize()) ? 0.1 : d->slider->stepSize(); + setValue(d->value + step); +} + +void QQuickRangeSliderNode::decrease() +{ + Q_D(QQuickRangeSliderNode); + qreal step = qFuzzyIsNull(d->slider->stepSize()) ? 0.1 : d->slider->stepSize(); + setValue(d->value - step); +} + +static const qreal defaultFrom = 0.0; +static const qreal defaultTo = 1.0; + +class QQuickRangeSliderPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickRangeSlider) + +public: + QQuickRangeSliderPrivate() : + from(defaultFrom), + to(defaultTo), + stepSize(0), + first(nullptr), + second(nullptr), + orientation(Qt::Horizontal), + snapMode(QQuickRangeSlider::NoSnap) + { + } + + qreal from; + qreal to; + qreal stepSize; + QQuickRangeSliderNode *first; + QQuickRangeSliderNode *second; + QPoint pressPoint; + Qt::Orientation orientation; + QQuickRangeSlider::SnapMode snapMode; +}; + +static qreal valueAt(const QQuickRangeSlider *slider, qreal position) +{ + return slider->from() + (slider->to() - slider->from()) * position; +} + +static qreal snapPosition(const QQuickRangeSlider *slider, qreal position) +{ + const qreal range = slider->to() - slider->from(); + if (qFuzzyIsNull(range)) + return position; + + const qreal effectiveStep = slider->stepSize() / range; + if (qFuzzyIsNull(effectiveStep)) + return position; + + return qRound(position / effectiveStep) * effectiveStep; +} + +static qreal positionAt(const QQuickRangeSlider *slider, QQuickItem *handle, const QPoint &point) +{ + if (slider->orientation() == Qt::Horizontal) { + const qreal hw = handle ? handle->width() : 0; + const qreal offset = hw / 2; + const qreal extent = slider->availableWidth() - hw; + if (!qFuzzyIsNull(extent)) { + if (slider->isMirrored()) + return (slider->width() - point.x() - slider->rightPadding() - offset) / extent; + return (point.x() - slider->leftPadding() - offset) / extent; + } + } else { + const qreal hh = handle ? handle->height() : 0; + const qreal offset = hh / 2; + const qreal extent = slider->availableHeight() - hh; + if (!qFuzzyIsNull(extent)) + return (slider->height() - point.y() - slider->bottomPadding() - offset) / extent; + } + return 0; +} + +QQuickRangeSlider::QQuickRangeSlider(QQuickItem *parent) : + QQuickControl(*(new QQuickRangeSliderPrivate), parent) +{ + Q_D(QQuickRangeSlider); + d->first = new QQuickRangeSliderNode(0.0, this); + d->second = new QQuickRangeSliderNode(1.0, this); + + setAcceptedMouseButtons(Qt::LeftButton); + setFlag(QQuickItem::ItemIsFocusScope); +} + +/*! + \qmlproperty real QtQuick.Controls::RangeSlider::from + + This property holds the starting value for the range. The default value is \c 0.0. + + \sa to, first.value, second.value +*/ +qreal QQuickRangeSlider::from() const +{ + Q_D(const QQuickRangeSlider); + return d->from; +} + +void QQuickRangeSlider::setFrom(qreal from) +{ + Q_D(QQuickRangeSlider); + if (qFuzzyCompare(d->from, from)) + return; + + d->from = from; + emit fromChanged(); + + if (isComponentComplete()) { + d->first->setValue(d->first->value()); + d->second->setValue(d->second->value()); + } +} + +/*! + \qmlproperty real QtQuick.Controls::RangeSlider::to + + This property holds the end value for the range. The default value is \c 1.0. + + \sa from, first.value, second.value +*/ +qreal QQuickRangeSlider::to() const +{ + Q_D(const QQuickRangeSlider); + return d->to; +} + +void QQuickRangeSlider::setTo(qreal to) +{ + Q_D(QQuickRangeSlider); + if (qFuzzyCompare(d->to, to)) + return; + + d->to = to; + emit toChanged(); + + if (isComponentComplete()) { + d->first->setValue(d->first->value()); + d->second->setValue(d->second->value()); + } +} + +/*! + \qmlpropertygroup QtQuick.Controls::RangeSlider::first + \qmlproperty real QtQuick.Controls::RangeSlider::first.value + \qmlproperty real QtQuick.Controls::RangeSlider::first.position + \qmlproperty real QtQuick.Controls::RangeSlider::first.visualPosition + \qmlproperty Item QtQuick.Controls::RangeSlider::first.handle + \qmlproperty bool QtQuick.Controls::RangeSlider::first.pressed + + \table + \header + \li Property + \li Description + \row + \li value + \li This property holds the value of the first handle in the range + \c from - \c to. + + If \l to is greater than \l from, the value of the first handle + must be greater than the second, and vice versa. + + Unlike \l {first.position}{position}, value is not updated while the + handle is dragged, but rather when it has been released. + + The default value is \c 0.0. + \row + \li handle + \li This property holds the first handle item. + \row + \li visualPosition + \li This property holds the visual position of the first handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the + value is equal to \c {1.0 - position}. This makes the value suitable for + visualizing the slider, taking right-to-left support into account. + \row + \li position + \li This property holds the logical position of the first handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. Unlike \l {first.value}{value}, position is + continuously updated while the handle is dragged. For visualizing a + slider, the right-to-left aware + \l {first.visualPosition}{visualPosition} should be used instead. + \row + \li pressed + \li This property holds whether the first handle is pressed. + \endtable + + \sa first.increase(), first.decrease() +*/ +QQuickRangeSliderNode *QQuickRangeSlider::first() const +{ + Q_D(const QQuickRangeSlider); + return d->first; +} + +/*! + \qmlpropertygroup QtQuick.Controls::RangeSlider::second + \qmlproperty real QtQuick.Controls::RangeSlider::second.value + \qmlproperty real QtQuick.Controls::RangeSlider::second.position + \qmlproperty real QtQuick.Controls::RangeSlider::second.visualPosition + \qmlproperty Item QtQuick.Controls::RangeSlider::second.handle + \qmlproperty bool QtQuick.Controls::RangeSlider::second.pressed + + \table + \header + \li Property + \li Description + \row + \li value + \li This property holds the value of the second handle in the range + \c from - \c to. + + If \l to is greater than \l from, the value of the first handle + must be greater than the second, and vice versa. + + Unlike \l {second.position}{position}, value is not updated while the + handle is dragged, but rather when it has been released. + + The default value is \c 0.0. + \row + \li handle + \li This property holds the second handle item. + \row + \li visualPosition + \li This property holds the visual position of the second handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the + value is equal to \c {1.0 - position}. This makes the value suitable for + visualizing the slider, taking right-to-left support into account. + \row + \li position + \li This property holds the logical position of the second handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. Unlike \l {second.value}{value}, position is + continuously updated while the handle is dragged. For visualizing a + slider, the right-to-left aware + \l {second.visualPosition}{visualPosition} should be used instead. + \row + \li pressed + \li This property holds whether the second handle is pressed. + \endtable + + \sa second.increase(), second.decrease() +*/ +QQuickRangeSliderNode *QQuickRangeSlider::second() const +{ + Q_D(const QQuickRangeSlider); + return d->second; +} + +/*! + \qmlproperty real QtQuick.Controls::RangeSlider::stepSize + + This property holds the step size. The default value is \c 0.0. + + \sa snapMode, first.increase(), first.decrease() +*/ +qreal QQuickRangeSlider::stepSize() const +{ + Q_D(const QQuickRangeSlider); + return d->stepSize; +} + +void QQuickRangeSlider::setStepSize(qreal step) +{ + Q_D(QQuickRangeSlider); + if (qFuzzyCompare(d->stepSize, step)) + return; + + d->stepSize = step; + emit stepSizeChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::RangeSlider::snapMode + + This property holds the snap mode. + + Possible values: + \value RangeSlider.NoSnap The slider does not snap (default). + \value RangeSlider.SnapAlways The slider snaps while the handle is dragged. + \value RangeSlider.SnapOnRelease The slider does not snap while being dragged, but only after the handle is released. + + For visual explanations of the various modes, see the + \l {Slider::}{snapMode} documentation of \l Slider. + + \sa stepSize +*/ +QQuickRangeSlider::SnapMode QQuickRangeSlider::snapMode() const +{ + Q_D(const QQuickRangeSlider); + return d->snapMode; +} + +void QQuickRangeSlider::setSnapMode(SnapMode mode) +{ + Q_D(QQuickRangeSlider); + if (d->snapMode == mode) + return; + + d->snapMode = mode; + emit snapModeChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::RangeSlider::orientation + + This property holds the orientation. + + Possible values: + \value Qt.Horizontal Horizontal (default) + \value Qt.Vertical Vertical +*/ +Qt::Orientation QQuickRangeSlider::orientation() const +{ + Q_D(const QQuickRangeSlider); + return d->orientation; +} + +void QQuickRangeSlider::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickRangeSlider); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + emit orientationChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::RangeSlider::setValues(real firstValue, real secondValue) + + Sets \l first.value and \l second.value with the given arguments. + + If \a to is larger than \a from and \a firstValue is larger than + \a secondValue, \a firstValue will be clamped to \a secondValue. + + If \a from is larger than \a to and \a secondValue is larger than + \a firstValue, \a secondValue will be clamped to \a firstValue. + + This function may be necessary to set the first and second values + after the control has been completed, as there is a circular + dependency between firstValue and secondValue which can cause + assigned values to be clamped to each other. + + \sa stepSize +*/ +void QQuickRangeSlider::setValues(qreal firstValue, qreal secondValue) +{ + Q_D(QQuickRangeSlider); + // Restrict the values to be within to and from. + const qreal smaller = qMin(d->to, d->from); + const qreal larger = qMax(d->to, d->from); + firstValue = qBound(smaller, firstValue, larger); + secondValue = qBound(smaller, secondValue, larger); + + if (d->from > d->to) { + // If the from and to values are reversed, the secondValue + // might be less than the first value, which is not allowed. + if (secondValue > firstValue) + secondValue = firstValue; + } else { + // Otherwise, clamp first to second if it's too large. + if (firstValue > secondValue) + firstValue = secondValue; + } + + // Then set both values. If they didn't change, no change signal will be emitted. + QQuickRangeSliderNodePrivate *firstPrivate = QQuickRangeSliderNodePrivate::get(d->first); + if (firstValue != firstPrivate->value) { + firstPrivate->value = firstValue; + emit d->first->valueChanged(); + } + + QQuickRangeSliderNodePrivate *secondPrivate = QQuickRangeSliderNodePrivate::get(d->second); + if (secondValue != secondPrivate->value) { + secondPrivate->value = secondValue; + emit d->second->valueChanged(); + } + + // After we've set both values, then we can update the positions. + // If we don't do this last, the positions may be incorrect. + firstPrivate->updatePosition(true); + secondPrivate->updatePosition(); +} + +void QQuickRangeSlider::focusInEvent(QFocusEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::focusInEvent(event); + + // The active focus ends up to RangeSlider when using forceActiveFocus() + // or QML KeyNavigation. We must forward the focus to one of the handles, + // because RangeSlider handles key events for the focused handle. If + // neither handle has active focus, RangeSlider doesn't do anything. + QQuickItem *handle = nextItemInFocusChain(); + // QQuickItem::nextItemInFocusChain() only works as desired with + // Qt::TabFocusAllControls. otherwise pick the first handle + if (!handle || handle == this) + handle = d->first->handle(); + if (handle) + handle->forceActiveFocus(event->reason()); +} + +void QQuickRangeSlider::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::keyPressEvent(event); + + QQuickRangeSliderNode *focusNode = d->first->handle()->hasActiveFocus() + ? d->first : (d->second->handle()->hasActiveFocus() ? d->second : nullptr); + if (!focusNode) + return; + + if (d->orientation == Qt::Horizontal) { + if (event->key() == Qt::Key_Left) { + focusNode->setPressed(true); + if (isMirrored()) + focusNode->increase(); + else + focusNode->decrease(); + event->accept(); + } else if (event->key() == Qt::Key_Right) { + focusNode->setPressed(true); + if (isMirrored()) + focusNode->decrease(); + else + focusNode->increase(); + event->accept(); + } + } else { + if (event->key() == Qt::Key_Up) { + focusNode->setPressed(true); + focusNode->increase(); + event->accept(); + } else if (event->key() == Qt::Key_Down) { + focusNode->setPressed(true); + focusNode->decrease(); + event->accept(); + } + } +} + +void QQuickRangeSlider::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::keyReleaseEvent(event); + d->first->setPressed(false); + d->second->setPressed(false); +} + +void QQuickRangeSlider::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::mousePressEvent(event); + d->pressPoint = event->pos(); + + QQuickItem *firstHandle = d->first->handle(); + QQuickItem *secondHandle = d->second->handle(); + const bool firstHit = firstHandle && firstHandle->contains(mapToItem(firstHandle, d->pressPoint)); + const bool secondHit = secondHandle && secondHandle->contains(mapToItem(secondHandle, d->pressPoint)); + QQuickRangeSliderNode *hitNode = nullptr; + QQuickRangeSliderNode *otherNode = nullptr; + + if (firstHit && secondHit) { + // choose highest + hitNode = firstHandle->z() > secondHandle->z() ? d->first : d->second; + otherNode = firstHandle->z() > secondHandle->z() ? d->second : d->first; + } else if (firstHit) { + hitNode = d->first; + otherNode = d->second; + } else if (secondHit) { + hitNode = d->second; + otherNode = d->first; + } else { + // find the nearest + const qreal firstDistance = QLineF(firstHandle->boundingRect().center(), + mapToItem(firstHandle, event->pos())).length(); + const qreal secondDistance = QLineF(secondHandle->boundingRect().center(), + mapToItem(secondHandle, event->pos())).length(); + + if (qFuzzyCompare(firstDistance, secondDistance)) { + // same distance => choose the one that can be moved towards the press position + const bool inverted = d->from > d->to; + const qreal pos = positionAt(this, firstHandle, event->pos()); + if ((!inverted && pos < d->first->position()) || (inverted && pos > d->first->position())) { + hitNode = d->first; + otherNode = d->second; + } else { + hitNode = d->second; + otherNode = d->first; + } + } else if (firstDistance < secondDistance) { + hitNode = d->first; + otherNode = d->second; + } else { + hitNode = d->second; + otherNode = d->first; + } + } + + if (hitNode) { + hitNode->setPressed(true); + hitNode->handle()->setZ(1); + } + if (otherNode) + otherNode->handle()->setZ(0); +} + +void QQuickRangeSlider::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::mouseMoveEvent(event); + if (!keepMouseGrab()) { + if (d->orientation == Qt::Horizontal) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().x() - d->pressPoint.x(), Qt::XAxis, event)); + else + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().y() - d->pressPoint.y(), Qt::YAxis, event)); + } + if (keepMouseGrab()) { + QQuickRangeSliderNode *pressedNode = d->first->isPressed() ? d->first : (d->second->isPressed() ? d->second : nullptr); + if (pressedNode) { + qreal pos = positionAt(this, pressedNode->handle(), event->pos()); + if (d->snapMode == SnapAlways) + pos = snapPosition(this, pos); + QQuickRangeSliderNodePrivate::get(pressedNode)->setPosition(pos); + } + } +} + +void QQuickRangeSlider::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickRangeSlider); + QQuickControl::mouseReleaseEvent(event); + + d->pressPoint = QPoint(); + if (!keepMouseGrab()) + return; + + QQuickRangeSliderNode *pressedNode = d->first->isPressed() ? d->first : (d->second->isPressed() ? d->second : nullptr); + if (!pressedNode) + return; + + qreal pos = positionAt(this, pressedNode->handle(), event->pos()); + if (d->snapMode != NoSnap) + pos = snapPosition(this, pos); + qreal val = valueAt(this, pos); + if (!qFuzzyCompare(val, pressedNode->value())) + pressedNode->setValue(val); + else if (d->snapMode != NoSnap) + QQuickRangeSliderNodePrivate::get(pressedNode)->setPosition(pos); + setKeepMouseGrab(false); + pressedNode->setPressed(false); +} + +void QQuickRangeSlider::mouseUngrabEvent() +{ + Q_D(QQuickRangeSlider); + QQuickControl::mouseUngrabEvent(); + d->pressPoint = QPoint(); + d->first->setPressed(false); + d->second->setPressed(false); +} + +void QQuickRangeSlider::mirrorChange() +{ + Q_D(QQuickRangeSlider); + QQuickControl::mirrorChange(); + emit d->first->visualPositionChanged(); + emit d->second->visualPositionChanged(); +} + +void QQuickRangeSlider::componentComplete() +{ + Q_D(QQuickRangeSlider); + QQuickControl::componentComplete(); + + QQuickRangeSliderNodePrivate *firstPrivate = QQuickRangeSliderNodePrivate::get(d->first); + QQuickRangeSliderNodePrivate *secondPrivate = QQuickRangeSliderNodePrivate::get(d->second); + + if (firstPrivate->isPendingValue || secondPrivate->isPendingValue + || !qFuzzyCompare(d->from, defaultFrom) || !qFuzzyCompare(d->to, defaultTo)) { + // Properties were set while we were loading. To avoid clamping issues that occur when setting the + // values of first and second overriding values set by the user, set them all at once at the end. + // Another reason that we must set these values here is that the from and to values might have made the old range invalid. + setValues(firstPrivate->isPendingValue ? firstPrivate->pendingValue : firstPrivate->value, + secondPrivate->isPendingValue ? secondPrivate->pendingValue : secondPrivate->value); + + firstPrivate->pendingValue = 0; + firstPrivate->isPendingValue = false; + secondPrivate->pendingValue = 0; + secondPrivate->isPendingValue = false; + } else { + // If there was no pending data, we must still update the positions, + // as first.setValue()/second.setValue() won't be called as part of default construction. + // Don't need to ignore the second position when updating the first position here, + // as our default values are guaranteed to be valid. + firstPrivate->updatePosition(); + secondPrivate->updatePosition(); + } +} + +/*! + \qmlmethod void QtQuick.Controls::RangeSlider::first.increase() + + Increases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. + + \sa first +*/ + +/*! + \qmlmethod void QtQuick.Controls::RangeSlider::first.decrease() + + Decreases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. + + \sa first +*/ + +/*! + \qmlmethod void QtQuick.Controls::RangeSlider::second.increase() + + Increases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. + + \sa second +*/ + +/*! + \qmlmethod void QtQuick.Controls::RangeSlider::second.decrease() + + Decreases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. + + \sa second +*/ + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickRangeSlider::accessibleRole() const +{ + return QAccessible::Slider; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickrangeslider_p.h b/src/quicktemplates2/qquickrangeslider_p.h new file mode 100644 index 00000000..4c2eb4bb --- /dev/null +++ b/src/quicktemplates2/qquickrangeslider_p.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRANGESLIDER_P_H +#define QQUICKRANGESLIDER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickRangeSliderPrivate; +class QQuickRangeSliderNode; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSlider : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL) + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged FINAL) + Q_PROPERTY(QQuickRangeSliderNode *first READ first CONSTANT) + Q_PROPERTY(QQuickRangeSliderNode *second READ second CONSTANT) + Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL) + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) + +public: + explicit QQuickRangeSlider(QQuickItem *parent = nullptr); + + qreal from() const; + void setFrom(qreal from); + + qreal to() const; + void setTo(qreal to); + + QQuickRangeSliderNode *first() const; + QQuickRangeSliderNode *second() const; + + qreal stepSize() const; + void setStepSize(qreal step); + + enum SnapMode { + NoSnap, + SnapAlways, + SnapOnRelease + }; + Q_ENUM(SnapMode) + + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + + Q_INVOKABLE void setValues(qreal firstValue, qreal secondValue); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void stepSizeChanged(); + void snapModeChanged(); + void orientationChanged(); + +protected: + void focusInEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void mirrorChange() override; + void componentComplete() override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + friend class QQuickRangeSliderNode; + + Q_DISABLE_COPY(QQuickRangeSlider) + Q_DECLARE_PRIVATE(QQuickRangeSlider) +}; + +class QQuickRangeSliderNodePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSliderNode : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL) + Q_PROPERTY(QQuickItem *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + +public: + explicit QQuickRangeSliderNode(qreal value, QQuickRangeSlider *slider); + ~QQuickRangeSliderNode(); + + qreal value() const; + void setValue(qreal value); + + qreal position() const; + qreal visualPosition() const; + + QQuickItem *handle() const; + void setHandle(QQuickItem *handle); + + bool isPressed() const; + void setPressed(bool pressed); + +public Q_SLOTS: + void increase(); + void decrease(); + +Q_SIGNALS: + void valueChanged(); + void positionChanged(); + void visualPositionChanged(); + void handleChanged(); + void pressedChanged(); + +private: + Q_DISABLE_COPY(QQuickRangeSliderNode) + Q_DECLARE_PRIVATE(QQuickRangeSliderNode) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRangeSlider) + +#endif // QQUICKRANGESLIDER_P_H diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp new file mode 100644 index 00000000..7ac1df34 --- /dev/null +++ b/src/quicktemplates2/qquickscrollbar.cpp @@ -0,0 +1,717 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickscrollbar_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtQml/qqmlinfo.h> +#include <QtQuick/private/qquickflickable_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ScrollBar + \inherits Control + \instantiates QQuickScrollBar + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-indicators + \brief Vertical or horizontal interactive scroll bar. + + \image qtquickcontrols2-scrollbar.gif + + ScrollBar is an interactive bar that can be used to scroll to a specific + position. A scroll bar can be either \l vertical or \l horizontal, and can + be attached to any \l Flickable, such as \l ListView and \l GridView. + + \code + Flickable { + // ... + ScrollBar.vertical: ScrollBar { } + } + \endcode + + \section1 Attaching ScrollBar to a Flickable + + When ScrollBar is attached \l {ScrollBar::vertical}{vertically} or + \l {ScrollBar::horizontal}{horizontally} to a Flickable, its geometry and + the following properties are automatically set and updated as appropriate: + + \list + \li \l orientation + \li \l position + \li \l size + \li \l active + \endlist + + An attached ScrollBar re-parents itself to the target Flickable. A vertically + attached ScrollBar resizes itself to the height of the Flickable, and positions + itself to either side of it based on the \l {Control::mirrored}{layout direction}. + A horizontally attached ScrollBar resizes itself to the width of the Flickable, + and positions itself to the bottom. The automatic geometry management can be disabled + by specifying another parent for the attached ScrollBar. This can be useful, for + example, if the ScrollBar should be placed outside a clipping Flickable. This is + demonstrated by the following example: + + \code + Flickable { + id: flickable + clip: true + // ... + ScrollBar.vertical: ScrollBar { + parent: flickable.parent + anchors.top: flickable.top + anchors.left: flickable.right + anchors.bottom: flickable.bottom + } + } + \endcode + + Notice that ScrollBar does not filter key events of the Flickable it is + attached to. The following example illustrates how to implement scrolling + with up and down keys: + + \code + Flickable { + focus: true + + Keys.onUpPressed: scrollBar.decrease() + Keys.onDownPressed: scrollBar.increase() + + ScrollBar.vertical: ScrollBar { id: scrollBar } + } + \endcode + + \section1 Binding the Active State of Horizontal and Vertical Scroll Bars + + Horizontal and vertical scroll bars do not share the \l active state with + each other by default. In order to keep both bars visible whilst scrolling + to either direction, establish a two-way binding between the active states + as presented by the following example: + + \snippet qtquickcontrols2-scrollbar-active.qml 1 + + \section1 Non-attached Scroll Bars + + It is possible to create an instance of ScrollBar without using the + attached property API. This is useful when the behavior of the attached + scoll bar is not sufficient or a \l Flickable is not in use. In the + following example, horizontal and vertical scroll bars are used to + scroll over the text without using \l Flickable: + + \snippet qtquickcontrols2-scrollbar-non-attached.qml 1 + + \image qtquickcontrols2-scrollbar-non-attached.png + + When using a non-attached ScrollBar, the following must be done manually: + + \list + \li Layout the scroll bar (with the \l {Item::}{x} and \l {Item::}{y} or + \l {Item::anchors}{anchor} properties, for example). + \li Set the \l size and \l position properties to determine the size and position + of the scroll bar in relation to the scrolled item. + \li Set the \l active property to determine when the scroll bar will be + visible. + \endlist + + \sa ScrollIndicator, {Customizing ScrollBar}, {Indicator Controls} +*/ + +class QQuickScrollBarPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickScrollBar) + +public: + QQuickScrollBarPrivate() : size(0), position(0), stepSize(0), offset(0), + active(false), pressed(false), moving(false), + orientation(Qt::Vertical) + { + } + + static QQuickScrollBarPrivate *get(QQuickScrollBar *bar) + { + return bar->d_func(); + } + + qreal positionAt(const QPoint &point) const; + + void resizeContent() override; + + qreal size; + qreal position; + qreal stepSize; + qreal offset; + bool active; + bool pressed; + bool moving; + Qt::Orientation orientation; +}; + +qreal QQuickScrollBarPrivate::positionAt(const QPoint &point) const +{ + Q_Q(const QQuickScrollBar); + if (orientation == Qt::Horizontal) + return (point.x() - q->leftPadding()) / q->availableWidth(); + else + return (point.y() - q->topPadding()) / q->availableHeight(); +} + +void QQuickScrollBarPrivate::resizeContent() +{ + Q_Q(QQuickScrollBar); + if (!contentItem) + return; + + if (orientation == Qt::Horizontal) { + contentItem->setPosition(QPointF(q->leftPadding() + position * q->availableWidth(), q->topPadding())); + contentItem->setSize(QSizeF(q->availableWidth() * size, q->availableHeight())); + } else { + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + position * q->availableHeight())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * size)); + } +} + +QQuickScrollBar::QQuickScrollBar(QQuickItem *parent) : + QQuickControl(*(new QQuickScrollBarPrivate), parent) +{ + setKeepMouseGrab(true); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QQuickScrollBarAttached *QQuickScrollBar::qmlAttachedProperties(QObject *object) +{ + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(object); + if (!flickable) + qmlInfo(object) << "ScrollBar must be attached to a Flickable"; + + return new QQuickScrollBarAttached(flickable); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollBar::size + + This property holds the size of the scroll bar, scaled to \c {0.0 - 1.0}. + + \sa {Flickable::visibleArea.heightRatio}{Flickable::visibleArea} + + This property is automatically set when the scroll bar is + \l {Attaching ScrollBar to a Flickable}{attached to a flickable}. +*/ +qreal QQuickScrollBar::size() const +{ + Q_D(const QQuickScrollBar); + return d->size; +} + +void QQuickScrollBar::setSize(qreal size) +{ + Q_D(QQuickScrollBar); + size = qBound<qreal>(0.0, size, 1.0 - d->position); + if (qFuzzyCompare(d->size, size)) + return; + + d->size = size; + if (isComponentComplete()) + d->resizeContent(); + emit sizeChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollBar::position + + This property holds the position of the scroll bar, scaled to \c {0.0 - 1.0}. + + \sa {Flickable::visibleArea.yPosition}{Flickable::visibleArea} + + This property is automatically set when the scroll bar is + \l {Attaching ScrollBar to a Flickable}{attached to a flickable}. +*/ +qreal QQuickScrollBar::position() const +{ + Q_D(const QQuickScrollBar); + return d->position; +} + +void QQuickScrollBar::setPosition(qreal position) +{ + Q_D(QQuickScrollBar); + position = qBound<qreal>(0.0, position, 1.0 - d->size); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + if (isComponentComplete()) + d->resizeContent(); + emit positionChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollBar::stepSize + + This property holds the step size. The default value is \c 0.0. + + \sa increase(), decrease() +*/ +qreal QQuickScrollBar::stepSize() const +{ + Q_D(const QQuickScrollBar); + return d->stepSize; +} + +void QQuickScrollBar::setStepSize(qreal step) +{ + Q_D(QQuickScrollBar); + if (qFuzzyCompare(d->stepSize, step)) + return; + + d->stepSize = step; + emit stepSizeChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::ScrollBar::active + + This property holds whether the scroll bar is active, i.e. when it's \l pressed + or the attached Flickable is \l {Flickable::moving}{moving}. + + It is possible to keep \l {Binding the Active State of Horizontal and Vertical Scroll Bars} + {both horizontal and vertical bars visible} while scrolling in either direction. + + This property is automatically set when the scroll bar is + \l {Attaching ScrollBar to a Flickable}{attached to a flickable}. +*/ +bool QQuickScrollBar::isActive() const +{ + Q_D(const QQuickScrollBar); + return d->active; +} + +void QQuickScrollBar::setActive(bool active) +{ + Q_D(QQuickScrollBar); + if (d->active == active) + return; + + d->active = active; + emit activeChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::ScrollBar::pressed + + This property holds whether the scroll bar is pressed. +*/ +bool QQuickScrollBar::isPressed() const +{ + Q_D(const QQuickScrollBar); + return d->pressed; +} + +void QQuickScrollBar::setPressed(bool pressed) +{ + Q_D(QQuickScrollBar); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + setAccessibleProperty("pressed", pressed); + setActive(d->pressed || d->moving); + emit pressedChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::ScrollBar::orientation + + This property holds the orientation of the scroll bar. + + Possible values: + \value Qt.Horizontal Horizontal + \value Qt.Vertical Vertical (default) + + This property is automatically set when the scroll bar is + \l {Attaching ScrollBar to a Flickable}{attached to a flickable}. +*/ +Qt::Orientation QQuickScrollBar::orientation() const +{ + Q_D(const QQuickScrollBar); + return d->orientation; +} + +void QQuickScrollBar::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickScrollBar); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + if (isComponentComplete()) + d->resizeContent(); + emit orientationChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::ScrollBar::increase() + + Increases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0. + + \sa stepSize +*/ +void QQuickScrollBar::increase() +{ + Q_D(QQuickScrollBar); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + bool wasActive = d->active; + setActive(true); + setPosition(d->position + step); + setActive(wasActive); +} + +/*! + \qmlmethod void QtQuick.Controls::ScrollBar::decrease() + + Decreases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0. + + \sa stepSize +*/ +void QQuickScrollBar::decrease() +{ + Q_D(QQuickScrollBar); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + bool wasActive = d->active; + setActive(true); + setPosition(d->position - step); + setActive(wasActive); +} + +void QQuickScrollBar::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickScrollBar); + QQuickControl::mousePressEvent(event); + d->offset = d->positionAt(event->pos()) - d->position; + if (d->offset < 0 || d->offset > d->size) + d->offset = d->size / 2; + setPressed(true); +} + +void QQuickScrollBar::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickScrollBar); + QQuickControl::mouseMoveEvent(event); + setPosition(qBound<qreal>(0.0, d->positionAt(event->pos()) - d->offset, 1.0 - d->size)); +} + +void QQuickScrollBar::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickScrollBar); + QQuickControl::mouseReleaseEvent(event); + setPosition(qBound<qreal>(0.0, d->positionAt(event->pos()) - d->offset, 1.0 - d->size)); + d->offset = 0.0; + setPressed(false); +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickScrollBar::accessibilityActiveChanged(bool active) +{ + QQuickControl::accessibilityActiveChanged(active); + + Q_D(QQuickScrollBar); + if (active) + setAccessibleProperty("pressed", d->pressed); +} + +QAccessible::Role QQuickScrollBar::accessibleRole() const +{ + return QAccessible::ScrollBar; +} +#endif + +class QQuickScrollBarAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ +public: + QQuickScrollBarAttachedPrivate(QQuickFlickable *flickable) : flickable(flickable), horizontal(nullptr), vertical(nullptr) { } + + void activateHorizontal(); + void activateVertical(); + void scrollHorizontal(); + void scrollVertical(); + void mirrorVertical(); + + void layoutHorizontal(bool move = true); + void layoutVertical(bool move = true); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemDestroyed(QQuickItem *item) override; + + QQuickFlickable *flickable; + QQuickScrollBar *horizontal; + QQuickScrollBar *vertical; +}; + +void QQuickScrollBarAttachedPrivate::activateHorizontal() +{ + QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(horizontal); + p->moving = flickable->isMovingHorizontally(); + horizontal->setActive(p->moving || p->pressed); +} + +void QQuickScrollBarAttachedPrivate::activateVertical() +{ + QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(vertical); + p->moving = flickable->isMovingVertically(); + vertical->setActive(p->moving || p->pressed); +} + +// TODO: QQuickFlickable::maxXYExtent() +class QQuickFriendlyFlickable : public QQuickFlickable +{ + friend class QQuickScrollBarAttachedPrivate; +}; + +void QQuickScrollBarAttachedPrivate::scrollHorizontal() +{ + QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable); + + const qreal viewwidth = f->width(); + const qreal maxxextent = -f->maxXExtent() + f->minXExtent(); + qreal cx = horizontal->position() * (maxxextent + viewwidth) - f->minXExtent(); + if (!qIsNaN(cx) && !qFuzzyCompare(cx, flickable->contentX())) + flickable->setContentX(cx); +} + +void QQuickScrollBarAttachedPrivate::scrollVertical() +{ + QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable); + + const qreal viewheight = f->height(); + const qreal maxyextent = -f->maxYExtent() + f->minYExtent(); + qreal cy = vertical->position() * (maxyextent + viewheight) - f->minYExtent(); + if (!qIsNaN(cy) && !qFuzzyCompare(cy, flickable->contentY())) + flickable->setContentY(cy); +} + +void QQuickScrollBarAttachedPrivate::mirrorVertical() +{ + layoutVertical(true); +} + +void QQuickScrollBarAttachedPrivate::layoutHorizontal(bool move) +{ + Q_ASSERT(horizontal && flickable); + if (horizontal->parentItem() != flickable) + return; + horizontal->setWidth(flickable->width()); + if (move) + horizontal->setY(flickable->height() - horizontal->height()); +} + +void QQuickScrollBarAttachedPrivate::layoutVertical(bool move) +{ + Q_ASSERT(vertical && flickable); + if (vertical->parentItem() != flickable) + return; + vertical->setHeight(flickable->height()); + if (move) + vertical->setX(vertical->isMirrored() ? 0 : flickable->width() - vertical->width()); +} + +void QQuickScrollBarAttachedPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(item); + Q_UNUSED(newGeometry); + if (horizontal && horizontal->height() > 0) { + bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), oldGeometry.height() - horizontal->height()); + layoutHorizontal(move); + } + if (vertical && vertical->width() > 0) { + bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), oldGeometry.width() - vertical->width()); + layoutVertical(move); + } +} + +void QQuickScrollBarAttachedPrivate::itemDestroyed(QQuickItem *item) +{ + if (item == horizontal) + horizontal = nullptr; + if (item == vertical) + vertical = nullptr; +} + +QQuickScrollBarAttached::QQuickScrollBarAttached(QQuickFlickable *flickable) : + QObject(*(new QQuickScrollBarAttachedPrivate(flickable)), flickable) +{ + Q_D(QQuickScrollBarAttached); + if (flickable) { + QQuickItemPrivate *p = QQuickItemPrivate::get(flickable); + p->updateOrAddGeometryChangeListener(d, QQuickItemPrivate::SizeChange); + } +} + +QQuickScrollBarAttached::~QQuickScrollBarAttached() +{ + Q_D(QQuickScrollBarAttached); + if (d->flickable) { + if (d->horizontal) + QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + if (d->vertical) + QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QQuickItemPrivate::get(d->flickable)->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + } +} + +/*! + \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::horizontal + + This property attaches a horizontal scroll bar to a \l Flickable. + + \code + Flickable { + contentWidth: 2000 + ScrollBar.horizontal: ScrollBar { } + } + \endcode + + \sa {Attaching ScrollBar to a Flickable} +*/ +QQuickScrollBar *QQuickScrollBarAttached::horizontal() const +{ + Q_D(const QQuickScrollBarAttached); + return d->horizontal; +} + +void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal) +{ + Q_D(QQuickScrollBarAttached); + if (d->horizontal == horizontal) + return; + + if (d->horizontal && d->flickable) { + QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::disconnect(d->horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal); + QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollBarAttachedPrivate::activateHorizontal); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + disconnect(area, SIGNAL(widthRatioChanged(qreal)), d->horizontal, SLOT(setSize(qreal))); + disconnect(area, SIGNAL(xPositionChanged(qreal)), d->horizontal, SLOT(setPosition(qreal))); + } + + d->horizontal = horizontal; + + if (horizontal && d->flickable) { + if (!horizontal->parentItem()) + horizontal->setParentItem(d->flickable); + horizontal->setOrientation(Qt::Horizontal); + + QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::connect(horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal); + QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollBarAttachedPrivate::activateHorizontal); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + connect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal))); + connect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal))); + + d->layoutHorizontal(); + horizontal->setSize(area->property("widthRatio").toReal()); + horizontal->setPosition(area->property("xPosition").toReal()); + } + emit horizontalChanged(); +} + +/*! + \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::vertical + + This property attaches a vertical scroll bar to a \l Flickable. + + \code + Flickable { + contentHeight: 2000 + ScrollBar.vertical: ScrollBar { } + } + \endcode + + \sa {Attaching ScrollBar to a Flickable} +*/ +QQuickScrollBar *QQuickScrollBarAttached::vertical() const +{ + Q_D(const QQuickScrollBarAttached); + return d->vertical; +} + +void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical) +{ + Q_D(QQuickScrollBarAttached); + if (d->vertical == vertical) + return; + + if (d->vertical && d->flickable) { + QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical); + QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical); + QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollBarAttachedPrivate::activateVertical); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + disconnect(area, SIGNAL(heightRatioChanged(qreal)), d->vertical, SLOT(setSize(qreal))); + disconnect(area, SIGNAL(yPositionChanged(qreal)), d->vertical, SLOT(setPosition(qreal))); + } + + d->vertical = vertical; + + if (vertical && d->flickable) { + if (!vertical->parentItem()) + vertical->setParentItem(d->flickable); + vertical->setOrientation(Qt::Vertical); + + QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::connect(vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical); + QObjectPrivate::connect(vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical); + QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollBarAttachedPrivate::activateVertical); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + connect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal))); + connect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal))); + + d->layoutVertical(); + vertical->setSize(area->property("heightRatio").toReal()); + vertical->setPosition(area->property("yPosition").toReal()); + } + emit verticalChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickscrollbar_p.h b/src/quicktemplates2/qquickscrollbar_p.h new file mode 100644 index 00000000..8feba604 --- /dev/null +++ b/src/quicktemplates2/qquickscrollbar_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSCROLLBAR_P_H +#define QQUICKSCROLLBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickFlickable; +class QQuickScrollBarAttached; +class QQuickScrollBarPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBar : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL) + Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) + +public: + explicit QQuickScrollBar(QQuickItem *parent = nullptr); + + static QQuickScrollBarAttached *qmlAttachedProperties(QObject *object); + + qreal size() const; + qreal position() const; + + qreal stepSize() const; + void setStepSize(qreal step); + + bool isActive() const; + void setActive(bool active); + + bool isPressed() const; + void setPressed(bool pressed); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + +public Q_SLOTS: + void increase(); + void decrease(); + void setSize(qreal size); + void setPosition(qreal position); + +Q_SIGNALS: + void sizeChanged(); + void positionChanged(); + void stepSizeChanged(); + void activeChanged(); + void pressedChanged(); + void orientationChanged(); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickScrollBar) + Q_DECLARE_PRIVATE(QQuickScrollBar) +}; + +class QQuickScrollBarAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBarAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickScrollBar *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL) + Q_PROPERTY(QQuickScrollBar *vertical READ vertical WRITE setVertical NOTIFY verticalChanged FINAL) + +public: + explicit QQuickScrollBarAttached(QQuickFlickable *flickable); + ~QQuickScrollBarAttached(); + + QQuickScrollBar *horizontal() const; + void setHorizontal(QQuickScrollBar *horizontal); + + QQuickScrollBar *vertical() const; + void setVertical(QQuickScrollBar *vertical); + +Q_SIGNALS: + void horizontalChanged(); + void verticalChanged(); + +private: + Q_DISABLE_COPY(QQuickScrollBarAttached) + Q_DECLARE_PRIVATE(QQuickScrollBarAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickScrollBar) +QML_DECLARE_TYPEINFO(QQuickScrollBar, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKSCROLLBAR_P_H diff --git a/src/quicktemplates2/qquickscrollindicator.cpp b/src/quicktemplates2/qquickscrollindicator.cpp new file mode 100644 index 00000000..7d93215d --- /dev/null +++ b/src/quicktemplates2/qquickscrollindicator.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickscrollindicator_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtQml/qqmlinfo.h> +#include <QtQuick/private/qquickflickable_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ScrollIndicator + \inherits Control + \instantiates QQuickScrollIndicator + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-indicators + \brief Vertical or horizontal non-interactive scroll indicator. + + \image qtquickcontrols2-scrollindicator.gif + + ScrollIndicator is a non-interactive indicator that indicates the current scroll + position. A scroll indicator can be either \l vertical or \l horizontal, and can + be attached to any \l Flickable, such as \l ListView and \l GridView. + + \code + Flickable { + // ... + ScrollIndicator.vertical: ScrollIndicator { } + } + \endcode + + \section1 Attaching ScrollIndicator to a Flickable + + \note When ScrollIndicator is attached \l {ScrollIndicator::vertical}{vertically} + or \l {ScrollIndicator::horizontal}{horizontally} to a Flickable, its geometry and + the following properties are automatically set and updated as appropriate: + + \list + \li \l orientation + \li \l position + \li \l size + \li \l active + \endlist + + An attached ScrollIndicator re-parents itself to the target Flickable. A vertically + attached ScrollIndicator resizes itself to the height of the Flickable, and positions + itself to either side of it based on the \l {Control::mirrored}{layout direction}. + A horizontally attached ScrollIndicator resizes itself to the width of the Flickable, + and positions itself to the bottom. The automatic geometry management can be disabled + by specifying another parent for the attached ScrollIndicator. This can be useful, for + example, if the ScrollIndicator should be placed outside a clipping Flickable. This is + demonstrated by the following example: + + \code + Flickable { + id: flickable + clip: true + // ... + ScrollIndicator.vertical: ScrollIndicator { + parent: flickable.parent + anchors.top: flickable.top + anchors.left: flickable.right + anchors.bottom: flickable.bottom + } + } + \endcode + + \section1 Binding the Active State of Horizontal and Vertical Scroll Indicators + + Horizontal and vertical scroll indicators do not share the \l active state with + each other by default. In order to keep both indicators visible whilst scrolling + to either direction, establish a two-way binding between the active states as + presented by the following example: + + \snippet qtquickcontrols2-scrollindicator-active.qml 1 + + \section1 Non-attached Scroll Indicators + + It is possible to create an instance of ScrollIndicator without using the + attached property API. This is useful when the behavior of the attached + scoll indicator is not sufficient or a \l Flickable is not in use. In the + following example, horizontal and vertical scroll indicators are used to + indicate how far the user has scrolled over the text (using \l MouseArea + instead of \l Flickable): + + \snippet qtquickcontrols2-scrollindicator-non-attached.qml 1 + + \image qtquickcontrols2-scrollindicator-non-attached.png + + \sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls} +*/ + +class QQuickScrollIndicatorPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickScrollIndicator) + +public: + QQuickScrollIndicatorPrivate() : size(0), position(0), + active(false), orientation(Qt::Vertical) + { + } + + void resizeContent() override; + + qreal size; + qreal position; + bool active; + Qt::Orientation orientation; +}; + +void QQuickScrollIndicatorPrivate::resizeContent() +{ + Q_Q(QQuickScrollIndicator); + if (!contentItem) + return; + + // - negative overshoot (pos < 0): clamp the pos to 0, and deduct the overshoot from the size + // - positive overshoot (pos + size > 1): clamp the size to 1-pos + const qreal clampedSize = qBound<qreal>(0, size + qMin<qreal>(0, position), 1.0 - position); + const qreal clampedPos = qBound<qreal>(0, position, 1.0 - clampedSize); + + if (orientation == Qt::Horizontal) { + contentItem->setPosition(QPointF(q->leftPadding() + clampedPos * q->availableWidth(), q->topPadding())); + contentItem->setSize(QSizeF(q->availableWidth() * clampedSize, q->availableHeight())); + } else { + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + clampedPos * q->availableHeight())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * clampedSize)); + } +} + +QQuickScrollIndicator::QQuickScrollIndicator(QQuickItem *parent) : + QQuickControl(*(new QQuickScrollIndicatorPrivate), parent) +{ +} + +QQuickScrollIndicatorAttached *QQuickScrollIndicator::qmlAttachedProperties(QObject *object) +{ + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(object); + if (!flickable) + qmlInfo(object) << "ScrollIndicator must be attached to a Flickable"; + + return new QQuickScrollIndicatorAttached(flickable); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollIndicator::size + + This property holds the size of the indicator, scaled to \c {0.0 - 1.0}. + + \sa {Flickable::visibleArea.heightRatio}{Flickable::visibleArea} + + This property is automatically set when the scroll indicator is + \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}. +*/ +qreal QQuickScrollIndicator::size() const +{ + Q_D(const QQuickScrollIndicator); + return d->size; +} + +void QQuickScrollIndicator::setSize(qreal size) +{ + Q_D(QQuickScrollIndicator); + if (qFuzzyCompare(d->size, size)) + return; + + d->size = size; + if (isComponentComplete()) + d->resizeContent(); + emit sizeChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollIndicator::position + + This property holds the position of the indicator, scaled to \c {0.0 - 1.0}. + + This property is automatically set when the scroll indicator is + \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}. + + \sa {Flickable::visibleArea.yPosition}{Flickable::visibleArea} +*/ +qreal QQuickScrollIndicator::position() const +{ + Q_D(const QQuickScrollIndicator); + return d->position; +} + +void QQuickScrollIndicator::setPosition(qreal position) +{ + Q_D(QQuickScrollIndicator); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + if (isComponentComplete()) + d->resizeContent(); + emit positionChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::ScrollIndicator::active + + This property holds whether the indicator is active, that is, when the + attached Flickable is \l {Flickable::moving}{moving}. + + It is possible to keep \l {Binding the Active State of Horizontal and Vertical Scroll Indicators} + {both horizontal and vertical indicators visible} while scrolling in either direction. + + This property is automatically set when the scroll indicator is + \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}. +*/ +bool QQuickScrollIndicator::isActive() const +{ + Q_D(const QQuickScrollIndicator); + return d->active; +} + +void QQuickScrollIndicator::setActive(bool active) +{ + Q_D(QQuickScrollIndicator); + if (d->active == active) + return; + + d->active = active; + emit activeChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::ScrollIndicator::orientation + + This property holds the orientation of the indicator. + + Possible values: + \value Qt.Horizontal Horizontal + \value Qt.Vertical Vertical (default) + + This property is automatically set when the scroll indicator is + \l {Attaching ScrollIndicator to a Flickable}{attached to a flickable}. +*/ +Qt::Orientation QQuickScrollIndicator::orientation() const +{ + Q_D(const QQuickScrollIndicator); + return d->orientation; +} + +void QQuickScrollIndicator::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickScrollIndicator); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + if (isComponentComplete()) + d->resizeContent(); + emit orientationChanged(); +} + +class QQuickScrollIndicatorAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ +public: + QQuickScrollIndicatorAttachedPrivate(QQuickFlickable *flickable) : flickable(flickable), horizontal(nullptr), vertical(nullptr) { } + + void activateHorizontal(); + void activateVertical(); + + void layoutHorizontal(bool move = true); + void layoutVertical(bool move = true); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemDestroyed(QQuickItem *item) override; + + QQuickFlickable *flickable; + QQuickScrollIndicator *horizontal; + QQuickScrollIndicator *vertical; +}; + +void QQuickScrollIndicatorAttachedPrivate::activateHorizontal() +{ + horizontal->setActive(flickable->isMovingHorizontally()); +} + +void QQuickScrollIndicatorAttachedPrivate::activateVertical() +{ + vertical->setActive(flickable->isMovingVertically()); +} + +void QQuickScrollIndicatorAttachedPrivate::layoutHorizontal(bool move) +{ + Q_ASSERT(horizontal && flickable); + if (horizontal->parentItem() != flickable) + return; + horizontal->setWidth(flickable->width()); + if (move) + horizontal->setY(flickable->height() - horizontal->height()); +} + +void QQuickScrollIndicatorAttachedPrivate::layoutVertical(bool move) +{ + Q_ASSERT(vertical && flickable); + if (vertical->parentItem() != flickable) + return; + vertical->setHeight(flickable->height()); + if (move && !QQuickItemPrivate::get(vertical)->isMirrored()) + vertical->setX(flickable->width() - vertical->width()); +} + +void QQuickScrollIndicatorAttachedPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(item); + Q_UNUSED(newGeometry); + if (horizontal && horizontal->height() > 0) { + bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), oldGeometry.height() - horizontal->height()); + layoutHorizontal(move); + } + if (vertical && vertical->width() > 0) { + bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), oldGeometry.width() - vertical->width()); + layoutVertical(move); + } +} + +void QQuickScrollIndicatorAttachedPrivate::itemDestroyed(QQuickItem *item) +{ + if (item == horizontal) + horizontal = nullptr; + if (item == vertical) + vertical = nullptr; +} + +QQuickScrollIndicatorAttached::QQuickScrollIndicatorAttached(QQuickFlickable *flickable) : + QObject(*(new QQuickScrollIndicatorAttachedPrivate(flickable)), flickable) +{ + Q_D(QQuickScrollIndicatorAttached); + if (flickable) { + QQuickItemPrivate *p = QQuickItemPrivate::get(flickable); + p->updateOrAddGeometryChangeListener(d, QQuickItemPrivate::SizeChange); + } +} + +QQuickScrollIndicatorAttached::~QQuickScrollIndicatorAttached() +{ + Q_D(QQuickScrollIndicatorAttached); + if (d->flickable) { + if (d->horizontal) + QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + if (d->vertical) + QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d,QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QQuickItemPrivate::get(d->flickable)->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + } +} + +/*! + \qmlattachedproperty ScrollIndicator QtQuick.Controls::ScrollIndicator::horizontal + + This property attaches a horizontal scroll indicator to a \l Flickable. + + \code + Flickable { + contentWidth: 2000 + ScrollIndicator.horizontal: ScrollIndicator { } + } + \endcode + + \sa {Attaching ScrollIndicator to a Flickable} +*/ +QQuickScrollIndicator *QQuickScrollIndicatorAttached::horizontal() const +{ + Q_D(const QQuickScrollIndicatorAttached); + return d->horizontal; +} + +void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizontal) +{ + Q_D(QQuickScrollIndicatorAttached); + if (d->horizontal == horizontal) + return; + + if (d->horizontal && d->flickable) { + QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + disconnect(area, SIGNAL(widthRatioChanged(qreal)), d->horizontal, SLOT(setSize(qreal))); + disconnect(area, SIGNAL(xPositionChanged(qreal)), d->horizontal, SLOT(setPosition(qreal))); + } + + d->horizontal = horizontal; + + if (horizontal && d->flickable) { + if (!horizontal->parentItem()) + horizontal->setParentItem(d->flickable); + horizontal->setOrientation(Qt::Horizontal); + + QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + connect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal))); + connect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal))); + + d->layoutHorizontal(); + horizontal->setSize(area->property("widthRatio").toReal()); + horizontal->setPosition(area->property("xPosition").toReal()); + } + emit horizontalChanged(); +} + +/*! + \qmlattachedproperty ScrollIndicator QtQuick.Controls::ScrollIndicator::vertical + + This property attaches a vertical scroll indicator to a \l Flickable. + + \code + Flickable { + contentHeight: 2000 + ScrollIndicator.vertical: ScrollIndicator { } + } + \endcode + + \sa {Attaching ScrollIndicator to a Flickable} +*/ +QQuickScrollIndicator *QQuickScrollIndicatorAttached::vertical() const +{ + Q_D(const QQuickScrollIndicatorAttached); + return d->vertical; +} + +void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical) +{ + Q_D(QQuickScrollIndicatorAttached); + if (d->vertical == vertical) + return; + + if (d->vertical && d->flickable) { + QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + disconnect(area, SIGNAL(heightRatioChanged(qreal)), d->vertical, SLOT(setSize(qreal))); + disconnect(area, SIGNAL(yPositionChanged(qreal)), d->vertical, SLOT(setPosition(qreal))); + } + + d->vertical = vertical; + + if (vertical && d->flickable) { + if (!vertical->parentItem()) + vertical->setParentItem(d->flickable); + vertical->setOrientation(Qt::Vertical); + + QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical); + + // TODO: export QQuickFlickableVisibleArea + QObject *area = d->flickable->property("visibleArea").value<QObject *>(); + connect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal))); + connect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal))); + + d->layoutVertical(); + vertical->setSize(area->property("heightRatio").toReal()); + vertical->setPosition(area->property("yPosition").toReal()); + } + emit verticalChanged(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickScrollIndicator::accessibleRole() const +{ + return QAccessible::Indicator; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickscrollindicator_p.h b/src/quicktemplates2/qquickscrollindicator_p.h new file mode 100644 index 00000000..1e120928 --- /dev/null +++ b/src/quicktemplates2/qquickscrollindicator_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSCROLLINDICATOR_P_H +#define QQUICKSCROLLINDICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickFlickable; +class QQuickScrollIndicatorAttached; +class QQuickScrollIndicatorPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicator : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL) + Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) + +public: + explicit QQuickScrollIndicator(QQuickItem *parent = nullptr); + + static QQuickScrollIndicatorAttached *qmlAttachedProperties(QObject *object); + + qreal size() const; + qreal position() const; + + bool isActive() const; + void setActive(bool active); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + +public Q_SLOTS: + void setSize(qreal size); + void setPosition(qreal position); + +Q_SIGNALS: + void sizeChanged(); + void positionChanged(); + void activeChanged(); + void orientationChanged(); + +protected: +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickScrollIndicator) + Q_DECLARE_PRIVATE(QQuickScrollIndicator) +}; + +class QQuickScrollIndicatorAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicatorAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickScrollIndicator *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL) + Q_PROPERTY(QQuickScrollIndicator *vertical READ vertical WRITE setVertical NOTIFY verticalChanged FINAL) + +public: + explicit QQuickScrollIndicatorAttached(QQuickFlickable *flickable); + ~QQuickScrollIndicatorAttached(); + + QQuickScrollIndicator *horizontal() const; + void setHorizontal(QQuickScrollIndicator *horizontal); + + QQuickScrollIndicator *vertical() const; + void setVertical(QQuickScrollIndicator *vertical); + +Q_SIGNALS: + void horizontalChanged(); + void verticalChanged(); + +private: + Q_DISABLE_COPY(QQuickScrollIndicatorAttached) + Q_DECLARE_PRIVATE(QQuickScrollIndicatorAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickScrollIndicator) +QML_DECLARE_TYPEINFO(QQuickScrollIndicator, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKSCROLLINDICATOR_P_H diff --git a/src/quicktemplates2/qquickshortcutcontext.cpp b/src/quicktemplates2/qquickshortcutcontext.cpp new file mode 100644 index 00000000..04aa4f4d --- /dev/null +++ b/src/quicktemplates2/qquickshortcutcontext.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshortcutcontext_p_p.h" +#include "qquickoverlay_p_p.h" +#include "qquickpopup_p.h" + +#include <QtGui/qguiapplication.h> + +QT_BEGIN_NAMESPACE + +static bool isBlockedByPopup(QQuickItem *item) +{ + if (!item || !item->window()) + return false; + + QQuickOverlay *overlay = QQuickOverlay::overlay(item->window()); + const auto popups = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + if (popup->isModal() || popup->closePolicy() & QQuickPopup::CloseOnEscape) + return item != popup->popupItem() && !popup->popupItem()->isAncestorOf(item); + } + + return false; +} + +bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context) +{ + QQuickItem *item = nullptr; + switch (context) { + case Qt::ApplicationShortcut: + return true; + case Qt::WindowShortcut: + while (obj && !obj->isWindowType()) { + obj = obj->parent(); + item = qobject_cast<QQuickItem *>(obj); + if (item) { + obj = item->window(); + } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(obj)) { + obj = popup->window(); + item = popup->popupItem(); + } + } + return obj && obj == QGuiApplication::focusWindow() && !isBlockedByPopup(item); + default: + return false; + } +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickshortcutcontext_p_p.h b/src/quicktemplates2/qquickshortcutcontext_p_p.h new file mode 100644 index 00000000..3866dfd8 --- /dev/null +++ b/src/quicktemplates2/qquickshortcutcontext_p_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHORTCUTCONTEXT_P_P_H +#define QQUICKSHORTCUTCONTEXT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qnamespace.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; + +struct Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickShortcutContext +{ + static bool matcher(QObject *object, Qt::ShortcutContext context); +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHORTCUTCONTEXT_P_P_H diff --git a/src/quicktemplates2/qquickslider.cpp b/src/quicktemplates2/qquickslider.cpp new file mode 100644 index 00000000..454002c2 --- /dev/null +++ b/src/quicktemplates2/qquickslider.cpp @@ -0,0 +1,598 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickslider_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtQuick/private/qquickwindow_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Slider + \inherits Control + \instantiates QQuickSlider + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Used to select a value by sliding a handle along a track. + + \image qtquickcontrols2-slider.gif + + Slider is used to select a value by sliding a handle along a track. + + In the example below, custom \l from, \l value, and \l to values are set: + + \code + Slider { + from: 1 + value: 25 + to: 100 + } + \endcode + + The \l position property is expressed as a fraction of the control's size, + in the range \c {0.0 - 1.0}. The \l visualPosition property is + the same, except that it is reversed in a + \l {Right-to-left User Interfaces}{right-to-left} application. The + visualPosition is useful for positioning the handle when styling Slider. + In the example above, \l visualPosition will be \c 0.24 in a left-to-right + application, and \c 0.76 in a right-to-left application. + + \sa {Customizing Slider}, {Input Controls} +*/ + +class QQuickSliderPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickSlider) + +public: + QQuickSliderPrivate() : from(0), to(1), value(0), position(0), stepSize(0), pressed(false), + orientation(Qt::Horizontal), snapMode(QQuickSlider::NoSnap), + handle(nullptr) + { + } + + qreal valueAt(qreal position) const; + qreal snapPosition(qreal position) const; + qreal positionAt(const QPoint &point) const; + void setPosition(qreal position); + void updatePosition(); + + qreal from; + qreal to; + qreal value; + qreal position; + qreal stepSize; + bool pressed; + QPoint pressPoint; + Qt::Orientation orientation; + QQuickSlider::SnapMode snapMode; + QQuickItem *handle; +}; + +qreal QQuickSliderPrivate::valueAt(qreal position) const +{ + return from + (to - from) * position; +} + +qreal QQuickSliderPrivate::snapPosition(qreal position) const +{ + const qreal range = to - from; + if (qFuzzyIsNull(range)) + return position; + + const qreal effectiveStep = stepSize / range; + if (qFuzzyIsNull(effectiveStep)) + return position; + + return qRound(position / effectiveStep) * effectiveStep; +} + +qreal QQuickSliderPrivate::positionAt(const QPoint &point) const +{ + Q_Q(const QQuickSlider); + if (orientation == Qt::Horizontal) { + const qreal hw = handle ? handle->width() : 0; + const qreal offset = hw / 2; + const qreal extent = q->availableWidth() - hw; + if (!qFuzzyIsNull(extent)) { + if (q->isMirrored()) + return (q->width() - point.x() - q->rightPadding() - offset) / extent; + return (point.x() - q->leftPadding() - offset) / extent; + } + } else { + const qreal hh = handle ? handle->height() : 0; + const qreal offset = hh / 2; + const qreal extent = q->availableHeight() - hh; + if (!qFuzzyIsNull(extent)) + return (q->height() - point.y() - q->bottomPadding() - offset) / extent; + } + return 0; +} + +void QQuickSliderPrivate::setPosition(qreal pos) +{ + Q_Q(QQuickSlider); + pos = qBound<qreal>(0.0, pos, 1.0); + if (qFuzzyCompare(position, pos)) + return; + + position = pos; + emit q->positionChanged(); + emit q->visualPositionChanged(); +} + +void QQuickSliderPrivate::updatePosition() +{ + qreal pos = 0; + if (!qFuzzyCompare(from, to)) + pos = (value - from) / (to - from); + setPosition(pos); +} + +QQuickSlider::QQuickSlider(QQuickItem *parent) : + QQuickControl(*(new QQuickSliderPrivate), parent) +{ + setActiveFocusOnTab(true); + setFocusPolicy(Qt::StrongFocus); + setAcceptedMouseButtons(Qt::LeftButton); +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::from + + This property holds the starting value for the range. The default value is \c 0.0. + + \sa to, value +*/ +qreal QQuickSlider::from() const +{ + Q_D(const QQuickSlider); + return d->from; +} + +void QQuickSlider::setFrom(qreal from) +{ + Q_D(QQuickSlider); + if (qFuzzyCompare(d->from, from)) + return; + + d->from = from; + emit fromChanged(); + if (isComponentComplete()) { + setValue(d->value); + d->updatePosition(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::to + + This property holds the end value for the range. The default value is \c 1.0. + + \sa from, value +*/ +qreal QQuickSlider::to() const +{ + Q_D(const QQuickSlider); + return d->to; +} + +void QQuickSlider::setTo(qreal to) +{ + Q_D(QQuickSlider); + if (qFuzzyCompare(d->to, to)) + return; + + d->to = to; + emit toChanged(); + if (isComponentComplete()) { + setValue(d->value); + d->updatePosition(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::value + + This property holds the value in the range \c from - \c to. The default value is \c 0.0. + + Unlike the \l position property, the \c value is not updated while the + handle is dragged. The value is updated after the value has been chosen + and the slider has been released. + + \sa position +*/ +qreal QQuickSlider::value() const +{ + Q_D(const QQuickSlider); + return d->value; +} + +void QQuickSlider::setValue(qreal value) +{ + Q_D(QQuickSlider); + if (isComponentComplete()) + value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to); + + if (qFuzzyCompare(d->value, value)) + return; + + d->value = value; + d->updatePosition(); + emit valueChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::position + \readonly + + This property holds the logical position of the handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. Unlike the \l value property, the \c position is + continuously updated while the handle is dragged. For visualizing a + slider, the right-to-left aware \l visualPosition should be used instead. + + \sa value, visualPosition +*/ +qreal QQuickSlider::position() const +{ + Q_D(const QQuickSlider); + return d->position; +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::visualPosition + \readonly + + This property holds the visual position of the handle. + + The position is expressed as a fraction of the control's size, in the range + \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the + value is equal to \c {1.0 - position}. This makes the value suitable for + visualizing the slider, taking right-to-left support into account. + + \sa position +*/ +qreal QQuickSlider::visualPosition() const +{ + Q_D(const QQuickSlider); + if (d->orientation == Qt::Vertical || isMirrored()) + return 1.0 - d->position; + return d->position; +} + +/*! + \qmlproperty real QtQuick.Controls::Slider::stepSize + + This property holds the step size. The default value is \c 0.0. + + \sa snapMode, increase(), decrease() +*/ +qreal QQuickSlider::stepSize() const +{ + Q_D(const QQuickSlider); + return d->stepSize; +} + +void QQuickSlider::setStepSize(qreal step) +{ + Q_D(QQuickSlider); + if (qFuzzyCompare(d->stepSize, step)) + return; + + d->stepSize = step; + emit stepSizeChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Slider::snapMode + + This property holds the snap mode. + + Possible values: + \value Slider.NoSnap The slider does not snap (default). + \value Slider.SnapAlways The slider snaps while the handle is dragged. + \value Slider.SnapOnRelease The slider does not snap while being dragged, but only after the handle is released. + + In the following table, the various modes are illustrated with animations. + The movement of the mouse cursor and the \l stepSize (\c 0.2) are identical + in each animation. + + \table + \header + \row \li \b Value \li \b Example + \row \li \c Slider.NoSnap \li \image qtquickcontrols2-slider-nosnap.gif + \row \li \c Slider.SnapAlways \li \image qtquickcontrols2-slider-snapalways.gif + \row \li \c Slider.SnapOnRelease \li \image qtquickcontrols2-slider-snaponrelease.gif + \endtable + + \sa stepSize +*/ +QQuickSlider::SnapMode QQuickSlider::snapMode() const +{ + Q_D(const QQuickSlider); + return d->snapMode; +} + +void QQuickSlider::setSnapMode(SnapMode mode) +{ + Q_D(QQuickSlider); + if (d->snapMode == mode) + return; + + d->snapMode = mode; + emit snapModeChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::Slider::pressed + + This property holds whether the slider is pressed. +*/ +bool QQuickSlider::isPressed() const +{ + Q_D(const QQuickSlider); + return d->pressed; +} + +void QQuickSlider::setPressed(bool pressed) +{ + Q_D(QQuickSlider); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + setAccessibleProperty("pressed", pressed); + emit pressedChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::Slider::orientation + + This property holds the orientation. + + Possible values: + \value Qt.Horizontal Horizontal (default) + \value Qt.Vertical Vertical +*/ +Qt::Orientation QQuickSlider::orientation() const +{ + Q_D(const QQuickSlider); + return d->orientation; +} + +void QQuickSlider::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickSlider); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + emit orientationChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Slider::handle + + This property holds the handle item. + + \sa {Customizing Slider} +*/ +QQuickItem *QQuickSlider::handle() const +{ + Q_D(const QQuickSlider); + return d->handle; +} + +void QQuickSlider::setHandle(QQuickItem *handle) +{ + Q_D(QQuickSlider); + if (d->handle == handle) + return; + + d->deleteDelegate(d->handle); + d->handle = handle; + if (handle && !handle->parentItem()) + handle->setParentItem(this); + emit handleChanged(); +} + +/*! + \qmlmethod void QtQuick.Controls::Slider::increase() + + Increases the value by \l stepSize or \c 0.1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickSlider::increase() +{ + Q_D(QQuickSlider); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + setValue(d->value + step); +} + +/*! + \qmlmethod void QtQuick.Controls::Slider::decrease() + + Decreases the value by \l stepSize or \c 0.1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickSlider::decrease() +{ + Q_D(QQuickSlider); + qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + setValue(d->value - step); +} + +void QQuickSlider::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickSlider); + QQuickControl::keyPressEvent(event); + if (d->orientation == Qt::Horizontal) { + if (event->key() == Qt::Key_Left) { + setPressed(true); + if (isMirrored()) + increase(); + else + decrease(); + event->accept(); + } else if (event->key() == Qt::Key_Right) { + setPressed(true); + if (isMirrored()) + decrease(); + else + increase(); + event->accept(); + } + } else { + if (event->key() == Qt::Key_Up) { + setPressed(true); + increase(); + event->accept(); + } else if (event->key() == Qt::Key_Down) { + setPressed(true); + decrease(); + event->accept(); + } + } +} + +void QQuickSlider::keyReleaseEvent(QKeyEvent *event) +{ + QQuickControl::keyReleaseEvent(event); + setPressed(false); +} + +void QQuickSlider::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickSlider); + QQuickControl::mousePressEvent(event); + d->pressPoint = event->pos(); + setPressed(true); +} + +void QQuickSlider::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSlider); + QQuickControl::mouseMoveEvent(event); + if (!keepMouseGrab()) { + if (d->orientation == Qt::Horizontal) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().x() - d->pressPoint.x(), Qt::XAxis, event)); + else + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().y() - d->pressPoint.y(), Qt::YAxis, event)); + } + if (keepMouseGrab()) { + qreal pos = d->positionAt(event->pos()); + if (d->snapMode == SnapAlways) + pos = d->snapPosition(pos); + d->setPosition(pos); + } +} + +void QQuickSlider::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickSlider); + QQuickControl::mouseReleaseEvent(event); + d->pressPoint = QPoint(); + qreal pos = d->positionAt(event->pos()); + if (d->snapMode != NoSnap) + pos = d->snapPosition(pos); + qreal val = d->valueAt(pos); + if (!qFuzzyCompare(val, d->value)) + setValue(val); + else if (d->snapMode != NoSnap) + d->setPosition(pos); + setKeepMouseGrab(false); + setPressed(false); +} + +void QQuickSlider::mouseUngrabEvent() +{ + Q_D(QQuickSlider); + QQuickControl::mouseUngrabEvent(); + d->pressPoint = QPoint(); + setPressed(false); +} + +void QQuickSlider::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickSlider); + QQuickControl::wheelEvent(event); + if (d->wheelEnabled) { + const qreal oldValue = d->value; + const QPointF angle = event->angleDelta(); + const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : angle.y()) / QWheelEvent::DefaultDeltasPerStep; + const qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; + setValue(oldValue + step * delta); + event->setAccepted(!qFuzzyCompare(d->value, oldValue)); + } +} + +void QQuickSlider::mirrorChange() +{ + QQuickControl::mirrorChange(); + emit visualPositionChanged(); +} + +void QQuickSlider::componentComplete() +{ + Q_D(QQuickSlider); + QQuickControl::componentComplete(); + setValue(d->value); + d->updatePosition(); +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickSlider::accessibilityActiveChanged(bool active) +{ + QQuickControl::accessibilityActiveChanged(active); + + Q_D(QQuickSlider); + if (active) + setAccessibleProperty("pressed", d->pressed); +} + +QAccessible::Role QQuickSlider::accessibleRole() const +{ + return QAccessible::Slider; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickslider_p.h b/src/quicktemplates2/qquickslider_p.h new file mode 100644 index 00000000..f6c1401a --- /dev/null +++ b/src/quicktemplates2/qquickslider_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSLIDER_P_H +#define QQUICKSLIDER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSliderPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSlider : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL) + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged FINAL) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL) + Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL) + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) + Q_PROPERTY(QQuickItem *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL) + +public: + explicit QQuickSlider(QQuickItem *parent = nullptr); + + qreal from() const; + void setFrom(qreal from); + + qreal to() const; + void setTo(qreal to); + + qreal value() const; + void setValue(qreal value); + + qreal position() const; + qreal visualPosition() const; + + qreal stepSize() const; + void setStepSize(qreal step); + + enum SnapMode { + NoSnap, + SnapAlways, + SnapOnRelease + }; + Q_ENUM(SnapMode) + + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + bool isPressed() const; + void setPressed(bool pressed); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + + QQuickItem *handle() const; + void setHandle(QQuickItem *handle); + +public Q_SLOTS: + void increase(); + void decrease(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void valueChanged(); + void positionChanged(); + void visualPositionChanged(); + void stepSizeChanged(); + void snapModeChanged(); + void pressedChanged(); + void orientationChanged(); + void handleChanged(); + +protected: + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void wheelEvent(QWheelEvent *event) override; + + void mirrorChange() override; + void componentComplete() override; + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickSlider) + Q_DECLARE_PRIVATE(QQuickSlider) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSlider) + +#endif // QQUICKSLIDER_P_H diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp new file mode 100644 index 00000000..473b6643 --- /dev/null +++ b/src/quicktemplates2/qquickspinbox.cpp @@ -0,0 +1,814 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickspinbox_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qguiapplication.h> +#include <QtGui/qstylehints.h> + +#include <QtQml/qqmlinfo.h> +#include <QtQml/private/qqmllocale_p.h> +#include <QtQml/private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +// copied from qabstractbutton.cpp +static const int AUTO_REPEAT_DELAY = 300; +static const int AUTO_REPEAT_INTERVAL = 100; + +/*! + \qmltype SpinBox + \inherits Control + \instantiates QQuickSpinBox + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup input + \brief Allows the user to select from a set of preset values. + + \image qtquickcontrols2-spinbox.png + + SpinBox allows the user to choose an integer value by clicking the up + or down indicator buttons, or by pressing up or down on the keyboard. + Optionally, SpinBox can be also made \l editable, so the user can enter + a text value in the input field. + + By default, SpinBox provides discrete values in the range of \c [0-99] + with a \l stepSize of \c 1. + + \snippet qtquickcontrols2-spinbox.qml 1 + + \section2 Custom Values + + \image qtquickcontrols2-spinbox-textual.png + + Even though SpinBox works on integer values, it can be customized to + accept arbitrary input values. The following snippet demonstrates how + \l validator, \l textFromValue and \l valueFromText can be used to + customize the default behavior. + + \snippet qtquickcontrols2-spinbox-textual.qml 1 + + In the same manner, SpinBox can be customized to accept floating point + numbers: + + \image qtquickcontrols2-spinbox-double.png + + \snippet qtquickcontrols2-spinbox-double.qml 1 + + \sa Tumbler, {Customizing SpinBox} +*/ + +class QQuickSpinBoxPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickSpinBox) + +public: + QQuickSpinBoxPrivate() : editable(false), from(0), to(99), value(0), stepSize(1), + delayTimer(0), repeatTimer(0), up(nullptr), down(nullptr), validator(nullptr) { } + + int boundValue(int value) const; + void updateValue(); + + int effectiveStepSize() const; + + bool upEnabled() const; + void updateUpEnabled(); + bool downEnabled() const; + void updateDownEnabled(); + + void startRepeatDelay(); + void startPressRepeat(); + void stopPressRepeat(); + + bool handleMousePressEvent(QQuickItem *child, QMouseEvent *event); + bool handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event); + bool handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event); + bool handleMouseUngrabEvent(QQuickItem *child); + + bool editable; + int from; + int to; + int value; + int stepSize; + int delayTimer; + int repeatTimer; + QQuickSpinButton *up; + QQuickSpinButton *down; + QValidator *validator; + mutable QJSValue textFromValue; + mutable QJSValue valueFromText; +}; + +int QQuickSpinBoxPrivate::boundValue(int value) const +{ + return from > to ? qBound(to, value, from) : qBound(from, value, to); +} + +void QQuickSpinBoxPrivate::updateValue() +{ + Q_Q(QQuickSpinBox); + if (contentItem) { + QVariant text = contentItem->property("text"); + if (text.isValid()) { + QQmlEngine *engine = qmlEngine(q); + if (engine) { + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + QJSValue loc(v4, QQmlLocale::wrap(v4, locale)); + QJSValue val = q->valueFromText().call(QJSValueList() << text.toString() << loc); + q->setValue(val.toInt()); + } + } + } +} + +int QQuickSpinBoxPrivate::effectiveStepSize() const +{ + return from > to ? -1 * stepSize : stepSize; +} + +bool QQuickSpinBoxPrivate::upEnabled() const +{ + const QQuickItem *upIndicator = up->indicator(); + return upIndicator && upIndicator->isEnabled(); +} + +void QQuickSpinBoxPrivate::updateUpEnabled() +{ + QQuickItem *upIndicator = up->indicator(); + if (!upIndicator) + return; + + upIndicator->setEnabled(from < to ? value < to : value > to); +} + +bool QQuickSpinBoxPrivate::downEnabled() const +{ + const QQuickItem *downIndicator = down->indicator(); + return downIndicator && downIndicator->isEnabled(); +} + +void QQuickSpinBoxPrivate::updateDownEnabled() +{ + QQuickItem *downIndicator = down->indicator(); + if (!downIndicator) + return; + + downIndicator->setEnabled(from < to ? value > from : value < from); +} + +void QQuickSpinBoxPrivate::startRepeatDelay() +{ + Q_Q(QQuickSpinBox); + stopPressRepeat(); + delayTimer = q->startTimer(AUTO_REPEAT_DELAY); +} + +void QQuickSpinBoxPrivate::startPressRepeat() +{ + Q_Q(QQuickSpinBox); + stopPressRepeat(); + repeatTimer = q->startTimer(AUTO_REPEAT_INTERVAL); +} + +void QQuickSpinBoxPrivate::stopPressRepeat() +{ + Q_Q(QQuickSpinBox); + if (delayTimer > 0) { + q->killTimer(delayTimer); + delayTimer = 0; + } + if (repeatTimer > 0) { + q->killTimer(repeatTimer); + repeatTimer = 0; + } +} + +bool QQuickSpinBoxPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent *event) +{ + Q_Q(QQuickSpinBox); + QQuickItem *ui = up->indicator(); + QQuickItem *di = down->indicator(); + up->setPressed(ui && ui->isEnabled() && ui->contains(ui->mapFromItem(child, event->pos()))); + down->setPressed(di && di->isEnabled() && di->contains(di->mapFromItem(child, event->pos()))); + + bool pressed = up->isPressed() || down->isPressed(); + q->setAccessibleProperty("pressed", pressed); + if (pressed) + startRepeatDelay(); + return pressed; +} + +bool QQuickSpinBoxPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event) +{ + Q_Q(QQuickSpinBox); + QQuickItem *ui = up->indicator(); + QQuickItem *di = down->indicator(); + up->setPressed(ui && ui->isEnabled() && ui->contains(ui->mapFromItem(child, event->pos()))); + down->setPressed(di && di->isEnabled() && di->contains(di->mapFromItem(child, event->pos()))); + + bool pressed = up->isPressed() || down->isPressed(); + q->setAccessibleProperty("pressed", pressed); + if (!pressed) + stopPressRepeat(); + return pressed; +} + +bool QQuickSpinBoxPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event) +{ + Q_Q(QQuickSpinBox); + QQuickItem *ui = up->indicator(); + QQuickItem *di = down->indicator(); + bool wasPressed = up->isPressed() || down->isPressed(); + if (up->isPressed()) { + up->setPressed(false); + if (repeatTimer <= 0 && ui && ui->contains(ui->mapFromItem(child, event->pos()))) + q->increase(); + } else if (down->isPressed()) { + down->setPressed(false); + if (repeatTimer <= 0 && di && di->contains(di->mapFromItem(child, event->pos()))) + q->decrease(); + } + + q->setAccessibleProperty("pressed", false); + stopPressRepeat(); + return wasPressed; +} + +bool QQuickSpinBoxPrivate::handleMouseUngrabEvent(QQuickItem *) +{ + Q_Q(QQuickSpinBox); + up->setPressed(false); + down->setPressed(false); + + q->setAccessibleProperty("pressed", false); + stopPressRepeat(); + return false; +} + +QQuickSpinBox::QQuickSpinBox(QQuickItem *parent) : + QQuickControl(*(new QQuickSpinBoxPrivate), parent) +{ + Q_D(QQuickSpinBox); + d->up = new QQuickSpinButton(this); + d->down = new QQuickSpinButton(this); + + setFlag(ItemIsFocusScope); + setFiltersChildMouseEvents(true); + setAcceptedMouseButtons(Qt::LeftButton); +} + +/*! + \qmlproperty int QtQuick.Controls::SpinBox::from + + This property holds the starting value for the range. The default value is \c 0. + + \sa to, value +*/ +int QQuickSpinBox::from() const +{ + Q_D(const QQuickSpinBox); + return d->from; +} + +void QQuickSpinBox::setFrom(int from) +{ + Q_D(QQuickSpinBox); + if (d->from == from) + return; + + d->from = from; + emit fromChanged(); + if (isComponentComplete()) + setValue(d->value); +} + +/*! + \qmlproperty int QtQuick.Controls::SpinBox::to + + This property holds the end value for the range. The default value is \c 99. + + \sa from, value +*/ +int QQuickSpinBox::to() const +{ + Q_D(const QQuickSpinBox); + return d->to; +} + +void QQuickSpinBox::setTo(int to) +{ + Q_D(QQuickSpinBox); + if (d->to == to) + return; + + d->to = to; + emit toChanged(); + if (isComponentComplete()) + setValue(d->value); +} + +/*! + \qmlproperty int QtQuick.Controls::SpinBox::value + + This property holds the value in the range \c from - \c to. The default value is \c 0. +*/ +int QQuickSpinBox::value() const +{ + Q_D(const QQuickSpinBox); + return d->value; +} + +void QQuickSpinBox::setValue(int value) +{ + Q_D(QQuickSpinBox); + if (isComponentComplete()) + value = d->boundValue(value); + + if (d->value == value) + return; + + d->value = value; + + d->updateUpEnabled(); + d->updateDownEnabled(); + + emit valueChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::SpinBox::stepSize + + This property holds the step size. The default value is \c 1. + + \sa increase(), decrease() +*/ +int QQuickSpinBox::stepSize() const +{ + Q_D(const QQuickSpinBox); + return d->stepSize; +} + +void QQuickSpinBox::setStepSize(int step) +{ + Q_D(QQuickSpinBox); + if (d->stepSize == step) + return; + + d->stepSize = step; + emit stepSizeChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::SpinBox::editable + + This property holds whether the spinbox is editable. The default value is \c false. + + \sa validator +*/ +bool QQuickSpinBox::isEditable() const +{ + Q_D(const QQuickSpinBox); + return d->editable; +} + +void QQuickSpinBox::setEditable(bool editable) +{ + Q_D(QQuickSpinBox); + if (d->editable == editable) + return; + + d->editable = editable; + emit editableChanged(); +} + +/*! + \qmlproperty Validator QtQuick.Controls::SpinBox::validator + + This property holds the input text validator for editable spinboxes. By + default, SpinBox uses \l IntValidator to accept input of integer numbers. + + \code + SpinBox { + id: control + validator: IntValidator { + locale: control.locale.name + bottom: Math.min(control.from, control.to) + top: Math.max(control.from, control.to) + } + } + \endcode + + \sa editable, textFromValue, valueFromText, {Control::locale}{locale} +*/ +QValidator *QQuickSpinBox::validator() const +{ + Q_D(const QQuickSpinBox); + return d->validator; +} + +void QQuickSpinBox::setValidator(QValidator *validator) +{ + Q_D(QQuickSpinBox); + if (d->validator == validator) + return; + + d->validator = validator; + emit validatorChanged(); +} + +/*! + \qmlproperty function QtQuick.Controls::SpinBox::textFromValue + + This property holds a callback function that is called whenever + an integer value needs to be converted to display text. + + The default function can be overridden to display custom text for a given + value. This applies to both editable and non-editable spinboxes; + for example, when using the up and down buttons or a mouse wheel to + increment and decrement the value, the new value is converted to display + text using this function. + + The callback function signature is \c {string function(value, locale)}. + The function can have one or two arguments, where the first argument + is the value to be converted, and the optional second argument is the + locale that should be used for the conversion, if applicable. + + The default implementation does the conversion using \l {QtQml::Locale}{Number.toLocaleString()}: + + \code + textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); } + \endcode + + \note When applying a custom \c textFromValue implementation for editable + spinboxes, a matching \l valueFromText implementation must be provided + to be able to convert the custom text back to an integer value. + + \sa valueFromText, validator, {Control::locale}{locale} +*/ +QJSValue QQuickSpinBox::textFromValue() const +{ + Q_D(const QQuickSpinBox); + if (!d->textFromValue.isCallable()) { + QQmlEngine *engine = qmlEngine(this); + if (engine) + d->textFromValue = engine->evaluate(QStringLiteral("function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }")); + } + return d->textFromValue; +} + +void QQuickSpinBox::setTextFromValue(const QJSValue &callback) +{ + Q_D(QQuickSpinBox); + if (!callback.isCallable()) { + qmlInfo(this) << "textFromValue must be a callable function"; + return; + } + d->textFromValue = callback; + emit textFromValueChanged(); +} + +/*! + \qmlproperty function QtQuick.Controls::SpinBox::valueFromText + + This property holds a callback function that is called whenever + input text needs to be converted to an integer value. + + This function only needs to be overridden when \l textFromValue + is overridden for an editable spinbox. + + The callback function signature is \c {int function(text, locale)}. + The function can have one or two arguments, where the first argument + is the text to be converted, and the optional second argument is the + locale that should be used for the conversion, if applicable. + + The default implementation does the conversion using \l {QtQml::Locale}{Number.fromLocaleString()}: + + \code + valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text); } + \endcode + + \note When applying a custom \l textFromValue implementation for editable + spinboxes, a matching \c valueFromText implementation must be provided + to be able to convert the custom text back to an integer value. + + \sa textFromValue, validator, {Control::locale}{locale} +*/ +QJSValue QQuickSpinBox::valueFromText() const +{ + Q_D(const QQuickSpinBox); + if (!d->valueFromText.isCallable()) { + QQmlEngine *engine = qmlEngine(this); + if (engine) + d->valueFromText = engine->evaluate(QStringLiteral("function(text, locale) { return Number.fromLocaleString(locale, text); }")); + } + return d->valueFromText; +} + +void QQuickSpinBox::setValueFromText(const QJSValue &callback) +{ + Q_D(QQuickSpinBox); + if (!callback.isCallable()) { + qmlInfo(this) << "valueFromText must be a callable function"; + return; + } + d->valueFromText = callback; + emit valueFromTextChanged(); +} + +/*! + \qmlpropertygroup QtQuick.Controls::SpinBox::up + \qmlproperty bool QtQuick.Controls::SpinBox::up.pressed + \qmlproperty Item QtQuick.Controls::SpinBox::up.indicator + + These properties hold the up indicator item and whether it is pressed. + + \sa increase() +*/ +QQuickSpinButton *QQuickSpinBox::up() const +{ + Q_D(const QQuickSpinBox); + return d->up; +} + +/*! + \qmlpropertygroup QtQuick.Controls::SpinBox::down + \qmlproperty bool QtQuick.Controls::SpinBox::down.pressed + \qmlproperty Item QtQuick.Controls::SpinBox::down.indicator + + These properties hold the down indicator item and whether it is pressed. + + \sa decrease() +*/ +QQuickSpinButton *QQuickSpinBox::down() const +{ + Q_D(const QQuickSpinBox); + return d->down; +} + +/*! + \qmlmethod void QtQuick.Controls::SpinBox::increase() + + Increases the value by \l stepSize, or \c 1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickSpinBox::increase() +{ + Q_D(QQuickSpinBox); + setValue(d->value + d->effectiveStepSize()); +} + +/*! + \qmlmethod void QtQuick.Controls::SpinBox::decrease() + + Decreases the value by \l stepSize, or \c 1 if stepSize is not defined. + + \sa stepSize +*/ +void QQuickSpinBox::decrease() +{ + Q_D(QQuickSpinBox); + setValue(d->value - d->effectiveStepSize()); +} + +void QQuickSpinBox::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::keyPressEvent(event); + + switch (event->key()) { + case Qt::Key_Up: + if (d->upEnabled()) { + increase(); + d->up->setPressed(true); + event->accept(); + } + break; + + case Qt::Key_Down: + if (d->downEnabled()) { + decrease(); + d->down->setPressed(true); + event->accept(); + } + break; + + default: + break; + } + + setAccessibleProperty("pressed", d->up->isPressed() || d->down->isPressed()); +} + +void QQuickSpinBox::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::keyReleaseEvent(event); + + if (d->editable && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) + d->updateValue(); + + d->up->setPressed(false); + d->down->setPressed(false); + setAccessibleProperty("pressed", false); +} + +bool QQuickSpinBox::childMouseEventFilter(QQuickItem *child, QEvent *event) +{ + Q_D(QQuickSpinBox); + 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; + } +} + +void QQuickSpinBox::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::mousePressEvent(event); + d->handleMousePressEvent(this, event); +} + +void QQuickSpinBox::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::mouseMoveEvent(event); + d->handleMouseMoveEvent(this, event); +} + +void QQuickSpinBox::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::mouseReleaseEvent(event); + d->handleMouseReleaseEvent(this, event); +} + +void QQuickSpinBox::mouseUngrabEvent() +{ + Q_D(QQuickSpinBox); + QQuickControl::mouseUngrabEvent(); + d->handleMouseUngrabEvent(this); +} + +void QQuickSpinBox::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::timerEvent(event); + if (event->timerId() == d->delayTimer) { + d->startPressRepeat(); + } else if (event->timerId() == d->repeatTimer) { + if (d->up->isPressed()) + increase(); + else if (d->down->isPressed()) + decrease(); + } +} + +void QQuickSpinBox::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::wheelEvent(event); + if (d->wheelEnabled) { + const int oldValue = d->value; + const QPointF angle = event->angleDelta(); + const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : angle.y()) / QWheelEvent::DefaultDeltasPerStep; + setValue(oldValue + qRound(d->effectiveStepSize() * delta)); + event->setAccepted(d->value != oldValue); + } +} + +void QQuickSpinBox::componentComplete() +{ + Q_D(QQuickSpinBox); + QQuickControl::componentComplete(); + d->updateUpEnabled(); + d->updateDownEnabled(); +} + +void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickSpinBox); + QQuickControl::itemChange(change, value); + if (d->editable && change == ItemActiveFocusHasChanged && !value.boolValue) + d->updateValue(); +} + +void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_UNUSED(oldItem); + if (newItem) + newItem->setActiveFocusOnTab(true); +} + +QFont QQuickSpinBox::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::EditorFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickSpinBox::accessibleRole() const +{ + return QAccessible::SpinBox; +} +#endif + +class QQuickSpinButtonPrivate : public QObjectPrivate +{ +public: + QQuickSpinButtonPrivate() : pressed(false), indicator(nullptr) { } + bool pressed; + QQuickItem *indicator; +}; + +QQuickSpinButton::QQuickSpinButton(QQuickSpinBox *parent) : + QObject(*(new QQuickSpinButtonPrivate), parent) +{ +} + +bool QQuickSpinButton::isPressed() const +{ + Q_D(const QQuickSpinButton); + return d->pressed; +} + +void QQuickSpinButton::setPressed(bool pressed) +{ + Q_D(QQuickSpinButton); + if (d->pressed == pressed) + return; + + d->pressed = pressed; + emit pressedChanged(); +} + +QQuickItem *QQuickSpinButton::indicator() const +{ + Q_D(const QQuickSpinButton); + return d->indicator; +} + +void QQuickSpinButton::setIndicator(QQuickItem *indicator) +{ + Q_D(QQuickSpinButton); + if (d->indicator == indicator) + return; + + QQuickControl *control = qobject_cast<QQuickControl*>(d->parent); + if (control) + QQuickControlPrivate::get(control)->deleteDelegate(d->indicator); + else + delete d->indicator; + + d->indicator = indicator; + + if (indicator) { + if (!indicator->parentItem()) + indicator->setParentItem(static_cast<QQuickItem *>(parent())); + indicator->setAcceptedMouseButtons(Qt::LeftButton); + } + emit indicatorChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickspinbox_p.h b/src/quicktemplates2/qquickspinbox_p.h new file mode 100644 index 00000000..3898a28b --- /dev/null +++ b/src/quicktemplates2/qquickspinbox_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSPINBOX_P_H +#define QQUICKSPINBOX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_NAMESPACE + +class QValidator; +class QQuickSpinButton; +class QQuickSpinButtonPrivate; +class QQuickSpinBoxPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinBox : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(int from READ from WRITE setFrom NOTIFY fromChanged FINAL) + Q_PROPERTY(int to READ to WRITE setTo NOTIFY toChanged FINAL) + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL) + Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL) + Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged FINAL) + Q_PROPERTY(QValidator *validator READ validator WRITE setValidator NOTIFY validatorChanged FINAL) + Q_PROPERTY(QJSValue textFromValue READ textFromValue WRITE setTextFromValue NOTIFY textFromValueChanged FINAL) + Q_PROPERTY(QJSValue valueFromText READ valueFromText WRITE setValueFromText NOTIFY valueFromTextChanged FINAL) + Q_PROPERTY(QQuickSpinButton *up READ up CONSTANT FINAL) + Q_PROPERTY(QQuickSpinButton *down READ down CONSTANT FINAL) + +public: + explicit QQuickSpinBox(QQuickItem *parent = nullptr); + + int from() const; + void setFrom(int from); + + int to() const; + void setTo(int to); + + int value() const; + void setValue(int value); + + int stepSize() const; + void setStepSize(int step); + + bool isEditable() const; + void setEditable(bool editable); + + QValidator *validator() const; + void setValidator(QValidator *validator); + + QJSValue textFromValue() const; + void setTextFromValue(const QJSValue &callback); + + QJSValue valueFromText() const; + void setValueFromText(const QJSValue &callback); + + QQuickSpinButton *up() const; + QQuickSpinButton *down() const; + +public Q_SLOTS: + void increase(); + void decrease(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void valueChanged(); + void stepSizeChanged(); + void editableChanged(); + void validatorChanged(); + void textFromValueChanged(); + void valueFromTextChanged(); + +protected: + bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void timerEvent(QTimerEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + + void componentComplete() override; + void itemChange(ItemChange change, const ItemChangeData &value) override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickSpinBox) + Q_DECLARE_PRIVATE(QQuickSpinBox) +}; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinButton : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL) + +public: + explicit QQuickSpinButton(QQuickSpinBox *parent); + + bool isPressed() const; + void setPressed(bool pressed); + + QQuickItem *indicator() const; + void setIndicator(QQuickItem *indicator); + +Q_SIGNALS: + void pressedChanged(); + void indicatorChanged(); + +private: + Q_DISABLE_COPY(QQuickSpinButton) + Q_DECLARE_PRIVATE(QQuickSpinButton) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSpinBox) + +#endif // QQUICKSPINBOX_P_H diff --git a/src/quicktemplates2/qquickstackview.cpp b/src/quicktemplates2/qquickstackview.cpp new file mode 100644 index 00000000..df9ab74f --- /dev/null +++ b/src/quicktemplates2/qquickstackview.cpp @@ -0,0 +1,1041 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstackview_p.h" +#include "qquickstackview_p_p.h" + +#include <QtQml/qjsvalue.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> + +#include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype StackView + \inherits Control + \instantiates QQuickStackView + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-navigation + \ingroup qtquickcontrols2-containers + \brief Provides a stack-based navigation model. + + \image qtquickcontrols2-stackview-wireframe.png + + StackView can be used with a set of inter-linked information pages. For + example, an email application with separate views to list the latest emails, + view a specific email, and list/view the attachments. The email list view + is pushed onto the stack as users open an email, and popped out as they + choose to go back. + + The following snippet demonstrates a simple use case, where the \c mainView + is pushed onto and popped out of the stack on relevant button click: + + \qml + ApplicationWindow { + title: qsTr("Hello World") + width: 640 + height: 480 + visible: true + + StackView { + id: stack + initialItem: mainView + anchors.fill: parent + } + + Component { + id: mainView + + Row { + spacing: 10 + + Button { + text: "Push" + onClicked: stack.push(mainView) + } + Button { + text: "Pop" + enabled: stack.depth > 1 + onClicked: stack.pop() + + } + Text { + text: stack.depth + } + } + } + } + \endqml + + \section1 Using StackView in an Application + + Using StackView in an application is as simple as adding it as a child to + a Window. The stack is usually anchored to the edges of the window, except + at the top or bottom where it might be anchored to a status bar, or some + other similar UI component. The stack can then be used by invoking its + navigation methods. The first item to show in the StackView is the one + that was assigned to \l initialItem, or the topmost item if \l initialItem + is not set. + + \section1 Basic Navigation + + StackView supports three primary navigation operations: push(), pop(), and + replace(). These correspond to classic stack operations where "push" adds + an item to the top of a stack, "pop" removes the top item from the + stack, and "replace" is like a pop followed by a push, which replaces the + topmost item with the new item. The topmost item in the stack + corresponds to the one that is \l{StackView::currentItem}{currently} + visible on screen. Logically, "push" navigates forward or deeper into the + application UI, "pop" navigates backward, and "replace" replaces the + \l currentItem. + + \section2 Pushing Items + + In the following animation, three \l Label controls are pushed onto a + stack view with the \l push() function: + + \image qtquickcontrols2-stackview-push.gif + + The stack now contains the following items: \c [A, B, C]. + + \note When the stack is empty, a push() operation will not have a + transition animation because there is nothing to transition from (typically + on application start-up). + + \section2 Popping Items + + Continuing on from the example above, the topmost item on the stack is + removed with a call to \l pop(): + + \image qtquickcontrols2-stackview-pop.gif + + The stack now contains the following items: \c [A, B]. + + \note A pop() operation on a stack with depth 1 or 0 does nothing. In such + cases, the stack can be emptied using the \l clear() method. + + \section3 Unwinding Items via Pop + + Sometimes, it is necessary to go back more than a single step in the stack. + For example, to return to a "main" item or some kind of section item in the + application. In such cases, it is possible to specify an item as a + parameter for pop(). This is called an "unwind" operation, where the stack + unwinds till the specified item. If the item is not found, stack unwinds + until it is left with one item, which becomes the \l currentItem. To + explicitly unwind to the bottom of the stack, it is recommended to use + \l{pop()}{pop(null)}, although any non-existent item will do. + + In the following animation, we unwind the stack to the first item by + calling \c pop(null): + + \image qtquickcontrols2-stackview-unwind.gif + + The stack now contains a single item: \c [A]. + + \section2 Replacing Items + + In the following animation, we \l replace the topmost item with \c D: + + \image qtquickcontrols2-stackview-replace.gif + + The stack now contains the following items: \c [A, B, D]. + + \section1 Deep Linking + + \e{Deep linking} means launching an application into a particular state. For + example, a newspaper application could be launched into showing a + particular article, bypassing the topmost item. In terms of StackView, deep linking means the ability to modify + the state of the stack, so much so that it is possible to push a set of + items to the top of the stack, or to completely reset the stack to a given + state. + + The API for deep linking in StackView is the same as for basic navigation. + Pushing an array instead of a single item adds all the items in that array + to the stack. The transition animation, however, is applied only for the + last item in the array. The normal semantics of push() apply for deep + linking, that is, it adds whatever is pushed onto the stack. + + \note Only the last item of the array is loaded. The rest of the items are + loaded only when needed, either on subsequent calls to pop or on request to + get an item using get(). + + This gives us the following result, given the stack [A, B, C]: + + \list + \li \l{push()}{push([D, E, F])} => [A, B, C, D, E, F] - "push" transition + animation between C and F + \li \l{replace()}{replace([D, E, F])} => [A, B, D, E, F] - "replace" + transition animation between C and F + \li \l{clear()} followed by \l{push()}{push([D, E, F])} => [D, E, F] - no + transition animation for pushing items as the stack was empty. + \endlist + + \section1 Finding Items + + An Item for which the application does not have a reference can be found + by calling find(). The method needs a callback function, which is invoked + for each item in the stack (starting at the top) until a match is found. + If the callback returns \c true, find() stops and returns the matching + item, otherwise \c null is returned. + + The code below searches the stack for an item named "order_id" and unwinds + to that item. + + \badcode + stackView.pop(stackView.find(function(item) { + return item.name == "order_id"; + })); + \endcode + + You can also get to an item in the stack using \l {get()}{get(index)}. + + \badcode + previousItem = stackView.get(myItem.StackView.index - 1)); + \endcode + + \section1 Transitions + + For each push or pop operation, different transition animations are applied + to entering and exiting items. These animations define how the entering item + should animate in, and the exiting item should animate out. The animations + can be customized by assigning different \l{Transition}s for the + \l pushEnter, \l pushExit, \l popEnter, \l popExit, \l replaceEnter, and + \l replaceExit properties of StackView. + + \note The transition animations affect each others' transitional behavior. + Customizing the animation for one and leaving the other may give unexpected + results. + + The following snippet defines a simple fade transition for push and pop + operations: + + \qml + StackView { + id: stackview + anchors.fill: parent + + pushEnter: Transition { + PropertyAnimation { + property: "opacity" + from: 0 + to:1 + duration: 200 + } + } + pushExit: Transition { + PropertyAnimation { + property: "opacity" + from: 1 + to:0 + duration: 200 + } + } + popEnter: Transition { + PropertyAnimation { + property: "opacity" + from: 0 + to:1 + duration: 200 + } + } + popExit: Transition { + PropertyAnimation { + property: "opacity" + from: 1 + to:0 + duration: 200 + } + } + } + \endqml + + \note Using anchors on the items added to a StackView is not supported. + Typically push, pop, and replace transitions animate the position, + which is not possible when anchors are applied. Notice that this + only applies to the root of the item. Using anchors for its children + works as expected. + + \sa {Customizing StackView}, {Navigation Controls}, {Container Controls} +*/ + +QQuickStackView::QQuickStackView(QQuickItem *parent) : + QQuickControl(*(new QQuickStackViewPrivate), parent) +{ + setFlag(ItemIsFocusScope); +} + +QQuickStackView::~QQuickStackView() +{ + Q_D(QQuickStackView); + if (d->transitioner) { + d->transitioner->setChangeListener(nullptr); + delete d->transitioner; + } + qDeleteAll(d->removals); + qDeleteAll(d->elements); +} + +QQuickStackAttached *QQuickStackView::qmlAttachedProperties(QObject *object) +{ + QQuickItem *item = qobject_cast<QQuickItem *>(object); + if (!item) { + qmlInfo(object) << "StackView must be attached to an Item"; + return nullptr; + } + return new QQuickStackAttached(item); +} + +/*! + \qmlproperty bool QtQuick.Controls::StackView::busy + \readonly + This property holds whether a transition is running. +*/ +bool QQuickStackView::isBusy() const +{ + Q_D(const QQuickStackView); + return d->busy; +} + +/*! + \qmlproperty int QtQuick.Controls::StackView::depth + \readonly + This property holds the number of items currently pushed onto the stack. +*/ +int QQuickStackView::depth() const +{ + Q_D(const QQuickStackView); + return d->elements.count(); +} + +/*! + \qmlproperty Item QtQuick.Controls::StackView::currentItem + \readonly + This property holds the current top-most item in the stack. +*/ +QQuickItem *QQuickStackView::currentItem() const +{ + Q_D(const QQuickStackView); + return d->currentItem; +} + +/*! + \qmlmethod Item QtQuick.Controls::StackView::get(index, behavior) + + Returns the item at position \a index in the stack, or \c null if the index + is out of bounds. + + Supported behavior values: + \value StackView.DontLoad The item is not forced to load (and \c null is returned if not yet loaded). + \value StackView.ForceLoad The item is forced to load. +*/ +QQuickItem *QQuickStackView::get(int index, LoadBehavior behavior) +{ + Q_D(QQuickStackView); + QQuickStackElement *element = d->elements.value(index); + if (element) { + if (behavior == ForceLoad) + element->load(this); + return element->item; + } + return nullptr; +} + +/*! + \qmlmethod Item QtQuick.Controls::StackView::find(callback, behavior) + + Search for a specific item inside the stack. The \a callback function is called + for each item in the stack (with the item and index as arguments) until the callback + function returns \c true. The return value is the item found. For example: + + \code + stackView.find(function(item, index) { + return item.isTheOne + }) + \endcode + + Supported behavior values: + \value StackView.DontLoad Unloaded items are skipped (the callback function is not called for them). + \value StackView.ForceLoad Unloaded items are forced to load. +*/ +QQuickItem *QQuickStackView::find(const QJSValue &callback, LoadBehavior behavior) +{ + Q_D(QQuickStackView); + QJSValue func(callback); + QQmlEngine *engine = qmlEngine(this); + if (!engine || !func.isCallable()) // TODO: warning? + return nullptr; + + for (int i = d->elements.count() - 1; i >= 0; --i) { + QQuickStackElement *element = d->elements.at(i); + if (behavior == ForceLoad) + element->load(this); + if (element->item) { + QJSValue rv = func.call(QJSValueList() << engine->newQObject(element->item) << i); + if (rv.toBool()) + return element->item; + } + } + + return nullptr; +} + +/*! + \qmlmethod Item QtQuick.Controls::StackView::push(item, properties, operation) + + Pushes an \a item onto the stack using the specified \a operation, and + optionally applies a set of \a properties on the item. The item can be + an \l Item, \l Component, or a \l [QML] url. Returns the item that became + current. + + StackView creates an instance automatically if the pushed item is a \l Component, + or a \l [QML] url. The optional \a properties argument specifies a map of initial + property values for the pushed item. For dynamically created items, these values + are applied before the creation is finalized. This is more efficient than setting + property values after creation, particularly where large sets of property values + are defined, and also allows property bindings to be set up (using \l{Qt::binding} + {Qt.binding()}) before the item is created. + + Pushing a single item: + \code + stackView.push(rect) + + // or with properties: + stackView.push(rect, {"color": "red"}) + \endcode + + Multiple items can be pushed at the same time either by passing them as + additional arguments, or as an array. The last item becomes the current + item. Each item can be followed by a set of properties to apply. + + Passing a variable amount of arguments: + \code + stackView.push(rect1, rect2, rect3) + + // or with properties: + stackView.push(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}) + \endcode + + Pushing an array of items: + \code + stackView.push([rect1, rect2, rect3]) + + // or with properties: + stackView.push([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}]) + \endcode + + An \a operation can be optionally specified as the last argument. Supported + operations: + + \value StackView.Transition An operation with transitions. + \value StackView.Immediate An immediate operation without transitions. + + \sa initialItem, {Pushing Items} +*/ +void QQuickStackView::push(QQmlV4Function *args) +{ + Q_D(QQuickStackView); + if (args->length() <= 0) { + qmlInfo(this) << "push: missing arguments"; + args->setReturnValue(QV4::Encode::null()); + return; + } + + QV4::ExecutionEngine *v4 = args->v4engine(); + QV4::Scope scope(v4); + + Operation operation = d->elements.isEmpty() ? Immediate : Transition; + QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]); + if (lastArg->isInt32()) + operation = static_cast<Operation>(lastArg->toInt32()); + + QList<QQuickStackElement *> elements = d->parseElements(args); + if (elements.isEmpty()) { + qmlInfo(this) << "push: nothing to push"; + args->setReturnValue(QV4::Encode::null()); + return; + } + + QQuickStackElement *exit = nullptr; + if (!d->elements.isEmpty()) + exit = d->elements.top(); + + if (d->pushElements(elements)) { + emit depthChanged(); + QQuickStackElement *enter = d->elements.top(); + d->pushTransition(enter, exit, boundingRect(), operation == Immediate); + d->setCurrentItem(enter->item); + } + + if (d->currentItem) { + QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem)); + args->setReturnValue(rv->asReturnedValue()); + } else { + args->setReturnValue(QV4::Encode::null()); + } +} + +/*! + \qmlmethod Item QtQuick.Controls::StackView::pop(item, operation) + + Pops one or more items off the stack. Returns the last item removed from the stack. + + If the \a item argument is specified, all items down to (but not + including) \a item will be popped. If \a item is \c null, all + items down to (but not including) the first item is popped. + If not specified, only the current item is popped. + + An \a operation can be optionally specified as the last argument. Supported + operations: + + \value StackView.Transition An operation with transitions. + \value StackView.Immediate An immediate operation without transitions. + + Examples: + \code + stackView.pop() + stackView.pop(someItem, StackView.Immediate) + stackView.pop(StackView.Immediate) + stackView.pop(null) + \endcode + + \sa clear(), {Popping Items}, {Unwinding Items via Pop} +*/ +void QQuickStackView::pop(QQmlV4Function *args) +{ + Q_D(QQuickStackView); + int argc = args->length(); + if (d->elements.count() <= 1 || argc > 2) { + if (argc > 2) + qmlInfo(this) << "pop: too many arguments"; + args->setReturnValue(QV4::Encode::null()); + return; + } + + QQuickStackElement *exit = d->elements.pop(); + QQuickStackElement *enter = d->elements.top(); + + QV4::ExecutionEngine *v4 = args->v4engine(); + QV4::Scope scope(v4); + + if (argc > 0) { + QV4::ScopedValue value(scope, (*args)[0]); + if (value->isNull()) { + enter = d->elements.value(0); + } else if (const QV4::QObjectWrapper *o = value->as<QV4::QObjectWrapper>()) { + QQuickItem *item = qobject_cast<QQuickItem *>(o->object()); + enter = d->findElement(item); + if (!enter) { + if (item != d->currentItem) + qmlInfo(this) << "pop: unknown argument: " << value->toQString(); // TODO: safe? + args->setReturnValue(QV4::Encode::null()); + d->elements.push(exit); // restore + return; + } + } + } + + Operation operation = Transition; + if (argc > 0) { + QV4::ScopedValue lastArg(scope, (*args)[argc - 1]); + if (lastArg->isInt32()) + operation = static_cast<Operation>(lastArg->toInt32()); + } + + QQuickItem *previousItem = nullptr; + + if (d->popElements(enter)) { + if (exit) + previousItem = exit->item; + emit depthChanged(); + d->popTransition(enter, exit, boundingRect(), operation == Immediate); + d->setCurrentItem(enter->item); + } + + if (previousItem) { + QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, previousItem)); + args->setReturnValue(rv->asReturnedValue()); + } else { + args->setReturnValue(QV4::Encode::null()); + } +} + +/*! + \qmlmethod Item QtQuick.Controls::StackView::replace(target, item, properties, operation) + + Replaces one or more items on the stack with the specified \a item and + \a operation, and optionally applies a set of \a properties on the + item. The item can be an \l Item, \l Component, or a \l [QML] url. + Returns the item that became current. + + If the \a target argument is specified, all items down to the \target + item will be replaced. If \a target is \c null, all items in the stack + will be replaced. If not specified, only the top item will be replaced. + + StackView creates an instance automatically if the replacing item is a \l Component, + or a \l [QML] url. The optional \a properties argument specifies a map of initial + property values for the replacing item. For dynamically created items, these values + are applied before the creation is finalized. This is more efficient than setting + property values after creation, particularly where large sets of property values + are defined, and also allows property bindings to be set up (using \l{Qt::binding} + {Qt.binding()}) before the item is created. + + Replace the top item: + \code + stackView.replace(rect) + + // or with properties: + stackView.replace(rect, {"color": "red"}) + \endcode + + Multiple items can be replaced at the same time either by passing them as + additional arguments, or as an array. Each item can be followed by a set + of properties to apply. + + Passing a variable amount of arguments: + \code + stackView.replace(rect1, rect2, rect3) + + // or with properties: + stackView.replace(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}) + \endcode + + Replacing an array of items: + \code + stackView.replace([rect1, rect2, rect3]) + + // or with properties: + stackView.replace([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}]) + \endcode + + An \a operation can be optionally specified as the last argument. Supported + operations: + + \value StackView.Transition An operation with transitions. + \value StackView.Immediate An immediate operation without transitions. + + \sa push(), {Replacing Items} +*/ +void QQuickStackView::replace(QQmlV4Function *args) +{ + Q_D(QQuickStackView); + if (args->length() <= 0) { + qmlInfo(this) << "replace: missing arguments"; + args->setReturnValue(QV4::Encode::null()); + return; + } + + QV4::ExecutionEngine *v4 = args->v4engine(); + QV4::Scope scope(v4); + + Operation operation = d->elements.isEmpty() ? Immediate : Transition; + QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]); + if (lastArg->isInt32()) + operation = static_cast<Operation>(lastArg->toInt32()); + + QQuickStackElement *target = nullptr; + QV4::ScopedValue firstArg(scope, (*args)[0]); + if (firstArg->isNull()) + target = d->elements.value(0); + else if (!firstArg->isInt32()) + target = d->findElement(firstArg); + + QList<QQuickStackElement *> elements = d->parseElements(args, target ? 1 : 0); + if (elements.isEmpty()) { + qmlInfo(this) << "replace: nothing to push"; + args->setReturnValue(QV4::Encode::null()); + return; + } + + int depth = d->elements.count(); + QQuickStackElement* exit = nullptr; + if (!d->elements.isEmpty()) + exit = d->elements.pop(); + + if (exit != target ? d->replaceElements(target, elements) : d->pushElements(elements)) { + if (depth != d->elements.count()) + emit depthChanged(); + QQuickStackElement *enter = d->elements.top(); + d->replaceTransition(enter, exit, boundingRect(), operation == Immediate); + d->setCurrentItem(enter->item); + } + + if (d->currentItem) { + QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem)); + args->setReturnValue(rv->asReturnedValue()); + } else { + args->setReturnValue(QV4::Encode::null()); + } +} + +/*! + \qmlmethod void QtQuick.Controls::StackView::clear() + + Removes all items from the stack. No animations are applied. +*/ +void QQuickStackView::clear() +{ + Q_D(QQuickStackView); + d->setCurrentItem(nullptr); + qDeleteAll(d->elements); + d->elements.clear(); + emit depthChanged(); +} + +/*! + \qmlproperty var QtQuick.Controls::StackView::initialItem + + This property holds the initial item that should be shown when the StackView + is created. The initial item can be an \l Item, \l Component, or a \l [QML] url. + Specifying an initial item is equivalent to: + \code + Component.onCompleted: stackView.push(myInitialItem) + \endcode + + \sa push() +*/ +QVariant QQuickStackView::initialItem() const +{ + Q_D(const QQuickStackView); + return d->initialItem; +} + +void QQuickStackView::setInitialItem(const QVariant &item) +{ + Q_D(QQuickStackView); + d->initialItem = item; +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::popEnter + + This property holds the transition that is applied to the item that + enters the stack when another item is popped off of it. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::popEnter() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->removeDisplacedTransition; + return nullptr; +} + +void QQuickStackView::setPopEnter(QQuickTransition *enter) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->removeDisplacedTransition == enter) + return; + + d->transitioner->removeDisplacedTransition = enter; + emit popEnterChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::popExit + + This property holds the transition that is applied to the item that + exits the stack when the item is popped off of it. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::popExit() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->removeTransition; + return nullptr; +} + +void QQuickStackView::setPopExit(QQuickTransition *exit) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->removeTransition == exit) + return; + + d->transitioner->removeTransition = exit; + emit popExitChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::pushEnter + + This property holds the transition that is applied to the item that + enters the stack when the item is pushed onto it. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::pushEnter() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->addTransition; + return nullptr; +} + +void QQuickStackView::setPushEnter(QQuickTransition *enter) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->addTransition == enter) + return; + + d->transitioner->addTransition = enter; + emit pushEnterChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::pushExit + + This property holds the transition that is applied to the item that + exits the stack when another item is pushed onto it. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::pushExit() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->addDisplacedTransition; + return nullptr; +} + +void QQuickStackView::setPushExit(QQuickTransition *exit) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->addDisplacedTransition == exit) + return; + + d->transitioner->addDisplacedTransition = exit; + emit pushExitChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::replaceEnter + + This property holds the transition that is applied to the item that + enters the stack when another item is replaced by it. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::replaceEnter() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->moveTransition; + return nullptr; +} + +void QQuickStackView::setReplaceEnter(QQuickTransition *enter) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->moveTransition == enter) + return; + + d->transitioner->moveTransition = enter; + emit replaceEnterChanged(); +} + +/*! + \qmlproperty Transition QtQuick.Controls::StackView::replaceExit + + This property holds the transition that is applied to the item that + exits the stack when it is replaced by another item. + + \sa {Customizing StackView} +*/ +QQuickTransition *QQuickStackView::replaceExit() const +{ + Q_D(const QQuickStackView); + if (d->transitioner) + return d->transitioner->moveDisplacedTransition; + return nullptr; +} + +void QQuickStackView::setReplaceExit(QQuickTransition *exit) +{ + Q_D(QQuickStackView); + d->ensureTransitioner(); + if (d->transitioner->moveDisplacedTransition == exit) + return; + + d->transitioner->moveDisplacedTransition = exit; + emit replaceExitChanged(); +} + +void QQuickStackView::componentComplete() +{ + QQuickControl::componentComplete(); + + Q_D(QQuickStackView); + QQuickStackElement *element = nullptr; + if (QObject *o = d->initialItem.value<QObject *>()) + element = QQuickStackElement::fromObject(o, this); + else if (d->initialItem.canConvert<QString>()) + element = QQuickStackElement::fromString(d->initialItem.toString(), this); + if (d->pushElement(element)) { + emit depthChanged(); + d->setCurrentItem(element->item); + element->setStatus(QQuickStackView::Active); + } +} + +void QQuickStackView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickControl::geometryChanged(newGeometry, oldGeometry); + + Q_D(QQuickStackView); + for (QQuickStackElement *element : qAsConst(d->elements)) { + if (element->item) { + if (!element->widthValid) + element->item->setWidth(newGeometry.width()); + if (!element->heightValid) + element->item->setHeight(newGeometry.height()); + } + } +} + +bool QQuickStackView::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + // in order to block accidental user interaction while busy/transitioning, + // StackView filters out childrens' mouse events. therefore we block all + // press events. however, since push() may be called from signal handlers + // such as onPressed or onDoubleClicked, we must let the current mouse + // grabber item receive the respective mouse release event to avoid + // breaking its state (QTBUG-50305). + if (event->type() == QEvent::MouseButtonPress) + return true; + QQuickWindow *window = item->window(); + return window && !window->mouseGrabberItem(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickStackView::accessibleRole() const +{ + return QAccessible::LayeredPane; +} +#endif + +void QQuickStackAttachedPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) +{ + Q_Q(QQuickStackAttached); + int oldIndex = element ? element->index : -1; + QQuickStackView *oldView = element ? element->view : nullptr; + QQuickStackView::Status oldStatus = element ? element->status : QQuickStackView::Inactive; + + QQuickStackView *newView = qobject_cast<QQuickStackView *>(parent); + element = newView ? QQuickStackViewPrivate::get(newView)->findElement(item) : nullptr; + + int newIndex = element ? element->index : -1; + QQuickStackView::Status newStatus = element ? element->status : QQuickStackView::Inactive; + + if (oldIndex != newIndex) + emit q->indexChanged(); + if (oldView != newView) + emit q->viewChanged(); + if (oldStatus != newStatus) + emit q->statusChanged(); +} + +QQuickStackAttached::QQuickStackAttached(QQuickItem *parent) : + QObject(*(new QQuickStackAttachedPrivate), parent) +{ + Q_D(QQuickStackAttached); + QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Parent); + d->itemParentChanged(parent, parent->parentItem()); +} + +QQuickStackAttached::~QQuickStackAttached() +{ + Q_D(QQuickStackAttached); + QQuickItem *parentItem = static_cast<QQuickItem *>(parent()); + QQuickItemPrivate::get(parentItem)->removeItemChangeListener(d, QQuickItemPrivate::Parent); +} + +/*! + \qmlattachedproperty int QtQuick.Controls::StackView::index + \readonly + + This attached property holds the stack index of the item it's + attached to, or \c -1 if the item is not in a stack. +*/ +int QQuickStackAttached::index() const +{ + Q_D(const QQuickStackAttached); + return d->element ? d->element->index : -1; +} + +/*! + \qmlattachedproperty StackView QtQuick.Controls::StackView::view + \readonly + + This attached property holds the stack view of the item it's + attached to, or \c null if the item is not in a stack. +*/ +QQuickStackView *QQuickStackAttached::view() const +{ + Q_D(const QQuickStackAttached); + return d->element ? d->element->view : nullptr; +} + +/*! + \qmlattachedproperty enumeration QtQuick.Controls::StackView::status + \readonly + + This attached property holds the stack status of the item it's + attached to, or \c StackView.Inactive if the item is not in a stack. + + Available values: + \value StackView.Inactive The item is inactive (or not in a stack). + \value StackView.Deactivating The item is being deactivated (popped off). + \value StackView.Activating The item is being activated (becoming the current item). + \value StackView.Active The item is active, that is, the current item. +*/ +QQuickStackView::Status QQuickStackAttached::status() const +{ + Q_D(const QQuickStackAttached); + return d->element ? d->element->status : QQuickStackView::Inactive; +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickstackview_p.cpp b/src/quicktemplates2/qquickstackview_p.cpp new file mode 100644 index 00000000..050bfde9 --- /dev/null +++ b/src/quicktemplates2/qquickstackview_p.cpp @@ -0,0 +1,557 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstackview_p_p.h" + +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlincubator.h> +#include <QtQml/private/qv4qobjectwrapper_p.h> +#include <QtQml/private/qqmlcomponent_p.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtQuick/private/qquickanimation_p.h> +#include <QtQuick/private/qquicktransition_p.h> +#include <QtQuick/private/qquickitemviewtransition_p.h> + +QT_BEGIN_NAMESPACE + +static QQuickStackAttached *attachedStackObject(QQuickStackElement *element) +{ + QQuickStackAttached *attached = qobject_cast<QQuickStackAttached *>(qmlAttachedPropertiesObject<QQuickStackView>(element->item, false)); + if (attached) + QQuickStackAttachedPrivate::get(attached)->element = element; + return attached; +} + +class QQuickStackIncubator : public QQmlIncubator +{ +public: + QQuickStackIncubator(QQuickStackElement *element) : QQmlIncubator(Synchronous), element(element) { } + +protected: + void setInitialState(QObject *object) override { element->incubate(object); } + +private: + QQuickStackElement *element; +}; + +QQuickStackElement::QQuickStackElement() : QQuickItemViewTransitionableItem(nullptr), + index(-1), init(false), removal(false), ownItem(false), ownComponent(false), widthValid(false), heightValid(false), + context(nullptr), component(nullptr), incubator(nullptr), view(nullptr), + status(QQuickStackView::Inactive) +{ +} + +QQuickStackElement::~QQuickStackElement() +{ + if (item) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed); + + if (ownComponent) + delete component; + + if (item) { + if (ownItem) { + item->setParentItem(nullptr); + item->deleteLater(); + item = nullptr; + } else { + item->setVisible(false); + if (!widthValid) + item->resetWidth(); + if (!heightValid) + item->resetHeight(); + if (item->parentItem() != originalParent) { + item->setParentItem(originalParent); + } else { + QQuickStackAttached *attached = attachedStackObject(this); + if (attached) + QQuickStackAttachedPrivate::get(attached)->itemParentChanged(item, nullptr); + } + } + } + + delete context; + delete incubator; +} + +QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickStackView *view) +{ + QQuickStackElement *element = new QQuickStackElement; + element->component = new QQmlComponent(qmlEngine(view), QUrl(str), view); + element->ownComponent = true; + return element; +} + +QQuickStackElement *QQuickStackElement::fromObject(QObject *object, QQuickStackView *view) +{ + QQuickStackElement *element = new QQuickStackElement; + element->component = qobject_cast<QQmlComponent *>(object); + if (!element->component) { + element->component = new QQmlComponent(qmlEngine(view), view); + element->ownComponent = true; + } + element->item = qobject_cast<QQuickItem *>(object); + if (element->item) + element->originalParent = element->item->parentItem(); + return element; +} + +bool QQuickStackElement::load(QQuickStackView *parent) +{ + setView(parent); + if (!item) { + ownItem = true; + + if (component->isLoading()) { + QObject::connect(component, &QQmlComponent::statusChanged, [this](QQmlComponent::Status status) { + if (status == QQmlComponent::Ready) + load(view); + else if (status == QQmlComponent::Error) + qWarning() << qPrintable(component->errorString().trimmed()); + }); + return true; + } + + QQmlContext *creationContext = component->creationContext(); + if (!creationContext) + creationContext = qmlContext(parent); + context = new QQmlContext(creationContext); + context->setContextObject(parent); + + delete incubator; + incubator = new QQuickStackIncubator(this); + component->create(*incubator, context); + if (component->isError()) + qWarning() << qPrintable(component->errorString().trimmed()); + } else { + initialize(); + } + return item; +} + +void QQuickStackElement::incubate(QObject *object) +{ + item = qmlobject_cast<QQuickItem *>(object); + if (item) { + QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership); + item->setParent(view); + initialize(); + } +} + +void QQuickStackElement::initialize() +{ + if (!item || init) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + if (!(widthValid = p->widthValid)) + item->setWidth(view->width()); + if (!(heightValid = p->heightValid)) + item->setHeight(view->height()); + item->setParentItem(view); + p->addItemChangeListener(this, QQuickItemPrivate::Destroyed); + + if (!properties.isUndefined()) { + QQmlComponentPrivate *d = QQmlComponentPrivate::get(component); + Q_ASSERT(d && d->engine); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(d->engine); + Q_ASSERT(v4); + QV4::Scope scope(v4); + QV4::ScopedValue ipv(scope, properties.value()); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value()); + d->initializeObjectWithInitialProperties(qmlContext, ipv, item); + properties.clear(); + } + + init = true; +} + +void QQuickStackElement::setIndex(int value) +{ + if (index == value) + return; + + index = value; + QQuickStackAttached *attached = attachedStackObject(this); + if (attached) + emit attached->indexChanged(); +} + +void QQuickStackElement::setView(QQuickStackView *value) +{ + if (view == value) + return; + + view = value; + QQuickStackAttached *attached = attachedStackObject(this); + if (attached) + emit attached->viewChanged(); +} + +void QQuickStackElement::setStatus(QQuickStackView::Status value) +{ + if (status == value) + return; + + status = value; + QQuickStackAttached *attached = attachedStackObject(this); + if (attached) + emit attached->statusChanged(); +} + +void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget) +{ + if (transitioner) + transitioner->transitionNextReposition(this, type, asTarget); +} + +bool QQuickStackElement::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds) +{ + if (transitioner) { + if (item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; + // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors + if (anchors && (anchors->fill() || anchors->centerIn())) + qmlInfo(item) << "StackView has detected conflicting anchors. Transitions may not execute properly."; + } + + // TODO: add force argument to QQuickItemViewTransitionableItem::prepareTransition()? + nextTransitionToSet = true; + nextTransitionFromSet = true; + nextTransitionFrom += QPointF(1, 1); + return QQuickItemViewTransitionableItem::prepareTransition(transitioner, index, viewBounds); + } + return false; +} + +void QQuickStackElement::startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status) +{ + setStatus(status); + if (transitioner) + QQuickItemViewTransitionableItem::startTransition(transitioner, index); +} + +void QQuickStackElement::itemDestroyed(QQuickItem *) +{ + item = nullptr; +} + +QQuickStackViewPrivate::QQuickStackViewPrivate() : busy(false), currentItem(nullptr), transitioner(nullptr) +{ +} + +void QQuickStackViewPrivate::setCurrentItem(QQuickItem *item) +{ + Q_Q(QQuickStackView); + if (currentItem == item) + return; + + currentItem = item; + if (item) + item->setVisible(true); + emit q->currentItemChanged(); +} + +static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4Function *args) +{ + if (props.isObject()) { + const QV4::QObjectWrapper *wrapper = props.as<QV4::QObjectWrapper>(); + if (!wrapper) { + QV4::ExecutionEngine *v4 = args->v4engine(); + element->properties.set(v4, props); + element->qmlCallingContext.set(v4, v4->qmlContext()); + return true; + } + } + return false; +} + +QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(QQmlV4Function *args, int from) +{ + QV4::ExecutionEngine *v4 = args->v4engine(); + QV4::Scope scope(v4); + + QList<QQuickStackElement *> elements; + + int argc = args->length(); + for (int i = from; i < argc; ++i) { + QV4::ScopedValue arg(scope, (*args)[i]); + if (QV4::ArrayObject *array = arg->as<QV4::ArrayObject>()) { + int len = array->getLength(); + for (int j = 0; j < len; ++j) { + QV4::ScopedValue value(scope, array->getIndexed(j)); + QQuickStackElement *element = createElement(value); + if (element) { + if (j < len - 1) { + QV4::ScopedValue props(scope, array->getIndexed(j + 1)); + if (initProperties(element, props, args)) + ++j; + } + elements += element; + } + } + } else { + QQuickStackElement *element = createElement(arg); + if (element) { + if (i < argc - 1) { + QV4::ScopedValue props(scope, (*args)[i + 1]); + if (initProperties(element, props, args)) + ++i; + } + elements += element; + } + } + } + return elements; +} + +QQuickStackElement *QQuickStackViewPrivate::findElement(QQuickItem *item) const +{ + if (item) { + for (QQuickStackElement *e : qAsConst(elements)) { + if (e->item == item) + return e; + } + } + return nullptr; +} + +QQuickStackElement *QQuickStackViewPrivate::findElement(const QV4::Value &value) const +{ + if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>()) + return findElement(qobject_cast<QQuickItem *>(o->object())); + return nullptr; +} + +QQuickStackElement *QQuickStackViewPrivate::createElement(const QV4::Value &value) +{ + Q_Q(QQuickStackView); + if (const QV4::String *s = value.as<QV4::String>()) + return QQuickStackElement::fromString(s->toQString(), q); + if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>()) + return QQuickStackElement::fromObject(o->object(), q); + return nullptr; +} + +bool QQuickStackViewPrivate::pushElements(const QList<QQuickStackElement *> &elems) +{ + Q_Q(QQuickStackView); + if (!elems.isEmpty()) { + for (QQuickStackElement *e : elems) { + e->setIndex(elements.count()); + elements += e; + } + return elements.top()->load(q); + } + return false; +} + +bool QQuickStackViewPrivate::pushElement(QQuickStackElement *element) +{ + if (element) + return pushElements(QList<QQuickStackElement *>() << element); + return false; +} + +bool QQuickStackViewPrivate::popElements(QQuickStackElement *element) +{ + Q_Q(QQuickStackView); + while (elements.count() > 1 && elements.top() != element) { + delete elements.pop(); + if (!element) + break; + } + return elements.top()->load(q); +} + +bool QQuickStackViewPrivate::replaceElements(QQuickStackElement *target, const QList<QQuickStackElement *> &elems) +{ + if (target) { + while (!elements.isEmpty()) { + QQuickStackElement* top = elements.pop(); + delete top; + if (top == target) + break; + } + } + return pushElements(elems); +} + +void QQuickStackViewPrivate::ensureTransitioner() +{ + if (!transitioner) { + transitioner = new QQuickItemViewTransitioner; + transitioner->setChangeListener(this); + } +} + +void QQuickStackViewPrivate::popTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate) +{ + ensureTransitioner(); + + if (exit) + exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true); + if (enter) + enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false); + + if (exit) { + exit->removal = true; + if (immediate || !exit->item || !exit->prepareTransition(transitioner, viewBounds)) + completeTransition(exit, transitioner->removeTransition, QQuickStackView::Deactivating); + else + exit->startTransition(transitioner, QQuickStackView::Deactivating); + } + if (enter) { + if (immediate || !enter->item || !enter->prepareTransition(transitioner, QRectF())) + completeTransition(enter, transitioner->removeDisplacedTransition, QQuickStackView::Activating); + else + enter->startTransition(transitioner, QQuickStackView::Activating); + } + + if (transitioner) { + setBusy(!transitioner->runningJobs.isEmpty()); + transitioner->resetTargetLists(); + } +} + +void QQuickStackViewPrivate::pushTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate) +{ + ensureTransitioner(); + + if (enter) + enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + if (exit) + exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false); + + if (enter) { + if (immediate || !enter->item || !enter->prepareTransition(transitioner, viewBounds)) + completeTransition(enter, transitioner->addTransition, QQuickStackView::Activating); + else + enter->startTransition(transitioner, QQuickStackView::Activating); + } + if (exit) { + if (immediate || !exit->item || !exit->prepareTransition(transitioner, QRectF())) + completeTransition(exit, transitioner->addDisplacedTransition, QQuickStackView::Deactivating); + else + exit->startTransition(transitioner, QQuickStackView::Deactivating); + } + + if (transitioner) { + setBusy(!transitioner->runningJobs.isEmpty()); + transitioner->resetTargetLists(); + } +} + +void QQuickStackViewPrivate::replaceTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate) +{ + ensureTransitioner(); + + if (exit) + exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false); + if (enter) + enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); + + if (exit) { + exit->removal = true; + if (immediate || !exit->item || !exit->prepareTransition(transitioner, QRectF())) + completeTransition(exit, transitioner->moveDisplacedTransition, QQuickStackView::Deactivating); + else + exit->startTransition(transitioner, QQuickStackView::Deactivating); + } + if (enter) { + if (immediate || !enter->item || !enter->prepareTransition(transitioner, viewBounds)) + completeTransition(enter, transitioner->moveTransition, QQuickStackView::Activating); + else + enter->startTransition(transitioner, QQuickStackView::Activating); + } + + if (transitioner) { + setBusy(!transitioner->runningJobs.isEmpty()); + transitioner->resetTargetLists(); + } +} + +void QQuickStackViewPrivate::completeTransition(QQuickStackElement *element, QQuickTransition *transition, QQuickStackView::Status status) +{ + element->setStatus(status); + if (transition) { + // TODO: add a proper way to complete a transition + QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); + int count = animations.count(&animations); + for (int i = 0; i < count; ++i) { + QQuickAbstractAnimation *anim = animations.at(&animations, i); + anim->complete(); + } + } + viewItemTransitionFinished(element); +} + +void QQuickStackViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *transitionable) +{ + QQuickStackElement *element = static_cast<QQuickStackElement *>(transitionable); + if (element->status == QQuickStackView::Activating) { + element->setStatus(QQuickStackView::Active); + } else if (element->status == QQuickStackView::Deactivating) { + element->setStatus(QQuickStackView::Inactive); + if (element->item) + element->item->setVisible(false); + if (element->removal || element->isPendingRemoval()) + removals += element; + } + + if (transitioner->runningJobs.isEmpty()) { + qDeleteAll(removals); + removals.clear(); + setBusy(false); + } +} + +void QQuickStackViewPrivate::setBusy(bool b) +{ + Q_Q(QQuickStackView); + if (busy == b) + return; + + busy = b; + q->setFiltersChildMouseEvents(busy); + emit q->busyChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickstackview_p.h b/src/quicktemplates2/qquickstackview_p.h new file mode 100644 index 00000000..f994f361 --- /dev/null +++ b/src/quicktemplates2/qquickstackview_p.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTACKVIEW_P_H +#define QQUICKSTACKVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlV4Function; +class QQuickTransition; +class QQuickStackElement; +class QQuickStackAttached; +class QQuickStackViewPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackView : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(bool busy READ isBusy NOTIFY busyChanged FINAL) + Q_PROPERTY(int depth READ depth NOTIFY depthChanged FINAL) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL) + Q_PROPERTY(QVariant initialItem READ initialItem WRITE setInitialItem FINAL) + Q_PROPERTY(QQuickTransition *popEnter READ popEnter WRITE setPopEnter NOTIFY popEnterChanged FINAL) + Q_PROPERTY(QQuickTransition *popExit READ popExit WRITE setPopExit NOTIFY popExitChanged FINAL) + Q_PROPERTY(QQuickTransition *pushEnter READ pushEnter WRITE setPushEnter NOTIFY pushEnterChanged FINAL) + Q_PROPERTY(QQuickTransition *pushExit READ pushExit WRITE setPushExit NOTIFY pushExitChanged FINAL) + Q_PROPERTY(QQuickTransition *replaceEnter READ replaceEnter WRITE setReplaceEnter NOTIFY replaceEnterChanged FINAL) + Q_PROPERTY(QQuickTransition *replaceExit READ replaceExit WRITE setReplaceExit NOTIFY replaceExitChanged FINAL) + +public: + explicit QQuickStackView(QQuickItem *parent = nullptr); + ~QQuickStackView(); + + static QQuickStackAttached *qmlAttachedProperties(QObject *object); + + bool isBusy() const; + int depth() const; + QQuickItem *currentItem() const; + + enum Status { + Inactive = 0, + Deactivating = 1, + Activating = 2, + Active = 3 + }; + Q_ENUM(Status) + + QVariant initialItem() const; + void setInitialItem(const QVariant &item); + + QQuickTransition *popEnter() const; + void setPopEnter(QQuickTransition *enter); + + QQuickTransition *popExit() const; + void setPopExit(QQuickTransition *exit); + + QQuickTransition *pushEnter() const; + void setPushEnter(QQuickTransition *enter); + + QQuickTransition *pushExit() const; + void setPushExit(QQuickTransition *exit); + + QQuickTransition *replaceEnter() const; + void setReplaceEnter(QQuickTransition *enter); + + QQuickTransition *replaceExit() const; + void setReplaceExit(QQuickTransition *exit); + + enum LoadBehavior { + DontLoad, + ForceLoad + }; + Q_ENUM(LoadBehavior) + + Q_INVOKABLE QQuickItem *get(int index, LoadBehavior behavior = DontLoad); + Q_INVOKABLE QQuickItem *find(const QJSValue &callback, LoadBehavior behavior = DontLoad); + + enum Operation { + Transition, + Immediate + }; + Q_ENUM(Operation) + + Q_INVOKABLE void push(QQmlV4Function *args); + Q_INVOKABLE void pop(QQmlV4Function *args); + Q_INVOKABLE void replace(QQmlV4Function *args); + +public Q_SLOTS: + void clear(); + +Q_SIGNALS: + void busyChanged(); + void depthChanged(); + void currentItemChanged(); + void popEnterChanged(); + void popExitChanged(); + void pushEnterChanged(); + void pushExitChanged(); + void replaceEnterChanged(); + void replaceExitChanged(); + +protected: + void componentComplete() override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + bool childMouseEventFilter(QQuickItem *, QEvent *) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickStackView) + Q_DECLARE_PRIVATE(QQuickStackView) +}; + +class QQuickStackAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL) + Q_PROPERTY(QQuickStackView *view READ view NOTIFY viewChanged FINAL) + Q_PROPERTY(QQuickStackView::Status status READ status NOTIFY statusChanged FINAL) + +public: + explicit QQuickStackAttached(QQuickItem *parent = nullptr); + ~QQuickStackAttached(); + + int index() const; + QQuickStackView *view() const; + QQuickStackView::Status status() const; + +Q_SIGNALS: + void indexChanged(); + void viewChanged(); + void statusChanged(); + +private: + Q_DISABLE_COPY(QQuickStackAttached) + Q_DECLARE_PRIVATE(QQuickStackAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickStackView) +QML_DECLARE_TYPEINFO(QQuickStackView, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKSTACKVIEW_P_H diff --git a/src/quicktemplates2/qquickstackview_p_p.h b/src/quicktemplates2/qquickstackview_p_p.h new file mode 100644 index 00000000..6abaaad6 --- /dev/null +++ b/src/quicktemplates2/qquickstackview_p_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTACKVIEW_P_P_H +#define QQUICKSTACKVIEW_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickstackview_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> +#include <QtQuick/private/qquickitemviewtransition_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQml/private/qv4persistent_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlContext; +class QQmlComponent; +class QQmlIncubator; + +class QQuickStackElement : public QQuickItemViewTransitionableItem, public QQuickItemChangeListener +{ + QQuickStackElement(); + +public: + ~QQuickStackElement(); + + static QQuickStackElement *fromString(const QString &str, QQuickStackView *view); + static QQuickStackElement *fromObject(QObject *object, QQuickStackView *view); + + bool load(QQuickStackView *parent); + void incubate(QObject *object); + void initialize(); + + void setIndex(int index); + void setView(QQuickStackView *view); + void setStatus(QQuickStackView::Status status); + + void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget); + bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds); + void startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status); + + void itemDestroyed(QQuickItem *item) override; + + int index; + bool init; + bool removal; + bool ownItem; + bool ownComponent; + bool widthValid; + bool heightValid; + QQmlContext *context; + QQmlComponent *component; + QQmlIncubator *incubator; + QQuickStackView *view; + QPointer<QQuickItem> originalParent; + QQuickStackView::Status status; + QV4::PersistentValue properties; + QV4::PersistentValue qmlCallingContext; +}; + +class QQuickStackViewPrivate : public QQuickControlPrivate, public QQuickItemViewTransitionChangeListener +{ + Q_DECLARE_PUBLIC(QQuickStackView) + +public: + QQuickStackViewPrivate(); + + static QQuickStackViewPrivate *get(QQuickStackView *view) + { + return view->d_func(); + } + + void setCurrentItem(QQuickItem *item); + + QList<QQuickStackElement *> parseElements(QQmlV4Function *args, int from = 0); + QQuickStackElement *findElement(QQuickItem *item) const; + QQuickStackElement *findElement(const QV4::Value &value) const; + QQuickStackElement *createElement(const QV4::Value &value); + bool pushElements(const QList<QQuickStackElement *> &elements); + bool pushElement(QQuickStackElement *element); + bool popElements(QQuickStackElement *element); + bool replaceElements(QQuickStackElement *element, const QList<QQuickStackElement *> &elements); + + void ensureTransitioner(); + void popTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate); + void pushTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate); + void replaceTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate); + void completeTransition(QQuickStackElement *element, QQuickTransition *transition, QQuickStackView::Status status); + + void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override; + void setBusy(bool busy); + + bool busy; + QVariant initialItem; + QQuickItem *currentItem; + QList<QQuickStackElement*> removals; + QStack<QQuickStackElement *> elements; + QQuickItemViewTransitioner *transitioner; +}; + +class QQuickStackAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickStackAttached) + +public: + QQuickStackAttachedPrivate() : element(nullptr) { } + + static QQuickStackAttachedPrivate *get(QQuickStackAttached *attached) + { + return attached->d_func(); + } + + void itemParentChanged(QQuickItem *item, QQuickItem *parent); + + QQuickStackElement *element; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSTACKVIEW_P_P_H diff --git a/src/quicktemplates2/qquickswipe_p.h b/src/quicktemplates2/qquickswipe_p.h new file mode 100644 index 00000000..f701c63c --- /dev/null +++ b/src/quicktemplates2/qquickswipe_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWIPE_P_H +#define QQUICKSWIPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponent; +class QQuickItem; +class QQuickSwipeDelegate; +class QQuickSwipePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipe : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL) + Q_PROPERTY(bool complete READ isComplete NOTIFY completeChanged FINAL) + Q_PROPERTY(QQmlComponent *left READ left WRITE setLeft NOTIFY leftChanged FINAL) + Q_PROPERTY(QQmlComponent *behind READ behind WRITE setBehind NOTIFY behindChanged FINAL) + Q_PROPERTY(QQmlComponent *right READ right WRITE setRight NOTIFY rightChanged FINAL) + Q_PROPERTY(QQuickItem *leftItem READ leftItem NOTIFY leftItemChanged FINAL) + Q_PROPERTY(QQuickItem *behindItem READ behindItem NOTIFY behindItemChanged FINAL) + Q_PROPERTY(QQuickItem *rightItem READ rightItem NOTIFY rightItemChanged FINAL) + +public: + explicit QQuickSwipe(QQuickSwipeDelegate *control); + + qreal position() const; + void setPosition(qreal position); + + bool isComplete() const; + void setComplete(bool complete); + + QQmlComponent *left() const; + void setLeft(QQmlComponent *left); + + QQmlComponent *behind() const; + void setBehind(QQmlComponent *behind); + + QQmlComponent *right() const; + void setRight(QQmlComponent *right); + + QQuickItem *leftItem() const; + void setLeftItem(QQuickItem *item); + + QQuickItem *behindItem() const; + void setBehindItem(QQuickItem *item); + + QQuickItem *rightItem() const; + void setRightItem(QQuickItem *item); + +Q_SIGNALS: + void positionChanged(); + void completeChanged(); + void leftChanged(); + void behindChanged(); + void rightChanged(); + void leftItemChanged(); + void behindItemChanged(); + void rightItemChanged(); + +private: + Q_DISABLE_COPY(QQuickSwipe) + Q_DECLARE_PRIVATE(QQuickSwipe) +}; + +QT_END_NAMESPACE + +#endif // QQUICKSWIPE_P_H diff --git a/src/quicktemplates2/qquickswipedelegate.cpp b/src/quicktemplates2/qquickswipedelegate.cpp new file mode 100644 index 00000000..0eac7fe1 --- /dev/null +++ b/src/quicktemplates2/qquickswipedelegate.cpp @@ -0,0 +1,930 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickswipedelegate_p.h" +#include "qquickswipedelegate_p_p.h" +#include "qquickcontrol_p_p.h" +#include "qquickitemdelegate_p_p.h" +#include "qquickvelocitycalculator_p_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformtheme.h> +#include <QtQml/qqmlinfo.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype SwipeDelegate + \inherits ItemDelegate + \instantiates QQuickSwipeDelegate + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-delegates + \brief Swipable item delegate. + + SwipeDelegate presents a view item that can be swiped left or right to + expose more options or information. It is used as a delegate in views such + as \l ListView. + + In the following example, SwipeDelegate is used in a \l ListView to allow + items to be removed from it by swiping to the left: + + \snippet qtquickcontrols2-swipedelegate.qml 1 + + SwipeDelegate 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. + + Information regarding the progress of a swipe, as well as the components + that should be shown upon swiping, are both available through the + \l {SwipeDelegate::}{swipe} grouped property object. For example, + \c swipe.position holds the position of the + swipe within the range \c -1.0 to \c 1.0. The \c swipe.left + property determines which item will be displayed when the control is swiped + to the right, and vice versa for \c swipe.right. The positioning of these + components is left to applications to decide. For example, without specifying + any position for \c swipe.left or \c swipe.right, the following will + occur: + + \image qtquickcontrols2-swipedelegate.gif + + If \c swipe.left and \c swipe.right are anchored to the left and + right of the \l {Control::}{background} item (respectively), they'll behave like this: + + \image qtquickcontrols2-swipedelegate-leading-trailing.gif + + When using \c swipe.left and \c swipe.right, the control cannot be + swiped past the left and right edges. To achieve this type of "wrapping" + behavior, set \c swipe.behind instead. This will result in the same + item being shown regardless of which direction the control is swiped. For + example, in the image below, we set \c swipe.behind and then swipe the + control repeatedly in both directions: + + \image qtquickcontrols2-swipedelegate-behind.gif + + \sa {Customizing SwipeDelegate}, {Delegate Controls} +*/ + +namespace { + enum PositionAnimation { + DontAnimatePosition, + AnimatePosition + }; +} + +class QQuickSwipePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickSwipe) + +public: + QQuickSwipePrivate(QQuickSwipeDelegate *control) : + control(control), + positionBeforePress(0), + position(0), + wasComplete(false), + complete(false), + left(nullptr), + behind(nullptr), + right(nullptr), + leftItem(nullptr), + behindItem(nullptr), + rightItem(nullptr) + { + } + + static QQuickSwipePrivate *get(QQuickSwipe *swipe); + + QQuickItem *createDelegateItem(QQmlComponent *component); + QQuickItem *showRelevantItemForPosition(qreal position); + QQuickItem *createRelevantItemForDistance(qreal distance); + void reposition(PositionAnimation animationPolicy); + void createLeftItem(); + void createBehindItem(); + void createRightItem(); + void createAndShowLeftItem(); + void createAndShowBehindItem(); + void createAndShowRightItem(); + + void warnAboutMixingDelegates(); + void warnAboutSettingDelegatesWhileVisible(); + + bool hasDelegates() const; + + QQuickSwipeDelegate *control; + // Same range as position, but is set before press events so that we can + // keep track of which direction the user must swipe when using left and right delegates. + qreal positionBeforePress; + qreal position; + // A "less strict" version of complete that is true if complete was true + // before the last press event. + bool wasComplete; + bool complete; + QQuickVelocityCalculator velocityCalculator; + QQmlComponent *left; + QQmlComponent *behind; + QQmlComponent *right; + QQuickItem *leftItem; + QQuickItem *behindItem; + QQuickItem *rightItem; +}; + +QQuickSwipePrivate *QQuickSwipePrivate::get(QQuickSwipe *swipe) +{ + return swipe->d_func(); +} + +QQuickItem *QQuickSwipePrivate::createDelegateItem(QQmlComponent *component) +{ + // If we don't use the correct context, it won't be possible to refer to + // the control's id from within the delegates. + QQmlContext *creationContext = component->creationContext(); + // The component might not have been created in QML, in which case + // the creation context will be null and we have to create it ourselves. + if (!creationContext) + creationContext = qmlContext(control); + QQmlContext *context = new QQmlContext(creationContext); + context->setContextObject(control); + QQuickItem *item = qobject_cast<QQuickItem*>(component->beginCreate(context)); + if (item) { + item->setParentItem(control); + component->completeCreate(); + } + return item; +} + +QQuickItem *QQuickSwipePrivate::showRelevantItemForPosition(qreal position) +{ + if (qFuzzyIsNull(position)) + return nullptr; + + if (behind) { + createAndShowBehindItem(); + return behindItem; + } + + if (right && position < 0.0) { + createAndShowRightItem(); + return rightItem; + } + + if (left && position > 0.0) { + createAndShowLeftItem(); + return leftItem; + } + + return nullptr; +} + +QQuickItem *QQuickSwipePrivate::createRelevantItemForDistance(qreal distance) +{ + if (qFuzzyIsNull(distance)) + return nullptr; + + if (behind) { + createBehindItem(); + return behindItem; + } + + // a) If the position before the press was 0.0, we know that *any* movement + // whose distance is negative will result in the right item being shown and + // vice versa. + // b) Once the control has been exposed (that is, swiped to the left or right, + // and hence the position is either -1.0 or 1.0), we must use the width of the + // relevant item to determine if the distance is larger than that item, + // in order to know whether or not to display it. + // c) If the control has been exposed, and the swipe is larger than the width + // of the relevant item from which the swipe started from, we must show the + // item on the other side (if any). + + if (right) { + if ((distance < 0.0 && positionBeforePress == 0.0) /* a) */ + || (rightItem && positionBeforePress == -1.0 && distance < rightItem->width()) /* b) */ + || (leftItem && positionBeforePress == 1.0 && qAbs(distance) > leftItem->width())) /* c) */ { + createRightItem(); + return rightItem; + } + } + + if (left) { + if ((distance > 0.0 && positionBeforePress == 0.0) /* a) */ + || (leftItem && positionBeforePress == 1.0 && qAbs(distance) < leftItem->width()) /* b) */ + || (rightItem && positionBeforePress == -1.0 && qAbs(distance) > rightItem->width())) /* c) */ { + createLeftItem(); + return leftItem; + } + } + + return nullptr; +} + +void QQuickSwipePrivate::reposition(PositionAnimation animationPolicy) +{ + QQuickItem *relevantItem = showRelevantItemForPosition(position); + const qreal relevantWidth = relevantItem ? relevantItem->width() : 0.0; + const qreal contentItemX = position * relevantWidth + control->leftPadding(); + + // "Behavior on x" relies on the property system to know when it should update, + // so we can prevent it from animating by setting the x position directly. + if (animationPolicy == AnimatePosition) { + if (QQuickItem *contentItem = control->contentItem()) + contentItem->setProperty("x", contentItemX); + if (QQuickItem *background = control->background()) + background->setProperty("x", position * relevantWidth); + } else { + if (QQuickItem *contentItem = control->contentItem()) + contentItem->setX(contentItemX); + if (QQuickItem *background = control->background()) + background->setX(position * relevantWidth); + } +} + +void QQuickSwipePrivate::createLeftItem() +{ + if (!leftItem) { + Q_Q(QQuickSwipe); + q->setLeftItem(createDelegateItem(left)); + if (!leftItem) + qmlInfo(control) << "Failed to create left item:" << left->errors(); + } +} + +void QQuickSwipePrivate::createBehindItem() +{ + if (!behindItem) { + Q_Q(QQuickSwipe); + q->setBehindItem(createDelegateItem(behind)); + if (!behindItem) + qmlInfo(control) << "Failed to create behind item:" << behind->errors(); + } +} + +void QQuickSwipePrivate::createRightItem() +{ + if (!rightItem) { + Q_Q(QQuickSwipe); + q->setRightItem(createDelegateItem(right)); + if (!rightItem) + qmlInfo(control) << "Failed to create right item:" << right->errors(); + } +} + +void QQuickSwipePrivate::createAndShowLeftItem() +{ + createLeftItem(); + + if (leftItem) + leftItem->setVisible(true); + + if (rightItem) + rightItem->setVisible(false); +} + +void QQuickSwipePrivate::createAndShowBehindItem() +{ + createBehindItem(); + + if (behindItem) + behindItem->setVisible(true); +} + +void QQuickSwipePrivate::createAndShowRightItem() +{ + createRightItem(); + + // This item may have already existed but was hidden. + if (rightItem) + rightItem->setVisible(true); + + // The left item isn't visible when the right item is visible, so save rendering effort by hiding it. + if (leftItem) + leftItem->setVisible(false); +} + +void QQuickSwipePrivate::warnAboutMixingDelegates() +{ + qmlInfo(control) << "cannot set both behind and left/right properties"; +} + +void QQuickSwipePrivate::warnAboutSettingDelegatesWhileVisible() +{ + qmlInfo(control) << "left/right/behind properties may only be set when swipe.position is 0"; +} + +bool QQuickSwipePrivate::hasDelegates() const +{ + return left || right || behind; +} + +QQuickSwipe::QQuickSwipe(QQuickSwipeDelegate *control) : + QObject(*(new QQuickSwipePrivate(control))) +{ +} + +QQmlComponent *QQuickSwipe::left() const +{ + Q_D(const QQuickSwipe); + return d->left; +} + +void QQuickSwipe::setLeft(QQmlComponent *left) +{ + Q_D(QQuickSwipe); + if (left == d->left) + return; + + if (d->behind) { + d->warnAboutMixingDelegates(); + return; + } + + if (!qFuzzyIsNull(d->position)) { + d->warnAboutSettingDelegatesWhileVisible(); + return; + } + + d->left = left; + + if (!d->left) { + delete d->leftItem; + d->leftItem = nullptr; + } + + d->control->setFiltersChildMouseEvents(d->hasDelegates()); + + emit leftChanged(); +} + +QQmlComponent *QQuickSwipe::behind() const +{ + Q_D(const QQuickSwipe); + return d->behind; +} + +void QQuickSwipe::setBehind(QQmlComponent *behind) +{ + Q_D(QQuickSwipe); + if (behind == d->behind) + return; + + if (d->left || d->right) { + d->warnAboutMixingDelegates(); + return; + } + + if (!qFuzzyIsNull(d->position)) { + d->warnAboutSettingDelegatesWhileVisible(); + return; + } + + d->behind = behind; + + if (!d->behind) { + delete d->behindItem; + d->behindItem = nullptr; + } + + d->control->setFiltersChildMouseEvents(d->hasDelegates()); + + emit behindChanged(); +} + +QQmlComponent *QQuickSwipe::right() const +{ + Q_D(const QQuickSwipe); + return d->right; +} + +void QQuickSwipe::setRight(QQmlComponent *right) +{ + Q_D(QQuickSwipe); + if (right == d->right) + return; + + if (d->behind) { + d->warnAboutMixingDelegates(); + return; + } + + if (!qFuzzyIsNull(d->position)) { + d->warnAboutSettingDelegatesWhileVisible(); + return; + } + + d->right = right; + + if (!d->right) { + delete d->rightItem; + d->rightItem = nullptr; + } + + d->control->setFiltersChildMouseEvents(d->hasDelegates()); + + emit rightChanged(); +} + +QQuickItem *QQuickSwipe::leftItem() const +{ + Q_D(const QQuickSwipe); + return d->leftItem; +} + +void QQuickSwipe::setLeftItem(QQuickItem *item) +{ + Q_D(QQuickSwipe); + if (item == d->leftItem) + return; + + delete d->leftItem; + d->leftItem = item; + + if (d->leftItem) { + d->leftItem->setParentItem(d->control); + + if (qFuzzyIsNull(d->leftItem->z())) + d->leftItem->setZ(-2); + } + + emit leftItemChanged(); +} + +QQuickItem *QQuickSwipe::behindItem() const +{ + Q_D(const QQuickSwipe); + return d->behindItem; +} + +void QQuickSwipe::setBehindItem(QQuickItem *item) +{ + Q_D(QQuickSwipe); + if (item == d->behindItem) + return; + + delete d->behindItem; + d->behindItem = item; + + if (d->behindItem) { + d->behindItem->setParentItem(d->control); + + if (qFuzzyIsNull(d->behindItem->z())) + d->behindItem->setZ(-2); + } + + emit behindItemChanged(); +} + +QQuickItem *QQuickSwipe::rightItem() const +{ + Q_D(const QQuickSwipe); + return d->rightItem; +} + +void QQuickSwipe::setRightItem(QQuickItem *item) +{ + Q_D(QQuickSwipe); + if (item == d->rightItem) + return; + + delete d->rightItem; + d->rightItem = item; + + if (d->rightItem) { + d->rightItem->setParentItem(d->control); + + if (qFuzzyIsNull(d->rightItem->z())) + d->rightItem->setZ(-2); + } + + emit rightItemChanged(); +} + +qreal QQuickSwipe::position() const +{ + Q_D(const QQuickSwipe); + return d->position; +} + +void QQuickSwipe::setPosition(qreal position) +{ + Q_D(QQuickSwipe); + const qreal adjustedPosition = qBound<qreal>(-1.0, position, 1.0); + if (adjustedPosition == d->position) + return; + + d->position = adjustedPosition; + d->reposition(AnimatePosition); + emit positionChanged(); +} + +bool QQuickSwipe::isComplete() const +{ + Q_D(const QQuickSwipe); + return d->complete; +} + +void QQuickSwipe::setComplete(bool complete) +{ + Q_D(QQuickSwipe); + if (complete == d->complete) + return; + + d->complete = complete; + emit completeChanged(); +} + +QQuickSwipeDelegatePrivate::QQuickSwipeDelegatePrivate(QQuickSwipeDelegate *control) : + swipe(control) +{ +} + +bool QQuickSwipeDelegatePrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickSwipeDelegate); + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe); + // If the position is 0, we want to handle events ourselves - we don't want child items to steal them. + // This code will only get called when a child item has been created; + // events will go through the regular channels (mousePressEvent()) until then. + if (qFuzzyIsNull(swipePrivate->position)) { + q->mousePressEvent(event); + return true; + } + + swipePrivate->positionBeforePress = swipePrivate->position; + swipePrivate->velocityCalculator.startMeasuring(event->pos(), event->timestamp()); + pressPoint = item->mapToItem(q, event->pos()); + return false; +} + +bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickSwipeDelegate); + + if (holdTimer > 0) { + if (QLineF(pressPoint, event->localPos()).length() > QGuiApplication::styleHints()->startDragDistance()) + stopPressAndHold(); + } + + // Protect against division by zero. + if (width == 0) + return false; + + // Don't bother reacting to events if we don't have any delegates. + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe); + if (!swipePrivate->left && !swipePrivate->right && !swipePrivate->behind) + return false; + + // Don't handle move events for the control if it wasn't pressed. + if (item == q && !pressed) + return false; + + const qreal distance = (event->pos() - pressPoint).x(); + if (!q->keepMouseGrab()) { + // Taken from QQuickDrawerPrivate::grabMouse; see comments there. + int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + const bool overThreshold = QQuickWindowPrivate::dragOverThreshold(distance, Qt::XAxis, event, threshold); + if (window && overThreshold) { + QQuickItem *grabber = q->window()->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + q->grabMouse(); + q->setKeepMouseGrab(overThreshold); + q->setPressed(true); + swipe.setComplete(false); + } + } + } + + if (q->keepMouseGrab()) { + // Ensure we don't try to calculate a position when the user tried to drag + // to the left when the left item is already exposed, and vice versa. + // The code below assumes that the drag is valid, so if we don't have this check, + // the wrong items are visible and the swiping wraps. + if (swipePrivate->behind + || ((swipePrivate->left || swipePrivate->right) + && (qFuzzyIsNull(swipePrivate->positionBeforePress) + || (swipePrivate->positionBeforePress == -1.0 && distance >= 0.0) + || (swipePrivate->positionBeforePress == 1.0 && distance <= 0.0)))) { + + // We must instantiate the items here so that we can calculate the + // position against the width of the relevant item. + QQuickItem *relevantItem = swipePrivate->createRelevantItemForDistance(distance); + // If there isn't any relevant item, the user may have swiped back to the 0 position, + // or they swiped back to a position that is equal to positionBeforePress. + const qreal normalizedDistance = relevantItem ? distance / relevantItem->width() : 0.0; + qreal position = 0; + + // If the control was exposed before the drag begun, the distance should be inverted. + // For example, if the control had been swiped to the right, the position would be 1.0. + // If the control was then swiped to the left by a distance of -20 pixels, the normalized + // distance might be -0.2, for example, which cannot be used as the position; the swipe + // started from the right, so we account for that by adding the position. + if (qFuzzyIsNull(normalizedDistance)) { + // There are two cases when the normalizedDistance can be 0, + // and we must distinguish between them: + // + // a) The swipe returns to the position that it was at before the press event. + // In this case, the distance will be 0. + // There would have been many position changes in the meantime, so we can't just + // ignore the move event; we have to set position to what it was before the press. + // + // b) If the position was at, 1.0, for example, and the control was then swiped + // to the left by the exact width of the left item, there won't be any relevant item + // (because the swipe's position would be at 0.0). In turn, the normalizedDistance + // would be 0 (because of the lack of a relevant item), but the distance will be non-zero. + position = qFuzzyIsNull(distance) ? swipePrivate->positionBeforePress : 0; + } else if (!swipePrivate->wasComplete) { + position = normalizedDistance; + } else { + position = distance > 0 ? normalizedDistance - 1.0 : normalizedDistance + 1.0; + } + + swipe.setPosition(position); + } + } else { + // The swipe wasn't initiated. + if (event->pos().y() < 0 || event->pos().y() > height) { + // The mouse went outside the vertical bounds of the control, so + // we should no longer consider it pressed. + q->setPressed(false); + } + } + + event->accept(); + + return q->keepMouseGrab(); +} + +static const qreal exposeVelocityThreshold = 300.0; + +bool QQuickSwipeDelegatePrivate::handleMouseReleaseEvent(QQuickItem *, QMouseEvent *event) +{ + Q_Q(QQuickSwipeDelegate); + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe); + swipePrivate->velocityCalculator.stopMeasuring(event->pos(), event->timestamp()); + + const bool hadGrabbedMouse = q->keepMouseGrab(); + q->setKeepMouseGrab(false); + + // The control can be exposed by either swiping past the halfway mark, or swiping fast enough. + const qreal swipeVelocity = swipePrivate->velocityCalculator.velocity().x(); + if (swipePrivate->position > 0.5 || + (swipePrivate->position > 0.0 && swipeVelocity > exposeVelocityThreshold)) { + swipe.setPosition(1.0); + swipe.setComplete(true); + swipePrivate->wasComplete = true; + } else if (swipePrivate->position < -0.5 || + (swipePrivate->position < 0.0 && swipeVelocity < -exposeVelocityThreshold)) { + swipe.setPosition(-1.0); + swipe.setComplete(true); + swipePrivate->wasComplete = true; + } else { + swipe.setPosition(0.0); + swipe.setComplete(false); + swipePrivate->wasComplete = false; + } + + // Only consume child events if we had grabbed the mouse. + return hadGrabbedMouse; +} + +static void warnIfHorizontallyAnchored(QQuickItem *item, const QString &itemName) +{ + if (!item) + return; + + QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; + if (anchors && (anchors->fill() || anchors->centerIn() || anchors->left().item || anchors->right().item) + && !item->property("_q_QQuickSwipeDelegate_warned").toBool()) { + qmlInfo(item) << QString::fromLatin1("SwipeDelegate: cannot use horizontal anchors with %1; unable to layout the item.").arg(itemName); + item->setProperty("_q_QQuickSwipeDelegate_warned", true); + } +} + +void QQuickSwipeDelegatePrivate::resizeContent() +{ + warnIfHorizontallyAnchored(background, QStringLiteral("background")); + warnIfHorizontallyAnchored(contentItem, QStringLiteral("contentItem")); + + // If the background and contentItem are repositioned due to a swipe, + // we don't want to call QQuickControlPrivate's implementation of this function, + // as it repositions the contentItem to be visible. + // However, we still want to resize the control vertically. + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe); + if (!swipePrivate->complete) { + QQuickItemDelegatePrivate::resizeContent(); + } else if (contentItem) { + Q_Q(QQuickSwipeDelegate); + contentItem->setY(q->topPadding()); + contentItem->setHeight(q->availableHeight()); + } +} + +QQuickSwipeDelegate::QQuickSwipeDelegate(QQuickItem *parent) : + QQuickItemDelegate(*(new QQuickSwipeDelegatePrivate(this)), parent) +{ +} + +/*! + \qmlpropertygroup QtQuick.Controls::SwipeDelegate::swipe + \qmlproperty real QtQuick.Controls::SwipeDelegate::swipe.position + \qmlproperty bool QtQuick.Controls::SwipeDelegate::swipe.complete + \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.left + \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.behind + \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.right + \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.leftItem + \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.behindItem + \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.rightItem + + \table + \header + \li Property + \li Description + \row + \li position + \li This read-only property holds the position of the swipe relative to either + side of the control. When this value reaches either + \c -1.0 (left side) or \c 1.0 (right side) and the mouse button is + released, \c complete will be \c true. + \row + \li complete + \li This read-only property holds whether the control is fully exposed after + having been swiped to the left or right. + + When complete is \c true, any interactive items declared in \c left, + \c right, or \c behind will receive mouse events. + \row + \li left + \li This property holds the left delegate. + + The left delegate sits behind both \l {Control::}{contentItem} and + \l {Control::}{background}. When the SwipeDelegate is swiped to the right, + this item will be gradually revealed. + + \include qquickswipedelegate-interaction.qdocinc + \row + \li behind + \li This property holds the delegate that is shown when the + SwipeDelegate is swiped to both the left and right. + + As with the \c left and \c right delegates, it sits behind both + \l {Control::}{contentItem} and \l {Control::}{background}. However, a + SwipeDelegate whose \c behind has been set can be continuously swiped + from either side, and will always show the same item. + + \include qquickswipedelegate-interaction.qdocinc + \row + \li right + \li This property holds the right delegate. + + The right delegate sits behind both \l {Control::}{contentItem} and + \l {Control::}{background}. When the SwipeDelegate is swiped to the left, + this item will be gradually revealed. + + \include qquickswipedelegate-interaction.qdocinc + \row + \li leftItem + \li This read-only property holds the item instantiated from the \c left component. + + If \c left has not been set, or the position hasn't changed since + creation of the SwipeDelegate, this property will be \c null. + \row + \li behindItem + \li This read-only property holds the item instantiated from the \c behind component. + + If \c behind has not been set, or the position hasn't changed since + creation of the SwipeDelegate, this property will be \c null. + \row + \li rightItem + \li This read-only property holds the item instantiated from the \c right component. + + If \c right has not been set, or the position hasn't changed since + creation of the SwipeDelegate, this property will be \c null. + \endtable + + \sa {Control::}{contentItem}, {Control::}{background} +*/ +QQuickSwipe *QQuickSwipeDelegate::swipe() const +{ + Q_D(const QQuickSwipeDelegate); + return const_cast<QQuickSwipe*>(&d->swipe); +} + +static bool isChildOrGrandchildOf(QQuickItem *child, QQuickItem *item) +{ + return item && (child == item || item->isAncestorOf(child)); +} + +bool QQuickSwipeDelegate::childMouseEventFilter(QQuickItem *child, QEvent *event) +{ + Q_D(QQuickSwipeDelegate); + // The contentItem is, by default, usually a non-interactive item like Text, and + // the same applies to the background. This means that simply stacking the left/right/behind + // items before these items won't allow us to get mouse events when the control is not currently exposed + // but has been previously. Therefore, we instead call setFiltersChildMouseEvents(true) in the constructor + // and filter out child events only when the child is the left/right/behind item. + const QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe); + if (!isChildOrGrandchildOf(child, swipePrivate->leftItem) && !isChildOrGrandchildOf(child, swipePrivate->behindItem) + && !isChildOrGrandchildOf(child, swipePrivate->rightItem)) { + return false; + } + + 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: { + // Make sure that the control gets release events if it has created child + // items that are stealing events from it. + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + QQuickItemDelegate::mouseReleaseEvent(mouseEvent); + return d->handleMouseReleaseEvent(child, mouseEvent); + } default: + return false; + } +} + +// We only override this to set positionBeforePress; +// otherwise, it's the same as the base class implementation. +void QQuickSwipeDelegate::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickSwipeDelegate); + QQuickItemDelegate::mousePressEvent(event); + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe); + swipePrivate->positionBeforePress = swipePrivate->position; + swipePrivate->velocityCalculator.startMeasuring(event->pos(), event->timestamp()); +} + +void QQuickSwipeDelegate::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSwipeDelegate); + if (filtersChildMouseEvents()) + d->handleMouseMoveEvent(this, event); + else + QQuickItemDelegate::mouseMoveEvent(event); +} + +void QQuickSwipeDelegate::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickSwipeDelegate); + if (!filtersChildMouseEvents() || !d->handleMouseReleaseEvent(this, event)) + QQuickItemDelegate::mouseReleaseEvent(event); +} + +void QQuickSwipeDelegate::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickSwipeDelegate); + QQuickControl::geometryChanged(newGeometry, oldGeometry); + + if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) { + QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe); + swipePrivate->reposition(DontAnimatePosition); + } +} + +QFont QQuickSwipeDelegate::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ListViewFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickSwipeDelegate::accessibleRole() const +{ + return QAccessible::ListItem; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswipedelegate_p.h b/src/quicktemplates2/qquickswipedelegate_p.h new file mode 100644 index 00000000..2e1c515d --- /dev/null +++ b/src/quicktemplates2/qquickswipedelegate_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWIPEDELEGATE_P_H +#define QQUICKSWIPEDELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickitemdelegate_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSwipeDelegatePrivate; +class QQuickSwipe; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeDelegate : public QQuickItemDelegate +{ + Q_OBJECT + Q_PROPERTY(QQuickSwipe *swipe READ swipe CONSTANT) + +public: + explicit QQuickSwipeDelegate(QQuickItem *parent = nullptr); + + QQuickSwipe *swipe() const; + +protected: + bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickSwipeDelegate) + Q_DECLARE_PRIVATE(QQuickSwipeDelegate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSwipeDelegate) + +#endif // QQUICKSWIPEDELEGATE_P_H diff --git a/src/quicktemplates2/qquickswipedelegate_p_p.h b/src/quicktemplates2/qquickswipedelegate_p_p.h new file mode 100644 index 00000000..0387ad70 --- /dev/null +++ b/src/quicktemplates2/qquickswipedelegate_p_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWIPEDELEGATE_P_P_H +#define QQUICKSWIPEDELEGATE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickitemdelegate_p_p.h> +#include <QtQuickTemplates2/private/qquickswipe_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSwipeDelegate; + +class QQuickSwipeDelegatePrivate : public QQuickItemDelegatePrivate +{ + Q_DECLARE_PUBLIC(QQuickSwipeDelegate) + +public: + QQuickSwipeDelegatePrivate(QQuickSwipeDelegate *control); + + bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); + + void resizeContent() override; + + QQuickSwipe swipe; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSWIPEDELEGATE_P_P_H diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp new file mode 100644 index 00000000..82d05fbe --- /dev/null +++ b/src/quicktemplates2/qquickswipeview.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickswipeview_p.h" + +#include <QtQml/qqmlinfo.h> +#include <QtQuickTemplates2/private/qquickcontainer_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype SwipeView + \inherits Container + \instantiates QQuickSwipeView + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-navigation + \ingroup qtquickcontrols2-containers + \brief Enables the user to navigate pages by swiping sideways. + + SwipeView provides a swipe-based navigation model. + + \image qtquickcontrols2-swipeview.gif + + SwipeView is populated with a set of pages. One page is visible at a time. + The user can navigate between the pages by swiping sideways. Notice that + SwipeView itself is entirely non-visual. It is recommended to combine it + with PageIndicator, to give the user a visual clue that there are multiple + pages. + + \snippet qtquickcontrols2-swipeview-indicator.qml 1 + + As shown above, SwipeView is typically populated with a static set of + pages that are defined inline as children of the view. It is also possible + to \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert}, + \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove} + pages dynamically at run time. + + \note SwipeView takes over the geometry management of items added to the + view. Using anchors on the items is not supported, and any \c width + or \c height assignment will be overridden by the view. Notice that + this only applies to the root of the item. Specifying width and height, + or using anchors for its children works as expected. + + \sa TabBar, PageIndicator, {Customizing SwipeView}, {Navigation Controls}, {Container Controls} +*/ + +class QQuickSwipeViewPrivate : public QQuickContainerPrivate +{ + Q_DECLARE_PUBLIC(QQuickSwipeView) + +public: + void resizeItem(QQuickItem *item); + void resizeItems(); + + static QQuickSwipeViewPrivate *get(QQuickSwipeView *view); +}; + +void QQuickSwipeViewPrivate::resizeItems() +{ + Q_Q(QQuickSwipeView); + const int count = q->count(); + for (int i = 0; i < count; ++i) { + QQuickItem *item = itemAt(i); + if (item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; + // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors + if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) { + qmlInfo(item) << "SwipeView has detected conflicting anchors. Unable to layout the item."; + item->setProperty("_q_QQuickSwipeView_warned", true); + } + + item->setSize(QSizeF(contentItem->width(), contentItem->height())); + } + } +} + +QQuickSwipeViewPrivate *QQuickSwipeViewPrivate::get(QQuickSwipeView *view) +{ + return view->d_func(); +} + +QQuickSwipeView::QQuickSwipeView(QQuickItem *parent) : + QQuickContainer(*(new QQuickSwipeViewPrivate), parent) +{ + setFlag(ItemIsFocusScope); + setActiveFocusOnTab(true); +} + +QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object) +{ + QQuickItem *item = qobject_cast<QQuickItem *>(object); + if (!item) { + qWarning() << "SwipeView: attached properties must be accessed from within a child item"; + return nullptr; + } + + return new QQuickSwipeViewAttached(item); +} + +void QQuickSwipeView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickSwipeView); + QQuickContainer::geometryChanged(newGeometry, oldGeometry); + d->resizeItems(); +} + +void QQuickSwipeView::itemAdded(int, QQuickItem *item) +{ + Q_D(QQuickSwipeView); + QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-51078, QTBUG-51669 + if (isComponentComplete()) + item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height())); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickSwipeView::accessibleRole() const +{ + return QAccessible::PageTabList; +} +#endif + +/*! + \qmlattachedproperty int QtQuick.Controls::SwipeView::index + \readonly + + This attached property holds the index of each child item in the SwipeView. + + It is attached to each child item of the SwipeView. +*/ + +/*! + \qmlattachedproperty bool QtQuick.Controls::SwipeView::isCurrentItem + \readonly + + This attached property is \c true if this child is the current item. + + It is attached to each child item of the SwipeView. +*/ + +/*! + \qmlattachedproperty SwipeView QtQuick.Controls::SwipeView::view + \readonly + + This attached property holds the view that manages this child item. + + It is attached to each child item of the SwipeView. +*/ + +class QQuickSwipeViewAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickSwipeViewAttached) +public: + QQuickSwipeViewAttachedPrivate(QQuickItem *item) : + item(item), + swipeView(nullptr), + index(-1), + isCurrent(false) + { + } + + ~QQuickSwipeViewAttachedPrivate() { + } + + void updateView(QQuickItem *parent); + + void itemChildAdded(QQuickItem *, QQuickItem *) override; + void itemChildRemoved(QQuickItem *, QQuickItem *) override; + void itemParentChanged(QQuickItem *, QQuickItem *) override; + void itemDestroyed(QQuickItem *) override; + + void updateIndex(); + void updateIsCurrent(); + + void setView(QQuickSwipeView *view); + void setIndex(int i); + void setIsCurrent(bool current); + + QQuickItem *item; + QQuickSwipeView *swipeView; + int index; + // Better to store this so that we don't need to lump its calculation + // together with index's calculation, as it would otherwise need to know + // the old index to know if it should emit the change signal. + bool isCurrent; +}; + +void QQuickSwipeViewAttachedPrivate::updateIndex() +{ + setIndex(swipeView ? QQuickSwipeViewPrivate::get(swipeView)->contentModel->indexOf(item, nullptr) : -1); +} + +void QQuickSwipeViewAttachedPrivate::updateIsCurrent() +{ + setIsCurrent(swipeView ? swipeView->currentIndex() == index : false); +} + +void QQuickSwipeViewAttachedPrivate::setView(QQuickSwipeView *view) +{ + if (view == swipeView) + return; + + if (swipeView) { + QQuickItemPrivate *p = QQuickItemPrivate::get(swipeView); + p->removeItemChangeListener(this, QQuickItemPrivate::Children); + + disconnect(swipeView, &QQuickSwipeView::currentIndexChanged, + this, &QQuickSwipeViewAttachedPrivate::updateIsCurrent); + disconnect(swipeView, &QQuickSwipeView::contentChildrenChanged, + this, &QQuickSwipeViewAttachedPrivate::updateIndex); + } + + swipeView = view; + + if (swipeView) { + QQuickItemPrivate *p = QQuickItemPrivate::get(swipeView); + p->addItemChangeListener(this, QQuickItemPrivate::Children); + + connect(swipeView, &QQuickSwipeView::currentIndexChanged, + this, &QQuickSwipeViewAttachedPrivate::updateIsCurrent); + connect(swipeView, &QQuickSwipeView::contentChildrenChanged, + this, &QQuickSwipeViewAttachedPrivate::updateIndex); + } + + Q_Q(QQuickSwipeViewAttached); + emit q->viewChanged(); + + updateIndex(); + updateIsCurrent(); +} + +void QQuickSwipeViewAttachedPrivate::setIsCurrent(bool current) +{ + if (current == isCurrent) + return; + + isCurrent = current; + Q_Q(QQuickSwipeViewAttached); + emit q->isCurrentItemChanged(); +} + +void QQuickSwipeViewAttachedPrivate::setIndex(int i) +{ + if (i == index) + return; + + index = i; + Q_Q(QQuickSwipeViewAttached); + emit q->indexChanged(); +} + +void QQuickSwipeViewAttachedPrivate::updateView(QQuickItem *parent) +{ + // parent can be, e.g.: + // - The contentItem of a ListView (typically the case) + // - A non-visual or weird type like TestCase, when child items are created from components + // wherein the attached properties are used + // - null, when the item was removed with removeItem() + QQuickSwipeView *view = nullptr; + if (parent) { + view = qobject_cast<QQuickSwipeView*>(parent); + if (!view) { + if (parent->parentItem() && parent->parentItem()->property("contentItem").isValid()) { + // The parent is the contentItem of some kind of view. + view = qobject_cast<QQuickSwipeView*>(parent->parentItem()->parentItem()); + } + } + } + + setView(view); +} + +void QQuickSwipeViewAttachedPrivate::itemChildAdded(QQuickItem *, QQuickItem *) +{ + updateIndex(); +} + +void QQuickSwipeViewAttachedPrivate::itemChildRemoved(QQuickItem *, QQuickItem *) +{ + updateIndex(); +} + +void QQuickSwipeViewAttachedPrivate::itemParentChanged(QQuickItem *, QQuickItem *parent) +{ + updateView(parent); +} + +void QQuickSwipeViewAttachedPrivate::itemDestroyed(QQuickItem *item) +{ + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent | QQuickItemPrivate::Destroyed); +} + +QQuickSwipeViewAttached::QQuickSwipeViewAttached(QQuickItem *item) : + QObject(*(new QQuickSwipeViewAttachedPrivate(item)), item) +{ + Q_D(QQuickSwipeViewAttached); + if (item->parentItem()) + d->updateView(item->parentItem()); + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->addItemChangeListener(d, QQuickItemPrivate::Parent | QQuickItemPrivate::Destroyed); +} + +QQuickSwipeViewAttached::~QQuickSwipeViewAttached() +{ + Q_D(QQuickSwipeViewAttached); + QQuickItem *item = qobject_cast<QQuickItem *>(parent()); + if (item) + QQuickItemPrivate::get(item)->removeItemChangeListener(d, QQuickItemPrivate::Parent | QQuickItemPrivate::Destroyed); +} + +QQuickSwipeView *QQuickSwipeViewAttached::view() const +{ + Q_D(const QQuickSwipeViewAttached); + return d->swipeView; +} + +int QQuickSwipeViewAttached::index() const +{ + Q_D(const QQuickSwipeViewAttached); + return d->index; +} + +bool QQuickSwipeViewAttached::isCurrentItem() const +{ + Q_D(const QQuickSwipeViewAttached); + return d->swipeView ? d->swipeView->currentIndex() == d->index : false; +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswipeview_p.h b/src/quicktemplates2/qquickswipeview_p.h new file mode 100644 index 00000000..c6cb62ba --- /dev/null +++ b/src/quicktemplates2/qquickswipeview_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWIPEVIEW_P_H +#define QQUICKSWIPEVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontainer_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSwipeViewAttached; +class QQuickSwipeViewPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeView : public QQuickContainer +{ + Q_OBJECT + +public: + explicit QQuickSwipeView(QQuickItem *parent = nullptr); + + static QQuickSwipeViewAttached *qmlAttachedProperties(QObject *object); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemAdded(int index, QQuickItem *item) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickSwipeView) + Q_DECLARE_PRIVATE(QQuickSwipeView) +}; + +class QQuickSwipeViewAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeViewAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL) + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY isCurrentItemChanged FINAL) + Q_PROPERTY(QQuickSwipeView *view READ view NOTIFY viewChanged FINAL) + +public: + explicit QQuickSwipeViewAttached(QQuickItem *delegateItem); + ~QQuickSwipeViewAttached(); + + int index() const; + bool isCurrentItem() const; + QQuickSwipeView *view() const; + +Q_SIGNALS: + void indexChanged(); + void isCurrentItemChanged(); + void viewChanged(); + +private: + Q_DISABLE_COPY(QQuickSwipeViewAttached) + Q_DECLARE_PRIVATE(QQuickSwipeViewAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSwipeView) +QML_DECLARE_TYPEINFO(QQuickSwipeView, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKSWIPEVIEW_P_H diff --git a/src/quicktemplates2/qquickswitch.cpp b/src/quicktemplates2/qquickswitch.cpp new file mode 100644 index 00000000..fa08a1d8 --- /dev/null +++ b/src/quicktemplates2/qquickswitch.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickswitch_p.h" +#include "qquickabstractbutton_p_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/qguiapplication.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/private/qquickevents_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Switch + \inherits AbstractButton + \instantiates QQuickSwitch + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Switch button that can be toggled on or off. + + \image qtquickcontrols2-switch.gif + + Switch is an option button that can be dragged or toggled on (checked) or + off (unchecked). Switches are typically used to select between two states. + For larger sets of options, such as those in a list, consider using + \l SwitchDelegate instead. + + Switch inherits its API from \l AbstractButton. For instance, the state + of the switch can be set with the \l {AbstractButton::}{checked} property. + + \code + ColumnLayout { + Switch { + text: qsTr("Wi-Fi") + } + Switch { + text: qsTr("Bluetooth") + } + } + \endcode + + \sa {Customizing Switch}, {Button Controls} +*/ + +class QQuickSwitchPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickSwitch) + +public: + QQuickSwitchPrivate() : position(0) { } + + qreal positionAt(const QPointF &point) const; + + qreal position; +}; + +qreal QQuickSwitchPrivate::positionAt(const QPointF &point) const +{ + Q_Q(const QQuickSwitch); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); + if (q->isMirrored()) + return 1.0 - pos; + return pos; +} + +QQuickSwitch::QQuickSwitch(QQuickItem *parent) : + QQuickAbstractButton(*(new QQuickSwitchPrivate), parent) +{ + Q_D(QQuickSwitch); + d->keepPressed = true; + setCheckable(true); +} + +/*! + \qmlproperty real QtQuick.Controls::Switch::position + \readonly + + \input includes/qquickswitch.qdocinc position +*/ +qreal QQuickSwitch::position() const +{ + Q_D(const QQuickSwitch); + return d->position; +} + +void QQuickSwitch::setPosition(qreal position) +{ + Q_D(QQuickSwitch); + position = qBound<qreal>(0.0, position, 1.0); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + emit positionChanged(); + emit visualPositionChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::Switch::visualPosition + \readonly + + \input includes/qquickswitch.qdocinc visualPosition +*/ +qreal QQuickSwitch::visualPosition() const +{ + Q_D(const QQuickSwitch); + if (isMirrored()) + return 1.0 - d->position; + 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(); +} + +void QQuickSwitch::nextCheckState() +{ + Q_D(QQuickSwitch); + 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 new file mode 100644 index 00000000..27a065b4 --- /dev/null +++ b/src/quicktemplates2/qquickswitch_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWITCH_P_H +#define QQUICKSWITCH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSwitchPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitch : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL) + +public: + explicit QQuickSwitch(QQuickItem *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + qreal visualPosition() const; + +Q_SIGNALS: + void positionChanged(); + void visualPositionChanged(); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + + void mirrorChange() override; + + void nextCheckState() override; + void checkStateSet() override; + +private: + Q_DISABLE_COPY(QQuickSwitch) + Q_DECLARE_PRIVATE(QQuickSwitch) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSwitch) + +#endif // QQUICKSWITCH_P_H diff --git a/src/quicktemplates2/qquickswitchdelegate.cpp b/src/quicktemplates2/qquickswitchdelegate.cpp new file mode 100644 index 00000000..81e282d9 --- /dev/null +++ b/src/quicktemplates2/qquickswitchdelegate.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickswitchdelegate_p.h" + +#include "qquickitemdelegate_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype SwitchDelegate + \inherits ItemDelegate + \instantiates QQuickSwitchDelegate + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-delegates + \brief Item delegate with a switch indicator that can be toggled on or off. + + \image qtquickcontrols2-switchdelegate.gif + + SwitchDelegate presents an item delegate that can be toggled on (checked) or + off (unchecked). Switch delegates are typically used to select one or more + options from a set of options. For smaller sets of options, or for options + that need to be uniquely identifiable, consider using \l Switch instead. + + SwitchDelegate inherits its API from \l ItemDelegate, which is inherited + from \l AbstractButton. For instance, you can set \l {AbstractButton::text}{text}, + and react to \l {AbstractButton::clicked}{clicks} using the \l AbstractButton + API. The state of the switch delegate can be set with the + \l {AbstractButton::}{checked} property. + + \code + ListView { + model: ["Option 1", "Option 2", "Option 3"] + delegate: SwitchDelegate { + text: modelData + } + } + \endcode + + \sa {Customizing SwitchDelegate}, {Delegate Controls} +*/ + +class QQuickSwitchDelegatePrivate : public QQuickItemDelegatePrivate +{ + Q_DECLARE_PUBLIC(QQuickSwitchDelegate) + +public: + QQuickSwitchDelegatePrivate() : + position(0) + { + } + + qreal positionAt(const QPointF &point) const; + + qreal position; +}; + +qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const +{ + Q_Q(const QQuickSwitchDelegate); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); + if (q->isMirrored()) + return 1.0 - pos; + return pos; +} + +QQuickSwitchDelegate::QQuickSwitchDelegate(QQuickItem *parent) : + QQuickItemDelegate(*(new QQuickSwitchDelegatePrivate), parent) +{ + Q_D(QQuickSwitchDelegate); + d->keepPressed = true; + setCheckable(true); +} + +/*! + \qmlproperty real QtQuick.Controls::SwitchDelegate::position + \readonly + + \input includes/qquickswitch.qdocinc position +*/ +qreal QQuickSwitchDelegate::position() const +{ + Q_D(const QQuickSwitchDelegate); + return d->position; +} + +void QQuickSwitchDelegate::setPosition(qreal position) +{ + Q_D(QQuickSwitchDelegate); + position = qBound<qreal>(0.0, position, 1.0); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + emit positionChanged(); + emit visualPositionChanged(); +} + +/*! + \qmlproperty real QtQuick.Controls::SwitchDelegate::visualPosition + \readonly + + \input includes/qquickswitch.qdocinc visualPosition +*/ +qreal QQuickSwitchDelegate::visualPosition() const +{ + Q_D(const QQuickSwitchDelegate); + if (isMirrored()) + return 1.0 - d->position; + 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); +} + +void QQuickSwitchDelegate::mirrorChange() +{ + QQuickItemDelegate::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 new file mode 100644 index 00000000..c0cc21ac --- /dev/null +++ b/src/quicktemplates2/qquickswitchdelegate_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSWITCHDELEGATE_P_H +#define QQUICKSWITCHDELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickitemdelegate_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickSwitchDelegatePrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitchDelegate : public QQuickItemDelegate +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL) + +public: + explicit QQuickSwitchDelegate(QQuickItem *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + qreal visualPosition() const; + +Q_SIGNALS: + void positionChanged(); + 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) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickSwitchDelegate) + +#endif // QQUICKSWITCHDELEGATE_P_H diff --git a/src/quicktemplates2/qquicktabbar.cpp b/src/quicktemplates2/qquicktabbar.cpp new file mode 100644 index 00000000..ee3436f9 --- /dev/null +++ b/src/quicktemplates2/qquicktabbar.cpp @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktabbar_p.h" +#include "qquicktabbutton_p.h" +#include "qquickcontainer_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype TabBar + \inherits Container + \instantiates QQuickTabBar + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-navigation + \ingroup qtquickcontrols2-containers + \brief Allows the user to switch between different views or subtasks. + + TabBar provides a tab-based navigation model. + + \image qtquickcontrols2-tabbar-wireframe.png + + TabBar is populated with TabButton controls, and can be used together with + any layout or container control that provides \c currentIndex -property, + such as \l StackLayout or \l SwipeView + + \snippet qtquickcontrols2-tabbar.qml 1 + + As shown above, TabBar is typically populated with a static set of tab buttons + that are defined inline as children of the tab bar. It is also possible to + \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert}, + \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove} + items dynamically at run time. The items can be accessed using + \l {Container::}{itemAt()} or \l {Container::}{contentChildren}. + + \section2 Resizing Tabs + + By default, TabBar resizes its buttons to fit the width of the control. + The available space is distributed equally to each button. The default + resizing behavior can be overridden by setting an explicit width for the + buttons. + + The following example illustrates how to keep each tab button at their + implicit size instead of being resized to fit the tabbar: + + \borderedimage qtquickcontrols2-tabbar-explicit.png + + \snippet qtquickcontrols2-tabbar-explicit.qml 1 + + \section2 Flickable Tabs + + If the total width of the buttons exceeds the available width of the tab bar, + it automatically becomes flickable. + + \image qtquickcontrols2-tabbar-flickable.png + + \snippet qtquickcontrols2-tabbar-flickable.qml 1 + + \sa TabButton, {Customizing TabBar}, {Navigation Controls}, {Container Controls} +*/ + +class QQuickTabBarPrivate : public QQuickContainerPrivate +{ + Q_DECLARE_PUBLIC(QQuickTabBar) + +public: + QQuickTabBarPrivate(); + + void updateCurrentItem(); + void updateCurrentIndex(); + void updateLayout(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + + bool updatingLayout; + QQuickTabBar::Position position; +}; + +QQuickTabBarPrivate::QQuickTabBarPrivate() : updatingLayout(false), position(QQuickTabBar::Header) +{ + changeTypes |= Geometry; +} + +void QQuickTabBarPrivate::updateCurrentItem() +{ + QQuickTabButton *button = qobject_cast<QQuickTabButton *>(contentModel->get(currentIndex)); + if (button) + button->setChecked(true); +} + +void QQuickTabBarPrivate::updateCurrentIndex() +{ + Q_Q(QQuickTabBar); + QQuickTabButton *button = qobject_cast<QQuickTabButton *>(q->sender()); + if (button && button->isChecked()) + q->setCurrentIndex(contentModel->indexOf(button, nullptr)); +} + +void QQuickTabBarPrivate::updateLayout() +{ + Q_Q(QQuickTabBar); + const int count = contentModel->count(); + if (count > 0 && contentItem) { + qreal reservedWidth = 0; + QVector<QQuickItem *> resizableItems; + resizableItems.reserve(count); + + for (int i = 0; i < count; ++i) { + QQuickItem *item = q->itemAt(i); + if (item) { + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + if (!p->widthValid) + resizableItems += item; + else + reservedWidth += item->width(); + } + } + + if (!resizableItems.isEmpty()) { + const qreal totalSpacing = qMax(0, count - 1) * spacing; + const qreal itemWidth = (contentItem->width() - reservedWidth - totalSpacing) / resizableItems.count(); + + updatingLayout = true; + for (QQuickItem *item : qAsConst(resizableItems)) { + item->setWidth(itemWidth); + QQuickItemPrivate::get(item)->widthValid = false; + } + updatingLayout = false; + } + } +} + +void QQuickTabBarPrivate::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +{ + if (!updatingLayout) + updateLayout(); +} + +QQuickTabBar::QQuickTabBar(QQuickItem *parent) : + QQuickContainer(*(new QQuickTabBarPrivate), parent) +{ + Q_D(QQuickTabBar); + setFlag(ItemIsFocusScope); + QObjectPrivate::connect(this, &QQuickTabBar::currentIndexChanged, d, &QQuickTabBarPrivate::updateCurrentItem); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::TabBar::position + + This property holds the position of the tab bar. + + \note If the tab bar is assigned as a header or footer of \l ApplicationWindow + or \l Page, the appropriate position is set automatically. + + Possible values: + \value TabBar.Header The tab bar is at the top, as a window or page header. + \value TabBar.Footer The tab bar is at the bottom, as a window or page footer. + + The default value is style-specific. + + \sa ApplicationWindow::header, ApplicationWindow::footer, Page::header, Page::footer +*/ +QQuickTabBar::Position QQuickTabBar::position() const +{ + Q_D(const QQuickTabBar); + return d->position; +} + +void QQuickTabBar::setPosition(Position position) +{ + Q_D(QQuickTabBar); + if (d->position == position) + return; + + d->position = position; + emit positionChanged(); +} + +void QQuickTabBar::updatePolish() +{ + Q_D(QQuickTabBar); + QQuickContainer::updatePolish(); + d->updateLayout(); +} + +void QQuickTabBar::componentComplete() +{ + Q_D(QQuickTabBar); + QQuickContainer::componentComplete(); + d->updateCurrentItem(); + d->updateLayout(); +} + +void QQuickTabBar::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickTabBar); + QQuickContainer::geometryChanged(newGeometry, oldGeometry); + d->updateLayout(); +} + +bool QQuickTabBar::isContent(QQuickItem *item) const +{ + return qobject_cast<QQuickTabButton *>(item); +} + +void QQuickTabBar::itemAdded(int index, QQuickItem *item) +{ + Q_D(QQuickTabBar); + Q_UNUSED(index); + QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-55129 + if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item)) + QObjectPrivate::connect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex); + if (isComponentComplete()) + polish(); +} + +void QQuickTabBar::itemRemoved(int index, QQuickItem *item) +{ + Q_D(QQuickTabBar); + Q_UNUSED(index); + if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item)) + QObjectPrivate::disconnect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex); + if (isComponentComplete()) + polish(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickTabBar::accessibleRole() const +{ + return QAccessible::PageTabList; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktabbar_p.h b/src/quicktemplates2/qquicktabbar_p.h new file mode 100644 index 00000000..7e590009 --- /dev/null +++ b/src/quicktemplates2/qquicktabbar_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTABBAR_P_H +#define QQUICKTABBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontainer_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickTabBarPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabBar : public QQuickContainer +{ + Q_OBJECT + Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL) + +public: + explicit QQuickTabBar(QQuickItem *parent = nullptr); + + enum Position { + Header, + Footer + }; + Q_ENUM(Position) + + Position position() const; + void setPosition(Position position); + +Q_SIGNALS: + void positionChanged(); + +protected: + void updatePolish() override; + void componentComplete() override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + bool isContent(QQuickItem *item) const override; + void itemAdded(int index, QQuickItem *item) override; + void itemRemoved(int index, QQuickItem *item) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickTabBar) + Q_DECLARE_PRIVATE(QQuickTabBar) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTabBar) + +#endif // QQUICKTABBAR_P_H diff --git a/src/quicktemplates2/qquicktabbutton.cpp b/src/quicktemplates2/qquicktabbutton.cpp new file mode 100644 index 00000000..b5cbdd12 --- /dev/null +++ b/src/quicktemplates2/qquicktabbutton.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktabbutton_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype TabButton + \inherits AbstractButton + \instantiates QQuickTabButton + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-navigation + \brief Button with a look suitable for a TabBar. + + \image qtquickcontrols2-tabbutton.png + + TabButton is used in conjunction with a \l TabBar. + + \snippet qtquickcontrols2-tabbutton.qml 1 + + TabButton inherits its API from AbstractButton. For instance, you can set + \l {AbstractButton::text}{text}, and react to \l {AbstractButton::clicked}{clicks} + using the AbstractButton API. + + \sa TabBar, {Customizing TabButton}, {Button Controls}, {Navigation Controls} +*/ + +QQuickTabButton::QQuickTabButton(QQuickItem *parent) : + QQuickAbstractButton(parent) +{ + setCheckable(true); + setAutoExclusive(true); +} + +QFont QQuickTabButton::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::TabButtonFont); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickTabButton::accessibleRole() const +{ + return QAccessible::PageTab; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktabbutton_p.h b/src/quicktemplates2/qquicktabbutton_p.h new file mode 100644 index 00000000..951a02b2 --- /dev/null +++ b/src/quicktemplates2/qquicktabbutton_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTABBUTTON_P_H +#define QQUICKTABBUTTON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabButton : public QQuickAbstractButton +{ + Q_OBJECT + +public: + explicit QQuickTabButton(QQuickItem *parent = nullptr); + +protected: + QFont defaultFont() const override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTabButton) + +#endif // QQUICKTABBUTTON_P_H diff --git a/src/quicktemplates2/qquicktextarea.cpp b/src/quicktemplates2/qquicktextarea.cpp new file mode 100644 index 00000000..a66d7bf5 --- /dev/null +++ b/src/quicktemplates2/qquicktextarea.cpp @@ -0,0 +1,666 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextarea_p.h" +#include "qquicktextarea_p_p.h" +#include "qquickcontrol_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qguiapplication.h> +#include <QtQml/qqmlinfo.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuick/private/qquickclipnode_p.h> +#include <QtQuick/private/qquickflickable_p.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtQuick/private/qquickaccessibleattached_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmltype TextArea + \inherits TextEdit + \instantiates QQuickTextArea + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Multi-line text input area. + + TextArea is a multi-line text editor. TextArea extends TextEdit with + a \l {placeholderText}{placeholder text} functionality, and adds decoration. + + \image qtquickcontrols2-textarea.png + + \code + TextArea { + placeholderText: qsTr("Enter description") + } + \endcode + + TextArea is not scrollable by itself. Especially on screen-size constrained + platforms, it is often preferable to make entire application pages scrollable. + On such a scrollable page, a non-scrollable TextArea might behave better than + nested scrollable controls. Notice, however, that in such a scenario, the background + decoration of the TextArea scrolls together with the rest of the scrollable + content. + + \section2 Scrollable TextArea + + If you want to make a TextArea scrollable, for example, when it covers + an entire application page, attach it to a \l Flickable and combine with a + \l ScrollBar or \l ScrollIndicator. + + \image qtquickcontrols2-textarea-flickable.png + + \snippet qtquickcontrols2-textarea-flickable.qml 1 + + A TextArea that is attached to a \l Flickable does the following: + + \list + \li Sets the content size automatically + \li Ensures that the background decoration stays in place + \li Clips the content + \endlist + + \sa TextField, {Customizing TextArea}, {Input Controls} +*/ + +/*! + \qmlsignal QtQuick.Controls::TextArea::pressAndHold(MouseEvent mouse) + + This signal is emitted when there is a long press (the delay depends on the platform plugin). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. +*/ + +QQuickTextAreaPrivate::QQuickTextAreaPrivate() + : background(nullptr), focusReason(Qt::OtherFocusReason), accessibleAttached(nullptr), flickable(nullptr) +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installActivationObserver(this); +#endif +} + +QQuickTextAreaPrivate::~QQuickTextAreaPrivate() +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::removeActivationObserver(this); +#endif +} + +void QQuickTextAreaPrivate::resizeBackground() +{ + Q_Q(QQuickTextArea); + if (background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(background); + if (!p->widthValid && qFuzzyIsNull(background->x())) { + if (flickable) + background->setWidth(flickable->width()); + else + background->setWidth(q->width()); + p->widthValid = false; + } + if (!p->heightValid && qFuzzyIsNull(background->y())) { + if (flickable) + background->setHeight(flickable->height()); + else + background->setHeight(q->height()); + p->heightValid = false; + } + } +} + +void QQuickTextAreaPrivate::attachFlickable(QQuickFlickable *item) +{ + Q_Q(QQuickTextArea); + flickable = item; + q->setParentItem(flickable->contentItem()); + + if (background) + background->setParentItem(flickable); + + QObjectPrivate::connect(q, &QQuickTextArea::contentSizeChanged, this, &QQuickTextAreaPrivate::resizeFlickableContent); + QObjectPrivate::connect(q, &QQuickTextEdit::cursorRectangleChanged, this, &QQuickTextAreaPrivate::ensureCursorVisible); + + QObject::connect(flickable, &QQuickFlickable::contentXChanged, q, &QQuickItem::update); + QObject::connect(flickable, &QQuickFlickable::contentYChanged, q, &QQuickItem::update); + + QQuickItemPrivate::get(flickable)->updateOrAddGeometryChangeListener(this, QQuickItemPrivate::SizeChange); + QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl); + QObjectPrivate::connect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl); + + resizeFlickableControl(); +} + +void QQuickTextAreaPrivate::detachFlickable() +{ + Q_Q(QQuickTextArea); + q->setParentItem(nullptr); + if (background && background->parentItem() == flickable) + background->setParentItem(q); + + QObjectPrivate::disconnect(q, &QQuickTextArea::contentSizeChanged, this, &QQuickTextAreaPrivate::resizeFlickableContent); + QObjectPrivate::disconnect(q, &QQuickTextEdit::cursorRectangleChanged, this, &QQuickTextAreaPrivate::ensureCursorVisible); + + QObject::disconnect(flickable, &QQuickFlickable::contentXChanged, q, &QQuickItem::update); + QObject::disconnect(flickable, &QQuickFlickable::contentYChanged, q, &QQuickItem::update); + + QQuickItemPrivate::get(flickable)->updateOrRemoveGeometryChangeListener(this, QQuickItemPrivate::SizeChange); + QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl); + QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickTextAreaPrivate::resizeFlickableControl); + + flickable = nullptr; +} + +void QQuickTextAreaPrivate::ensureCursorVisible() +{ + Q_Q(QQuickTextArea); + if (!flickable) + return; + + const qreal cx = flickable->contentX(); + const qreal cy = flickable->contentY(); + const qreal w = flickable->width(); + const qreal h = flickable->height(); + + const qreal tp = q->topPadding(); + const qreal lp = q->leftPadding(); + const QRectF cr = q->cursorRectangle(); + + if (cr.left() <= cx + lp) { + flickable->setContentX(cr.left() - lp); + } else { + // calculate the rectangle of the next character and ensure that + // it's visible if it's on the same line with the cursor + const qreal rp = q->rightPadding(); + const QRectF nr = q->cursorPosition() < q->length() ? q->positionToRectangle(q->cursorPosition() + 1) : QRectF(); + if (qFuzzyCompare(nr.y(), cr.y()) && nr.right() >= cx + lp + w - rp) + flickable->setContentX(nr.right() - w + rp); + else if (cr.right() >= cx + lp + w - rp) + flickable->setContentX(cr.right() - w + rp); + } + + if (cr.top() <= cy + tp) { + flickable->setContentY(cr.top() - tp); + } else { + const qreal bp = q->bottomPadding(); + if (cr.bottom() >= cy + tp + h - bp) + flickable->setContentY(cr.bottom() - h + bp); + } +} + +void QQuickTextAreaPrivate::resizeFlickableControl() +{ + Q_Q(QQuickTextArea); + if (!flickable) + return; + + const qreal w = wrapMode == QQuickTextArea::NoWrap ? qMax(flickable->width(), flickable->contentWidth()) : flickable->width(); + const qreal h = qMax(flickable->height(), flickable->contentHeight()); + q->setSize(QSizeF(w, h)); + + resizeBackground(); +} + +void QQuickTextAreaPrivate::resizeFlickableContent() +{ + Q_Q(QQuickTextArea); + if (!flickable) + return; + + flickable->setContentWidth(q->contentWidth() + q->leftPadding() + q->rightPadding()); + flickable->setContentHeight(q->contentHeight() + q->topPadding() + q->bottomPadding()); +} + +void QQuickTextAreaPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(item); + Q_UNUSED(newGeometry); + Q_UNUSED(oldGeometry); + + resizeFlickableControl(); +} + +qreal QQuickTextAreaPrivate::getImplicitWidth() const +{ + return QQuickItemPrivate::getImplicitWidth(); +} + +qreal QQuickTextAreaPrivate::getImplicitHeight() const +{ + return QQuickItemPrivate::getImplicitHeight(); +} + +void QQuickTextAreaPrivate::implicitWidthChanged() +{ + Q_Q(QQuickTextArea); + QQuickItemPrivate::implicitWidthChanged(); + emit q->implicitWidthChanged3(); +} + +void QQuickTextAreaPrivate::implicitHeightChanged() +{ + Q_Q(QQuickTextArea); + QQuickItemPrivate::implicitHeightChanged(); + emit q->implicitHeightChanged3(); +} + +QQuickTextArea::QQuickTextArea(QQuickItem *parent) : + QQuickTextEdit(*(new QQuickTextAreaPrivate), parent) +{ + Q_D(QQuickTextArea); + setActiveFocusOnTab(true); + d->setImplicitResizeEnabled(false); + d->pressHandler.control = this; +#ifndef QT_NO_CURSOR + setCursor(Qt::IBeamCursor); +#endif + QObjectPrivate::connect(this, &QQuickTextEdit::readOnlyChanged, + d, &QQuickTextAreaPrivate::_q_readOnlyChanged); +} + +QQuickTextArea::~QQuickTextArea() +{ +} + +QQuickTextAreaAttached *QQuickTextArea::qmlAttachedProperties(QObject *object) +{ + return new QQuickTextAreaAttached(object); +} + +/*! + \internal + + Determine which font is implicitly imposed on this control by its ancestors + and QGuiApplication::font, resolve this against its own font (attributes from + the implicit font are copied over). Then propagate this font to this + control's children. +*/ +void QQuickTextAreaPrivate::resolveFont() +{ + Q_Q(QQuickTextArea); + inheritFont(QQuickControlPrivate::parentFont(q)); +} + +void QQuickTextAreaPrivate::inheritFont(const QFont &f) +{ + Q_Q(QQuickTextArea); + QFont parentFont = font.resolve(f); + parentFont.resolve(font.resolve() | f.resolve()); + + const QFont defaultFont = QQuickControlPrivate::themeFont(QPlatformTheme::EditorFont); + const QFont resolvedFont = parentFont.resolve(defaultFont); + + const bool changed = resolvedFont != sourceFont; + q->QQuickTextEdit::setFont(resolvedFont); + if (changed) + emit q->fontChanged(); +} + +void QQuickTextAreaPrivate::_q_readOnlyChanged(bool isReadOnly) +{ +#ifndef QT_NO_ACCESSIBILITY + if (accessibleAttached) + accessibleAttached->set_readOnly(isReadOnly); +#else + Q_UNUSED(isReadOnly) +#endif +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active) +{ + if (accessibleAttached || !active) + return; + + Q_Q(QQuickTextArea); + accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true)); + if (accessibleAttached) { + accessibleAttached->setRole(accessibleRole()); + accessibleAttached->set_readOnly(q->isReadOnly()); + accessibleAttached->setDescription(placeholder); + } else { + qWarning() << "QQuickTextArea: " << q << " QQuickAccessibleAttached object creation failed!"; + } +} + +QAccessible::Role QQuickTextAreaPrivate::accessibleRole() const +{ + return QAccessible::EditableText; +} +#endif + +void QQuickTextAreaPrivate::deleteDelegate(QObject *delegate) +{ + if (componentComplete) + delete delegate; + else + pendingDeletions.append(delegate); +} + +QFont QQuickTextArea::font() const +{ + return QQuickTextEdit::font(); +} + +void QQuickTextArea::setFont(const QFont &font) +{ + Q_D(QQuickTextArea); + if (d->font.resolve() == font.resolve() && d->font == font) + return; + + d->font = font; + d->resolveFont(); +} + +/*! + \qmlproperty Item QtQuick.Controls::TextArea::background + + This property holds the background item. + + \input qquickcontrol-background.qdocinc notes + + \sa {Customizing TextArea} +*/ +QQuickItem *QQuickTextArea::background() const +{ + Q_D(const QQuickTextArea); + return d->background; +} + +void QQuickTextArea::setBackground(QQuickItem *background) +{ + Q_D(QQuickTextArea); + if (d->background == background) + return; + + d->deleteDelegate(d->background); + d->background = background; + if (background) { + background->setParentItem(this); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + if (isComponentComplete()) + d->resizeBackground(); + } + emit backgroundChanged(); +} + +/*! + \qmlproperty string QtQuick.Controls::TextArea::placeholderText + + This property holds the short hint that is displayed in the text area before + the user enters a value. +*/ +QString QQuickTextArea::placeholderText() const +{ + Q_D(const QQuickTextArea); + return d->placeholder; +} + +void QQuickTextArea::setPlaceholderText(const QString &text) +{ + Q_D(QQuickTextArea); + if (d->placeholder == text) + return; + + d->placeholder = text; +#ifndef QT_NO_ACCESSIBILITY + if (d->accessibleAttached) + d->accessibleAttached->setDescription(text); +#endif + emit placeholderTextChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::TextArea::focusReason + + \include qquickcontrol-focusreason.qdocinc +*/ +Qt::FocusReason QQuickTextArea::focusReason() const +{ + Q_D(const QQuickTextArea); + return d->focusReason; +} + +void QQuickTextArea::setFocusReason(Qt::FocusReason reason) +{ + Q_D(QQuickTextArea); + if (d->focusReason == reason) + return; + + d->focusReason = reason; + emit focusReasonChanged(); +} + +bool QQuickTextArea::contains(const QPointF &point) const +{ + Q_D(const QQuickTextArea); + if (d->flickable && !d->flickable->contains(d->flickable->mapFromItem(this, point))) + return false; + return QQuickTextEdit::contains(point); +} + +void QQuickTextArea::classBegin() +{ + Q_D(QQuickTextArea); + QQuickTextEdit::classBegin(); + d->resolveFont(); +} + +void QQuickTextArea::componentComplete() +{ + Q_D(QQuickTextArea); + QQuickTextEdit::componentComplete(); +#ifndef QT_NO_ACCESSIBILITY + if (!d->accessibleAttached && QAccessible::isActive()) + d->accessibilityActiveChanged(true); +#endif + + qDeleteAll(d->pendingDeletions); + d->pendingDeletions.clear(); +} + +void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + Q_D(QQuickTextArea); + QQuickTextEdit::itemChange(change, value); + if (change == ItemParentHasChanged && value.item) + d->resolveFont(); +} + +void QQuickTextArea::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickTextArea); + QQuickTextEdit::geometryChanged(newGeometry, oldGeometry); + d->resizeBackground(); +} + +QSGNode *QQuickTextArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_D(QQuickTextArea); + QQuickDefaultClipNode *clipNode = static_cast<QQuickDefaultClipNode *>(oldNode); + if (!clipNode) + clipNode = new QQuickDefaultClipNode(QRectF()); + + QQuickItem *clipper = this; + if (d->flickable) + clipper = d->flickable; + + const QRectF cr = clipper->clipRect().adjusted(leftPadding(), topPadding(), -rightPadding(), -bottomPadding()); + clipNode->setRect(!d->flickable ? cr : cr.translated(d->flickable->contentX(), d->flickable->contentY())); + clipNode->update(); + + QSGNode *textNode = QQuickTextEdit::updatePaintNode(clipNode->firstChild(), data); + if (!textNode->parent()) + clipNode->appendChildNode(textNode); + + if (d->cursorItem) { + QQuickDefaultClipNode *cursorNode = QQuickItemPrivate::get(d->cursorItem)->clipNode(); + if (cursorNode) + cursorNode->setClipRect(d->cursorItem->mapRectFromItem(clipper, cr)); + } + + return clipNode; +} + +void QQuickTextArea::focusInEvent(QFocusEvent *event) +{ + QQuickTextEdit::focusInEvent(event); + setFocusReason(event->reason()); +} + +void QQuickTextArea::focusOutEvent(QFocusEvent *event) +{ + QQuickTextEdit::focusOutEvent(event); + setFocusReason(event->reason()); +} + +void QQuickTextArea::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickTextArea); + d->pressHandler.mousePressEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextEdit::mousePressEvent(event); + } +} + +void QQuickTextArea::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickTextArea); + d->pressHandler.mouseMoveEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextEdit::mouseMoveEvent(event); + } +} + +void QQuickTextArea::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickTextArea); + d->pressHandler.mouseReleaseEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextEdit::mouseReleaseEvent(event); + } +} + +void QQuickTextArea::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickTextArea); + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextEdit::mouseDoubleClickEvent(event); +} + +void QQuickTextArea::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickTextArea); + if (event->timerId() == d->pressHandler.timer.timerId()) { + d->pressHandler.timerEvent(event); + } else { + QQuickTextEdit::timerEvent(event); + } +} + +class QQuickTextAreaAttachedPrivate : public QObjectPrivate +{ +public: + QQuickTextAreaAttachedPrivate() : control(nullptr) { } + + QQuickTextArea *control; +}; + +QQuickTextAreaAttached::QQuickTextAreaAttached(QObject *parent) : + QObject(*(new QQuickTextAreaAttachedPrivate), parent) +{ +} + +QQuickTextAreaAttached::~QQuickTextAreaAttached() +{ +} + +/*! + \qmlattachedproperty TextArea QtQuick.Controls::TextArea::flickable + + This property attaches a text area to a \l Flickable. + + \sa ScrollBar, ScrollIndicator, {Scrollable TextArea} +*/ +QQuickTextArea *QQuickTextAreaAttached::flickable() const +{ + Q_D(const QQuickTextAreaAttached); + return d->control; +} + +void QQuickTextAreaAttached::setFlickable(QQuickTextArea *control) +{ + Q_D(QQuickTextAreaAttached); + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(parent()); + if (!flickable) { + qmlInfo(parent()) << "TextArea must be attached to a Flickable"; + return; + } + + if (d->control == control) + return; + + if (d->control) + QQuickTextAreaPrivate::get(d->control)->detachFlickable(); + + d->control = control; + + if (control) + QQuickTextAreaPrivate::get(control)->attachFlickable(flickable); + + emit flickableChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktextarea_p.h b/src/quicktemplates2/qquicktextarea_p.h new file mode 100644 index 00000000..f00943aa --- /dev/null +++ b/src/quicktemplates2/qquicktextarea_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTAREA_P_H +#define QQUICKTEXTAREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktextedit_p.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickText; +class QQuickTextAreaPrivate; +class QQuickTextAreaAttached; +class QQuickMouseEvent; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextArea : public QQuickTextEdit +{ + Q_OBJECT + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged3 FINAL) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged3 FINAL) + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText NOTIFY placeholderTextChanged FINAL) + Q_PROPERTY(Qt::FocusReason focusReason READ focusReason WRITE setFocusReason NOTIFY focusReasonChanged FINAL) + +public: + explicit QQuickTextArea(QQuickItem *parent = nullptr); + ~QQuickTextArea(); + + static QQuickTextAreaAttached *qmlAttachedProperties(QObject *object); + + QFont font() const; + void setFont(const QFont &font); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + + QString placeholderText() const; + void setPlaceholderText(const QString &text); + + Qt::FocusReason focusReason() const; + void setFocusReason(Qt::FocusReason reason); + + bool contains(const QPointF &point) const override; + +Q_SIGNALS: + void fontChanged(); + void implicitWidthChanged3(); + void implicitHeightChanged3(); + void backgroundChanged(); + void placeholderTextChanged(); + void focusReasonChanged(); + void pressAndHold(QQuickMouseEvent *event); + +protected: + void classBegin() override; + void componentComplete() override; + + void itemChange(ItemChange change, const ItemChangeData &value) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) override; + + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void timerEvent(QTimerEvent *event) override; + +private: + Q_DISABLE_COPY(QQuickTextArea) + Q_DECLARE_PRIVATE(QQuickTextArea) +}; + +class QQuickTextAreaAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextAreaAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickTextArea *flickable READ flickable WRITE setFlickable NOTIFY flickableChanged FINAL) + +public: + explicit QQuickTextAreaAttached(QObject *parent); + ~QQuickTextAreaAttached(); + + QQuickTextArea *flickable() const; + void setFlickable(QQuickTextArea *control); + +Q_SIGNALS: + void flickableChanged(); + +private: + Q_DISABLE_COPY(QQuickTextAreaAttached) + Q_DECLARE_PRIVATE(QQuickTextAreaAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTextArea) +QML_DECLARE_TYPEINFO(QQuickTextArea, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKTEXTAREA_P_H diff --git a/src/quicktemplates2/qquicktextarea_p_p.h b/src/quicktemplates2/qquicktextarea_p_p.h new file mode 100644 index 00000000..1598a87e --- /dev/null +++ b/src/quicktemplates2/qquicktextarea_p_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTAREA_P_P_H +#define QQUICKTEXTAREA_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktextedit_p_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuickTemplates2/private/qquickpresshandler_p_p.h> + +#include "qquicktextarea_p.h" + +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQuickFlickable; +class QQuickAccessibleAttached; + +class QQuickTextAreaPrivate : public QQuickTextEditPrivate, public QQuickItemChangeListener +#ifndef QT_NO_ACCESSIBILITY + , public QAccessible::ActivationObserver +#endif +{ + Q_DECLARE_PUBLIC(QQuickTextArea) + +public: + QQuickTextAreaPrivate(); + ~QQuickTextAreaPrivate(); + + static QQuickTextAreaPrivate *get(QQuickTextArea *item) { + return static_cast<QQuickTextAreaPrivate *>(QObjectPrivate::get(item)); } + + void resizeBackground(); + void resolveFont(); + void inheritFont(const QFont &f); + + void attachFlickable(QQuickFlickable *flickable); + void detachFlickable(); + void ensureCursorVisible(); + void resizeFlickableControl(); + void resizeFlickableContent(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + + qreal getImplicitWidth() const override; + qreal getImplicitHeight() const override; + + void implicitWidthChanged() override; + void implicitHeightChanged() override; + + void _q_readOnlyChanged(bool isReadOnly); + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + + void deleteDelegate(QObject *object); + + QFont font; + QQuickItem *background; + QString placeholder; + Qt::FocusReason focusReason; + QQuickPressHandler pressHandler; + QQuickAccessibleAttached *accessibleAttached; + QQuickFlickable *flickable; + // This list contains the default delegates which were + // replaced with custom ones via declarative assignments + // before Component.completed() was emitted. See QTBUG-50992. + QVector<QObject *> pendingDeletions; +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXTAREA_P_P_H diff --git a/src/quicktemplates2/qquicktextfield.cpp b/src/quicktemplates2/qquicktextfield.cpp new file mode 100644 index 00000000..610df907 --- /dev/null +++ b/src/quicktemplates2/qquicktextfield.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextfield_p.h" +#include "qquicktextfield_p_p.h" +#include "qquickcontrol_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtCore/qbasictimer.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuick/private/qquicktextinput_p.h> +#include <QtQuick/private/qquickclipnode_p.h> + +#ifndef QT_NO_ACCESSIBILITY +#include <QtQuick/private/qquickaccessibleattached_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmltype TextField + \inherits TextInput + \instantiates QQuickTextField + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Single-line text input field. + + TextField is a single line text editor. TextField extends TextInput with + a \l {placeholderText}{placeholder text} functionality, and adds decoration. + + \table + \row \li \image qtquickcontrols2-textfield-normal.png + \li A text field in its normal state. + \row \li \image qtquickcontrols2-textfield-focused.png + \li A text field that has active focus. + \row \li \image qtquickcontrols2-textfield-disabled.png + \li A text field that is disabled. + \endtable + + \code + TextField { + placeholderText: qsTr("Enter name") + } + \endcode + + \sa TextArea, {Customizing TextField}, {Input Controls} +*/ + +/*! + \qmlsignal QtQuick.Controls::TextField::pressAndHold(MouseEvent mouse) + + This signal is emitted when there is a long press (the delay depends on the platform plugin). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. +*/ + +QQuickTextFieldPrivate::QQuickTextFieldPrivate() + : background(nullptr) + , focusReason(Qt::OtherFocusReason) + , accessibleAttached(nullptr) +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installActivationObserver(this); +#endif +} + +QQuickTextFieldPrivate::~QQuickTextFieldPrivate() +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::removeActivationObserver(this); +#endif +} + +void QQuickTextFieldPrivate::resizeBackground() +{ + Q_Q(QQuickTextField); + if (background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(background); + if (!p->widthValid && qFuzzyIsNull(background->x())) { + background->setWidth(q->width()); + p->widthValid = false; + } + if (!p->heightValid && qFuzzyIsNull(background->y())) { + background->setHeight(q->height()); + p->heightValid = false; + } + } +} + +qreal QQuickTextFieldPrivate::getImplicitWidth() const +{ + return QQuickItemPrivate::getImplicitWidth(); +} + +qreal QQuickTextFieldPrivate::getImplicitHeight() const +{ + return QQuickItemPrivate::getImplicitHeight(); +} + +void QQuickTextFieldPrivate::implicitWidthChanged() +{ + Q_Q(QQuickTextField); + QQuickItemPrivate::implicitWidthChanged(); + emit q->implicitWidthChanged3(); +} + +void QQuickTextFieldPrivate::implicitHeightChanged() +{ + Q_Q(QQuickTextField); + QQuickItemPrivate::implicitHeightChanged(); + emit q->implicitHeightChanged3(); +} + +QQuickTextField::QQuickTextField(QQuickItem *parent) : + QQuickTextInput(*(new QQuickTextFieldPrivate), parent) +{ + Q_D(QQuickTextField); + d->pressHandler.control = this; + d->setImplicitResizeEnabled(false); + setActiveFocusOnTab(true); +#ifndef QT_NO_CURSOR + setCursor(Qt::IBeamCursor); +#endif + QObjectPrivate::connect(this, &QQuickTextInput::readOnlyChanged, + d, &QQuickTextFieldPrivate::_q_readOnlyChanged); + QObjectPrivate::connect(this, &QQuickTextInput::echoModeChanged, + d, &QQuickTextFieldPrivate::_q_echoModeChanged); +} + +QQuickTextField::~QQuickTextField() +{ +} + +/*! + \internal + + Determine which font is implicitly imposed on this control by its ancestors + and QGuiApplication::font, resolve this against its own font (attributes from + the implicit font are copied over). Then propagate this font to this + control's children. +*/ +void QQuickTextFieldPrivate::resolveFont() +{ + Q_Q(QQuickTextField); + inheritFont(QQuickControlPrivate::parentFont(q)); +} + +void QQuickTextFieldPrivate::inheritFont(const QFont &f) +{ + Q_Q(QQuickTextField); + QFont parentFont = font.resolve(f); + parentFont.resolve(font.resolve() | f.resolve()); + + const QFont defaultFont = QQuickControlPrivate::themeFont(QPlatformTheme::EditorFont); + const QFont resolvedFont = parentFont.resolve(defaultFont); + + const bool changed = resolvedFont != sourceFont; + q->QQuickTextInput::setFont(resolvedFont); + if (changed) + emit q->fontChanged(); +} + +void QQuickTextFieldPrivate::_q_readOnlyChanged(bool isReadOnly) +{ +#ifndef QT_NO_ACCESSIBILITY + if (accessibleAttached) + accessibleAttached->set_readOnly(isReadOnly); +#else + Q_UNUSED(isReadOnly) +#endif +} + +void QQuickTextFieldPrivate::_q_echoModeChanged(QQuickTextField::EchoMode echoMode) +{ +#ifndef QT_NO_ACCESSIBILITY + if (accessibleAttached) + accessibleAttached->set_passwordEdit((echoMode == QQuickTextField::Password || echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false); +#else + Q_UNUSED(echoMode) +#endif +} + +#ifndef QT_NO_ACCESSIBILITY +void QQuickTextFieldPrivate::accessibilityActiveChanged(bool active) +{ + if (accessibleAttached || !active) + return; + + Q_Q(QQuickTextField); + accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true)); + if (accessibleAttached) { + accessibleAttached->setRole(accessibleRole()); + accessibleAttached->set_readOnly(m_readOnly); + accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextField::Password || m_echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false); + accessibleAttached->setDescription(placeholder); + } else { + qWarning() << "QQuickTextField: " << q << " QQuickAccessibleAttached object creation failed!"; + } +} + +QAccessible::Role QQuickTextFieldPrivate::accessibleRole() const +{ + return QAccessible::EditableText; +} +#endif + +/* + Deletes "delegate" if Component.completed() has been emitted, + otherwise stores it in pendingDeletions. +*/ +void QQuickTextFieldPrivate::deleteDelegate(QObject *delegate) +{ + if (componentComplete) + delete delegate; + else + pendingDeletions.append(delegate); +} + +QFont QQuickTextField::font() const +{ + return QQuickTextInput::font(); +} + +void QQuickTextField::setFont(const QFont &font) +{ + Q_D(QQuickTextField); + if (d->font.resolve() == font.resolve() && d->font == font) + return; + + d->font = font; + d->resolveFont(); +} + +/*! + \qmlproperty Item QtQuick.Controls::TextField::background + + This property holds the background item. + + \input qquickcontrol-background.qdocinc notes + + \sa {Customizing TextField} +*/ +QQuickItem *QQuickTextField::background() const +{ + Q_D(const QQuickTextField); + return d->background; +} + +void QQuickTextField::setBackground(QQuickItem *background) +{ + Q_D(QQuickTextField); + if (d->background == background) + return; + + d->deleteDelegate(d->background); + d->background = background; + if (background) { + background->setParentItem(this); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + if (isComponentComplete()) + d->resizeBackground(); + } + emit backgroundChanged(); +} + +/*! + \qmlproperty string QtQuick.Controls::TextField::placeholderText + + This property holds the hint that is displayed in the TextField before the user + enters text. +*/ +QString QQuickTextField::placeholderText() const +{ + Q_D(const QQuickTextField); + return d->placeholder; +} + +void QQuickTextField::setPlaceholderText(const QString &text) +{ + Q_D(QQuickTextField); + if (d->placeholder == text) + return; + + d->placeholder = text; +#ifndef QT_NO_ACCESSIBILITY + if (d->accessibleAttached) + d->accessibleAttached->setDescription(text); +#endif + emit placeholderTextChanged(); +} + +/*! + \qmlproperty enumeration QtQuick.Controls::TextField::focusReason + + \include qquickcontrol-focusreason.qdocinc +*/ +Qt::FocusReason QQuickTextField::focusReason() const +{ + Q_D(const QQuickTextField); + return d->focusReason; +} + +void QQuickTextField::setFocusReason(Qt::FocusReason reason) +{ + Q_D(QQuickTextField); + if (d->focusReason == reason) + return; + + d->focusReason = reason; + emit focusReasonChanged(); +} + +void QQuickTextField::classBegin() +{ + Q_D(QQuickTextField); + QQuickTextInput::classBegin(); + d->resolveFont(); +} + +void QQuickTextField::componentComplete() +{ + Q_D(QQuickTextField); + QQuickTextInput::componentComplete(); +#ifndef QT_NO_ACCESSIBILITY + if (!d->accessibleAttached && QAccessible::isActive()) + d->accessibilityActiveChanged(true); +#endif + + qDeleteAll(d->pendingDeletions); + d->pendingDeletions.clear(); +} + +void QQuickTextField::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + Q_D(QQuickTextField); + QQuickTextInput::itemChange(change, value); + if (change == ItemParentHasChanged && value.item) + d->resolveFont(); +} + +void QQuickTextField::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickTextField); + QQuickTextInput::geometryChanged(newGeometry, oldGeometry); + d->resizeBackground(); +} + +QSGNode *QQuickTextField::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + QQuickDefaultClipNode *clipNode = static_cast<QQuickDefaultClipNode *>(oldNode); + if (!clipNode) + clipNode = new QQuickDefaultClipNode(QRectF()); + + clipNode->setRect(clipRect().adjusted(leftPadding(), topPadding(), -rightPadding(), -bottomPadding())); + clipNode->update(); + + QSGNode *textNode = QQuickTextInput::updatePaintNode(clipNode->firstChild(), data); + if (!textNode->parent()) + clipNode->appendChildNode(textNode); + + return clipNode; +} + +void QQuickTextField::focusInEvent(QFocusEvent *event) +{ + QQuickTextInput::focusInEvent(event); + setFocusReason(event->reason()); +} + +void QQuickTextField::focusOutEvent(QFocusEvent *event) +{ + QQuickTextInput::focusOutEvent(event); + setFocusReason(event->reason()); +} + +void QQuickTextField::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickTextField); + d->pressHandler.mousePressEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextInput::mousePressEvent(event); + } +} + +void QQuickTextField::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickTextField); + d->pressHandler.mouseMoveEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextInput::mouseMoveEvent(event); + } +} + +void QQuickTextField::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickTextField); + d->pressHandler.mouseReleaseEvent(event); + if (d->pressHandler.isActive()) { + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextInput::mouseReleaseEvent(event); + } +} + +void QQuickTextField::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickTextField); + if (d->pressHandler.delayedMousePressEvent) { + QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent); + d->pressHandler.clearDelayedMouseEvent(); + } + QQuickTextInput::mouseDoubleClickEvent(event); +} + +void QQuickTextField::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickTextField); + if (event->timerId() == d->pressHandler.timer.timerId()) { + d->pressHandler.timerEvent(event); + } else { + QQuickTextInput::timerEvent(event); + } +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktextfield_p.h b/src/quicktemplates2/qquicktextfield_p.h new file mode 100644 index 00000000..591d6edd --- /dev/null +++ b/src/quicktemplates2/qquicktextfield_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTFIELD_P_H +#define QQUICKTEXTFIELD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktextinput_p.h> +#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickText; +class QQuickTextFieldPrivate; +class QQuickMouseEvent; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextField : public QQuickTextInput +{ + Q_OBJECT + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged3 FINAL) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged3 FINAL) + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) + Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText NOTIFY placeholderTextChanged FINAL) + Q_PROPERTY(Qt::FocusReason focusReason READ focusReason WRITE setFocusReason NOTIFY focusReasonChanged FINAL) + +public: + explicit QQuickTextField(QQuickItem *parent = nullptr); + ~QQuickTextField(); + + QFont font() const; + void setFont(const QFont &font); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); + + QString placeholderText() const; + void setPlaceholderText(const QString &text); + + Qt::FocusReason focusReason() const; + void setFocusReason(Qt::FocusReason reason); + +Q_SIGNALS: + void fontChanged(); + void implicitWidthChanged3(); + void implicitHeightChanged3(); + void backgroundChanged(); + void placeholderTextChanged(); + void focusReasonChanged(); + void pressAndHold(QQuickMouseEvent *mouse); + +protected: + void classBegin() override; + void componentComplete() override; + + void itemChange(ItemChange change, const ItemChangeData &value) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) override; + + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void timerEvent(QTimerEvent *event) override; + +private: + Q_DISABLE_COPY(QQuickTextField) + Q_DECLARE_PRIVATE(QQuickTextField) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTextField) + +#endif // QQUICKTEXTFIELD_P_H diff --git a/src/quicktemplates2/qquicktextfield_p_p.h b/src/quicktemplates2/qquicktextfield_p_p.h new file mode 100644 index 00000000..5800f448 --- /dev/null +++ b/src/quicktemplates2/qquicktextfield_p_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTFIELD_P_P_H +#define QQUICKTEXTFIELD_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qquicktextinput_p_p.h> +#include <QtQuickTemplates2/private/qquickpresshandler_p_p.h> + +#include "qquicktextfield_p.h" + +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQuickAccessibleAttached; + +class QQuickTextFieldPrivate : public QQuickTextInputPrivate +#ifndef QT_NO_ACCESSIBILITY + , public QAccessible::ActivationObserver +#endif +{ + Q_DECLARE_PUBLIC(QQuickTextField) + +public: + QQuickTextFieldPrivate(); + ~QQuickTextFieldPrivate(); + + static QQuickTextFieldPrivate *get(QQuickTextField *item) { + return static_cast<QQuickTextFieldPrivate *>(QObjectPrivate::get(item)); } + + void resizeBackground(); + void resolveFont(); + void inheritFont(const QFont &f); + + qreal getImplicitWidth() const override; + qreal getImplicitHeight() const override; + + void implicitWidthChanged() override; + void implicitHeightChanged() override; + + void _q_readOnlyChanged(bool isReadOnly); + void _q_echoModeChanged(QQuickTextField::EchoMode echoMode); + +#ifndef QT_NO_ACCESSIBILITY + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; +#endif + + void deleteDelegate(QObject *object); + + QFont font; + QQuickItem *background; + QString placeholder; + Qt::FocusReason focusReason; + QQuickPressHandler pressHandler; + QQuickAccessibleAttached *accessibleAttached; + // This list contains the default delegates which were + // replaced with custom ones via declarative assignments + // before Component.completed() was emitted. See QTBUG-50992. + QVector<QObject *> pendingDeletions; +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXTFIELD_P_P_H diff --git a/src/quicktemplates2/qquicktoolbar.cpp b/src/quicktemplates2/qquicktoolbar.cpp new file mode 100644 index 00000000..12ecb702 --- /dev/null +++ b/src/quicktemplates2/qquicktoolbar.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktoolbar_p.h" +#include "qquickpane_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ToolBar + \inherits Pane + \instantiates QQuickToolBar + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-containers + \brief Container for context-sensitive controls. + + ToolBar is a container of application-wide and context sensitive + actions and controls, such as navigation buttons and search fields. + ToolBar is commonly used as a \l {ApplicationWindow::header}{header} + or a \l {ApplicationWindow::footer}{footer} of an \l ApplicationWindow. + + ToolBar does not provide a layout of its own, but requires you to + position its contents, for instance by creating a \l RowLayout. If only + a single item is used within the ToolBar, it will resize to fit the + implicit size of its contained item. This makes it particularly suitable + for use together with layouts. + + \image qtquickcontrols2-toolbar.png + + \code + ApplicationWindow { + visible:true + + header: ToolBar { + RowLayout { + anchors.fill: parent + ToolButton { + text: qsTr("‹") + onClicked: stack.pop() + } + Label { + text: "Title" + elide: Label.ElideRight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + Layout.fillWidth: true + } + ToolButton { + text: qsTr("⋮") + onClicked: menu.open() + } + } + } + + StackView { + id: stack + anchors.fill: parent + } + } + \endcode + + \sa ApplicationWindow, ToolButton, {Customizing ToolBar}, {Container Controls} +*/ + +class QQuickToolBarPrivate : public QQuickPanePrivate +{ +public: + QQuickToolBarPrivate() : position(QQuickToolBar::Header) { } + + QQuickToolBar::Position position; +}; + +QQuickToolBar::QQuickToolBar(QQuickItem *parent) : + QQuickPane(*(new QQuickToolBarPrivate), parent) +{ +} + +/*! + \qmlproperty enumeration QtQuick.Controls::ToolBar::position + + This property holds the position of the toolbar. + + \note If the toolbar is assigned as a header or footer of \l ApplicationWindow + or \l Page, the appropriate position is set automatically. + + Possible values: + \value ToolBar.Header The toolbar is at the top, as a window or page header. + \value ToolBar.Footer The toolbar is at the bottom, as a window or page footer. + + The default value is style-specific. + + \sa ApplicationWindow::header, ApplicationWindow::footer, Page::header, Page::footer +*/ +QQuickToolBar::Position QQuickToolBar::position() const +{ + Q_D(const QQuickToolBar); + return d->position; +} + +void QQuickToolBar::setPosition(Position position) +{ + Q_D(QQuickToolBar); + if (d->position == position) + return; + + d->position = position; + emit positionChanged(); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickToolBar::accessibleRole() const +{ + return QAccessible::ToolBar; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktoolbar_p.h b/src/quicktemplates2/qquicktoolbar_p.h new file mode 100644 index 00000000..9fd85e40 --- /dev/null +++ b/src/quicktemplates2/qquicktoolbar_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTOOLBAR_P_H +#define QQUICKTOOLBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickpane_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickToolBarPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolBar : public QQuickPane +{ + Q_OBJECT + Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL) + +public: + explicit QQuickToolBar(QQuickItem *parent = nullptr); + + enum Position { + Header, + Footer + }; + Q_ENUM(Position) + + Position position() const; + void setPosition(Position position); + +Q_SIGNALS: + void positionChanged(); + +protected: +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickToolBar) + Q_DECLARE_PRIVATE(QQuickToolBar) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickToolBar) + +#endif // QQUICKTOOLBAR_P_H diff --git a/src/quicktemplates2/qquicktoolbutton.cpp b/src/quicktemplates2/qquicktoolbutton.cpp new file mode 100644 index 00000000..e8827b4c --- /dev/null +++ b/src/quicktemplates2/qquicktoolbutton.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktoolbutton_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtGui/qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ToolButton + \inherits Button + \instantiates QQuickToolButton + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-buttons + \brief Button with a look suitable for a ToolBar. + + ToolButton is functionally similar to \l Button, but provides a look that + is more suitable within a \l ToolBar. + + \image qtquickcontrols2-toolbar.png + + \snippet qtquickcontrols2-toolbar.qml 1 + + ToolButton inherits its API from AbstractButton. For instance, you can set + \l {AbstractButton::text}{text}, and react to \l {AbstractButton::clicked}{clicks} + using the AbstractButton API. + + \sa ToolBar, {Customizing ToolButton}, {Button Controls} +*/ + +QQuickToolButton::QQuickToolButton(QQuickItem *parent) : + QQuickButton(parent) +{ +} + +QFont QQuickToolButton::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::ToolButtonFont); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktoolbutton_p.h b/src/quicktemplates2/qquicktoolbutton_p.h new file mode 100644 index 00000000..0041e9c9 --- /dev/null +++ b/src/quicktemplates2/qquicktoolbutton_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTOOLBUTTON_P_H +#define QQUICKTOOLBUTTON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickbutton_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolButton : public QQuickButton +{ + Q_OBJECT + +public: + explicit QQuickToolButton(QQuickItem *parent = nullptr); + +protected: + QFont defaultFont() const override; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickToolButton) + +#endif // QQUICKTOOLBUTTON_P_H diff --git a/src/quicktemplates2/qquicktooltip.cpp b/src/quicktemplates2/qquicktooltip.cpp new file mode 100644 index 00000000..3bd47457 --- /dev/null +++ b/src/quicktemplates2/qquicktooltip.cpp @@ -0,0 +1,531 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktooltip_p.h" +#include "qquickpopup_p_p.h" +#include "qquickcontrol_p_p.h" + +#include <QtCore/qbasictimer.h> +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickwindow.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ToolTip + \inherits Popup + \instantiates QQuickToolTip + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-popups + \brief Provides tool tips for any control. + + A tool tip is a short piece of text that informs the user of a control's + function. It is typically placed above or below the parent control. The + tip text can be any \l{Rich Text Processing}{rich text} formatted string. + + \image qtquickcontrols2-tooltip.png + + \section2 Attached Tool Tips + + The most straight-forward way to setup tool tips for controls is to + specify \l text and \l {visible}{visibility} via attached properties. + The following example illustrates this approach: + + \snippet qtquickcontrols2-tooltip.qml 1 + + Under normal circumstances, there is only one tool tip visible at a time. + In order to save resources, all items that use the ToolTip attached property + share the same visual tool tip label instance. Even though the visuals are + shared, \c text, \c timeout and \c delay are stored individually for each item + that uses the respective attached property. However, multiple items cannot + make the shared tool tip visible at the same time. The shared tool tip is only + shown for the last item that made it visible. The position of the shared tool + tip is determined by the framework. + + \section2 Delay and Timeout + + Tool tips are typically transient in a sense that they are shown as a + result of a certain external event or user interaction, and they usually + hide after a certain timeout. It is possible to control the delay when + a tool tip is shown, and the timeout when it is hidden. This makes it + possible to implement varying strategies for showing and hiding tool tips. + + For example, on touch screens, it is a common pattern to show a tool tip + as a result of pressing and holding down a button. The following example + demonstrates how to delay showing a tool tip until the press-and-hold + interval is reached. In this example, the tool tip hides as soon as the + button is released. + + \snippet qtquickcontrols2-tooltip-pressandhold.qml 1 + + With pointer devices, however, it might be desired to show a tool tip as + a result of hovering a button for a while. The following example presents + how to show a tool tip after hovering a button for a second, and hide it + after a timeout of five seconds. + + \snippet qtquickcontrols2-tooltip-hover.qml 1 + + \section2 Custom Tool Tips + + Should one need more fine-grained control over the tool tip position, or + multiple simultaneous tool tip instances are needed, it is also possible + to create local tool tip instances. This way, it is possible to + \l {Customizing ToolTip}{customize} the tool tip, and the whole \l Popup + API is available. The following example presents a tool tip that presents + the value of a slider when the handle is dragged. + + \image qtquickcontrols2-tooltip-slider.png + + \snippet qtquickcontrols2-tooltip-slider.qml 1 + + \sa {Customizing ToolTip}, {Popup Controls} +*/ + +class QQuickToolTipPrivate : public QQuickPopupPrivate +{ + Q_DECLARE_PUBLIC(QQuickToolTip) + +public: + QQuickToolTipPrivate() : delay(0), timeout(-1) { } + + void startDelay(); + void stopDelay(); + + void startTimeout(); + void stopTimeout(); + + int delay; + int timeout; + QString text; + QBasicTimer delayTimer; + QBasicTimer timeoutTimer; +}; + +void QQuickToolTipPrivate::startDelay() +{ + Q_Q(QQuickToolTip); + if (delay > 0) + delayTimer.start(delay, q); +} + +void QQuickToolTipPrivate::stopDelay() +{ + delayTimer.stop(); +} + +void QQuickToolTipPrivate::startTimeout() +{ + Q_Q(QQuickToolTip); + if (timeout > 0) + timeoutTimer.start(timeout, q); +} + +void QQuickToolTipPrivate::stopTimeout() +{ + timeoutTimer.stop(); +} + +QQuickToolTip::QQuickToolTip(QQuickItem *parent) : + QQuickPopup(*(new QQuickToolTipPrivate), parent) +{ + Q_D(QQuickToolTip); + d->allowVerticalFlip = true; + d->allowHorizontalFlip = true; +} + +/*! + \qmlproperty string QtQuick.Controls::ToolTip::text + + This property holds the text shown on the tool tip. +*/ +QString QQuickToolTip::text() const +{ + Q_D(const QQuickToolTip); + return d->text; +} + +void QQuickToolTip::setText(const QString &text) +{ + Q_D(QQuickToolTip); + if (d->text == text) + return; + + d->text = text; + setAccessibleName(text); + emit textChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::ToolTip::delay + + This property holds the delay (milliseconds) after which the tool tip is + shown. A tooltip with a negative delay is shown immediately. The default + value is \c 0. + + \sa {Delay and Timeout} +*/ +int QQuickToolTip::delay() const +{ + Q_D(const QQuickToolTip); + return d->delay; +} + +void QQuickToolTip::setDelay(int delay) +{ + Q_D(QQuickToolTip); + if (d->delay == delay) + return; + + d->delay = delay; + emit delayChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::ToolTip::timeout + + This property holds the timeout (milliseconds) after which the tool tip is + hidden. A tooltip with a negative timeout does not hide automatically. The + default value is \c -1. + + \sa {Delay and Timeout} +*/ +int QQuickToolTip::timeout() const +{ + Q_D(const QQuickToolTip); + return d->timeout; +} + +void QQuickToolTip::setTimeout(int timeout) +{ + Q_D(QQuickToolTip); + if (d->timeout == timeout) + return; + + if (timeout <= 0) + d->stopTimeout(); + else if (isVisible()) + d->startTimeout(); + + d->timeout = timeout; + emit timeoutChanged(); +} + +void QQuickToolTip::setVisible(bool visible) +{ + Q_D(QQuickToolTip); + if (visible) { + if (!d->visible && d->delay > 0) { + d->startDelay(); + return; + } + } else { + d->stopDelay(); + } + QQuickPopup::setVisible(visible); +} + +QQuickToolTipAttached *QQuickToolTip::qmlAttachedProperties(QObject *object) +{ + QQuickItem *item = qobject_cast<QQuickItem *>(object); + if (!item) + qmlInfo(object) << "ToolTip must be attached to an Item"; + + return new QQuickToolTipAttached(object); +} + +QFont QQuickToolTip::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::TipLabelFont); +} + +void QQuickToolTip::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + Q_D(QQuickToolTip); + QQuickPopup::itemChange(change, data); + if (change == QQuickItem::ItemVisibleHasChanged) { + if (data.boolValue) + d->startTimeout(); + else + d->stopTimeout(); + + QQuickToolTipAttached *attached = qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(d->parentItem, false)); + if (attached) + emit attached->visibleChanged(); + } +} + +void QQuickToolTip::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickToolTip); + if (event->timerId() == d->timeoutTimer.timerId()) { + d->stopTimeout(); + QQuickPopup::setVisible(false); + } else if (event->timerId() == d->delayTimer.timerId()) { + d->stopDelay(); + QQuickPopup::setVisible(true); + } +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickToolTip::accessibleRole() const +{ + return QAccessible::ToolTip; +} + +void QQuickToolTip::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickToolTip); + QQuickPopup::accessibilityActiveChanged(active); + + if (active) + setAccessibleName(d->text); +} +#endif + +class QQuickToolTipAttachedPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickToolTipAttached) + +public: + QQuickToolTipAttachedPrivate() : delay(0), timeout(-1) { } + + QQuickToolTip *instance(bool create) const; + + int delay; + int timeout; + QString text; +}; + +QQuickToolTip *QQuickToolTipAttachedPrivate::instance(bool create) const +{ + QQmlEngine *engine = qmlEngine(parent); + if (!engine) + return nullptr; + + static const char *name = "_q_QQuickToolTip"; + + QQuickToolTip *tip = engine->property(name).value<QQuickToolTip *>(); + if (!tip && create) { + // TODO: a cleaner way to create the instance? QQml(Meta)Type? + QQmlComponent component(engine); + component.setData("import QtQuick.Controls 2.0; ToolTip { }", QUrl()); + + QObject *object = component.create(); + if (object) + object->setParent(engine); + + tip = qobject_cast<QQuickToolTip *>(object); + if (!tip) + delete object; + else + engine->setProperty(name, QVariant::fromValue(object)); + } + return tip; +} + +QQuickToolTipAttached::QQuickToolTipAttached(QObject *parent) : + QObject(*(new QQuickToolTipAttachedPrivate), parent) +{ +} + +/*! + \qmlattachedproperty string QtQuick.Controls::ToolTip::text + + This attached property holds the text of the shared tool tip. + The property can be attached to any item. + + \sa {Attached Tool Tips} +*/ +QString QQuickToolTipAttached::text() const +{ + Q_D(const QQuickToolTipAttached); + return d->text; +} + +void QQuickToolTipAttached::setText(const QString &text) +{ + Q_D(QQuickToolTipAttached); + if (d->text == text) + return; + + d->text = text; + emit textChanged(); + + if (isVisible()) + d->instance(true)->setText(text); +} + +/*! + \qmlattachedproperty int QtQuick.Controls::ToolTip::delay + + This attached property holds the delay (milliseconds) of the shared tool tip. + The property can be attached to any item. + + \sa {Attached Tool Tips}, {Delay and Timeout} +*/ +int QQuickToolTipAttached::delay() const +{ + Q_D(const QQuickToolTipAttached); + return d->delay; +} + +void QQuickToolTipAttached::setDelay(int delay) +{ + Q_D(QQuickToolTipAttached); + if (d->delay == delay) + return; + + d->delay = delay; + emit delayChanged(); + + if (isVisible()) + d->instance(true)->setDelay(delay); +} + +/*! + \qmlattachedproperty int QtQuick.Controls::ToolTip::timeout + + This attached property holds the timeout (milliseconds) of the shared tool tip. + The property can be attached to any item. + + \sa {Attached Tool Tips}, {Delay and Timeout} +*/ +int QQuickToolTipAttached::timeout() const +{ + Q_D(const QQuickToolTipAttached); + return d->timeout; +} + +void QQuickToolTipAttached::setTimeout(int timeout) +{ + Q_D(QQuickToolTipAttached); + if (d->timeout == timeout) + return; + + d->timeout = timeout; + emit timeoutChanged(); + + if (isVisible()) + d->instance(true)->setTimeout(timeout); +} + +/*! + \qmlattachedproperty bool QtQuick.Controls::ToolTip::visible + + This attached property holds whether the shared tool tip is visible. + The property can be attached to any item. + + \sa {Attached Tool Tips} +*/ +bool QQuickToolTipAttached::isVisible() const +{ + Q_D(const QQuickToolTipAttached); + QQuickToolTip *tip = d->instance(false); + if (!tip) + return false; + + return tip->isVisible() && tip->parentItem() == parent(); +} + +void QQuickToolTipAttached::setVisible(bool visible) +{ + Q_D(QQuickToolTipAttached); + if (visible) + show(d->text); + else + hide(); +} + +/*! + \qmlattachedproperty ToolTip QtQuick.Controls::ToolTip::toolTip + + This attached property holds the shared tool tip instance. The property + can be attached to any item. + + \sa {Attached Tool Tips} +*/ +QQuickToolTip *QQuickToolTipAttached::toolTip() const +{ + Q_D(const QQuickToolTipAttached); + return d->instance(true); +} + +/*! + \qmlattachedmethod void QtQuick.Controls::ToolTip::show(string text, int timeout = -1) + + This attached method shows the shared tooltip with \a text and \a timeout (milliseconds). + The method can be attached to any item. + + \sa {Attached Tool Tips} +*/ +void QQuickToolTipAttached::show(const QString &text, int ms) +{ + Q_D(QQuickToolTipAttached); + QQuickToolTip *tip = d->instance(true); + if (!tip) + return; + + tip->resetWidth(); + tip->resetHeight(); + tip->setParentItem(qobject_cast<QQuickItem *>(parent())); + tip->setTimeout(ms >= 0 ? ms : d->timeout); + tip->setDelay(d->delay); + tip->setText(text); + tip->open(); +} + +/*! + \qmlattachedmethod void QtQuick.Controls::ToolTip::hide() + + This attached method hides the shared tooltip. The method can be attached to any item. + + \sa {Attached Tool Tips} +*/ +void QQuickToolTipAttached::hide() +{ + Q_D(QQuickToolTipAttached); + QQuickToolTip *tip = d->instance(false); + if (!tip) + return; + + tip->close(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquicktooltip_p.h b/src/quicktemplates2/qquicktooltip_p.h new file mode 100644 index 00000000..9b628747 --- /dev/null +++ b/src/quicktemplates2/qquicktooltip_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTOOLTIP_P_H +#define QQUICKTOOLTIP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickpopup_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickToolTipPrivate; +class QQuickToolTipAttached; +class QQuickToolTipAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTip : public QQuickPopup +{ + Q_OBJECT + Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL) + Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged FINAL) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL) + +public: + explicit QQuickToolTip(QQuickItem *parent = nullptr); + + QString text() const; + void setText(const QString &text); + + int delay() const; + void setDelay(int delay); + + int timeout() const; + void setTimeout(int timeout); + + void setVisible(bool visible) override; + + static QQuickToolTipAttached *qmlAttachedProperties(QObject *object); + +Q_SIGNALS: + void textChanged(); + void delayChanged(); + void timeoutChanged(); + +protected: + QFont defaultFont() const override; + + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override; + void timerEvent(QTimerEvent *event) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; + void accessibilityActiveChanged(bool active) override; +#endif + +private: + Q_DISABLE_COPY(QQuickToolTip) + Q_DECLARE_PRIVATE(QQuickToolTip) +}; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTipAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL) + Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL) + Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged FINAL) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) + Q_PROPERTY(QQuickToolTip *toolTip READ toolTip CONSTANT FINAL) + +public: + explicit QQuickToolTipAttached(QObject *parent); + + QString text() const; + void setText(const QString &text); + + int delay() const; + void setDelay(int delay); + + int timeout() const; + void setTimeout(int timeout); + + bool isVisible() const; + void setVisible(bool visible); + + QQuickToolTip *toolTip() const; + +Q_SIGNALS: + void textChanged(); + void delayChanged(); + void timeoutChanged(); + void visibleChanged(); + +public Q_SLOTS: + void show(const QString &text, int ms = -1); + void hide(); + +private: + Q_DISABLE_COPY(QQuickToolTipAttached) + Q_DECLARE_PRIVATE(QQuickToolTipAttached) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickToolTip) +QML_DECLARE_TYPEINFO(QQuickToolTip, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKTOOLTIP_P_H diff --git a/src/quicktemplates2/qquicktumbler.cpp b/src/quicktemplates2/qquicktumbler.cpp new file mode 100644 index 00000000..9f19f661 --- /dev/null +++ b/src/quicktemplates2/qquicktumbler.cpp @@ -0,0 +1,603 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktumbler_p.h" + +#include <QtQuick/private/qquickflickable_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Tumbler + \inherits Control + \instantiates QQuickTumbler + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols2-input + \brief Spinnable wheel of items that can be selected. + + \image qtquickcontrols2-tumbler-wrap.gif + + \code + Tumbler { + model: 5 + // ... + } + \endcode + + \section1 Non-wrapping Tumbler + + The default contentItem of Tumbler is a \l PathView, which wraps when it + reaches the top and bottom. To achieve a non-wrapping Tumbler, use ListView + as the contentItem: + + \snippet tst_tumbler.qml contentItem + + \sa {Customizing Tumbler}, {Input Controls} +*/ + +class QQuickTumblerPrivate : public QQuickControlPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickTumbler) + +public: + QQuickTumblerPrivate() : + delegate(nullptr), + visibleItemCount(3) + { + } + + ~QQuickTumblerPrivate() + { + } + + QVariant model; + QQmlComponent *delegate; + int visibleItemCount; + + void _q_updateItemHeights(); + void _q_updateItemWidths(); + + void itemChildAdded(QQuickItem *, QQuickItem *) override; + void itemChildRemoved(QQuickItem *, QQuickItem *) override; +}; + +static QList<QQuickItem *> contentItemChildItems(QQuickItem *contentItem) +{ + if (!contentItem) + return QList<QQuickItem *>(); + + // PathView has no contentItem property, but ListView does. + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(contentItem); + return flickable ? flickable->contentItem()->childItems() : contentItem->childItems(); +} + +namespace { + static inline qreal delegateHeight(const QQuickTumbler *tumbler) + { + return tumbler->availableHeight() / tumbler->visibleItemCount(); + } + + enum ContentItemType { + UnsupportedContentItemType, + PathViewContentItem, + ListViewContentItem + }; + + static inline QQuickItem *actualContentItem(QQuickItem *rootContentItem, ContentItemType contentType) + { + if (contentType == PathViewContentItem) + return rootContentItem; + else if (contentType == ListViewContentItem) + return qobject_cast<QQuickFlickable*>(rootContentItem)->contentItem(); + + return nullptr; + } + + static inline ContentItemType contentItemType(QQuickItem *rootContentItem) + { + if (rootContentItem->inherits("QQuickPathView")) + return PathViewContentItem; + else if (rootContentItem->inherits("QQuickListView")) + return ListViewContentItem; + + return UnsupportedContentItemType; + } + + static inline ContentItemType contentItemTypeFromDelegate(QQuickItem *delegateItem) + { + if (delegateItem->parentItem()->inherits("QQuickPathView")) { + return PathViewContentItem; + } else if (delegateItem->parentItem()->parentItem() + && delegateItem->parentItem()->parentItem()->inherits("QQuickListView")) { + return ListViewContentItem; + } + + return UnsupportedContentItemType; + } +} + +void QQuickTumblerPrivate::_q_updateItemHeights() +{ + // Can't use our own private padding members here, as the padding property might be set, + // which doesn't affect them, only their getters. + Q_Q(const QQuickTumbler); + const qreal itemHeight = delegateHeight(q); + const auto items = contentItemChildItems(contentItem); + for (QQuickItem *childItem : items) + childItem->setHeight(itemHeight); +} + +void QQuickTumblerPrivate::_q_updateItemWidths() +{ + Q_Q(const QQuickTumbler); + const qreal availableWidth = q->availableWidth(); + const auto items = contentItemChildItems(contentItem); + for (QQuickItem *childItem : items) + childItem->setWidth(availableWidth); +} + +void QQuickTumblerPrivate::itemChildAdded(QQuickItem *, QQuickItem *) +{ + _q_updateItemWidths(); + _q_updateItemHeights(); +} + +void QQuickTumblerPrivate::itemChildRemoved(QQuickItem *, QQuickItem *) +{ + _q_updateItemWidths(); + _q_updateItemHeights(); +} + +QQuickTumbler::QQuickTumbler(QQuickItem *parent) : + QQuickControl(*(new QQuickTumblerPrivate), parent) +{ + setActiveFocusOnTab(true); + + connect(this, SIGNAL(leftPaddingChanged()), this, SLOT(_q_updateItemWidths())); + connect(this, SIGNAL(rightPaddingChanged()), this, SLOT(_q_updateItemWidths())); + connect(this, SIGNAL(topPaddingChanged()), this, SLOT(_q_updateItemHeights())); + connect(this, SIGNAL(bottomPaddingChanged()), this, SLOT(_q_updateItemHeights())); +} + +QQuickTumbler::~QQuickTumbler() +{ +} + +/*! + \qmlproperty variant QtQuick.Controls::Tumbler::model + + This property holds the model that provides data for this tumbler. +*/ +QVariant QQuickTumbler::model() const +{ + Q_D(const QQuickTumbler); + return d->model; +} + +void QQuickTumbler::setModel(const QVariant &model) +{ + Q_D(QQuickTumbler); + if (model == d->model) + return; + + d->model = model; + emit modelChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::Tumbler::count + \readonly + + This property holds the number of items in the model. +*/ +int QQuickTumbler::count() const +{ + Q_D(const QQuickTumbler); + return d->contentItem->property("count").toInt(); +} + +/*! + \qmlproperty int QtQuick.Controls::Tumbler::currentIndex + + This property holds the index of the current item. +*/ +int QQuickTumbler::currentIndex() const +{ + Q_D(const QQuickTumbler); + return d->contentItem ? d->contentItem->property("currentIndex").toInt() : -1; +} + +void QQuickTumbler::setCurrentIndex(int currentIndex) +{ + Q_D(QQuickTumbler); + d->contentItem->setProperty("currentIndex", currentIndex); +} + +/*! + \qmlproperty Item QtQuick.Controls::Tumbler::currentItem + \readonly + + This property holds the item at the current index. +*/ +QQuickItem *QQuickTumbler::currentItem() const +{ + Q_D(const QQuickTumbler); + return d->contentItem ? d->contentItem->property("currentItem").value<QQuickItem*>() : nullptr; +} + +/*! + \qmlproperty Component QtQuick.Controls::Tumbler::delegate + + This property holds the delegate used to display each item. +*/ +QQmlComponent *QQuickTumbler::delegate() const +{ + Q_D(const QQuickTumbler); + return d->delegate; +} + +void QQuickTumbler::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQuickTumbler); + if (delegate == d->delegate) + return; + + d->delegate = delegate; + emit delegateChanged(); +} + +/*! + \qmlproperty int QtQuick.Controls::Tumbler::visibleItemCount + + This property holds the number of items visible in the tumbler. It must be + an odd number, as the current item is always vertically centered. +*/ +int QQuickTumbler::visibleItemCount() const +{ + Q_D(const QQuickTumbler); + return d->visibleItemCount; +} + +void QQuickTumbler::setVisibleItemCount(int visibleItemCount) +{ + Q_D(QQuickTumbler); + if (visibleItemCount == d->visibleItemCount) + return; + + d->visibleItemCount = visibleItemCount; + d->_q_updateItemHeights(); + emit visibleItemCountChanged(); +} + +QQuickTumblerAttached *QQuickTumbler::qmlAttachedProperties(QObject *object) +{ + QQuickItem *delegateItem = qobject_cast<QQuickItem *>(object); + if (!delegateItem) { + qWarning() << "Tumbler: attached properties of Tumbler must be accessed from within a delegate item"; + return nullptr; + } + + return new QQuickTumblerAttached(delegateItem); +} + +void QQuickTumbler::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickTumbler); + + QQuickControl::geometryChanged(newGeometry, oldGeometry); + + d->_q_updateItemHeights(); + + if (newGeometry.width() != oldGeometry.width()) + d->_q_updateItemWidths(); +} + +void QQuickTumbler::componentComplete() +{ + Q_D(QQuickTumbler); + QQuickControl::componentComplete(); + d->_q_updateItemHeights(); + d->_q_updateItemWidths(); +} + +void QQuickTumbler::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickTumbler); + + QQuickControl::contentItemChange(newItem, oldItem); + + // Since we use the currentIndex of the contentItem directly, we must + // ensure that we keep track of the currentIndex so it doesn't get lost + // between contentItem changes. + const int previousCurrentIndex = currentIndex(); + + if (oldItem) { + disconnect(oldItem, SIGNAL(currentIndexChanged()), this, SIGNAL(currentIndexChanged())); + disconnect(oldItem, SIGNAL(currentItemChanged()), this, SIGNAL(currentItemChanged())); + disconnect(oldItem, SIGNAL(countChanged()), this, SIGNAL(countChanged())); + + ContentItemType oldContentItemType = contentItemType(oldItem); + QQuickItem *actualOldContentItem = actualContentItem(oldItem, oldContentItemType); + QQuickItemPrivate *actualContentItemPrivate = QQuickItemPrivate::get(actualOldContentItem); + actualContentItemPrivate->removeItemChangeListener(d, QQuickItemPrivate::Children); + } + + if (newItem) { + ContentItemType contentType = contentItemType(newItem); + if (contentType == UnsupportedContentItemType) { + qWarning() << "Tumbler: contentItems other than PathView and ListView are not supported"; + return; + } + + connect(newItem, SIGNAL(currentIndexChanged()), this, SIGNAL(currentIndexChanged())); + connect(newItem, SIGNAL(currentItemChanged()), this, SIGNAL(currentItemChanged())); + connect(newItem, SIGNAL(countChanged()), this, SIGNAL(countChanged())); + + QQuickItem *actualNewContentItem = actualContentItem(newItem, contentType); + QQuickItemPrivate *actualContentItemPrivate = QQuickItemPrivate::get(actualNewContentItem); + actualContentItemPrivate->addItemChangeListener(d, QQuickItemPrivate::Children); + + // If the previous currentIndex is -1, it means we had no contentItem previously. + if (previousCurrentIndex != -1) { + // Can't call setCurrentIndex here, as contentItemChange() is + // called *before* the contentItem is set. + newItem->setProperty("currentIndex", previousCurrentIndex); + } + } +} + +void QQuickTumbler::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickTumbler); + + QQuickControl::keyPressEvent(event); + + if (event->isAutoRepeat()) + return; + + if (event->key() == Qt::Key_Up) { + QMetaObject::invokeMethod(d->contentItem, "decrementCurrentIndex"); + } else if (event->key() == Qt::Key_Down) { + QMetaObject::invokeMethod(d->contentItem, "incrementCurrentIndex"); + } +} + +class QQuickTumblerAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickTumblerAttached) +public: + QQuickTumblerAttachedPrivate(QQuickItem *delegateItem) : + tumbler(nullptr), + index(-1), + displacement(0) + { + if (!delegateItem->parentItem()) { + qWarning() << "Tumbler: attached properties must be accessed from within a delegate item that has a parent"; + return; + } + + QVariant indexContextProperty = qmlContext(delegateItem)->contextProperty(QStringLiteral("index")); + if (!indexContextProperty.isValid()) { + qWarning() << "Tumbler: attempting to access attached property on item without an \"index\" property"; + return; + } + + index = indexContextProperty.toInt(); + const ContentItemType contentItemType = contentItemTypeFromDelegate(delegateItem); + if (contentItemType == UnsupportedContentItemType) + return; + + // ListView has an "additional" content item. + tumbler = qobject_cast<QQuickTumbler* >(contentItemType == PathViewContentItem + ? delegateItem->parentItem()->parentItem() : delegateItem->parentItem()->parentItem()->parentItem()); + Q_ASSERT(tumbler); + } + + ~QQuickTumblerAttachedPrivate() { + } + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemChildAdded(QQuickItem *, QQuickItem *) override; + void itemChildRemoved(QQuickItem *, QQuickItem *) override; + + void _q_calculateDisplacement(); + void emitIfDisplacementChanged(qreal oldDisplacement, qreal newDisplacement); + + // The Tumbler that contains the delegate. Required to calculated the displacement. + QPointer<QQuickTumbler> tumbler; + // The index of the delegate. Used to calculate the displacement. + int index; + // The displacement for our delegate. + qreal displacement; +}; + +void QQuickTumblerAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +{ + _q_calculateDisplacement(); +} + +void QQuickTumblerAttachedPrivate::itemChildAdded(QQuickItem *, QQuickItem *) +{ + _q_calculateDisplacement(); +} + +void QQuickTumblerAttachedPrivate::itemChildRemoved(QQuickItem *item, QQuickItem *child) +{ + _q_calculateDisplacement(); + + if (parent == child) { + // The child that was removed from the contentItem was the delegate + // that our properties are attached to. If we don't remove the change + // listener, the contentItem will attempt to notify a destroyed + // listener, causing a crash. + + // item is the "actual content item" of Tumbler's contentItem, i.e. a PathView or ListView.contentItem + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Children); + } +} + +void QQuickTumblerAttachedPrivate::_q_calculateDisplacement() +{ + const int previousDisplacement = displacement; + displacement = 0; + + if (!tumbler) { + emitIfDisplacementChanged(previousDisplacement, displacement); + return; + } + + const int count = tumbler->count(); + // This can happen in tests, so it may happen in normal usage too. + if (count == 0) { + emitIfDisplacementChanged(previousDisplacement, displacement); + return; + } + + ContentItemType contentType = contentItemType(tumbler->contentItem()); + if (contentType == UnsupportedContentItemType) { + emitIfDisplacementChanged(previousDisplacement, displacement); + return; + } + + qreal offset = 0; + + if (contentType == PathViewContentItem) { + offset = tumbler->contentItem()->property("offset").toReal(); + + displacement = count > 1 ? count - index - offset : 0; + // Don't add 1 if count <= visibleItemCount + const int visibleItems = tumbler->visibleItemCount(); + int halfVisibleItems = visibleItems / 2 + (visibleItems < count ? 1 : 0); + if (displacement > halfVisibleItems) + displacement -= count; + else if (displacement < -halfVisibleItems) + displacement += count; + } else { + const qreal contentY = tumbler->contentItem()->property("contentY").toReal(); + const qreal delegateH = delegateHeight(tumbler); + const qreal preferredHighlightBegin = tumbler->contentItem()->property("preferredHighlightBegin").toReal(); + // Tumbler's displacement goes from negative at the top to positive towards the bottom, so we must switch this around. + const qreal reverseDisplacement = (contentY + preferredHighlightBegin) / delegateH; + displacement = reverseDisplacement - index; + } + + emitIfDisplacementChanged(previousDisplacement, displacement); +} + +void QQuickTumblerAttachedPrivate::emitIfDisplacementChanged(qreal oldDisplacement, qreal newDisplacement) +{ + Q_Q(QQuickTumblerAttached); + if (newDisplacement != oldDisplacement) + emit q->displacementChanged(); +} + +QQuickTumblerAttached::QQuickTumblerAttached(QQuickItem *delegateItem) : + QObject(*(new QQuickTumblerAttachedPrivate(delegateItem)), delegateItem) +{ + Q_D(QQuickTumblerAttached); + if (d->tumbler) { + QQuickItem *rootContentItem = d->tumbler->contentItem(); + const ContentItemType contentType = contentItemType(rootContentItem); + QQuickItemPrivate *p = QQuickItemPrivate::get(actualContentItem(rootContentItem, contentType)); + p->addItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Children); + + const char *contentItemSignal = contentType == PathViewContentItem + ? SIGNAL(offsetChanged()) : SIGNAL(contentYChanged()); + connect(d->tumbler->contentItem(), contentItemSignal, this, SLOT(_q_calculateDisplacement())); + + d->_q_calculateDisplacement(); + } +} + +QQuickTumblerAttached::~QQuickTumblerAttached() +{ + Q_D(QQuickTumblerAttached); + if (!d->tumbler || !d->tumbler->contentItem()) + return; + + QQuickItem *rootContentItem = d->tumbler->contentItem(); + const ContentItemType contentType = contentItemType(rootContentItem); + QQuickItem *actualItem = actualContentItem(rootContentItem, contentType); + if (!actualItem) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(actualItem); + p->removeItemChangeListener(d, QQuickItemPrivate::Geometry | QQuickItemPrivate::Children); +} + +/*! + \qmlattachedproperty Tumbler QtQuick.Controls::Tumbler::tumbler + \readonly + + This attached property holds the tumbler. The property can be attached to + a tumbler delegate. The value is \c null if the item is not a tumbler delegate. +*/ +QQuickTumbler *QQuickTumblerAttached::tumbler() const +{ + Q_D(const QQuickTumblerAttached); + return d->tumbler; +} + +/*! + \qmlattachedproperty real QtQuick.Controls::Tumbler::displacement + \readonly + + This attached property holds a value from \c {-visibleItemCount / 2} to + \c {visibleItemCount / 2}, which represents how far away this item is from + being the current item, with \c 0 being completely current. + + For example, the item below will be 40% opaque when it is not the current item, + and transition to 100% opacity when it becomes the current item: + + \code + delegate: Text { + text: modelData + opacity: 0.4 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.6 + } + \endcode +*/ +qreal QQuickTumblerAttached::displacement() const +{ + Q_D(const QQuickTumblerAttached); + return d->displacement; +} + +QT_END_NAMESPACE + +#include "moc_qquicktumbler_p.cpp" diff --git a/src/quicktemplates2/qquicktumbler_p.h b/src/quicktemplates2/qquicktumbler_p.h new file mode 100644 index 00000000..e28102e4 --- /dev/null +++ b/src/quicktemplates2/qquicktumbler_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTUMBLER_P_H +#define QQUICKTUMBLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvariant.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickTumblerAttached; +class QQuickTumblerPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumbler : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged FINAL) + Q_PROPERTY(int count READ count NOTIFY countChanged FINAL) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL) + Q_PROPERTY(int visibleItemCount READ visibleItemCount WRITE setVisibleItemCount NOTIFY visibleItemCountChanged FINAL) + +public: + explicit QQuickTumbler(QQuickItem *parent = nullptr); + ~QQuickTumbler(); + + QVariant model() const; + void setModel(const QVariant &model); + + int count() const; + + int currentIndex() const; + void setCurrentIndex(int currentIndex); + QQuickItem *currentItem() const; + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *delegate); + + int visibleItemCount() const; + void setVisibleItemCount(int visibleItemCount); + + static QQuickTumblerAttached *qmlAttachedProperties(QObject *object); + +Q_SIGNALS: + void modelChanged(); + void countChanged(); + void currentIndexChanged(); + void currentItemChanged(); + void delegateChanged(); + void visibleItemCountChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void componentComplete() override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + void keyPressEvent(QKeyEvent *event) override; + +private: + Q_DISABLE_COPY(QQuickTumbler) + Q_DECLARE_PRIVATE(QQuickTumbler) + + Q_PRIVATE_SLOT(d_func(), void _q_updateItemWidths()) + Q_PRIVATE_SLOT(d_func(), void _q_updateItemHeights()) +}; + +class QQuickTumblerAttachedPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumblerAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickTumbler *tumbler READ tumbler CONSTANT) + Q_PROPERTY(qreal displacement READ displacement NOTIFY displacementChanged FINAL) + +public: + explicit QQuickTumblerAttached(QQuickItem *delegateItem); + ~QQuickTumblerAttached(); + + QQuickTumbler *tumbler() const; + qreal displacement() const; + +Q_SIGNALS: + void displacementChanged(); + +private: + Q_DISABLE_COPY(QQuickTumblerAttached) + Q_DECLARE_PRIVATE(QQuickTumblerAttached) + + Q_PRIVATE_SLOT(d_func(), void _q_calculateDisplacement()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTumbler) +QML_DECLARE_TYPEINFO(QQuickTumbler, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKTUMBLER_P_H diff --git a/src/quicktemplates2/qquickvelocitycalculator.cpp b/src/quicktemplates2/qquickvelocitycalculator.cpp new file mode 100644 index 00000000..be3b2fef --- /dev/null +++ b/src/quicktemplates2/qquickvelocitycalculator.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickvelocitycalculator_p_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/* + Usage: + + QQuickVelocityCalculator velocityCalculator; + + // ... + + velocityCalcular.startMeasuring(event->pos(), event->timestamp()); + velocityCalcular.stopMeasuring(event->pos(), event->timestamp()); + + // ... + + if (velocityCalculator.velocity().x() > someAmount) + doSomething(); + else if (velocityCalculator.velocity().x() < -someAmount) + doSomethingElse(); +*/ + +QQuickVelocityCalculator::QQuickVelocityCalculator() : + m_point1Timestamp(0), + m_point2Timestamp(0) +{ +} + +void QQuickVelocityCalculator::startMeasuring(const QPointF &point1, qint64 timestamp) +{ + m_point1 = point1; + + if (timestamp != 0) + m_point1Timestamp = timestamp; + else + m_timer.start(); +} + +void QQuickVelocityCalculator::stopMeasuring(const QPointF &point2, qint64 timestamp) +{ + if (timestamp == 0 && !m_timer.isValid()) { + qWarning() << "QQuickVelocityCalculator: a call to stopMeasuring() must be preceded by a call to startMeasuring()"; + return; + } + + m_point2 = point2; + m_point2Timestamp = timestamp != 0 ? timestamp : m_timer.elapsed(); + m_timer.invalidate(); +} + +void QQuickVelocityCalculator::reset() +{ + m_point1 = QPointF(); + m_point2 = QPointF(); + m_point1Timestamp = 0; + m_point2Timestamp = 0; + m_timer.invalidate(); +} + +QPointF QQuickVelocityCalculator::velocity() const +{ + if ((m_point2Timestamp == 0 || m_point1Timestamp == m_point2Timestamp) && !m_timer.isValid()) + return QPointF(); + + const qreal secondsElapsed = (m_point2Timestamp != 0 ? m_point2Timestamp - m_point1Timestamp : m_timer.elapsed()) / 1000.0; + const QPointF distance = m_point2 - m_point1; + return distance / secondsElapsed; +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickvelocitycalculator_p_p.h b/src/quicktemplates2/qquickvelocitycalculator_p_p.h new file mode 100644 index 00000000..c89fc10e --- /dev/null +++ b/src/quicktemplates2/qquickvelocitycalculator_p_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVELOCITYCALCULATOR_P_P_H +#define QQUICKVELOCITYCALCULATOR_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qpoint.h> +#include <QtCore/qelapsedtimer.h> + +QT_BEGIN_NAMESPACE + +class QQuickVelocityCalculator +{ +public: + QQuickVelocityCalculator(); + + void startMeasuring(const QPointF &point1, qint64 timestamp = 0); + void stopMeasuring(const QPointF &m_point2, qint64 timestamp = 0); + void reset(); + QPointF velocity() const; + +private: + QPointF m_point1; + QPointF m_point2; + qint64 m_point1Timestamp; + qint64 m_point2Timestamp; + // When a timestamp isn't available, we must use a timer. + // When stopMeasuring() has been called, we store the elapsed time in point2timestamp. + QElapsedTimer m_timer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKVELOCITYCALCULATOR_P_P_H diff --git a/src/quicktemplates2/qtquicktemplates2global_p.h b/src/quicktemplates2/qtquicktemplates2global_p.h new file mode 100644 index 00000000..50430b38 --- /dev/null +++ b/src/quicktemplates2/qtquicktemplates2global_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTQUICKTEMPLATES2GLOBAL_P_H +#define QTQUICKTEMPLATES2GLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_QUICKTEMPLATES2_LIB) +# define Q_QUICKTEMPLATES2_PRIVATE_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICKTEMPLATES2_PRIVATE_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_QUICKTEMPLATES2_PRIVATE_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QTQUICKTEMPLATES2GLOBAL_P_H diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri new file mode 100644 index 00000000..5caa1bd7 --- /dev/null +++ b/src/quicktemplates2/quicktemplates2.pri @@ -0,0 +1,116 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qquickabstractbutton_p.h \ + $$PWD/qquickabstractbutton_p_p.h \ + $$PWD/qquickapplicationwindow_p.h \ + $$PWD/qquickbusyindicator_p.h \ + $$PWD/qquickbutton_p.h \ + $$PWD/qquickbuttongroup_p.h \ + $$PWD/qquickcheckbox_p.h \ + $$PWD/qquickcheckdelegate_p.h \ + $$PWD/qquickcombobox_p.h \ + $$PWD/qquickcontainer_p.h \ + $$PWD/qquickcontainer_p_p.h \ + $$PWD/qquickcontrol_p.h \ + $$PWD/qquickcontrol_p_p.h \ + $$PWD/qquickdial_p.h \ + $$PWD/qquickdrawer_p.h \ + $$PWD/qquickdrawer_p_p.h \ + $$PWD/qquickframe_p.h \ + $$PWD/qquickframe_p_p.h \ + $$PWD/qquickgroupbox_p.h \ + $$PWD/qquickitemdelegate_p.h \ + $$PWD/qquickitemdelegate_p_p.h \ + $$PWD/qquicklabel_p.h \ + $$PWD/qquicklabel_p_p.h \ + $$PWD/qquickmenu_p.h \ + $$PWD/qquickmenu_p_p.h \ + $$PWD/qquickmenuitem_p.h \ + $$PWD/qquickoverlay_p.h \ + $$PWD/qquickoverlay_p_p.h \ + $$PWD/qquickpage_p.h \ + $$PWD/qquickpageindicator_p.h \ + $$PWD/qquickpane_p.h \ + $$PWD/qquickpane_p_p.h \ + $$PWD/qquickpopup_p.h \ + $$PWD/qquickpopup_p_p.h \ + $$PWD/qquickpresshandler_p_p.h \ + $$PWD/qquickprogressbar_p.h \ + $$PWD/qquickradiobutton_p.h \ + $$PWD/qquickradiodelegate_p.h \ + $$PWD/qquickrangeslider_p.h \ + $$PWD/qquickscrollbar_p.h \ + $$PWD/qquickscrollindicator_p.h \ + $$PWD/qquickshortcutcontext_p_p.h \ + $$PWD/qquickslider_p.h \ + $$PWD/qquickspinbox_p.h \ + $$PWD/qquickstackview_p.h \ + $$PWD/qquickstackview_p_p.h \ + $$PWD/qquickswipe_p.h \ + $$PWD/qquickswipedelegate_p.h \ + $$PWD/qquickswipedelegate_p_p.h \ + $$PWD/qquickswipeview_p.h \ + $$PWD/qquickswitch_p.h \ + $$PWD/qquickswitchdelegate_p.h \ + $$PWD/qquicktabbar_p.h \ + $$PWD/qquicktabbutton_p.h \ + $$PWD/qquicktextarea_p.h \ + $$PWD/qquicktextarea_p_p.h \ + $$PWD/qquicktextfield_p.h \ + $$PWD/qquicktextfield_p_p.h \ + $$PWD/qquicktoolbar_p.h \ + $$PWD/qquicktoolbutton_p.h \ + $$PWD/qquicktooltip_p.h \ + $$PWD/qquicktumbler_p.h \ + $$PWD/qquickvelocitycalculator_p_p.h + +SOURCES += \ + $$PWD/qquickabstractbutton.cpp \ + $$PWD/qquickapplicationwindow.cpp \ + $$PWD/qquickbusyindicator.cpp \ + $$PWD/qquickbutton.cpp \ + $$PWD/qquickbuttongroup.cpp \ + $$PWD/qquickcheckbox.cpp \ + $$PWD/qquickcheckdelegate.cpp \ + $$PWD/qquickcombobox.cpp \ + $$PWD/qquickcontainer.cpp \ + $$PWD/qquickcontrol.cpp \ + $$PWD/qquickdial.cpp \ + $$PWD/qquickdrawer.cpp \ + $$PWD/qquickframe.cpp \ + $$PWD/qquickgroupbox.cpp \ + $$PWD/qquickitemdelegate.cpp \ + $$PWD/qquicklabel.cpp \ + $$PWD/qquickmenu.cpp \ + $$PWD/qquickmenuitem.cpp \ + $$PWD/qquickoverlay.cpp \ + $$PWD/qquickpage.cpp \ + $$PWD/qquickpageindicator.cpp \ + $$PWD/qquickpane.cpp \ + $$PWD/qquickpopup.cpp \ + $$PWD/qquickpresshandler.cpp \ + $$PWD/qquickprogressbar.cpp \ + $$PWD/qquickradiobutton.cpp \ + $$PWD/qquickradiodelegate.cpp \ + $$PWD/qquickrangeslider.cpp \ + $$PWD/qquickscrollbar.cpp \ + $$PWD/qquickscrollindicator.cpp \ + $$PWD/qquickshortcutcontext.cpp \ + $$PWD/qquickslider.cpp \ + $$PWD/qquickspinbox.cpp \ + $$PWD/qquickstackview.cpp \ + $$PWD/qquickstackview_p.cpp \ + $$PWD/qquickswipedelegate.cpp \ + $$PWD/qquickswipeview.cpp \ + $$PWD/qquickswitch.cpp \ + $$PWD/qquickswitchdelegate.cpp \ + $$PWD/qquicktabbar.cpp \ + $$PWD/qquicktabbutton.cpp \ + $$PWD/qquicktextarea.cpp \ + $$PWD/qquicktextfield.cpp \ + $$PWD/qquicktoolbar.cpp \ + $$PWD/qquicktoolbutton.cpp \ + $$PWD/qquicktooltip.cpp \ + $$PWD/qquicktumbler.cpp \ + $$PWD/qquickvelocitycalculator.cpp diff --git a/src/quicktemplates2/quicktemplates2.pro b/src/quicktemplates2/quicktemplates2.pro new file mode 100644 index 00000000..59871a54 --- /dev/null +++ b/src/quicktemplates2/quicktemplates2.pro @@ -0,0 +1,14 @@ +TARGET = QtQuickTemplates2 +MODULE = quicktemplates2 +CONFIG += internal_module + +QT += quick +QT_PRIVATE += core-private gui-private qml-private quick-private + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +HEADERS += \ + $$PWD/qtquicktemplates2global_p.h + +include(quicktemplates2.pri) +load(qt_module) |