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:
Henning Gruendl
2020-09-24 10:35:22 +02:00
committed by Thomas Hartmann
parent fcb3fabd13
commit 15db0bb173
8 changed files with 133 additions and 79 deletions

View File

@@ -63,7 +63,7 @@ Column {
Section {
anchors.left: parent.left
anchors.right: parent.right
caption: "Rectangle"
caption: qsTr("Rectangle")
SectionLayout {
rows: 2

View File

@@ -45,7 +45,10 @@ Item {
width: 96
implicitHeight: spinBox.height
onFocusChanged: transaction.end();
onFocusChanged: {
restoreCursor();
transaction.end();
}
StudioControls.RealSpinBox {
id: spinBox
@@ -60,6 +63,8 @@ Item {
transaction.end();
}
onDragging: holdCursorInPlace();
onRealValueModified: {
if (transaction.active())
commitValue();

View File

@@ -55,7 +55,8 @@ Rectangle {
visible: text !== StudioTheme.Constants.actionIcon || actionIndicator.forceVisible
|| (myControl !== undefined &&
((myControl.edit !== undefined && myControl.edit)
|| (myControl.hover !== undefined && myControl.hover)))
|| (myControl.hover !== undefined && myControl.hover)
|| (myControl.drag !== undefined && myControl.drag)))
color: StudioTheme.Values.themeTextColor
font.family: StudioTheme.Constants.iconFont.family
font.pixelSize: StudioTheme.Values.myIconFontSize

View File

@@ -55,6 +55,7 @@ T.SpinBox {
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 alias actionIndicatorVisible: actionIndicator.visible
@@ -77,6 +78,7 @@ T.SpinBox {
signal compressedRealValueModified
signal dragStarted
signal dragEnded
signal dragging
// Use custom wheel handling due to bugs
property bool __wheelEnabled: false

View File

@@ -70,61 +70,25 @@ TextInput {
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 {
id: dragModifierWorkaround
Keys.onPressed: {
event.accepted = true
if (event.modifiers & Qt.ControlModifier)
dragHandler.multiplier = 0.1
if (event.modifiers & Qt.ControlModifier) {
mouseArea.stepSize = myControl.minStepSize
mouseArea.calcValue(myControl.realValueModified)
}
if (event.modifiers & Qt.ShiftModifier)
dragHandler.multiplier = 10.0
if (event.modifiers & Qt.ShiftModifier) {
mouseArea.stepSize = myControl.maxStepSize
mouseArea.calcValue(myControl.realValueModified)
}
}
Keys.onReleased: {
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)
}
TapHandler {
id: tapHandler
acceptedDevices: PointerDevice.Mouse
enabled: true
onTapped: {
textInput.forceActiveFocus()
textInput.deselect() // QTBUG-75862
}
}
MouseArea {
id: mouseArea
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
enabled: true
hoverEnabled: true
@@ -156,7 +120,90 @@ TextInput {
cursorShape: Qt.PointingHandCursor
// Sets the global hover
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: {
if (!myControl.__wheelEnabled)
return
@@ -187,14 +234,6 @@ TextInput {
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
}
PropertyChanges {
target: dragHandler
enabled: true
}
PropertyChanges {
target: tapHandler
enabled: true
}
PropertyChanges {
target: mouseArea
cursorShape: Qt.PointingHandCursor
@@ -217,14 +256,6 @@ TextInput {
color: StudioTheme.Values.themeFocusEdit
border.color: StudioTheme.Values.themeInteraction
}
PropertyChanges {
target: dragHandler
enabled: false
}
PropertyChanges {
target: tapHandler
enabled: false
}
PropertyChanges {
target: mouseArea
cursorShape: Qt.IBeamCursor

View File

@@ -50,7 +50,7 @@ QtObject {
property real sliderControlSize: 12
property real sliderControlSizeMulti: values.sliderControlSize * values.scaleFactor
property int dragLength: 400 // px
property int dragThreshold: 10 // px
property real spinControlIconSize: 8
property real spinControlIconSizeMulti: values.spinControlIconSize * values.scaleFactor

View File

@@ -42,6 +42,7 @@
#include <QFontDatabase>
#include <QMessageBox>
#include <QQmlContext>
#include <QWindow>
#include <coreplugin/icore.h>
@@ -495,7 +496,9 @@ void PropertyEditorContextObject::hideCursor()
return;
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
m_lastPos = QCursor::pos();
if (QWidget *w = QApplication::activeWindow())
m_lastPos = QCursor::pos(w->screen());
}
void PropertyEditorContextObject::restoreCursor()
@@ -503,8 +506,19 @@ void PropertyEditorContextObject::restoreCursor()
if (!QApplication::overrideCursor())
return;
QCursor::setPos(m_lastPos);
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)

View File

@@ -91,6 +91,7 @@ public:
Q_INVOKABLE void hideCursor();
Q_INVOKABLE void restoreCursor();
Q_INVOKABLE void holdCursorInPlace();
Q_INVOKABLE QStringList styleNamesForFamily(const QString &family);