Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/PopupButton.qml

Raw
import QtQuick
import QtQuick.Templates as T
import Quickshell.Widgets

FocusScope {
    id: root

    required property Theme theme
    property string text: ""
    property string icon: ""
    property url iconSource: ""
    property string role: "normal"
    property bool showText: true
    property string accessibleName: text
    property string tooltip: accessibleName
    property bool checked: false
    property int iconSize: 14
    property bool primary: role === "primary"
    property bool interactive: true
    property bool hoverable: interactive
    property alias radius: background.radius
    property alias color: background.color
    property alias border: background.border
    signal clicked

    property bool destructive: role === "destructive"
    readonly property color stateColor: destructive ? theme.error : (primary || checked ? theme.ui.accent : theme.ui.normal)
    readonly property color foregroundColor: primary ? theme.content.backdrop : (destructive ? theme.error : theme.content.normal)
    readonly property bool pressed: activation.down || activation.enterHeld
    readonly property bool hovered: hover.hovered
    readonly property bool visualFocus: activation.visualFocus
    readonly property bool controlFocused: activation.activeFocus
    readonly property bool tooltipVisible: tip.visible

    implicitWidth: Math.min(320, (label.visible ? label.implicitWidth : 0) + (root.iconSource.toString().length > 0 ? iconSize : (glyph.visible ? glyph.implicitWidth : 0)) + (label.visible && (glyph.visible || root.iconSource.toString().length > 0) ? labelRow.spacing : 0) + 24)
    implicitHeight: 34
    activeFocusOnTab: interactive && enabled
    opacity: enabled ? 1 : 0.48
    Accessible.role: Accessible.Button
    Accessible.name: accessibleName
    Accessible.checked: checked
    Accessible.focusable: interactive && enabled
    Accessible.onPressAction: if (interactive && enabled)
        activation.click()

    Rectangle {
        id: background

        anchors.fill: parent
        radius: 8
        color: root.theme.withAlpha(root.stateColor, root.primary ? (root.pressed ? 0.7 : root.hovered ? 0.88 : 1) : (root.pressed ? 0.24 : (root.hovered ? 0.14 : (root.checked ? 0.16 : (root.role === "ghost" ? 0 : 0.06)))))
        border.width: root.controlFocused ? 2 : (root.checked ? 1 : 0)
        border.color: root.controlFocused ? root.theme.content.focus : root.theme.withAlpha(root.stateColor, 0.65)

        Behavior on color {
            ColorAnimation {
                duration: root.theme.lowPowerMode ? 0 : 90
            }
        }
    }

    // Native pointer, Space activation and cancellation; the wrapper retains
    // the caller's string icon, checked binding and Rectangle styling API.
    T.Button {
        id: activation

        property bool enterHeld: false

        anchors.fill: parent
        enabled: root.interactive && root.enabled
        focus: true
        focusPolicy: Qt.ClickFocus
        autoRepeat: false
        Accessible.ignored: true
        onClicked: root.clicked()
        onActiveFocusChanged: if (!activeFocus)
            enterHeld = false
        onEnabledChanged: if (!enabled)
            enterHeld = false
        Keys.onPressed: event => {
            const enter = event.key === Qt.Key_Return || event.key === Qt.Key_Enter;
            if (enter || event.key === Qt.Key_Space && event.isAutoRepeat) {
                event.accepted = true;
                if (enter && !event.isAutoRepeat)
                    enterHeld = true;
            }
        }
        Keys.onReleased: event => {
            const enter = event.key === Qt.Key_Return || event.key === Qt.Key_Enter;
            if (enter || event.key === Qt.Key_Space && event.isAutoRepeat) {
                event.accepted = true;
                if (enter && !event.isAutoRepeat) {
                    const activate = enterHeld && enabled;
                    enterHeld = false;
                    if (activate)
                        root.clicked();
                }
            }
        }
    }

    Item {
        anchors.fill: parent
        anchors.margins: 4
        clip: true

        Row {
            id: labelRow

            anchors.centerIn: parent
            spacing: 7

            IconImage {
                visible: root.iconSource.toString().length > 0
                anchors.verticalCenter: parent.verticalCenter
                width: root.iconSize
                height: root.iconSize
                source: root.iconSource
                Accessible.ignored: true
            }

            Text {
                id: glyph

                visible: root.icon.length > 0 && root.iconSource.toString().length === 0
                anchors.verticalCenter: parent.verticalCenter
                color: root.foregroundColor
                font.family: "Symbols Nerd Font Mono"
                font.pixelSize: root.iconSize
                text: root.icon
                Accessible.ignored: true
            }

            NuguText {
                id: label

                visible: root.showText && root.text.length > 0
                anchors.verticalCenter: parent.verticalCenter
                width: Math.min(implicitWidth, Math.max(0, root.width - 24 - (root.iconSource.toString().length > 0 ? root.iconSize + labelRow.spacing : (glyph.visible ? glyph.implicitWidth + labelRow.spacing : 0))))
                theme: root.theme
                color: root.foregroundColor
                text: root.text
                Accessible.ignored: true
            }
        }
    }

    HoverHandler {
        id: hover
        enabled: root.hoverable
        cursorShape: root.enabled && root.interactive ? Qt.PointingHandCursor : Qt.ArrowCursor
    }

    PopupToolTip {
        id: tip

        theme: root.theme
        text: root.tooltip
        visible: root.visible && text.length > 0 && !root.pressed && (root.visualFocus || (root.hovered && !root.activeFocus))
    }
}