I want to implement a Slider
using QtQuick.Controls where only the handle
is clickable and can be used to drag the handle
. If you click the groove
, nothing should happen, the handle
should stay where it is. How can I restrict the mouseArea
of the Slider
to the handle
only?
In the below example the Slider
is clickable on the whole Slider
width
and height
:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Window {
id: mainItem
width: 800
height: 400
visible: true
Slider{
id: autoSlider
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
maximumValue: 1.0
value: 0
updateValueWhileDragging : false
style: SliderStyle {
groove: Rectangle {
implicitWidth: 350
implicitHeight: 8
color: "gray"
radius: 8
}
handle: Rectangle {
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 45
implicitHeight: 45
radius: 12
}
}
}
}
I thought about changing the Slider.qml
template in the "..\qml\QtQuick\Controls" folder, but I couldn't really figure out what to do.
All my search efforts lead to nothing. Any help would be greatly appreciated.
I could be wrong, but I think that this goes against the behaviour of sliders on all Desktop platforms, which is something to consider for usability reasons. That aside, let's try:
diff --git a/src/controls/Slider.qml b/src/controls/Slider.qml
index 20d1102..da6f051 100644
--- a/src/controls/Slider.qml
+++ b/src/controls/Slider.qml
@@ -258,7 +258,7 @@ Control {
}
onPositionChanged: {
- if (pressed)
+ if (pressed && handleHovered)
updateHandlePosition(mouse, preventStealing)
We don't want the handle position to change when the handle wasn't hovered to begin with, because that would mean the mouse is outside the handle.
var point = mouseArea.mapToItem(fakeHandle, mouse.x, mouse.y)
@@ -272,18 +272,21 @@ Control {
if (handleHovered) {
var point = mouseArea.mapToItem(fakeHandle, mouse.x, mouse.y)
clickOffset = __horizontal ? fakeHandle.width/2 - point.x : fakeHandle.height/2 - point.y
+
+ pressX = mouse.x
+ pressY = mouse.y
+ updateHandlePosition(mouse, !Settings.hasTouchScreen)
}
- pressX = mouse.x
- pressY = mouse.y
- updateHandlePosition(mouse, !Settings.hasTouchScreen)
}
Next, move the handle position update within the if (handleHovered)
condition of the onPressed
handle.
onReleased: {
- updateHandlePosition(mouse, Settings.hasTouchScreen)
- // If we don't update while dragging, this is the only
- // moment that the range is updated.
- if (!slider.updateValueWhileDragging)
- range.position = __horizontal ? fakeHandle.x : fakeHandle.y;
+ if (handleHovered) {
+ updateHandlePosition(mouse, Settings.hasTouchScreen)
+ // If we don't update while dragging, this is the only
+ // moment that the range is updated.
+ if (!slider.updateValueWhileDragging)
+ range.position = __horizontal ? fakeHandle.x : fakeHandle.y;
+ }
clickOffset = 0
preventStealing = false
}
Finally, make a similar change to the onReleased
handler.
This will get you what you want, albeit a little rough around the edges — literally. I think the interaction is not 100%, but try it out. With a little more experimenting, you should be able to get it working perfectly.
Another hacky option is to modify Slider.qml
to have a MouseArea
either side of the handle, above the slider itself. Each of these would fill the available space on either side of the handle, effectively stealing all mouse events the groove would receive for those areas.