// Pixel3Arch patch to ii's stock Overview.qml (2026-07-07): opening the
// search/overview automatically brings up the on-screen keyboard
// (GlobalStates.oskOpen), and closing it closes the keyboard too.
//
// Also: while the OSK is open, this panel does NOT register with
// GlobalFocusGrab.addDismissable — see the onOskOpenChanged handler below.
// Reason: GlobalFocusGrab's HyprlandFocusGrab (hyprland_focus_grab_v1, a
// real Wayland protocol) clears whenever a tap lands outside its
// whitelisted surfaces, and wvkbd (the real OSK — see OnScreenKeyboard.qml)
// is a separate process that CANNOT be added to that whitelist — Quickshell
// only exposes whitelisting its own in-process windows, and there's no
// hyprctl or other lever either. Two attempts at a "shield window" to fake
// wvkbd's inclusion both failed on real device testing (see
// OnScreenKeyboard.qml's history for what was tried and why). So instead of
// fighting the grab, this panel just stops being dismissable-by-outside-tap
// while the keyboard is up — it still closes normally via Escape, the
// explicit toggle, or picking a search result. Everything else in this file
// is unchanged stock ii — diff against upstream before re-applying this
// patch if ii updates.
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import Qt.labs.synchronizer
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland

Scope {
    id: overviewScope
    property bool dontAutoCancelSearch: false

    PanelWindow {
        id: panelWindow
        property string searchingText: ""
        readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
        property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
        visible: GlobalStates.overviewOpen

        WlrLayershell.namespace: "quickshell:overview"
        // Overlay, not Top: the second-stage edge swipe must bring the
        // overview up over a fullscreen app (fullscreen renders above Top).
        WlrLayershell.layer: WlrLayer.Overlay
        WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
        color: "transparent"

        mask: Region {
            item: GlobalStates.overviewOpen ? columnLayout : null
        }

        anchors {
            top: true
            bottom: true
            left: true
            right: true
        }

        Connections {
            target: GlobalStates
            function onOverviewOpenChanged() {
                if (!GlobalStates.overviewOpen) {
                    searchWidget.disableExpandAnimation();
                    overviewScope.dontAutoCancelSearch = false;
                    GlobalFocusGrab.dismiss();
                    GlobalStates.oskOpen = false;
                } else {
                    if (!overviewScope.dontAutoCancelSearch) {
                        searchWidget.cancelSearch();
                    }
                    // Search wants text input — bring up the on-screen
                    // keyboard automatically (Pixel3Arch, 2026-07-07). See
                    // overlays/quickshell-ii-patches/OnScreenKeyboard.qml.
                    // This also enters oskOpen=true, so the dismissable
                    // registration below (onOskOpenChanged) intentionally
                    // does NOT add panelWindow here — it's added only once
                    // oskOpen settles, to avoid a double-add race.
                    GlobalStates.oskOpen = true;
                }
            }
        }

        // Registering as dismissable is gated on the OSK's state, not
        // just overviewOpen — see the file-header comment for why. This
        // fires both when overview opens (right after oskOpen flips true
        // above) and whenever the OSK is toggled independently while
        // overview stays open (e.g. closing the keyboard without closing
        // search).
        Connections {
            target: GlobalStates
            function onOskOpenChanged() {
                if (!GlobalStates.overviewOpen) return;
                if (GlobalStates.oskOpen) {
                    GlobalFocusGrab.removeDismissable(panelWindow);
                } else {
                    GlobalFocusGrab.addDismissable(panelWindow);
                }
            }
        }

        Connections {
            target: GlobalFocusGrab
            function onDismissed() {
                GlobalStates.overviewOpen = false;
            }
        }
        implicitWidth: columnLayout.implicitWidth
        implicitHeight: columnLayout.implicitHeight

        function setSearchingText(text) {
            searchWidget.setSearchingText(text);
            searchWidget.focusFirstItem();
        }

        // Tap-to-dismiss for the dead space INSIDE the overview page: the
        // window's input mask only covers columnLayout, and the workspace
        // grid's gaps (between workspaces, around the grid) consume nothing —
        // taps there used to be silently ignored, which read as the page
        // being stuck. Declared before (= stacked below) the column, sized to
        // the overview grid only, so the search bar's padding stays inert and
        // every real control (workspaces, windows, search) wins the tap.
        // While the OSK is up this is the ONLY outside-tap dismiss — the
        // focus-grab route is deliberately disabled then (see header).
        MouseArea {
            enabled: GlobalStates.overviewOpen && overviewLoader.active
            x: columnLayout.x + overviewLoader.x
            y: columnLayout.y + overviewLoader.y
            width: overviewLoader.width
            height: overviewLoader.height
            onClicked: GlobalStates.overviewOpen = false
        }

        Column {
            id: columnLayout
            visible: GlobalStates.overviewOpen
            anchors {
                horizontalCenter: parent.horizontalCenter
                top: parent.top
            }
            spacing: -8

            Keys.onPressed: event => {
                if (event.key === Qt.Key_Escape) {
                    GlobalStates.overviewOpen = false;
                }
            }

            SearchWidget {
                id: searchWidget
                anchors.horizontalCenter: parent.horizontalCenter
                Synchronizer on searchingText {
                    property alias source: panelWindow.searchingText
                }
            }

            Loader {
                id: overviewLoader
                anchors.horizontalCenter: parent.horizontalCenter
                active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
                sourceComponent: OverviewWidget {
                    screen: panelWindow.screen
                    visible: (panelWindow.searchingText == "")
                }
            }
        }
    }

    function toggleClipboard() {
        if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
            GlobalStates.overviewOpen = false;
            return;
        }
        overviewScope.dontAutoCancelSearch = true;
        panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
        GlobalStates.overviewOpen = true;
    }

    function toggleEmojis() {
        if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
            GlobalStates.overviewOpen = false;
            return;
        }
        overviewScope.dontAutoCancelSearch = true;
        panelWindow.setSearchingText(Config.options.search.prefix.emojis);
        GlobalStates.overviewOpen = true;
    }

    IpcHandler {
        target: "search"

        function toggle() {
            GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
        }
        function workspacesToggle() {
            GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
        }
        function close() {
            GlobalStates.overviewOpen = false;
        }
        function open() {
            GlobalStates.overviewOpen = true;
        }
        // Device-side acceptance/debug surface: lets deploy checks exercise
        // the exact query path the text field uses without synthesizing keys.
        function setQuery(query: string): void {
            overviewScope.dontAutoCancelSearch = true;
            GlobalStates.overviewOpen = true;
            panelWindow.setSearchingText(query);
        }
        function status(): string {
            return JSON.stringify({
                open: GlobalStates.overviewOpen,
                query: LauncherSearch.query,
                results: LauncherSearch.results.length,
                desktopEntries: DesktopEntries.applications.values.length,
                appSearchEntries: AppSearch.list.length,
                widget: searchWidget.debugState()
            });
        }
        function toggleReleaseInterrupt() {
            GlobalStates.superReleaseMightTrigger = false;
        }
        function clipboardToggle() {
            overviewScope.toggleClipboard();
        }
    }

    GlobalShortcut {
        name: "searchToggle"
        description: "Toggles search on press"

        onPressed: {
            GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
        }
    }
    GlobalShortcut {
        name: "overviewWorkspacesClose"
        description: "Closes overview on press"

        onPressed: {
            GlobalStates.overviewOpen = false;
        }
    }
    GlobalShortcut {
        name: "overviewWorkspacesToggle"
        description: "Toggles overview on press"

        onPressed: {
            GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
        }
    }
    GlobalShortcut {
        name: "searchToggleRelease"
        description: "Toggles search on release"

        onPressed: {
            GlobalStates.superReleaseMightTrigger = true;
        }

        onReleased: {
            if (!GlobalStates.superReleaseMightTrigger) {
                GlobalStates.superReleaseMightTrigger = true;
                return;
            }
            GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
        }
    }
    GlobalShortcut {
        name: "searchToggleReleaseInterrupt"
        description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."

        onPressed: {
            GlobalStates.superReleaseMightTrigger = false;
        }
    }
    GlobalShortcut {
        name: "overviewClipboardToggle"
        description: "Toggle clipboard query on overview widget"

        onPressed: {
            overviewScope.toggleClipboard();
        }
    }

    GlobalShortcut {
        name: "overviewEmojiToggle"
        description: "Toggle emoji query on overview widget"

        onPressed: {
            overviewScope.toggleEmojis();
        }
    }
}
