Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/Screenshots.qml

Raw
import QtQuick

Item {
    id: root

    property Niri niriService
    property Osd osdService
    property string lastColor: ""
    property var recent: []
    property var pending: []
    property int requestSerial: 0
    property var lastAction: ({})
    property var selection: ({
            "model": "normalized-cross-screen-rect",
            "phase": "idle",
            "press": {
                "x": 0,
                "y": 0
            },
            "current": {
                "x": 0,
                "y": 0
            },
            "rect": {
                "x": 0,
                "y": 0,
                "width": 0,
                "height": 0
            },
            "cursor": "default",
            "completionFlash": false
        })
    property var lastOsd: ({})
    readonly property bool nativeActionRoute: !!niriService && !!niriService.available
    readonly property var nativeActions: ["screenshot-screen", "screenshot-window", "screenshot", "pick-color"]
    readonly property var boundedActions: ["focused-screen", "focused-window", "interactive", "select-region", "pick-color", "copy", "save", "open-record", "clear"]
    readonly property var toolbarActions: ["copy", "save", "cancel", "color"]

    function rectFromPoints(start, end) {
        const x = Math.min(start.x, end.x);
        const y = Math.min(start.y, end.y);
        return {
            "x": x,
            "y": y,
            "width": Math.abs(end.x - start.x),
            "height": Math.abs(end.y - start.y)
        };
    }

    function record(id, kind, path) {
        const next = [
            {
                "id": id,
                "kind": kind,
                "path": path,
                "source": "niri-native",
                "saved": true
            }
        ];
        for (let i = 0; i < recent.length; i++) {
            if (recent[i].id !== id)
                next.push(recent[i]);
        }
        recent = next.slice(0, 12);
    }

    // Niri 26.04, revision 8ed0da44d974c32c6877d2f4630c314da0717ecb:
    // https://github.com/niri-wm/niri/blob/8ed0da44d974c32c6877d2f4630c314da0717ecb/src/niri.rs#L5643-L5745
    // Only a non-null completion path proves a save. Null covers clipboard-only,
    // failed writes, and paths not representable as UTF-8. Encoding/render failures
    // and native cancellation emit no completion. Submission is not save success.
    // There is no request ID or cancellation event. Pending means unconfirmed,
    // not necessarily still running, and clear() forgets it without cancelling Niri.
    // Exact paths are the only correlation: same-path requests consume one match
    // FIFO, but concurrent/external saves to that path cannot be distinguished.
    function acceptCaptured(path) {
        if (typeof path !== "string" || path.length === 0)
            return;

        const index = pending.findIndex(request => request.path === path);
        if (index < 0)
            return;

        const request = pending[index];
        pending = pending.filter((_, i) => i !== index);
        record(request.id, request.kind, path);
        if (lastAction.id === request.id) {
            lastAction = Object.assign({}, lastAction, {
                "status": "saved",
                "saved": true,
                "completed": true
            });
        }
        if (selection.requestId === request.id) {
            selection = Object.assign({}, selection, {
                "phase": "completed",
                "cursor": "default",
                "completionFlash": true
            });
        }
    }

    function cornerHandles(rect) {
        const size = rect.width > 0 && rect.height > 0 ? 14 : 0;
        return [
            {
                "id": "top-left",
                "x": rect.x,
                "y": rect.y,
                "size": size,
                "role": "ui.accent"
            },
            {
                "id": "top-right",
                "x": rect.x + rect.width,
                "y": rect.y,
                "size": size,
                "role": "ui.accent"
            },
            {
                "id": "bottom-left",
                "x": rect.x,
                "y": rect.y + rect.height,
                "size": size,
                "role": "ui.accent"
            },
            {
                "id": "bottom-right",
                "x": rect.x + rect.width,
                "y": rect.y + rect.height,
                "size": size,
                "role": "ui.accent"
            }
        ];
    }

    function selectionVisuals(selectionState) {
        const rect = selectionState.rect || {
            "x": 0,
            "y": 0,
            "width": 0,
            "height": 0
        };
        const active = selectionState.phase === "dragging" || selectionState.phase === "pending" || selectionState.phase === "completed";
        return {
            "source": "Nuguland screenSnip plus Nuguland Geom.qml",
            "parts": ["dimmed-mask", "clear-cutout", "corner-handles", "dimensions", "toolbar-actions"],
            "dimmedMask": {
                "kind": "dimmed-mask",
                "visible": active,
                "opacity": active ? 0.42 : 0,
                "role": "ui.backdrop"
            },
            "clearCutout": {
                "kind": "clear-cutout",
                "rect": rect
            },
            "cornerHandles": {
                "kind": "corner-handles",
                "handles": cornerHandles(rect)
            },
            "dimensions": {
                "kind": "dimensions",
                "text": `${Math.round(rect.width)}×${Math.round(rect.height)}`,
                "width": Math.round(rect.width),
                "height": Math.round(rect.height)
            },
            "toolbarActions": {
                "kind": "toolbar-actions",
                "actions": toolbarActions,
                "anchor": rect.height >= 48 ? "selection-top" : "screen-top"
            },
            "nugulandCornerAccents": {
                "bounded": true,
                "freePhysicsLoop": false,
                "anchors": cornerHandles(rect).map(handle => {
                    return ({
                            "id": handle.id,
                            "x": handle.x,
                            "y": handle.y
                        });
                })
            }
        };
    }

    function screenshotsPanelContract() {
        return {
            "source": "Nuguland AreaPicker.qml plus Nuguland RegionSelector.qml WRegionSelectionPanel.qml",
            "ownedBy": "PopoutHost.qml inline ScreenshotsPanel",
            "niriCapturePanel": true,
            "captureActionGrid": true,
            "regionSelectionPreview": true,
            "recentCaptureRows": true,
            "colorPickSwatch": true,
            "toolbarActionRail": true,
            "boundedCornerAccents": true,
            "serviceActionsOnly": true,
            "captureBackend": "niri-native",
            "panelActions": ["focused-screen", "focused-window", "select-region", "pick-color"],
            "selectionParts": ["dimmed-mask", "clear-cutout", "corner-handles", "dimensions", "toolbar-actions"]
        };
    }

    function currentState() {
        return {
            "backend": "Screenshots.qml",
            "usesNiriNative": true,
            "nativeActions": nativeActions,
            "nativeActionRoute": nativeActionRoute,
            "helperProcesses": [],
            "serviceOwnedActions": true,
            "boundedActions": boundedActions,
            "screenshotsPanelContract": screenshotsPanelContract(),
            "selection": selection,
            "selectionVisuals": selectionVisuals(selection),
            "recent": recent,
            "recentCount": recent.length,
            "pending": pending,
            "pendingCount": pending.length,
            "lastColor": lastColor,
            "lastAction": lastAction,
            "osd": lastOsd
        };
    }

    function stateJson() {
        return JSON.stringify(currentState());
    }

    function beginSelection(x, y) {
        const point = {
            "x": x,
            "y": y
        };
        selection = {
            "model": "normalized-cross-screen-rect",
            "phase": "dragging",
            "press": point,
            "current": point,
            "rect": {
                "x": x,
                "y": y,
                "width": 0,
                "height": 0
            },
            "cursor": "crosshair",
            "completionFlash": false
        };
        lastAction = {
            "kind": "select-region-begin",
            "point": point
        };
        return stateJson();
    }

    function updateSelection(x, y) {
        const start = selection.press || {
            "x": 0,
            "y": 0
        };
        const current = {
            "x": x,
            "y": y
        };
        selection = Object.assign({}, selection, {
            "phase": "dragging",
            "current": current,
            "rect": rectFromPoints(start, current),
            "cursor": "crosshair",
            "completionFlash": false,
            "requestId": null
        });
        lastAction = {
            "kind": "select-region-update",
            "point": current
        };
        return stateJson();
    }

    function completeSelection(path) {
        return capture("select-region", path);
    }

    function cancelSelection() {
        selection = Object.assign({}, selection, {
            "phase": "idle",
            "cursor": "default",
            "completionFlash": false,
            "requestId": null
        });
        // This only dismisses our preview, not Niri's native selection UI.
        lastAction = {
            "kind": "cancel"
        };
        return stateJson();
    }

    function capture(kind, path) {
        const actionKind = String(kind);
        const target = typeof path === "string" ? path : "";
        const interactive = actionKind === "interactive" || actionKind === "select-region";
        const nativeAction = actionKind === "focused-screen" ? "screenshot-screen" : actionKind === "focused-window" ? "screenshot-window" : interactive ? "screenshot" : "";
        const status = !nativeAction ? "unsupported" : !target ? "invalid-path" : !nativeActionRoute ? "unavailable" : "pending";
        // These IDs belong to local history only; Niri never receives them.
        const id = status === "pending" ? `capture-${actionKind}-${++requestSerial}` : null;
        lastAction = {
            "id": id,
            "kind": actionKind,
            "nativeAction": nativeAction,
            "path": target,
            "nativeActionRoute": status === "pending",
            "interactive": interactive,
            "status": status,
            "saved": false,
            "completed": false
        };
        if (actionKind === "select-region") {
            selection = Object.assign({}, selection, {
                "phase": status === "pending" ? "pending" : "idle",
                "cursor": "default",
                "completionFlash": false,
                "requestId": id
            });
        }
        if (status === "pending") {
            pending = pending.concat([lastAction]);
            if (actionKind === "focused-screen")
                niriService.screenshotScreen(target);
            else if (actionKind === "focused-window")
                niriService.screenshotWindow(target);
            else
                niriService.screenshot(target);
        }
        return stateJson();
    }

    function acceptPickedColor(color) {
        const value = String(color || "").toLowerCase();
        if (!/^#[0-9a-f]{6}$/.test(value))
            return;

        lastColor = value;
        lastAction = Object.assign({}, lastAction, {
            "color": lastColor,
            "completed": true
        });
        if (osdService) {
            try {
                lastOsd = JSON.parse(osdService.trigger("color", lastColor));
            } catch (error) {
                lastOsd = {
                    "event": "color",
                    "value": lastColor
                };
            }
        } else {
            lastOsd = {
                "event": "color",
                "value": lastColor
            };
        }
    }

    function pickColor() {
        lastColor = "";
        if (nativeActionRoute)
            niriService.pickColor();

        lastAction = {
            "kind": "pick-color",
            "nativeAction": "pick-color",
            "color": lastColor,
            "nativeActionRoute": nativeActionRoute,
            "interactive": true,
            "completed": false
        };
        lastOsd = {
            "event": "color",
            "value": "picking"
        };
        return stateJson();
    }

    function recordAction(kind, id) {
        lastAction = {
            "kind": String(kind),
            "id": String(id),
            "nativeActionRoute": false
        };
        return stateJson();
    }

    function clear() {
        recent = [];
        pending = [];
        if (selection.phase === "pending")
            cancelSelection();
        lastAction = {
            "kind": "clear"
        };
        return stateJson();
    }

    Connections {
        function onScreenshotCaptured(path) {
            root.acceptCaptured(path);
        }

        function onLastPickedColorChanged() {
            root.acceptPickedColor(root.niriService.lastPickedColor);
        }

        target: root.niriService
    }
}