// One color of a theme, with the three honest ways to change it.
//
// The swatch is the value, large enough to judge against the card it sits on.
// Under it: the hex, typed and committed on Enter or when focus leaves --
// never per keystroke, because a half-typed "#82a" is a real color and would
// repaint the whole desktop on the way to the one you meant. A rejected value
// marks the field and snaps back rather than quietly keeping bad text.
//
// The wheel and the eyedropper are the other two, and all three are keyboard
// reachable: this component is the reason the old accent editor could not be
// operated without a mouse.

import QtQuick
import qs.config

Rectangle {
    id: root

    property string role: ""
    property string swatchColor: "#000000"
    property bool picking: false

    // A hex the caller accepted. Rejected values never reach here.
    signal committed(value: string)
    signal wheelRequested
    signal pickRequested

    // Set while the field holds something that is not a #rrggbb.
    property bool invalid: false

    implicitHeight: body.implicitHeight + 22
    radius: Theme.cardRadius
    color: Theme.alpha(Theme.fg, 0.045)
    border.width: 1
    border.color: Theme.alpha(Theme.fg, 0.09)

    function normalized(value: string): string {
        const text = String(value).trim().toLowerCase();
        const prefixed = text.startsWith("#") ? text : "#" + text;
        return /^#[0-9a-f]{6}$/.test(prefixed) ? prefixed : "";
    }

    function commit(): void {
        const hex = root.normalized(hexInput.text);
        if (hex === "") {
            root.invalid = true;
            return;
        }
        root.invalid = false;
        hexInput.text = hex;
        if (hex !== String(root.swatchColor).toLowerCase())
            root.committed(hex);
    }

    // An accepted change elsewhere -- a theme click, the eyedropper, the HSV
    // sliders -- replaces what is shown, unless it would yank the field out
    // from under someone mid-edit.
    onSwatchColorChanged: if (!hexInput.activeFocus) {
        hexInput.text = String(root.swatchColor).toLowerCase();
        root.invalid = false;
    }

    // The wheel and eyedropper buttons: square, glyph-only, and each its own
    // focus stop with a spoken name, because a 30px square with a ⌖ in it is
    // otherwise unidentifiable to anything but sight.
    component IconButton: Rectangle {
        id: button

        property string glyph: ""
        property string title: ""
        property bool enabled: true

        signal triggered

        width: 30
        height: 30
        radius: 8
        opacity: button.enabled ? 1 : 0.45
        color: hover.hovered && button.enabled
            ? Theme.alpha(Theme.fg, 0.16) : Theme.alpha(Theme.fg, 0.08)
        border.width: button.activeFocus ? 2 : 1
        border.color: button.activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.1)
        activeFocusOnTab: button.enabled

        Accessible.role: Accessible.Button
        Accessible.name: button.title
        Accessible.focusable: true
        Accessible.focused: button.activeFocus

        Keys.onReturnPressed: if (button.enabled) button.triggered()
        Keys.onSpacePressed: if (button.enabled) button.triggered()

        Text {
            anchors.centerIn: parent
            text: button.glyph
            color: Theme.fg
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSize
        }

        HoverHandler {
            id: hover
            enabled: button.enabled
            cursorShape: Qt.PointingHandCursor
        }

        TapHandler {
            enabled: button.enabled
            onTapped: button.triggered()
        }
    }

    Column {
        id: body

        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: parent.top
        anchors.margins: 11
        spacing: 7

        Text {
            text: root.role
            color: Theme.fgMuted
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSizeSmall
            font.weight: Font.DemiBold
            font.capitalization: Font.AllUppercase
            font.letterSpacing: 0.6
        }

        Rectangle {
            width: parent.width
            height: 44
            radius: 9
            color: root.swatchColor
            border.width: 1
            border.color: Theme.alpha(Theme.fg, 0.18)
        }

        Item {
            width: parent.width
            height: 30

            Rectangle {
                id: hexField

                anchors.left: parent.left
                anchors.right: buttons.left
                anchors.rightMargin: 6
                anchors.verticalCenter: parent.verticalCenter
                height: 30
                radius: 8
                color: Theme.alpha(Theme.fg, 0.07)
                border.width: hexInput.activeFocus || root.invalid ? 2 : 1
                border.color: root.invalid
                    ? Theme.danger
                    : (hexInput.activeFocus
                        ? Theme.alpha(Theme.accent, 0.55) : Theme.alpha(Theme.fg, 0.12))

                TextInput {
                    id: hexInput

                    anchors.fill: parent
                    anchors.leftMargin: 9
                    anchors.rightMargin: 9
                    activeFocusOnTab: true
                    verticalAlignment: TextInput.AlignVCenter
                    maximumLength: 7
                    text: String(root.swatchColor).toLowerCase()
                    color: root.invalid ? Theme.danger : Theme.fg
                    selectionColor: Theme.alpha(Theme.accent, 0.4)
                    selectedTextColor: Theme.fg
                    font.family: Theme.fontMono
                    font.pixelSize: Theme.fontSizeSmall
                    clip: true

                    Accessible.role: Accessible.EditableText
                    Accessible.name: root.role + " colour, hex"

                    onTextEdited: root.invalid = false
                    onAccepted: root.commit()

                    onActiveFocusChanged: {
                        if (hexInput.activeFocus)
                            return;
                        // Leaving with something unusable in the field reverts
                        // rather than leaving a lie on screen.
                        if (root.normalized(hexInput.text) === "") {
                            root.invalid = false;
                            hexInput.text = String(root.swatchColor).toLowerCase();
                            return;
                        }
                        root.commit();
                    }
                }
            }

            Row {
                id: buttons

                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                spacing: 6

                IconButton {
                    glyph: "◐"
                    title: "Open the colour wheel for " + root.role
                    onTriggered: root.wheelRequested()
                }

                IconButton {
                    glyph: "⌖"
                    title: "Pick " + root.role + " from the screen"
                    enabled: !root.picking
                    onTriggered: root.pickRequested()
                }
            }
        }
    }
}
