pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Services
import qs.Widgets

Item {
    id: root
    readonly property var log: Log.scoped("DankModalConnected")

    property var modalHandle: root
    property string layerNamespace: "dms:modal"
    property alias content: contentLoader.sourceComponent
    property alias contentLoader: contentLoader
    property Item directContent: null
    property real modalWidth: 400
    property real modalHeight: 300
    property var targetScreen
    readonly property var effectiveScreen: contentWindow.screen ?? targetScreen
    readonly property real screenWidth: effectiveScreen?.width ?? 1920
    readonly property real screenHeight: effectiveScreen?.height ?? 1080
    readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
    property bool showBackground: true
    property real backgroundOpacity: 0.5
    property string positioning: "center"
    property point customPosition: Qt.point(0, 0)
    property bool closeOnEscapeKey: true
    property bool closeOnBackgroundClick: true
    property string animationType: "scale"

    property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide

    readonly property bool frameConnectedMode: FrameTransitionState.effectiveFrameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)

    readonly property string resolvedConnectedBarSide: frameConnectedMode ? preferredConnectedBarSide : ""

    readonly property bool frameOwnsConnectedChrome: frameConnectedMode && resolvedConnectedBarSide !== "" && !allowStacking && CompositorService.usesConnectedFrameChromeForScreen(effectiveScreen)

    function _dockOccupiesSide(side) {
        if (!SettingsData.showDock)
            return false;
        switch (side) {
        case "top":
            return SettingsData.dockPosition === SettingsData.Position.Top;
        case "bottom":
            return SettingsData.dockPosition === SettingsData.Position.Bottom;
        case "left":
            return SettingsData.dockPosition === SettingsData.Position.Left;
        case "right":
            return SettingsData.dockPosition === SettingsData.Position.Right;
        }
        return false;
    }

    readonly property bool _dockBlocksEmergence: frameOwnsConnectedChrome && _dockOccupiesSide(resolvedConnectedBarSide)

    readonly property bool connectedMotionParity: frameOwnsConnectedChrome
    property int animationDuration: connectedMotionParity ? Theme.popoutAnimationDuration : Theme.modalAnimationDuration
    property real animationScaleCollapsed: Theme.effectScaleCollapsed
    property real animationOffset: Theme.effectAnimOffset
    property list<real> animationEnterCurve: connectedMotionParity ? Theme.variantPopoutEnterCurve : Theme.variantModalEnterCurve
    property list<real> animationExitCurve: connectedMotionParity ? Theme.variantPopoutExitCurve : Theme.variantModalExitCurve
    property color backgroundColor: Theme.surfaceContainer
    property color borderColor: Theme.outlineMedium
    property real borderWidth: 0
    property real cornerRadius: Theme.cornerRadius
    readonly property bool connectedSurfaceOverride: frameOwnsConnectedChrome
    readonly property color effectiveBackgroundColor: connectedSurfaceOverride ? Theme.connectedSurfaceColor : backgroundColor
    readonly property color effectiveBorderColor: connectedSurfaceOverride ? Theme.withAlpha(borderColor, 0) : borderColor
    readonly property real effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
    readonly property real effectiveCornerRadius: connectedSurfaceOverride ? Theme.connectedSurfaceRadius : cornerRadius
    readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
    property bool enableShadow: true
    property alias modalFocusScope: focusScope
    property bool shouldBeVisible: false
    property bool shouldHaveFocus: shouldBeVisible
    property bool allowFocusOverride: false
    property bool allowStacking: false
    property bool keepContentLoaded: false
    property bool keepPopoutsOpen: false
    property var customKeyboardFocus: null
    property bool useOverlayLayer: false
    property real frozenMotionOffsetX: 0
    property real frozenMotionOffsetY: 0
    readonly property alias contentWindow: contentWindow
    readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
    readonly property bool useBackground: false

    signal opened
    signal dialogClosed
    signal backgroundClicked

    Timer {
        id: _syncTimer
        interval: 0
        onTriggered: root._flushSync()
    }

    property bool animationsEnabled: true

    property bool _fullSyncPending: false

    function _currentScreenName() {
        return effectiveScreen ? effectiveScreen.name : "";
    }

    ConnectedModalChrome {
        id: modalChrome
        modalHandle: root.modalHandle
        claimPrefix: root.layerNamespace + ":modal"
        surfaceKind: "modal"
        screenName: root._currentScreenName()
        enabled: root.frameOwnsConnectedChrome
        active: root.shouldBeVisible
        presented: root.shouldBeVisible || contentWindow.visible
        dockBlocked: root._dockBlocksEmergence
        dockSide: root.resolvedConnectedBarSide
        onRecoveryRequested: root._queueFullSync()
    }

    function _publishModalChromeState() {
        const presented = shouldBeVisible || contentWindow.visible;
        const phase = !presented ? "hidden" : (!shouldBeVisible && contentWindow.visible ? "closing" : (!contentWindow.visible ? "opening" : "open"));
        const bodyRect = {
            "x": alignedX,
            "y": alignedY,
            "width": alignedWidth,
            "height": alignedHeight
        };
        const animationOffset = {
            "x": modalContainer ? modalContainer.animX : 0,
            "y": modalContainer ? modalContainer.animY : 0
        };
        const state = {
            "kind": "modal",
            "screenName": root._currentScreenName(),
            "phase": phase,
            "visible": presented,
            "presented": presented,
            "barSide": resolvedConnectedBarSide,
            "bodyRect": bodyRect,
            "animationOffset": animationOffset,
            "scale": 1,
            "opacity": Theme.connectedSurfaceColor.a,
            "bodyX": alignedX,
            "bodyY": alignedY,
            "bodyW": alignedWidth,
            "bodyH": alignedHeight,
            "animX": animationOffset.x,
            "animY": animationOffset.y,
            "omitStartConnector": false,
            "omitEndConnector": false,
            "dockRetractSide": root._dockBlocksEmergence ? resolvedConnectedBarSide : ""
        };
        return modalChrome.publish(state);
    }

    function _syncModalChromeState() {
        _publishModalChromeState();
    }

    function _queueFullSync() {
        _fullSyncPending = true;
        if (!_syncTimer.running)
            _syncTimer.restart();
    }
    function _flushSync() {
        if (!_fullSyncPending)
            return;
        _fullSyncPending = false;
        _syncModalChromeState();
    }

    function _syncModalAnim() {
        if (!frameOwnsConnectedChrome)
            return;
        if (!modalContainer)
            return;
        modalChrome.updateAnim(modalContainer.animX, modalContainer.animY);
    }

    function _syncModalBody() {
        if (!frameOwnsConnectedChrome)
            return;
        modalChrome.updateBody(alignedX, alignedY, alignedWidth, alignedHeight);
    }

    function _releaseModalChrome() {
        modalChrome.release();
    }

    onFrameOwnsConnectedChromeChanged: _syncModalChromeState()
    onResolvedConnectedBarSideChanged: _queueFullSync()
    onShouldBeVisibleChanged: _queueFullSync()
    // Low resource scalar writes: publish synchronously to stay in the same frame
    onAlignedXChanged: _syncModalBody()
    onAlignedYChanged: _syncModalBody()
    onAlignedWidthChanged: _syncModalBody()
    onAlignedHeightChanged: _syncModalBody()

    Connections {
        target: contentWindow
        function onVisibleChanged() {
            if (contentWindow.visible)
                root._syncModalChromeState();
            else
                root._releaseModalChrome();
        }
    }

    function open() {
        closeTimer.stop();
        animationsEnabled = false;
        frozenMotionOffsetX = modalContainer ? modalContainer.offsetX : 0;
        frozenMotionOffsetY = modalContainer ? modalContainer.offsetY : animationOffset;

        const focusedScreen = root.targetScreen ?? CompositorService.getFocusedScreen();
        if (focusedScreen) {
            contentWindow.screen = focusedScreen;
        }

        ModalManager.openModal(modalHandle);
        if (Theme.isDirectionalEffect || root.useBackground) {
            contentWindow.visible = true;
        }

        Qt.callLater(() => {
            animationsEnabled = true;
            shouldBeVisible = true;
            if (!contentWindow.visible)
                contentWindow.visible = true;
            opened();
            shouldHaveFocus = false;
            Qt.callLater(() => shouldHaveFocus = Qt.binding(() => shouldBeVisible));
        });
    }

    function close() {
        if (modalContainer) {
            frozenMotionOffsetX = modalContainer.offsetX;
            frozenMotionOffsetY = modalContainer.offsetY;
        }
        shouldBeVisible = false;
        shouldHaveFocus = false;
        ModalManager.closeModal(modalHandle);
        closeTimer.restart();
    }

    function instantClose() {
        animationsEnabled = false;
        shouldBeVisible = false;
        shouldHaveFocus = false;
        ModalManager.closeModal(modalHandle);
        closeTimer.stop();
        contentWindow.visible = false;
        dialogClosed();
        Qt.callLater(() => animationsEnabled = true);
    }

    function toggle() {
        shouldBeVisible ? close() : open();
    }

    Connections {
        target: ModalManager
        function onCloseAllModalsExcept(excludedModal) {
            if (excludedModal !== modalHandle && !allowStacking && shouldBeVisible)
                close();
        }
    }

    Connections {
        target: Quickshell
        function onScreensChanged() {
            if (!contentWindow.screen)
                return;
            const currentScreenName = contentWindow.screen.name;
            let screenStillExists = false;
            for (let i = 0; i < Quickshell.screens.length; i++) {
                if (Quickshell.screens[i].name === currentScreenName) {
                    screenStillExists = true;
                    break;
                }
            }
            if (screenStillExists) {
                if (root.shouldBeVisible)
                    root._queueFullSync();
                return;
            }
            root._releaseModalChrome();
            const newScreen = CompositorService.getFocusedScreen();
            if (newScreen) {
                contentWindow.screen = newScreen;
            }
        }
    }

    Timer {
        id: closeTimer
        interval: Theme.variantCloseInterval(animationDuration)
        onTriggered: {
            if (shouldBeVisible)
                return;
            contentWindow.visible = false;
            dialogClosed();
        }
    }

    readonly property var shadowLevel: Theme.elevationLevel3
    readonly property real shadowFallbackOffset: 6
    readonly property real alignedWidth: Theme.px(modalWidth, dpr)
    readonly property real alignedHeight: Theme.px(modalHeight, dpr)

    readonly property real _connectedAlignedX: {
        switch (resolvedConnectedBarSide) {
        case "top":
        case "bottom":
            {
                const insetL = SettingsData.frameEdgeInsetForSide(effectiveScreen, "left");
                const insetR = SettingsData.frameEdgeInsetForSide(effectiveScreen, "right");
                const usable = Math.max(0, screenWidth - insetL - insetR);
                return insetL + Math.max(0, (usable - alignedWidth) / 2);
            }
        case "left":
            return SettingsData.frameEdgeInsetForSide(effectiveScreen, "left");
        case "right":
            return screenWidth - alignedWidth - SettingsData.frameEdgeInsetForSide(effectiveScreen, "right");
        }
        return 0;
    }

    readonly property real _connectedAlignedY: {
        switch (resolvedConnectedBarSide) {
        case "top":
            return SettingsData.frameEdgeInsetForSide(effectiveScreen, "top");
        case "bottom":
            return screenHeight - alignedHeight - SettingsData.frameEdgeInsetForSide(effectiveScreen, "bottom");
        case "left":
        case "right":
            {
                const insetT = SettingsData.frameEdgeInsetForSide(effectiveScreen, "top");
                const insetB = SettingsData.frameEdgeInsetForSide(effectiveScreen, "bottom");
                const usable = Math.max(0, screenHeight - insetT - insetB);
                return insetT + Math.max(0, (usable - alignedHeight) / 2);
            }
        }
        return 0;
    }

    readonly property real alignedX: Theme.snap(frameOwnsConnectedChrome ? _connectedAlignedX : (() => {
            switch (positioning) {
            case "center":
                return (screenWidth - alignedWidth) / 2;
            case "top-right":
                return Math.max(Theme.spacingL, screenWidth - alignedWidth - Theme.spacingL);
            case "custom":
                return customPosition.x;
            default:
                return 0;
            }
        })(), dpr)

    readonly property real alignedY: Theme.snap(frameOwnsConnectedChrome ? _connectedAlignedY : (() => {
            switch (positioning) {
            case "center":
                return (screenHeight - alignedHeight) / 2;
            case "top-right":
                return Theme.barHeight + Theme.spacingXS;
            case "custom":
                return customPosition.y;
            default:
                return 0;
            }
        })(), dpr)

    PanelWindow {
        id: contentWindow
        visible: false
        color: "transparent"

        WindowBlur {
            targetWindow: contentWindow
            blurEnabled: root.effectiveBlurEnabled && !root.frameOwnsConnectedChrome
            readonly property real s: Math.min(1, modalContainer.scaleValue)
            blurX: connectedReveal.x + modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr)
            blurY: connectedReveal.y + modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr)
            blurWidth: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0
            blurHeight: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0
            blurRadius: root.effectiveCornerRadius
        }

        WlrLayershell.namespace: root.layerNamespace
        WlrLayershell.layer: root.useOverlayLayer ? WlrLayer.Overlay : LayerShell.fromEnv("DMS_MODAL_LAYER", WlrLayer.Top, {
            "allow": ["top", "overlay"],
            "invalidLayer": WlrLayer.Top,
            "label": "modals",
            "error": true
        })
        WlrLayershell.exclusiveZone: -1
        WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldHaveFocus, customKeyboardFocus)

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

        onVisibleChanged: {
            if (visible)
                return;
            if (Qt.inputMethod) {
                Qt.inputMethod.hide();
                Qt.inputMethod.reset();
            }
        }

        MouseArea {
            anchors.fill: parent
            enabled: root.closeOnBackgroundClick && root.shouldBeVisible
            z: -2
            onClicked: root.backgroundClicked()
        }

        Rectangle {
            anchors.fill: parent
            z: -1
            color: "black"
            opacity: root.useBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
            visible: opacity > 0

            Behavior on opacity {
                enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
                NumberAnimation {
                    duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
                    easing.type: Easing.BezierSpline
                    easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
                }
            }
        }

        Item {
            id: connectedReveal
            // Clip to final footprint while frame-owned chrome grows from the bar edge.
            x: root.alignedX
            y: root.alignedY
            width: root.alignedWidth
            height: root.alignedHeight
            clip: root.frameOwnsConnectedChrome

            Item {
                id: modalContainer
                x: Theme.snap(animX, root.dpr)
                y: Theme.snap(animY, root.dpr)

                width: root.alignedWidth
                height: root.alignedHeight

                MouseArea {
                    anchors.fill: parent
                    enabled: root.shouldBeVisible
                    hoverEnabled: false
                    acceptedButtons: Qt.AllButtons
                    onPressed: mouse => mouse.accepted = true
                    onClicked: mouse => mouse.accepted = true
                    z: -1
                }

                readonly property bool slide: root.animationType === "slide"
                readonly property bool directionalEffect: Theme.isDirectionalEffect
                readonly property bool depthEffect: Theme.isDepthEffect
                readonly property real directionalTravel: Math.max(root.animationOffset, Math.max(root.alignedWidth, root.alignedHeight) * 0.8)
                readonly property real depthTravel: Math.max(root.animationOffset * 0.8, 36)
                readonly property real customAnchorX: root.alignedX + root.alignedWidth * 0.5
                readonly property real customAnchorY: root.alignedY + root.alignedHeight * 0.5
                readonly property real customDistLeft: customAnchorX
                readonly property real customDistRight: root.screenWidth - customAnchorX
                readonly property real customDistTop: customAnchorY
                readonly property real customDistBottom: root.screenHeight - customAnchorY
                readonly property real connectedEmergenceTravelX: Math.max(root.animationOffset, root.alignedWidth + Theme.spacingL)
                readonly property real connectedEmergenceTravelY: Math.max(root.animationOffset, root.alignedHeight + Theme.spacingL)
                readonly property real offsetX: {
                    if (root.frameOwnsConnectedChrome) {
                        switch (root.resolvedConnectedBarSide) {
                        case "left":
                            return -connectedEmergenceTravelX;
                        case "right":
                            return connectedEmergenceTravelX;
                        }
                        return 0;
                    }
                    if (slide && !directionalEffect && !depthEffect)
                        return 15;
                    if (directionalEffect) {
                        switch (root.positioning) {
                        case "top-right":
                            return 0;
                        case "custom":
                            if (customDistLeft <= customDistRight && customDistLeft <= customDistTop && customDistLeft <= customDistBottom)
                                return -directionalTravel;
                            if (customDistRight <= customDistTop && customDistRight <= customDistBottom)
                                return directionalTravel;
                            return 0;
                        default:
                            return 0;
                        }
                    }
                    if (depthEffect) {
                        switch (root.positioning) {
                        case "top-right":
                            return 0;
                        case "custom":
                            if (customDistLeft <= customDistRight && customDistLeft <= customDistTop && customDistLeft <= customDistBottom)
                                return -depthTravel;
                            if (customDistRight <= customDistTop && customDistRight <= customDistBottom)
                                return depthTravel;
                            return 0;
                        default:
                            return 0;
                        }
                    }
                    return 0;
                }
                readonly property real offsetY: {
                    if (root.frameOwnsConnectedChrome) {
                        switch (root.resolvedConnectedBarSide) {
                        case "top":
                            return -connectedEmergenceTravelY;
                        case "bottom":
                            return connectedEmergenceTravelY;
                        }
                        return 0;
                    }
                    if (slide && !directionalEffect && !depthEffect)
                        return -30;
                    if (directionalEffect) {
                        switch (root.positioning) {
                        case "top-right":
                            return -Math.max(directionalTravel * 0.65, 96);
                        case "custom":
                            if (customDistTop <= customDistBottom && customDistTop <= customDistLeft && customDistTop <= customDistRight)
                                return -directionalTravel;
                            if (customDistBottom <= customDistLeft && customDistBottom <= customDistRight)
                                return directionalTravel;
                            return 0;
                        default:
                            return -Math.max(directionalTravel, root.screenHeight * 0.24);
                        }
                    }
                    if (depthEffect) {
                        switch (root.positioning) {
                        case "top-right":
                            return -depthTravel * 0.75;
                        case "custom":
                            if (customDistTop <= customDistBottom && customDistTop <= customDistLeft && customDistTop <= customDistRight)
                                return -depthTravel;
                            if (customDistBottom <= customDistLeft && customDistBottom <= customDistRight)
                                return depthTravel;
                            return depthTravel * 0.45;
                        default:
                            return -depthTravel;
                        }
                    }
                    return root.animationOffset;
                }

                readonly property real computedScaleCollapsed: root.animationScaleCollapsed

                QtObject {
                    id: morph
                    property real openProgress: root.shouldBeVisible ? 1 : 0
                    Behavior on openProgress {
                        enabled: root.animationsEnabled
                        NumberAnimation {
                            duration: Theme.variantDuration(root.animationDuration, root.shouldBeVisible)
                            easing.type: Easing.BezierSpline
                            easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
                        }
                    }
                }

                readonly property real animX: root.frozenMotionOffsetX * (1 - morph.openProgress)
                readonly property real animY: root.frozenMotionOffsetY * (1 - morph.openProgress)
                readonly property real scaleValue: computedScaleCollapsed + (1.0 - computedScaleCollapsed) * morph.openProgress

                onAnimXChanged: if (root.frameOwnsConnectedChrome)
                    root._syncModalAnim()
                onAnimYChanged: if (root.frameOwnsConnectedChrome)
                    root._syncModalAnim()

                Item {
                    id: contentContainer
                    anchors.centerIn: parent
                    width: parent.width
                    height: parent.height
                    clip: false

                    Item {
                        id: animatedContent
                        anchors.fill: parent
                        clip: false

                        property real publishedOpacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (root.shouldBeVisible ? 1 : 0)

                        opacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (root.shouldBeVisible ? 1 : 0)
                        scale: modalContainer.scaleValue
                        transformOrigin: Item.Center

                        Behavior on opacity {
                            enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
                            NumberAnimation {
                                duration: Math.round(Theme.variantDuration(animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
                                easing.type: Easing.BezierSpline
                                easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
                            }
                        }

                        Behavior on publishedOpacity {
                            enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
                            NumberAnimation {
                                duration: Math.round(Theme.variantDuration(animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
                                easing.type: Easing.BezierSpline
                                easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
                            }
                        }

                        ElevationShadow {
                            id: modalShadowLayer
                            anchors.fill: parent
                            level: root.shadowLevel
                            fallbackOffset: root.shadowFallbackOffset
                            targetRadius: root.effectiveCornerRadius
                            targetColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.effectiveBackgroundColor, 0) : root.effectiveBackgroundColor
                            borderColor: root.frameOwnsConnectedChrome ? Theme.withAlpha(root.effectiveBorderColor, 0) : root.effectiveBorderColor
                            borderWidth: root.frameOwnsConnectedChrome ? 0 : root.effectiveBorderWidth
                            shadowEnabled: !root.frameOwnsConnectedChrome && root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
                        }

                        Rectangle {
                            anchors.fill: parent
                            radius: root.effectiveCornerRadius
                            color: "transparent"
                            border.color: (root.connectedSurfaceOverride || root.frameOwnsConnectedChrome) ? Theme.withAlpha(BlurService.borderColor, 0) : BlurService.borderColor
                            border.width: (root.connectedSurfaceOverride || root.frameOwnsConnectedChrome) ? 0 : BlurService.borderWidth
                            z: 100
                        }

                        FocusScope {
                            anchors.fill: parent
                            focus: root.shouldBeVisible
                            clip: false

                            Item {
                                id: directContentWrapper
                                anchors.fill: parent
                                visible: root.directContent !== null
                                focus: true
                                clip: false

                                Component.onCompleted: {
                                    if (root.directContent) {
                                        root.directContent.parent = directContentWrapper;
                                        root.directContent.anchors.fill = directContentWrapper;
                                        Qt.callLater(() => root.directContent.forceActiveFocus());
                                    }
                                }

                                Connections {
                                    target: root
                                    function onDirectContentChanged() {
                                        if (root.directContent) {
                                            root.directContent.parent = directContentWrapper;
                                            root.directContent.anchors.fill = directContentWrapper;
                                            Qt.callLater(() => root.directContent.forceActiveFocus());
                                        }
                                    }
                                }
                            }

                            Loader {
                                id: contentLoader
                                anchors.fill: parent
                                active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || contentWindow.visible)
                                asynchronous: false
                                focus: true
                                clip: false
                                visible: root.directContent === null

                                onLoaded: {
                                    if (item) {
                                        Qt.callLater(() => item.forceActiveFocus());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        FocusScope {
            id: focusScope
            objectName: "modalFocusScope"
            anchors.fill: parent
            visible: root.shouldBeVisible || contentWindow.visible
            focus: root.shouldBeVisible
            Keys.onEscapePressed: event => {
                if (root.closeOnEscapeKey && shouldHaveFocus) {
                    root.close();
                    event.accepted = true;
                }
            }
        }
    }
}
