f31522130f
Build system (5 gaps hardened): - COOKBOOK_OFFLINE defaults to true (fork-mode) - normalize_patch handles diff -ruN format - New 'repo validate-patches' command (25/25 relibc patches) - 14 patched Qt/Wayland/display recipes added to protected list - relibc archive regenerated with current patch chain Boot fixes (fixable): - Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset) - D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped) - redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped) - daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch) - udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async) - relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs - greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait) - greeter-ui: built and linked (header guard unification, sem_compat stubs removed) - mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps - greeter config: removed stale keymapd dependency from display/greeter services - prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified Unfixable (diagnosed, upstream): - i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort - kded6/greeter-ui: page fault 0x8 — Qt library null deref - Thread panics fd != -1 — Rust std library on Redox - DHCP timeout / eth0 MAC — QEMU user-mode networking - hwrngd/thermald — no hardware RNG/thermal in VM - live preload allocation — BIOS memory fragmentation, continues on demand
1373 lines
44 KiB
C++
1373 lines
44 KiB
C++
// Copyright (C) 2017 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
// Qt-Security score:significant reason:default
|
|
|
|
#include "qquickrangeslider_p.h"
|
|
#include "qquickcontrol_p_p.h"
|
|
#include "qquickdeferredexecute_p_p.h"
|
|
|
|
#include <QtCore/qscopedpointer.h>
|
|
#include <QtQuick/private/qquickwindow_p.h>
|
|
#include <QtQuickTemplates2/private/qtquicktemplates2math_p.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
/*!
|
|
\qmltype RangeSlider
|
|
\inherits Control
|
|
//! \nativetype QQuickRangeSlider
|
|
\inqmlmodule QtQuick.Controls
|
|
\since 5.7
|
|
\ingroup qtquickcontrols-input
|
|
\ingroup qtquickcontrols-focusscopes
|
|
\brief Used to select a range of values by sliding two handles along a track.
|
|
|
|
\image qtquickcontrols-rangeslider.gif
|
|
{Range slider with two handles defining range}
|
|
|
|
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
|
|
|
|
In order to perform an action when the value for a particular handle changes,
|
|
use the following syntax:
|
|
|
|
\code
|
|
first.onMoved: console.log("first.value changed to " + first.value)
|
|
\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.
|
|
|
|
For a slider that allows the user to select a single value, see \l Slider.
|
|
|
|
\sa {Customizing RangeSlider}, {Input Controls},
|
|
{Focus Management in Qt Quick Controls}
|
|
*/
|
|
|
|
class QQuickRangeSliderNodePrivate : public QObjectPrivate
|
|
{
|
|
Q_DECLARE_PUBLIC(QQuickRangeSliderNode)
|
|
public:
|
|
QQuickRangeSliderNodePrivate(qreal value, QQuickRangeSlider *slider)
|
|
: value(value),
|
|
slider(slider)
|
|
{
|
|
}
|
|
|
|
bool isFirst() const;
|
|
|
|
void setPosition(qreal position, bool ignoreOtherPosition = false);
|
|
void updatePosition(bool ignoreOtherPosition = false);
|
|
|
|
void cancelHandle();
|
|
void executeHandle(bool complete = false);
|
|
|
|
static QQuickRangeSliderNodePrivate *get(QQuickRangeSliderNode *node);
|
|
|
|
qreal value = 0;
|
|
bool isPendingValue = false;
|
|
qreal pendingValue = 0;
|
|
qreal position = 0;
|
|
QQuickDeferredPointer<QQuickItem> handle;
|
|
QQuickRangeSlider *slider = nullptr;
|
|
bool pressed = false;
|
|
bool hovered = false;
|
|
int touchId = -1;
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
void QQuickRangeSliderNodePrivate::cancelHandle()
|
|
{
|
|
Q_Q(QQuickRangeSliderNode);
|
|
quickCancelDeferred(q, handleName());
|
|
}
|
|
|
|
void QQuickRangeSliderNodePrivate::executeHandle(bool complete)
|
|
{
|
|
Q_Q(QQuickRangeSliderNode);
|
|
if (handle.wasExecuted())
|
|
return;
|
|
|
|
if (!handle || complete)
|
|
quickBeginDeferred(q, handleName(), handle);
|
|
if (complete)
|
|
quickCompleteDeferred(q, handleName(), handle);
|
|
}
|
|
|
|
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
|
|
{
|
|
QQuickRangeSliderNodePrivate *d = const_cast<QQuickRangeSliderNodePrivate *>(d_func());
|
|
if (!d->handle)
|
|
d->executeHandle();
|
|
return d->handle;
|
|
}
|
|
|
|
void QQuickRangeSliderNode::setHandle(QQuickItem *handle)
|
|
{
|
|
Q_D(QQuickRangeSliderNode);
|
|
if (d->handle == handle)
|
|
return;
|
|
|
|
QQuickControlPrivate::warnIfCustomizationNotSupported(d->slider, handle, QStringLiteral("handle"));
|
|
|
|
if (!d->handle.isExecuting())
|
|
d->cancelHandle();
|
|
|
|
const qreal oldImplicitHandleWidth = implicitHandleWidth();
|
|
const qreal oldImplicitHandleHeight = implicitHandleHeight();
|
|
|
|
QQuickControlPrivate::get(d->slider)->removeImplicitSizeListener(d->handle);
|
|
QQuickControlPrivate::hideOldItem(d->handle);
|
|
d->handle = handle;
|
|
|
|
if (handle) {
|
|
if (!handle->parentItem())
|
|
handle->setParentItem(d->slider);
|
|
|
|
QQuickItem *firstHandle = QQuickRangeSliderNodePrivate::get(d->slider->first())->handle;
|
|
QQuickItem *secondHandle = QQuickRangeSliderNodePrivate::get(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);
|
|
QQuickControlPrivate::get(d->slider)->addImplicitSizeListener(handle);
|
|
}
|
|
|
|
if (!qFuzzyCompare(oldImplicitHandleWidth, implicitHandleWidth()))
|
|
emit implicitHandleWidthChanged();
|
|
if (!qFuzzyCompare(oldImplicitHandleHeight, implicitHandleHeight()))
|
|
emit implicitHandleHeightChanged();
|
|
if (!d->handle.isExecuting())
|
|
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();
|
|
}
|
|
|
|
bool QQuickRangeSliderNode::isHovered() const
|
|
{
|
|
Q_D(const QQuickRangeSliderNode);
|
|
return d->hovered;
|
|
}
|
|
|
|
void QQuickRangeSliderNode::setHovered(bool hovered)
|
|
{
|
|
Q_D(QQuickRangeSliderNode);
|
|
if (d->hovered == hovered)
|
|
return;
|
|
|
|
d->hovered = hovered;
|
|
emit hoveredChanged();
|
|
}
|
|
|
|
qreal QQuickRangeSliderNode::implicitHandleWidth() const
|
|
{
|
|
Q_D(const QQuickRangeSliderNode);
|
|
if (!d->handle)
|
|
return 0;
|
|
return d->handle->implicitWidth();
|
|
}
|
|
|
|
qreal QQuickRangeSliderNode::implicitHandleHeight() const
|
|
{
|
|
Q_D(const QQuickRangeSliderNode);
|
|
if (!d->handle)
|
|
return 0;
|
|
return d->handle->implicitHeight();
|
|
}
|
|
|
|
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:
|
|
static const QQuickRangeSliderPrivate *get(const QQuickRangeSlider *slider);
|
|
|
|
QQuickRangeSliderNode *pressedNode(int touchId = -1) const;
|
|
|
|
#if QT_CONFIG(quicktemplates2_multitouch)
|
|
bool acceptTouch(const QTouchEvent::TouchPoint &point) override;
|
|
#endif
|
|
bool handlePress(const QPointF &point, ulong timestamp) override;
|
|
bool handleMove(const QPointF &point, ulong timestamp) override;
|
|
bool handleRelease(const QPointF &point, ulong timestamp) override;
|
|
void handleUngrab() override;
|
|
|
|
void updateHover(const QPointF &pos);
|
|
|
|
void itemImplicitWidthChanged(QQuickItem *item) override;
|
|
void itemImplicitHeightChanged(QQuickItem *item) override;
|
|
void itemDestroyed(QQuickItem *item) override;
|
|
|
|
void updateAllValuesAreInteger();
|
|
|
|
qreal from = defaultFrom;
|
|
qreal to = defaultTo;
|
|
qreal stepSize = 0;
|
|
qreal touchDragThreshold = -1;
|
|
QQuickRangeSliderNode *first = nullptr;
|
|
QQuickRangeSliderNode *second = nullptr;
|
|
QPointF pressPoint;
|
|
Qt::Orientation orientation = Qt::Horizontal;
|
|
QQuickRangeSlider::SnapMode snapMode = QQuickRangeSlider::NoSnap;
|
|
bool live = true;
|
|
bool allValuesAreInteger = false;
|
|
};
|
|
|
|
static qreal valueAt(const QQuickRangeSlider *slider, qreal position)
|
|
{
|
|
qreal value = slider->from() + (slider->to() - slider->from()) * position;
|
|
|
|
// See the comment in QQuickDialPrivate::valueAt for why we do this.
|
|
if (QQuickRangeSliderPrivate::get(slider)->allValuesAreInteger)
|
|
value = qRound(value);
|
|
|
|
return value;
|
|
}
|
|
|
|
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 QPointF &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;
|
|
}
|
|
|
|
const QQuickRangeSliderPrivate *QQuickRangeSliderPrivate::get(const QQuickRangeSlider *slider)
|
|
{
|
|
return slider->d_func();
|
|
}
|
|
|
|
QQuickRangeSliderNode *QQuickRangeSliderPrivate::pressedNode(int touchId) const
|
|
{
|
|
if (touchId == -1)
|
|
return first->isPressed() ? first : (second->isPressed() ? second : nullptr);
|
|
if (QQuickRangeSliderNodePrivate::get(first)->touchId == touchId)
|
|
return first;
|
|
if (QQuickRangeSliderNodePrivate::get(second)->touchId == touchId)
|
|
return second;
|
|
return nullptr;
|
|
}
|
|
|
|
#if QT_CONFIG(quicktemplates2_multitouch)
|
|
bool QQuickRangeSliderPrivate::acceptTouch(const QTouchEvent::TouchPoint &point)
|
|
{
|
|
int firstId = QQuickRangeSliderNodePrivate::get(first)->touchId;
|
|
int secondId = QQuickRangeSliderNodePrivate::get(second)->touchId;
|
|
|
|
if (((firstId == -1 || secondId == -1) && point.state() == QEventPoint::Pressed) || point.id() == firstId || point.id() == secondId) {
|
|
touchId = point.id();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool QQuickRangeSliderPrivate::handlePress(const QPointF &point, ulong timestamp)
|
|
{
|
|
Q_Q(QQuickRangeSlider);
|
|
QQuickControlPrivate::handlePress(point, timestamp);
|
|
pressPoint = point;
|
|
|
|
QQuickItem *firstHandle = first->handle();
|
|
QQuickItem *secondHandle = second->handle();
|
|
const bool firstHit = firstHandle && !first->isPressed() && firstHandle->contains(q->mapToItem(firstHandle, point));
|
|
const bool secondHit = secondHandle && !second->isPressed() && secondHandle->contains(q->mapToItem(secondHandle, point));
|
|
QQuickRangeSliderNode *hitNode = nullptr;
|
|
QQuickRangeSliderNode *otherNode = nullptr;
|
|
|
|
if (firstHit && secondHit) {
|
|
// choose highest
|
|
hitNode = firstHandle->z() > secondHandle->z() ? first : second;
|
|
otherNode = firstHandle->z() > secondHandle->z() ? second : first;
|
|
} else if (firstHit) {
|
|
hitNode = first;
|
|
otherNode = second;
|
|
} else if (secondHit) {
|
|
hitNode = second;
|
|
otherNode = first;
|
|
} else {
|
|
// find the nearest
|
|
const qreal firstPos = positionAt(q, firstHandle, point);
|
|
const qreal secondPos = positionAt(q, secondHandle, point);
|
|
const qreal firstDistance = qAbs(firstPos - first->position());
|
|
const qreal secondDistance = qAbs(secondPos - second->position());
|
|
|
|
if (qFuzzyCompare(firstDistance, secondDistance)) {
|
|
// same distance => choose the one that can be moved towards the press position
|
|
const bool inverted = from > to;
|
|
if ((!inverted && firstPos < first->position()) || (inverted && firstPos > first->position())) {
|
|
hitNode = first;
|
|
otherNode = second;
|
|
} else {
|
|
hitNode = second;
|
|
otherNode = first;
|
|
}
|
|
} else if (firstDistance < secondDistance) {
|
|
hitNode = first;
|
|
otherNode = second;
|
|
} else {
|
|
hitNode = second;
|
|
otherNode = first;
|
|
}
|
|
}
|
|
|
|
if (hitNode) {
|
|
hitNode->setPressed(true);
|
|
if (QQuickItem *handle = hitNode->handle()) {
|
|
handle->setZ(1);
|
|
|
|
// A specific handle was hit, so it should get focus, rather than the default
|
|
// (first handle) that gets focus whenever the RangeSlider itself does - see focusInEvent().
|
|
if (focusPolicy & Qt::ClickFocus)
|
|
handle->forceActiveFocus(Qt::MouseFocusReason);
|
|
}
|
|
QQuickRangeSliderNodePrivate::get(hitNode)->touchId = touchId;
|
|
}
|
|
if (otherNode) {
|
|
if (QQuickItem *handle = otherNode->handle())
|
|
handle->setZ(0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool QQuickRangeSliderPrivate::handleMove(const QPointF &point, ulong timestamp)
|
|
{
|
|
Q_Q(QQuickRangeSlider);
|
|
QQuickControlPrivate::handleMove(point, timestamp);
|
|
QQuickRangeSliderNode *pressedNode = QQuickRangeSliderPrivate::pressedNode(touchId);
|
|
if (pressedNode) {
|
|
const qreal oldPos = pressedNode->position();
|
|
qreal pos = positionAt(q, pressedNode->handle(), point);
|
|
if (snapMode == QQuickRangeSlider::SnapAlways)
|
|
pos = snapPosition(q, pos);
|
|
if (live)
|
|
pressedNode->setValue(valueAt(q, pos));
|
|
else
|
|
QQuickRangeSliderNodePrivate::get(pressedNode)->setPosition(pos);
|
|
|
|
if (!qFuzzyCompare(pressedNode->position(), oldPos))
|
|
emit pressedNode->moved();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool QQuickRangeSliderPrivate::handleRelease(const QPointF &point, ulong timestamp)
|
|
{
|
|
Q_Q(QQuickRangeSlider);
|
|
QQuickControlPrivate::handleRelease(point, timestamp);
|
|
pressPoint = QPointF();
|
|
|
|
QQuickRangeSliderNode *pressedNode = QQuickRangeSliderPrivate::pressedNode(touchId);
|
|
if (!pressedNode)
|
|
return true;
|
|
QQuickRangeSliderNodePrivate *pressedNodePrivate = QQuickRangeSliderNodePrivate::get(pressedNode);
|
|
|
|
const qreal oldPos = pressedNode->position();
|
|
qreal pos = positionAt(q, pressedNode->handle(), point);
|
|
if (snapMode != QQuickRangeSlider::NoSnap)
|
|
pos = snapPosition(q, pos);
|
|
qreal val = valueAt(q, pos);
|
|
if (!qFuzzyCompare(val, pressedNode->value()))
|
|
pressedNode->setValue(val);
|
|
else if (snapMode != QQuickRangeSlider::NoSnap)
|
|
pressedNodePrivate->setPosition(pos);
|
|
q->setKeepMouseGrab(false);
|
|
q->setKeepTouchGrab(false);
|
|
|
|
if (!qFuzzyCompare(pressedNode->position(), oldPos))
|
|
emit pressedNode->moved();
|
|
|
|
pressedNode->setPressed(false);
|
|
pressedNodePrivate->touchId = -1;
|
|
return true;
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::handleUngrab()
|
|
{
|
|
QQuickControlPrivate::handleUngrab();
|
|
pressPoint = QPointF();
|
|
first->setPressed(false);
|
|
second->setPressed(false);
|
|
QQuickRangeSliderNodePrivate::get(first)->touchId = -1;
|
|
QQuickRangeSliderNodePrivate::get(second)->touchId = -1;
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::updateHover(const QPointF &pos)
|
|
{
|
|
Q_Q(QQuickRangeSlider);
|
|
QQuickItem *firstHandle = first->handle();
|
|
QQuickItem *secondHandle = second->handle();
|
|
bool firstHandleHovered = firstHandle && firstHandle->isEnabled()
|
|
&& firstHandle->contains(q->mapToItem(firstHandle, pos));
|
|
bool secondHandleHovered = secondHandle && secondHandle->isEnabled()
|
|
&& secondHandle->contains(q->mapToItem(secondHandle, pos));
|
|
|
|
if (firstHandleHovered && secondHandleHovered) {
|
|
// Only hover the handle with the higher Z value.
|
|
const bool firstHandleHasHigherZ = firstHandle->z() > secondHandle->z();
|
|
firstHandleHovered = firstHandleHasHigherZ;
|
|
secondHandleHovered = !firstHandleHasHigherZ;
|
|
}
|
|
first->setHovered(firstHandleHovered);
|
|
second->setHovered(secondHandleHovered);
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::itemImplicitWidthChanged(QQuickItem *item)
|
|
{
|
|
QQuickControlPrivate::itemImplicitWidthChanged(item);
|
|
if (item == first->handle())
|
|
emit first->implicitHandleWidthChanged();
|
|
else if (item == second->handle())
|
|
emit second->implicitHandleWidthChanged();
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::itemImplicitHeightChanged(QQuickItem *item)
|
|
{
|
|
QQuickControlPrivate::itemImplicitHeightChanged(item);
|
|
if (item == first->handle())
|
|
emit first->implicitHandleHeightChanged();
|
|
else if (item == second->handle())
|
|
emit second->implicitHandleHeightChanged();
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::itemDestroyed(QQuickItem *item)
|
|
{
|
|
QQuickControlPrivate::itemDestroyed(item);
|
|
if (item == first->handle())
|
|
first->setHandle(nullptr);
|
|
else if (item == second->handle())
|
|
second->setHandle(nullptr);
|
|
}
|
|
|
|
void QQuickRangeSliderPrivate::updateAllValuesAreInteger()
|
|
{
|
|
allValuesAreInteger = areRepresentableAsInteger(to, from, stepSize) && stepSize != 0.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);
|
|
d->setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Fixed);
|
|
|
|
setFlag(QQuickItem::ItemIsFocusScope);
|
|
#ifdef Q_OS_MACOS
|
|
setFocusPolicy(Qt::TabFocus);
|
|
#else
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
#endif
|
|
setAcceptedMouseButtons(Qt::LeftButton);
|
|
#if QT_CONFIG(quicktemplates2_multitouch)
|
|
setAcceptTouchEvents(true);
|
|
#endif
|
|
#if QT_CONFIG(cursor)
|
|
setCursor(Qt::ArrowCursor);
|
|
#endif
|
|
}
|
|
|
|
QQuickRangeSlider::~QQuickRangeSlider()
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
d->removeImplicitSizeListener(d->first->handle());
|
|
d->removeImplicitSizeListener(d->second->handle());
|
|
}
|
|
|
|
/*!
|
|
\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();
|
|
d->updateAllValuesAreInteger();
|
|
|
|
if (isComponentComplete()) {
|
|
d->first->setValue(d->first->value());
|
|
d->second->setValue(d->second->value());
|
|
auto *firstPrivate = QQuickRangeSliderNodePrivate::get(d->first);
|
|
auto *secondPrivate = QQuickRangeSliderNodePrivate::get(d->second);
|
|
firstPrivate->updatePosition(true);
|
|
secondPrivate->updatePosition();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\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();
|
|
d->updateAllValuesAreInteger();
|
|
|
|
if (isComponentComplete()) {
|
|
d->first->setValue(d->first->value());
|
|
d->second->setValue(d->second->value());
|
|
auto *firstPrivate = QQuickRangeSliderNodePrivate::get(d->first);
|
|
auto *secondPrivate = QQuickRangeSliderNodePrivate::get(d->second);
|
|
firstPrivate->updatePosition(true);
|
|
secondPrivate->updatePosition();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlproperty real QtQuick.Controls::RangeSlider::touchDragThreshold
|
|
|
|
This property holds the threshold (in logical pixels) at which a touch drag event will be initiated.
|
|
The mouse drag threshold won't be affected.
|
|
The default value is \c Application.styleHints.startDragDistance.
|
|
|
|
\sa QStyleHints
|
|
|
|
*/
|
|
qreal QQuickRangeSlider::touchDragThreshold() const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
return d->touchDragThreshold;
|
|
}
|
|
|
|
void QQuickRangeSlider::setTouchDragThreshold(qreal touchDragThreshold)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
if (d->touchDragThreshold == touchDragThreshold)
|
|
return;
|
|
|
|
d->touchDragThreshold = touchDragThreshold;
|
|
emit touchDragThresholdChanged();
|
|
}
|
|
|
|
void QQuickRangeSlider::resetTouchDragThreshold()
|
|
{
|
|
setTouchDragThreshold(-1);
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.5 (Qt 5.12)
|
|
\qmlmethod real QtQuick.Controls::RangeSlider::valueAt(real position)
|
|
|
|
Returns the value for the given \a position.
|
|
|
|
\sa first.value, second.value, first.position, second.position, live
|
|
*/
|
|
qreal QQuickRangeSlider::valueAt(qreal position) const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
const qreal value = (d->to - d->from) * position;
|
|
if (qFuzzyIsNull(d->stepSize))
|
|
return d->from + value;
|
|
return d->from + qRound(value / d->stepSize) * d->stepSize;
|
|
}
|
|
|
|
/*!
|
|
\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
|
|
\qmlproperty bool QtQuick.Controls::RangeSlider::first.hovered
|
|
\qmlproperty real QtQuick.Controls::RangeSlider::first.implicitHandleWidth
|
|
\qmlproperty real QtQuick.Controls::RangeSlider::first.implicitHandleHeight
|
|
|
|
\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 from is greater than \l to, the value of the first handle
|
|
must be greater than the second, and vice versa.
|
|
|
|
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}. 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 by either touch,
|
|
mouse, or keys.
|
|
\row
|
|
\li hovered
|
|
\li This property holds whether the first handle is hovered.
|
|
This property was introduced in \l{QtQuick.Controls} 2.1.
|
|
\row
|
|
\li implicitHandleWidth
|
|
\li This property holds the implicit width of the first handle.
|
|
This property was introduced in \l{QtQuick.Controls} 2.5.
|
|
\row
|
|
\li implicitHandleHeight
|
|
\li This property holds the implicit height of the first handle.
|
|
This property was introduced in \l{QtQuick.Controls} 2.5.
|
|
\endtable
|
|
|
|
\sa first.moved(), first.increase(), first.decrease()
|
|
*/
|
|
QQuickRangeSliderNode *QQuickRangeSlider::first() const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
return d->first;
|
|
}
|
|
|
|
/*!
|
|
\qmlsignal void QtQuick.Controls::RangeSlider::first.moved()
|
|
\qmlsignal void QtQuick.Controls::RangeSlider::second.moved()
|
|
\since QtQuick.Controls 2.5
|
|
|
|
This signal is emitted when either the first or second handle has been
|
|
interactively moved by the user by either touch, mouse, or keys.
|
|
|
|
\sa first, 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
|
|
\qmlproperty bool QtQuick.Controls::RangeSlider::second.hovered
|
|
\qmlproperty real QtQuick.Controls::RangeSlider::second.implicitHandleWidth
|
|
\qmlproperty real QtQuick.Controls::RangeSlider::second.implicitHandleHeight
|
|
|
|
\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 from is greater than \l to, the value of the first handle
|
|
must be greater than the second, and vice versa.
|
|
|
|
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}. 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 by either touch,
|
|
mouse, or keys.
|
|
\row
|
|
\li hovered
|
|
\li This property holds whether the second handle is hovered.
|
|
This property was introduced in \l{QtQuick.Controls} 2.1.
|
|
\row
|
|
\li implicitHandleWidth
|
|
\li This property holds the implicit width of the second handle.
|
|
This property was introduced in \l{QtQuick.Controls} 2.5.
|
|
\row
|
|
\li implicitHandleHeight
|
|
\li This property holds the implicit height of the second handle.
|
|
This property was introduced in \l{QtQuick.Controls} 2.5.
|
|
\endtable
|
|
|
|
\sa second.moved(), 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();
|
|
d->updateAllValuesAreInteger();
|
|
}
|
|
|
|
/*!
|
|
\qmlproperty enumeration QtQuick.Controls::RangeSlider::snapMode
|
|
|
|
This property holds the snap mode.
|
|
|
|
The snap mode determines how the slider handles behave with
|
|
regards to the \l stepSize.
|
|
|
|
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
|
|
|
|
\sa horizontal, 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;
|
|
|
|
if (orientation == Qt::Horizontal)
|
|
d->setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Fixed);
|
|
else
|
|
d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Expanding);
|
|
|
|
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 \l to is larger than \l from and \a firstValue is larger than
|
|
\a secondValue, firstValue will be clamped to secondValue.
|
|
|
|
If \l from is larger than \l to and secondValue is larger than
|
|
firstValue, secondValue will be clamped to 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();
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.2 (Qt 5.9)
|
|
\qmlproperty bool QtQuick.Controls::RangeSlider::live
|
|
|
|
This property holds whether the slider provides live updates for the \l first.value
|
|
and \l second.value properties while the respective handles are dragged.
|
|
|
|
The default value is \c true.
|
|
|
|
\sa first.value, second.value
|
|
*/
|
|
bool QQuickRangeSlider::live() const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
return d->live;
|
|
}
|
|
|
|
void QQuickRangeSlider::setLive(bool live)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
if (d->live == live)
|
|
return;
|
|
|
|
d->live = live;
|
|
emit liveChanged();
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.3 (Qt 5.10)
|
|
\qmlproperty bool QtQuick.Controls::RangeSlider::horizontal
|
|
\readonly
|
|
|
|
This property holds whether the slider is horizontal.
|
|
|
|
\sa orientation
|
|
*/
|
|
bool QQuickRangeSlider::isHorizontal() const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
return d->orientation == Qt::Horizontal;
|
|
}
|
|
|
|
/*!
|
|
\since QtQuick.Controls 2.3 (Qt 5.10)
|
|
\qmlproperty bool QtQuick.Controls::RangeSlider::vertical
|
|
\readonly
|
|
|
|
This property holds whether the slider is vertical.
|
|
|
|
\sa orientation
|
|
*/
|
|
bool QQuickRangeSlider::isVertical() const
|
|
{
|
|
Q_D(const QQuickRangeSlider);
|
|
return d->orientation == Qt::Vertical;
|
|
}
|
|
|
|
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;
|
|
|
|
const qreal oldValue = focusNode->value();
|
|
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();
|
|
}
|
|
}
|
|
if (!qFuzzyCompare(focusNode->value(), oldValue))
|
|
emit focusNode->moved();
|
|
}
|
|
|
|
void QQuickRangeSlider::hoverEnterEvent(QHoverEvent *event)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickControl::hoverEnterEvent(event);
|
|
d->updateHover(event->position());
|
|
event->ignore();
|
|
}
|
|
|
|
void QQuickRangeSlider::hoverMoveEvent(QHoverEvent *event)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickControl::hoverMoveEvent(event);
|
|
d->updateHover(event->position());
|
|
event->ignore();
|
|
}
|
|
|
|
void QQuickRangeSlider::hoverLeaveEvent(QHoverEvent *event)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickControl::hoverLeaveEvent(event);
|
|
d->first->setHovered(false);
|
|
d->second->setHovered(false);
|
|
event->ignore();
|
|
}
|
|
|
|
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->handleMove(event->position(), event->timestamp());
|
|
setKeepMouseGrab(true);
|
|
}
|
|
|
|
#if QT_CONFIG(quicktemplates2_multitouch)
|
|
void QQuickRangeSlider::touchEvent(QTouchEvent *event)
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
switch (event->type()) {
|
|
case QEvent::TouchUpdate:
|
|
for (const QTouchEvent::TouchPoint &point : event->points()) {
|
|
if (!d->acceptTouch(point))
|
|
continue;
|
|
|
|
switch (point.state()) {
|
|
case QEventPoint::Pressed:
|
|
d->handlePress(point.position(), event->timestamp());
|
|
break;
|
|
case QEventPoint::Updated:
|
|
if (!keepTouchGrab()) {
|
|
if (d->orientation == Qt::Horizontal) {
|
|
setKeepTouchGrab(QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().x() - point.pressPosition().x(),
|
|
Qt::XAxis, point, qRound(d->touchDragThreshold)));
|
|
} else {
|
|
setKeepTouchGrab(QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().y() - point.pressPosition().y(),
|
|
Qt::YAxis, point, qRound(d->touchDragThreshold)));
|
|
}
|
|
}
|
|
if (keepTouchGrab())
|
|
d->handleMove(point.position(), event->timestamp());
|
|
break;
|
|
case QEventPoint::Released:
|
|
d->handleRelease(point.position(), event->timestamp());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
QQuickControl::touchEvent(event);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void QQuickRangeSlider::mirrorChange()
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickControl::mirrorChange();
|
|
emit d->first->visualPositionChanged();
|
|
emit d->second->visualPositionChanged();
|
|
}
|
|
|
|
void QQuickRangeSlider::classBegin()
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickControl::classBegin();
|
|
|
|
QQmlContext *context = qmlContext(this);
|
|
if (context) {
|
|
QQmlEngine::setContextForObject(d->first, context);
|
|
QQmlEngine::setContextForObject(d->second, context);
|
|
}
|
|
}
|
|
|
|
void QQuickRangeSlider::componentComplete()
|
|
{
|
|
Q_D(QQuickRangeSlider);
|
|
QQuickRangeSliderNodePrivate *firstPrivate = QQuickRangeSliderNodePrivate::get(d->first);
|
|
QQuickRangeSliderNodePrivate *secondPrivate = QQuickRangeSliderNodePrivate::get(d->second);
|
|
firstPrivate->executeHandle(true);
|
|
secondPrivate->executeHandle(true);
|
|
|
|
QQuickControl::componentComplete();
|
|
|
|
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();
|
|
}
|
|
|
|
d->updateAllValuesAreInteger();
|
|
}
|
|
|
|
/*!
|
|
\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
|
|
*/
|
|
|
|
#if QT_CONFIG(accessibility)
|
|
QAccessible::Role QQuickRangeSlider::accessibleRole() const
|
|
{
|
|
return QAccessible::Slider;
|
|
}
|
|
#endif
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "moc_qquickrangeslider_p.cpp"
|