forked from qt-creator/qt-creator
QmlDesigner: Rework SpinBox dragging functionality
* Replace DragHandler with MouseArea due to the DragHandler not being able to accept MouseEvents * Replace TapHandler with MouseArea due to MouseArea stealing press signals from TapHandler, but needed to get press events due to removal of DragHandler * Add functionality to keep cursor in place while dragging * Keep ActionIndicator visible while dragging * Fix qsTr in RectangleSpecifics Change-Id: I6558623287e1864359128d4194c9db78736ee3a4 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Thomas Hartmann
parent
fcb3fabd13
commit
15db0bb173
@@ -63,7 +63,7 @@ Column {
|
|||||||
Section {
|
Section {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
caption: "Rectangle"
|
caption: qsTr("Rectangle")
|
||||||
|
|
||||||
SectionLayout {
|
SectionLayout {
|
||||||
rows: 2
|
rows: 2
|
||||||
|
@@ -45,7 +45,10 @@ Item {
|
|||||||
width: 96
|
width: 96
|
||||||
implicitHeight: spinBox.height
|
implicitHeight: spinBox.height
|
||||||
|
|
||||||
onFocusChanged: transaction.end();
|
onFocusChanged: {
|
||||||
|
restoreCursor();
|
||||||
|
transaction.end();
|
||||||
|
}
|
||||||
|
|
||||||
StudioControls.RealSpinBox {
|
StudioControls.RealSpinBox {
|
||||||
id: spinBox
|
id: spinBox
|
||||||
@@ -60,6 +63,8 @@ Item {
|
|||||||
transaction.end();
|
transaction.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragging: holdCursorInPlace();
|
||||||
|
|
||||||
onRealValueModified: {
|
onRealValueModified: {
|
||||||
if (transaction.active())
|
if (transaction.active())
|
||||||
commitValue();
|
commitValue();
|
||||||
|
@@ -55,7 +55,8 @@ Rectangle {
|
|||||||
visible: text !== StudioTheme.Constants.actionIcon || actionIndicator.forceVisible
|
visible: text !== StudioTheme.Constants.actionIcon || actionIndicator.forceVisible
|
||||||
|| (myControl !== undefined &&
|
|| (myControl !== undefined &&
|
||||||
((myControl.edit !== undefined && myControl.edit)
|
((myControl.edit !== undefined && myControl.edit)
|
||||||
|| (myControl.hover !== undefined && myControl.hover)))
|
|| (myControl.hover !== undefined && myControl.hover)
|
||||||
|
|| (myControl.drag !== undefined && myControl.drag)))
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
font.pixelSize: StudioTheme.Values.myIconFontSize
|
font.pixelSize: StudioTheme.Values.myIconFontSize
|
||||||
|
@@ -55,6 +55,7 @@ T.SpinBox {
|
|||||||
|
|
||||||
property bool dirty: false // user modification flag
|
property bool dirty: false // user modification flag
|
||||||
|
|
||||||
|
// TODO Not used anymore. Will be removed when all dependencies were removed.
|
||||||
property real realDragRange: mySpinBox.realTo - mySpinBox.realFrom
|
property real realDragRange: mySpinBox.realTo - mySpinBox.realFrom
|
||||||
|
|
||||||
property alias actionIndicatorVisible: actionIndicator.visible
|
property alias actionIndicatorVisible: actionIndicator.visible
|
||||||
@@ -77,6 +78,7 @@ T.SpinBox {
|
|||||||
signal compressedRealValueModified
|
signal compressedRealValueModified
|
||||||
signal dragStarted
|
signal dragStarted
|
||||||
signal dragEnded
|
signal dragEnded
|
||||||
|
signal dragging
|
||||||
|
|
||||||
// Use custom wheel handling due to bugs
|
// Use custom wheel handling due to bugs
|
||||||
property bool __wheelEnabled: false
|
property bool __wheelEnabled: false
|
||||||
|
@@ -70,61 +70,25 @@ TextInput {
|
|||||||
height: StudioTheme.Values.height
|
height: StudioTheme.Values.height
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
|
||||||
id: dragHandler
|
|
||||||
target: null
|
|
||||||
acceptedDevices: PointerDevice.Mouse
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
property real initialValue: myControl.realValue
|
|
||||||
property real multiplier: 1.0
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
if (dragHandler.active) {
|
|
||||||
dragHandler.initialValue = myControl.realValue
|
|
||||||
mouseArea.cursorShape = Qt.ClosedHandCursor // TODO
|
|
||||||
myControl.drag = true
|
|
||||||
myControl.dragStarted()
|
|
||||||
// Force focus on the non visible component to receive key events
|
|
||||||
dragModifierWorkaround.forceActiveFocus()
|
|
||||||
} else {
|
|
||||||
if (myControl.compressedValueTimer.running) {
|
|
||||||
myControl.compressedValueTimer.stop()
|
|
||||||
calcValue(myControl.compressedRealValueModified)
|
|
||||||
}
|
|
||||||
mouseArea.cursorShape = Qt.PointingHandCursor // TODO
|
|
||||||
myControl.drag = false
|
|
||||||
myControl.dragEnded()
|
|
||||||
// Avoid active focus on the component after dragging
|
|
||||||
dragModifierWorkaround.focus = false
|
|
||||||
textInput.focus = false
|
|
||||||
myControl.focus = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onTranslationChanged: calcValue(myControl.realValueModified)
|
|
||||||
onMultiplierChanged: calcValue(myControl.realValueModified)
|
|
||||||
|
|
||||||
function calcValue(callback) {
|
|
||||||
var tmp = myControl.realDragRange / StudioTheme.Values.dragLength
|
|
||||||
myControl.setRealValue(dragHandler.initialValue + (tmp * dragHandler.translation.x * dragHandler.multiplier))
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: dragModifierWorkaround
|
id: dragModifierWorkaround
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
|
|
||||||
if (event.modifiers & Qt.ControlModifier)
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
dragHandler.multiplier = 0.1
|
mouseArea.stepSize = myControl.minStepSize
|
||||||
|
mouseArea.calcValue(myControl.realValueModified)
|
||||||
|
}
|
||||||
|
|
||||||
if (event.modifiers & Qt.ShiftModifier)
|
if (event.modifiers & Qt.ShiftModifier) {
|
||||||
dragHandler.multiplier = 10.0
|
mouseArea.stepSize = myControl.maxStepSize
|
||||||
|
mouseArea.calcValue(myControl.realValueModified)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Keys.onReleased: {
|
Keys.onReleased: {
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
dragHandler.multiplier = 1.0
|
mouseArea.stepSize = myControl.realStepSize
|
||||||
|
mouseArea.calcValue(myControl.realValueModified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,21 +97,21 @@ TextInput {
|
|||||||
event.accepted = (event.key === Qt.Key_Up || event.key === Qt.Key_Down)
|
event.accepted = (event.key === Qt.Key_Up || event.key === Qt.Key_Down)
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
id: tapHandler
|
|
||||||
acceptedDevices: PointerDevice.Mouse
|
|
||||||
enabled: true
|
|
||||||
onTapped: {
|
|
||||||
textInput.forceActiveFocus()
|
|
||||||
textInput.deselect() // QTBUG-75862
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
property real stepSize: myControl.realStepSize
|
property real stepSize: myControl.realStepSize
|
||||||
|
|
||||||
|
property bool dragging: false
|
||||||
|
property bool wasDragging: false
|
||||||
|
property bool potentialDragStart: false
|
||||||
|
|
||||||
|
property real initialValue: myControl.realValue
|
||||||
|
|
||||||
|
property real pressStartX: 0.0
|
||||||
|
property real dragStartX: 0.0
|
||||||
|
property real translationX: 0.0
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: true
|
enabled: true
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
@@ -156,7 +120,90 @@ TextInput {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
// Sets the global hover
|
// Sets the global hover
|
||||||
onContainsMouseChanged: myControl.hover = containsMouse
|
onContainsMouseChanged: myControl.hover = containsMouse
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
|
onPositionChanged: {
|
||||||
|
if (!mouseArea.dragging
|
||||||
|
&& !myControl.edit
|
||||||
|
&& Math.abs(mouseArea.pressStartX - mouse.x) > StudioTheme.Values.dragThreshold
|
||||||
|
&& mouse.buttons === 1
|
||||||
|
&& mouseArea.potentialDragStart) {
|
||||||
|
mouseArea.dragging = true
|
||||||
|
mouseArea.potentialDragStart = false
|
||||||
|
mouseArea.initialValue = myControl.realValue
|
||||||
|
mouseArea.cursorShape = Qt.ClosedHandCursor
|
||||||
|
mouseArea.dragStartX = mouseArea.mouseX
|
||||||
|
|
||||||
|
myControl.drag = true
|
||||||
|
myControl.dragStarted()
|
||||||
|
// Force focus on the non visible component to receive key events
|
||||||
|
dragModifierWorkaround.forceActiveFocus()
|
||||||
|
textInput.deselect()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mouseArea.dragging)
|
||||||
|
return
|
||||||
|
|
||||||
|
mouse.accepted = true
|
||||||
|
|
||||||
|
mouseArea.translationX += (mouseArea.mouseX - mouseArea.dragStartX)
|
||||||
|
mouseArea.calcValue(myControl.realValueModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCanceled: mouseArea.endDrag()
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (mouseArea.wasDragging) {
|
||||||
|
mouseArea.wasDragging = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
textInput.forceActiveFocus()
|
||||||
|
textInput.deselect() // QTBUG-75862
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
mouseArea.potentialDragStart = true
|
||||||
|
mouseArea.pressStartX = mouseArea.mouseX
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: mouseArea.endDrag()
|
||||||
|
|
||||||
|
function endDrag() {
|
||||||
|
if (!mouseArea.dragging)
|
||||||
|
return
|
||||||
|
|
||||||
|
mouseArea.dragging = false
|
||||||
|
mouseArea.wasDragging = true
|
||||||
|
|
||||||
|
if (myControl.compressedValueTimer.running) {
|
||||||
|
myControl.compressedValueTimer.stop()
|
||||||
|
mouseArea.calcValue(myControl.compressedRealValueModified)
|
||||||
|
}
|
||||||
|
mouseArea.cursorShape = Qt.PointingHandCursor
|
||||||
|
myControl.drag = false
|
||||||
|
myControl.dragEnded()
|
||||||
|
// Avoid active focus on the component after dragging
|
||||||
|
dragModifierWorkaround.focus = false
|
||||||
|
textInput.focus = false
|
||||||
|
myControl.focus = false
|
||||||
|
|
||||||
|
mouseArea.translationX = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcValue(callback) {
|
||||||
|
var minTranslation = (myControl.realFrom - mouseArea.initialValue) / mouseArea.stepSize
|
||||||
|
var maxTranslation = (myControl.realTo - mouseArea.initialValue) / mouseArea.stepSize
|
||||||
|
|
||||||
|
mouseArea.translationX = Math.min(Math.max(mouseArea.translationX, minTranslation), maxTranslation)
|
||||||
|
|
||||||
|
myControl.setRealValue(mouseArea.initialValue + (mouseArea.translationX * mouseArea.stepSize))
|
||||||
|
|
||||||
|
if (mouseArea.dragging)
|
||||||
|
myControl.dragging()
|
||||||
|
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
onWheel: {
|
onWheel: {
|
||||||
if (!myControl.__wheelEnabled)
|
if (!myControl.__wheelEnabled)
|
||||||
return
|
return
|
||||||
@@ -187,14 +234,6 @@ TextInput {
|
|||||||
color: StudioTheme.Values.themeControlBackground
|
color: StudioTheme.Values.themeControlBackground
|
||||||
border.color: StudioTheme.Values.themeControlOutline
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
}
|
}
|
||||||
PropertyChanges {
|
|
||||||
target: dragHandler
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: tapHandler
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: mouseArea
|
target: mouseArea
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
@@ -217,14 +256,6 @@ TextInput {
|
|||||||
color: StudioTheme.Values.themeFocusEdit
|
color: StudioTheme.Values.themeFocusEdit
|
||||||
border.color: StudioTheme.Values.themeInteraction
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
}
|
}
|
||||||
PropertyChanges {
|
|
||||||
target: dragHandler
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: tapHandler
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: mouseArea
|
target: mouseArea
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
|
@@ -50,7 +50,7 @@ QtObject {
|
|||||||
property real sliderControlSize: 12
|
property real sliderControlSize: 12
|
||||||
property real sliderControlSizeMulti: values.sliderControlSize * values.scaleFactor
|
property real sliderControlSizeMulti: values.sliderControlSize * values.scaleFactor
|
||||||
|
|
||||||
property int dragLength: 400 // px
|
property int dragThreshold: 10 // px
|
||||||
property real spinControlIconSize: 8
|
property real spinControlIconSize: 8
|
||||||
property real spinControlIconSizeMulti: values.spinControlIconSize * values.scaleFactor
|
property real spinControlIconSizeMulti: values.spinControlIconSize * values.scaleFactor
|
||||||
|
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -495,7 +496,9 @@ void PropertyEditorContextObject::hideCursor()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
||||||
m_lastPos = QCursor::pos();
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
m_lastPos = QCursor::pos(w->screen());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorContextObject::restoreCursor()
|
void PropertyEditorContextObject::restoreCursor()
|
||||||
@@ -503,8 +506,19 @@ void PropertyEditorContextObject::restoreCursor()
|
|||||||
if (!QApplication::overrideCursor())
|
if (!QApplication::overrideCursor())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QCursor::setPos(m_lastPos);
|
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyEditorContextObject::holdCursorInPlace()
|
||||||
|
{
|
||||||
|
if (!QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &family)
|
QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &family)
|
||||||
|
@@ -91,6 +91,7 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void hideCursor();
|
Q_INVOKABLE void hideCursor();
|
||||||
Q_INVOKABLE void restoreCursor();
|
Q_INVOKABLE void restoreCursor();
|
||||||
|
Q_INVOKABLE void holdCursorInPlace();
|
||||||
|
|
||||||
Q_INVOKABLE QStringList styleNamesForFamily(const QString &family);
|
Q_INVOKABLE QStringList styleNamesForFamily(const QString &family);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user