Bondrusiek
Bondrusiek

Reputation: 3

Swipeable button in QML

i wanna create a component which can slide a rectangle from left to right to simulate ON or OFF like a iOS left-to-right button to shutdown phone (https://youtu.be/qEJ5PerUqFw?t=42 second 40 like this). Do you know how can I do this ?

Best regard

I try to use Slider and SwipeDelegate components.

Upvotes: -2

Views: 118

Answers (2)

Stephen Quan
Stephen Quan

Reputation: 26214

Slider is pretty close.

Slider {
    property bool checked: !pressed && value === to
    stepSize: to
    snapMode: Slider.SnapOnRelease
}

You can customize the Slider style to get closer to what you want:

// SwipeButton.qml
import QtQuick
import QtQuick.Controls
Slider {
    id: control
    property bool checked: !pressed && value === to
    property string fromText: qsTr("Swipe me")
    property color fromColor: "white"
    property color fromTextColor: "green"
    property url fromHandle: "FromHandle.svg"
    property string toText: qsTr("Swiped!")
    property color toColor: "green"
    property color toTextColor: "white"
    property url toHandle: "ToHandle.svg"
    stepSize: to
    snapMode: Slider.SnapOnRelease
    background: Rectangle {
        implicitWidth: 300
        implicitHeight: 50
        border.color: c(fromTextColor, toTextColor, control.visualPosition)
        color: c(fromColor, toColor, control.visualPosition)
        radius: 4
        Text {
            anchors.centerIn: parent
            text: fromText
            opacity: 1-control.visualPosition
            color: fromTextColor
        }
        Text {
            anchors.centerIn: parent
            text: toText
            opacity: control.visualPosition
            color: toTextColor
        }
    }
    handle: Item {
        x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
        y: control.topPadding + control.availableHeight / 2 - height / 2
        width: 42
        height: 42
        Image {
            anchors.fill: parent
            source: fromHandle
            sourceSize: Qt.size(width, height)
            opacity: 1-control.visualPosition
cache: false
        }
        Image {
            anchors.fill: parent
            source: toHandle
            sourceSize: Qt.size(width, height)
            opacity: control.visualPosition
cache: false
        }
    }
    function c(c1, c2, t) {
        c1 = Qt.color(c1);
        c2 = Qt.color(c2);
        let r = c1.r * (1-t) + c2.r * t;
        let g = c1.g * (1-t) + c2.g * t;
        let b = c1.b * (1-t) + c2.b * t;
        return Qt.rgba(r,g,b,1);
    }
}

// FromHandle.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" stroke="white" stroke-width="0.2" fill="green" />
<path stroke="white" stroke-width="1" fill="transparent" d="M 13 8 l 8 8 l -8 8"/>
</svg>

// ToHandle.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" stroke="green" stroke-width="0.2" fill="white" />
<path stroke="green" stroke-width="1" fill="transparent" d="M 10 16 l 4 4 l 8 -8"/>
</svg>

You can Try it Online

References:

Upvotes: 0

folibis
folibis

Reputation: 12874

In QML, you can create whatever you want, but you should stick to standard controls because they are understandable and familiar to users. If you still want to create some kind of custom control, you can create something like this:

import QtQuick
import QtQuick.Controls

Window {
    id: window
    visible: true
    width: 600
    height: 400
    title: "Slider test"

    Rectangle {
        id: container
        anchors.centerIn: parent
        width: 300
        height: 50
        radius: 25
        color: "lightgrey"
        border.color: "grey"
        signal myEvent(bool isOn)

        Rectangle {
            id: draggableRect
            width: 50
            height: 48
            radius: 25
            color: "orange"
            border.color: "grey"

            x: 1
            y: 1

            MouseArea {
                id: dragArea
                anchors.fill: parent
                drag.target: parent
                drag.axis: Drag.XAxis
                drag.maximumX: 250
                drag.minimumX: 0

                onReleased: {
                    if (draggableRect.x < 240)
                    {
                        backAnim.running = true;
                    }
                    else
                    {
                        draggableRect.x = 250;
                        container.myEvent(true);
                    }
                }
            }

            PropertyAnimation { id: backAnim; target: draggableRect; property: "x"; to: 1; duration: 300; onFinished: container.myEvent(false); }
        }
    }


    Connections {
        target: container
        function onMyEvent(isOn) { console.log(isOn ? "ON" : "OFF"); }
    }
}

Upvotes: 1

Related Questions