pragma Singleton

// Pointer size and text scale.
//
// These are the two settings that must agree across three consumers that do not
// share a configuration system: the compositor draws the cursor, GTK
// applications read gsettings, and the shell renders its own text. Panama's
// store is the source of truth, and this pushes the value out to the other two
// so they cannot disagree.
//
//   pointer size  -> gsettings (GTK) + `hyprctl setcursor` (compositor)
//   text scale    -> gsettings (GTK)
//
// The shell's own font size is not scaled here. Theme.qml's sizes are part of
// the design rather than a user preference, and scaling them at runtime would
// reflow every panel against layouts that were tuned at the design size. Text
// scale therefore affects applications, which is where it matters, and the
// page says so rather than implying it does more.

import Quickshell
import Quickshell.Io
import QtQuick
import qs.config

Singleton {
    id: root

    property string lastError: ""

    readonly property bool busy: runner.running || root.pending.length > 0

    readonly property string cursorTheme: DesktopPreferences.get("cursorTheme")
    readonly property int cursorSize: DesktopPreferences.get("cursorSize")
    readonly property real textScale: DesktopPreferences.get("textScale")

    // A short queue, because applying one setting takes several commands and
    // Process runs one at a time.
    property var pending: []

    Process {
        id: runner
        onExited: (exitCode, exitStatus) => {
            if (exitCode !== 0)
                root.lastError = "That accessibility setting could not be applied.";
            root.drain();
        }
    }

    function drain(): void {
        if (runner.running || root.pending.length === 0)
            return;
        const next = root.pending[0];
        root.pending = root.pending.slice(1);
        runner.exec(next);
    }

    function enqueue(commands: var): void {
        root.pending = root.pending.concat(commands);
        root.drain();
    }

    Component.onCompleted: {
        settle.restart();
    }

    // Push the stored values outward once at startup, so a value changed in a
    // previous session is in effect in this one even though gsettings and the
    // compositor do not read Panama's store.
    Timer {
        id: settle
        interval: 1200
        onTriggered: root.applyAll()
    }

    Connections {
        target: DesktopPreferences
        function onRevisionChanged(): void { coalesce.restart(); }
    }

    Timer {
        id: coalesce
        interval: 250
        onTriggered: root.applyAll()
    }

    function applyAll(): void {
        root.lastError = "";
        const size = String(root.cursorSize);
        const commands = [
            ["gsettings", "set", "org.gnome.desktop.interface", "cursor-size", size],
            ["gsettings", "set", "org.gnome.desktop.interface", "text-scaling-factor", String(root.textScale)]
        ];
        commands.push(["hyprctl", "setcursor", root.cursorTheme, size]);
        root.enqueue(commands);
    }
}
