// SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
// SPDX-FileCopyrightText: 2021-2024 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2024-2025 Luis Büchi <luis.buechi@kdemail.net>
// SPDX-License-Identifier: GPL-2.0-or-later

pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Layouts

import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.state as MobileShellState
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin

import org.kde.kwin 3.0 as KWinComponents
import org.kde.kwin.private.effects 1.0
import org.kde.kitemmodels


/**
 * Component that provides a task switcher.
 */
FocusScope {
    id: root
    focus: true

    property TaskSwitcherPlugin.MobileTaskSwitcherState state
    readonly property QtObject effect: KWinComponents.SceneView.effect
    readonly property QtObject targetScreen: KWinComponents.SceneView.screen

    readonly property real navBottomMargin: MobileShell.Constants.navigationPanelOnSide(width, height) ? 0 : MobileShell.Constants.navigationPanelThickness
    readonly property real navRightMargin: MobileShell.Constants.navigationPanelOnSide(width, height) ? MobileShell.Constants.navigationPanelThickness : 0
    readonly property real topMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : MobileShell.Constants.topPanelHeight
    readonly property real bottomMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navBottomMargin
    readonly property real leftMargin: 0
    readonly property real rightMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navRightMargin

    property var taskSwitcherHelpers: TaskSwitcherHelpers {
        taskSwitcher: root
        taskList: taskList
    }

    MobileShell.HapticsEffect {
        id: haptics
    }

    property TaskSwitcherPlugin.TaskFilterModel tasksModel: TaskSwitcherPlugin.TaskFilterModel {
        screenName: root.targetScreen.name
        windowModel: root.state.taskModel
    }

    readonly property int tasksCount: taskList.count

    // keep track of task list events
    property int oldTasksCount: tasksCount
    onTasksCountChanged: {
        // we need to subtract 1 from the current index when the closed task index is smaller
        // this is because this part of the list has been shifted down by 1 when the closed task was removed.
        if (taskSwitcherHelpers.lastClosedTask < state.currentTaskIndex) {
            state.currentTaskIndex -= 1;

            // animated at the same speed as the task x position in the TaskList so that the task appears not to move from the perspective of the user.
            taskSwitcherHelpers.animateGoToTaskIndex(state.currentTaskIndex, Kirigami.Units.longDuration, Easing.InOutQuad);
            taskSwitcherHelpers.lastClosedTask = -1;
        }

        if (tasksCount === 0 && oldTasksCount !== 0) {
            hide();
        } else if (tasksCount < oldTasksCount) {
            if (state.currentTaskIndex < 0) {
                // if the user is on the first task, and it is closed, scroll right
                taskSwitcherHelpers.animateGoToTaskIndex(0, Kirigami.Units.longDuration);
            } else if (state.currentTaskIndex >= tasksCount) {
                // if the user is on the last task, and it is closed, scroll left
                taskSwitcherHelpers.animateGoToTaskIndex(tasksCount - 1, Kirigami.Units.longDuration);
            }
        }

        oldTasksCount = tasksCount;
    }

    Keys.onEscapePressed: hide();

    Component.onCompleted: {
        initialSetup();
    }

    function initialSetup(): void {
        taskSwitcherHelpers.cancelAnimations();
        state.updateWasInActiveTask(KWinComponents.Workspace.activeWindow);

        // ensure the task drawer is not opened and reset values to defaults
        taskSwitcherHelpers.reachedHeightThreshold = false;
        taskSwitcherHelpers.gestureState = TaskSwitcherHelpers.GestureStates.Undecided;
        taskSwitcherHelpers.isInTaskScrubMode = false;
        taskSwitcherHelpers.inLastFrame = false;

        taskSwitcherHelpers.hasVibrated = false;

        taskSwitcherHelpers.closingFactor = 1;

        taskSwitcherHelpers.taskSwitchCanLaunch = false;
        taskSwitchCanLaunchTimer.restart()

        taskList.taskOffsetEasing = Easing.InOutQuart;
        taskList.homeTouchPositionX = 0;

        backgroundColorOpacityAn.enabled = false;
        backgroundColorOpacity = state.wasInActiveTask ? 1 : 0;
        backgroundColorOpacityAn.enabled = true;

        // reset the offset to have the task drawer off screen
        taskList.setTaskOffsetValue(state.wasInActiveTask ? taskSwitcherHelpers.taskOffsetValue : taskSwitcherHelpers.homeOffsetValue, true);

        // task index from the last time using the switcher
        state.initialTaskIndex = Math.min(state.currentTaskIndex, tasksCount - 1);
        if (state.wasInActiveTask) {
            // if we were in an active task instead set initial task index to the position of that task
            state.initialTaskIndex = taskSwitcherHelpers.getTaskIndexFromWindow(KWinComponents.Workspace.activeWindow);
        } else {
            // reset the task index to the start if on home screen
            state.initialTaskIndex = 0
        }
        state.currentTaskIndex = state.initialTaskIndex

        taskSwitcherHelpers.currentDisplayTask = state.currentTaskIndex;

        taskSwitcherHelpers.goToTaskIndex(state.initialTaskIndex);
        taskList.minimizeAll();

        // fully open the switcher (if this is a button press, not gesture)
        if (!state.gestureInProgress) {
            taskSwitcherHelpers.fromButton = true;
            if (state.wasInActiveTask) {
                taskList.setTaskOffsetValue(0, true);
            } else {
                taskList.setTaskOffsetValue(0);
            }
            backgroundColorOpacity = 1;
            taskSwitcherHelpers.open();
        }
    }

    // called by c++ plugin
    function hideAnimation(): void {
        closeAnim.restart();
    }

    function instantHide(): void {
        state.deactivate(true);
    }

    function hide(): void {
        state.deactivate(false);
    }

    Connections {
        target: root.state

        // task scrub mode allows scrubbing through a number of tasks with a mostly horizontal motion
        function taskScrubMode(): void {
            taskList.setTaskOffsetValue(0, false, Easing.OutQuart);
            if (!root.taskSwitcherHelpers.isInTaskScrubMode) {
                root.backgroundColorOpacity = 1;
                root.taskSwitcherHelpers.cancelAnimations();
                root.taskSwitcherHelpers.open();
                if (!root.taskSwitcherHelpers.hasVibrated) {
                    // Haptic feedback when the task scrub mode engages
                    haptics.buttonVibrate();
                    root.taskSwitcherHelpers.hasVibrated = true;
                }
            }
            // TODO this makes sense, but makes scrub mode feel a bit weird
            // improve trigger distance logic for task scrub mode to fix
            let newTaskIndex = Math.max(0, Math.min(root.tasksCount - 1, Math.floor(root.state.touchXPosition / root.taskSwitcherHelpers.taskScrubDistance) + root.state.initialTaskIndex - (root.state.wasInActiveTask ? 0 : 1)));
            if (newTaskIndex != root.state.currentTaskIndex || !root.taskSwitcherHelpers.isInTaskScrubMode) {
                root.taskSwitcherHelpers.animateGoToTaskIndex(newTaskIndex);
                root.taskSwitcherHelpers.isInTaskScrubMode = true;
            }
        }

        function onTouchPositionChanged(): void {
            let unmodifiedYposition = Math.abs(root.state.touchYPosition)
            if (root.taskSwitcherHelpers.isInTaskScrubMode || // once in scrub mode, let's not allow to go out, that can result in inconsistent UX
                (Math.abs(root.state.xVelocity) > Math.abs(root.state.yVelocity) * 3 && // gesture needs to be almost completely horizontal
                Math.abs(root.state.xVelocity) < 2.5 && // and not with a fast flick TODO! evaluate whether to keep this, it's kinda awkward
                Math.abs(root.state.touchXPosition) > root.taskSwitcherHelpers.taskScrubDistance * 0.95 && // and have moved far enough sideways
                unmodifiedYposition < Kirigami.Units.largeSpacing * 2 && // and be close to the screen edge
                root.tasksCount > 0 && // and there needs to be more than none task open
                !root.taskSwitcherHelpers.taskDrawerOpened // and the task drawer must not be open
                )) {
                taskScrubMode();
            } else {
                if (root.taskSwitcherHelpers.currentlyBeingClosed) {
                    // if the task switch is still open but playing the close animation
                    // setup some values and return to the initial setup so that the user can always navigate with no down time
                    root.state.wasInActiveTask = root.taskSwitcherHelpers.openAppAnim.running ? true : false
                    taskList.setTaskOffsetValue(root.state.wasInActiveTask ? root.taskSwitcherHelpers.taskOffsetValue : root.taskSwitcherHelpers.homeOffsetValue, true);
                    root.state.status = !root.state.wasInActiveTask ? (root.taskSwitcherHelpers.openAppAnim.closeAnim && !root.taskSwitcherHelpers.taskDrawerWillOpen ? TaskSwitcherPlugin.MobileTaskSwitcherState.Active : TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive) : TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive
                    root.initialSetup();
                } else if (root.taskSwitcherHelpers.openAnim.running) {
                    root.taskSwitcherHelpers.cancelAnimations();
                    root.state.status = root.taskSwitcherHelpers.stateClass.Active;
                }

                root.state.yPosition = unmodifiedYposition + (root.taskSwitcherHelpers.taskDrawerOpened || !root.state.wasInActiveTask ? root.taskSwitcherHelpers.openedYPosition : 0);

                let newXPosition = root.taskSwitcherHelpers.xPositionFromTaskIndex(root.state.initialTaskIndex);
                if (root.taskSwitcherHelpers.notHomeScreenState && !root.taskSwitcherHelpers.currentlyBeingClosed) {
                    newXPosition = newXPosition - (root.state.touchXPosition / root.taskSwitcherHelpers.currentScale);
                }
                root.state.xPosition = newXPosition;

                // allows the user to move the task drawer left and right when on the home screen
                taskList.homeTouchPositionX = root.taskSwitcherHelpers.notHomeScreenState ? 0 : (root.state.touchXPosition * 0.35);

                // dynamically update the task switcher state based off of the touch position and velocity
                updateTaskSwitcherState()
            }
        }

        function updateTaskSwitcherState(): void {
            let unmodifiedYposition = Math.abs(root.state.touchYPosition)

            // if the touch is above heightThreshold, set reachedHeightThreshold to true
            if (unmodifiedYposition > root.taskSwitcherHelpers.heightThreshold) {
                // set reachedHeightThreshold when above or below two separate points to helps prevent flickering when the task switcher moves in and out of view
                root.taskSwitcherHelpers.reachedHeightThreshold = true;
                root.backgroundColorOpacity = root.taskSwitcherHelpers.notHomeScreenState ? 0 : 1;
            } else if (unmodifiedYposition > root.taskSwitcherHelpers.undoYThreshold) {
                root.backgroundColorOpacity = 1;
            } else {
                root.backgroundColorOpacity = root.taskSwitcherHelpers.notHomeScreenState ? 1 : 0;
            }

            if (root.state.totalSquaredVelocity > root.state.flickVelocityThreshold) {
                // flick
                // ratio between y and x velocity as threshold between vertical and horizontal flick
                let xyVelocityRatio = 1.7; // with 1.7 swipes up to ~60° from horizontal are counted as horizontal
                if (root.state.yVelocity > Math.abs(root.state.xVelocity) * xyVelocityRatio) {
                    // downwards flick
                    root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.Undecided);
                    if (unmodifiedYposition < root.taskSwitcherHelpers.undoYThreshold) {
                        taskList.setTaskOffsetValue(root.taskSwitcherHelpers.notHomeScreenState ? 0 : root.taskSwitcherHelpers.homeOffsetValue);
                    }
                } else if (-root.state.yVelocity > Math.abs(root.state.xVelocity) * xyVelocityRatio || (root.taskSwitcherHelpers.reachedHeightThreshold && root.taskSwitcherHelpers.notHomeScreenState)) {
                    // upwards flick or if the touch is above heightThreshold
                    if (root.taskSwitcherHelpers.notHomeScreenState) {
                        // if in app or task switcher, go home
                        root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.Home);
                        if (root.taskSwitcherHelpers.reachedHeightThreshold) {
                            taskList.setTaskOffsetValue(root.taskSwitcherHelpers.taskOffsetValue);
                        }
                    } else if (unmodifiedYposition > root.taskSwitcherHelpers.undoYThreshold) {
                        // else, keep the task switcher in view
                        root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.TaskSwitcher);
                        taskList.setTaskOffsetValue(root.taskSwitcherHelpers.peekOffsetValue);
                    }
                } else if (!root.taskSwitcherHelpers.reachedHeightThreshold && !root.taskSwitcherHelpers.isInTaskScrubMode) {
                    // sideways flick
                    if (root.taskSwitcherHelpers.notHomeScreenState) {
                        taskList.setTaskOffsetValue(0, unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition ? true : false);
                    }
                    root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.HorizontalSwipe);
                }
            } else {
                if (unmodifiedYposition > root.taskSwitcherHelpers.undoYThreshold) {
                    // if just moveing out of undoYThreshold, set the state to home
                    if (root.taskSwitcherHelpers.gestureState < TaskSwitcherHelpers.GestureStates.TaskSwitcher) {
                        root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.Home)
                    }
                    // if the touch is above heightThreshold, it will return home
                    if (unmodifiedYposition > root.taskSwitcherHelpers.heightThreshold) {
                        root.taskSwitcherHelpers.hasVibrated = true;
                        if (root.taskSwitcherHelpers.notHomeScreenState) {
                            // move the task switcher out of view
                            root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.Home);
                            taskList.setTaskOffsetValue(root.taskSwitcherHelpers.taskOffsetValue);
                        } else {
                            // keep the task switcher in view when above heightThreshold and from home
                            root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.TaskSwitcher);
                            taskList.setTaskOffsetValue(root.taskSwitcherHelpers.peekOffsetValue);
                        }
                        // minus largeSpacing from the heightThreshold to help prevent flickering when the task switcher moves in and out of view
                    } else if ((unmodifiedYposition < root.taskSwitcherHelpers.heightThreshold - Kirigami.Units.largeSpacing) || root.taskSwitcherHelpers.reachedHeightThreshold == false) {
                        // set reachedHeightThreshold when above or below two separate points to helps prevent flickering when the task switcher moves in and out of view
                        root.taskSwitcherHelpers.reachedHeightThreshold = false;
                        if (root.state.totalSquaredVelocity < root.state.flickVelocityThreshold && root.taskSwitcherHelpers.taskSwitchCanLaunch) {
                            // if velocity is small enough, move the task switcher into view
                            root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.TaskSwitcher);
                            taskList.setTaskOffsetValue(root.taskSwitcherHelpers.notHomeScreenState ? 0 : root.taskSwitcherHelpers.peekOffsetValue);
                        }
                    }
                } else {
                    // if under the undo threshold, it will go back to the task switcher if it is open
                    if (root.taskSwitcherHelpers.taskDrawerOpened) {
                        root.taskSwitcherHelpers.reachedHeightThreshold = false;
                        root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.TaskSwitcher)
                        taskList.setTaskOffsetValue(0);
                    } else {
                        root.taskSwitcherHelpers.reachedHeightThreshold = false;
                        root.setTaskDrawerState(TaskSwitcherHelpers.GestureStates.Undecided)
                        taskList.setTaskOffsetValue(root.taskSwitcherHelpers.notHomeScreenState ? 0 : root.taskSwitcherHelpers.homeOffsetValue);
                    }
                }
            }
        }

        // returns to the currently centered app. usually used to "back out" of the switcher
        // if accidentally invoked, but can also be used to switch to an adjacent app and then open it
        function returnToApp(): void {
            let newIndex = root.taskSwitcherHelpers.getNearestTaskIndex();
            root.taskSwitcherHelpers.openApp(newIndex);
        }

        // diagonal quick switch gesture logic
        function quickSwitch(): void {
            // should "quick switch" to adjacent app in task switcher, but only if we were in an app before
            let unmodifiedYposition = Math.abs(root.state.touchYPosition)
            let newIndex = root.state.currentTaskIndex;
            let shouldSwitch = false;
            if (root.state.xVelocity > 0) {
                if (root.taskSwitcherHelpers.notHomeScreenState) {
                    // flick to the right, go to the app on the left
                    newIndex = root.state.currentTaskIndex + 1;
                }
                if (newIndex < root.tasksCount) {
                    // switch only if flick doesn't go over end of list
                    shouldSwitch = true;
                }
            } else if (root.state.xVelocity < 0) {
                if (root.taskSwitcherHelpers.notHomeScreenState) {
                    // flick to the left, go to app to the right
                    newIndex = root.state.currentTaskIndex - 1;
                    if (newIndex >= 0) {
                        // switch only if flick doesn't go over end of list
                        shouldSwitch = true;
                    }
                } else {
                    // flick to the left on the home screen, dismiss the gesture
                    root.taskSwitcherHelpers.close();
                    return;
                }
            }
            if (shouldSwitch) {
                if (!root.taskSwitcherHelpers.taskDrawerOpened && unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition) {
                    // if in a app, switch it to the new task when it is under the openedYPosition
                    taskList.setTaskOffsetValue(0, unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition && root.taskSwitcherHelpers.notHomeScreenState);
                    root.taskSwitcherHelpers.openApp(newIndex, Kirigami.Units.longDuration * 4, Easing.OutExpo);
                } else {
                    // if already in the task switcher or above the openedYPosition, only change the focus to the new task
                    root.taskSwitcherHelpers.animateGoToTaskIndex(newIndex);
                    root.taskSwitcherHelpers.open();
                }
            } else {
                // if not switching, just open task switcher
                root.taskSwitcherHelpers.animateGoToTaskIndex(root.state.currentTaskIndex);
                root.taskSwitcherHelpers.open();
            }
        }

        // Logic for deciding how to handle the end of a gesture input
        function onGestureInProgressChanged(): void {
            root.taskSwitcherHelpers.fromButton = false;
            if (root.state.gestureInProgress) {
                root.taskSwitcherHelpers.currentDisplayTask = root.state.currentTaskIndex;
                return;
            }

            if (taskList.count === 0) {
                // dismiss the gesture if the task list is empty
                root.taskSwitcherHelpers.close();
            } if (root.taskSwitcherHelpers.isInTaskScrubMode) {
                // TODO! do we want to handle upwards flick to dismiss in task scrub mode?
                let unmodifiedYposition = Math.abs(root.state.touchYPosition)
                root.backgroundColorOpacity = 1;
                if (root.taskSwitcherHelpers.taskDrawerOpened || unmodifiedYposition > root.taskSwitcherHelpers.undoYThreshold) {
                    root.taskSwitcherHelpers.animateGoToTaskIndex(root.state.currentTaskIndex);
                    root.taskSwitcherHelpers.open();
                    root.taskSwitcherHelpers.isInTaskScrubMode = false;
                } else {
                    root.taskSwitcherHelpers.openApp(root.state.currentTaskIndex);
                }
            } else if (root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.Undecided) {
                if (root.taskSwitcherHelpers.taskDrawerOpened) {
                    // if in the task switcher, return to it
                    root.taskSwitcherHelpers.animateGoToTaskIndex(root.state.currentTaskIndex);
                    root.taskSwitcherHelpers.open();
                } else if (root.state.wasInActiveTask) {
                    // if inside a app, return to it
                    returnToApp();
                } else {
                    // else dismiss the gesture
                    root.taskSwitcherHelpers.close();
                }
            } else if (root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.HorizontalSwipe) {
                // sideways flick
                root.backgroundColorOpacity = 1;
                quickSwitch();
            } else if (root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.TaskSwitcher) {
                // open the task drawer
                root.backgroundColorOpacity = 1;
                root.taskSwitcherHelpers.animateGoToTaskIndex(root.state.currentTaskIndex);
                root.taskSwitcherHelpers.open();
            } else if (root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.Home) {
                root.taskSwitcherHelpers.close();
            }
        }

        function onVelocityChanged(): void {

        }

        function onXPositionChanged(): void {
            root.taskSwitcherHelpers.updateTaskIndex();
        }
    }

    // kind of a hack, but this prevents the gesture from immediately activating the task switcher when it is not supposed to
    Timer {
        id: taskSwitchCanLaunchTimer
        interval: 1; running: true; repeat: false
        onTriggered: root.taskSwitcherHelpers.taskSwitchCanLaunch = true;
    }

    function setTaskDrawerState(value: int): void {
        if (taskSwitcherHelpers.gestureState != TaskSwitcherHelpers.GestureStates.TaskSwitcher && value == TaskSwitcherHelpers.GestureStates.TaskSwitcher) {
            // vibrate only if switching to task drawer
            if (!taskSwitcherHelpers.hasVibrated) {
                // Haptic feedback when the task scrub mode engages
                haptics.buttonVibrate();
                taskSwitcherHelpers.hasVibrated = true;
            }

        }
        taskSwitcherHelpers.gestureState = value;
    }

    // view of the desktop background
    KWinComponents.DesktopBackground {
        id: backgroundItem
        activity: KWinComponents.Workspace.currentActivity
        desktop: KWinComponents.Workspace.currentDesktop
        outputName: root.targetScreen.name
    }

    // background colour
    Rectangle {
        id: backgroundRect
        anchors.fill: root

        opacity: container.opacity
        color: {
            return Qt.rgba(0, 0, 0, 0.6 * root.taskSwitcherHelpers.closingFactor * root.backgroundColorOpacity);
        }
    }

    // animate the background opacity based off of the state.
    property real backgroundColorOpacity: 1
    Behavior on backgroundColorOpacity {
        id: backgroundColorOpacityAn
        NumberAnimation {
            duration: Kirigami.Units.longDuration
        }
    }

    // status bar
    // TODO: improve load times, it is quite slow
    // MobileShell.StatusBar {
    //     id: statusBar
    //     z: 1
    //     colorGroup: Kirigami.Theme.ComplementaryColorGroup
    //     backgroundColor: "transparent"
    //
    //     height: root.topMargin
    //     anchors.top: parent.top
    //     anchors.left: parent.left
    //     anchors.right: parent.right
    // }

    // Gesture panel
    Component {
        id: gesturePanelComponent

        MobileShell.GesturePanel {
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.bottom: parent.bottom

            Kirigami.Theme.inherit: false
            Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
            opaqueBar: false

            // Trigger home action on tap
            onHandleClicked: {
                if (taskList.count === 0) {
                    root.hide();
                } else {
                    if (taskList.count > 1 &&
                        root.state.elapsedTimeSinceStart != -1 &&
                        root.state.elapsedTimeSinceStart < root.state.doubleClickInterval) {
                        root.taskSwitcherHelpers.openApp(1);
                        return;
                    }

                    const currentIndex = root.state.currentTaskIndex;
                    root.taskSwitcherHelpers.openApp(root.state.currentTaskIndex);
                }
            }
        }
    }

    // Navigation panel
    Component {
        id: navigationPanelComponent

        MobileShell.NavigationPanel {
            id: navigationPanel
            backgroundColor: Qt.rgba(0, 0, 0, 0.1)
            foregroundColorGroup: Kirigami.Theme.Complementary
            shadow: false

            isVertical: MobileShell.Constants.navigationPanelOnSide(root.width, root.height)

            MobileShellState.PanelSettingsDBusClient {
                id: panelSettings
                screenName: Screen.name
            }

            leftPadding: panelSettings.navigationPanelLeftPadding
            rightPadding: panelSettings.navigationPanelRightPadding

            leftAction: MobileShell.NavigationPanelAction {
                enabled: true
                iconSource: "mobile-task-switcher"
                shrinkSize: 4

                onTriggered: {
                    if (taskList.count === 0) {
                        root.hide();
                    } else {
                        if (taskList.count > 1 &&
                            root.state.elapsedTimeSinceStart != -1 &&
                            root.state.elapsedTimeSinceStart < root.state.doubleClickInterval) {
                            root.taskSwitcherHelpers.openApp(1);
                            return;
                        }

                        const currentIndex = root.state.currentTaskIndex;
                        root.taskSwitcherHelpers.openApp(root.state.currentTaskIndex);
                    }
                }
            }

            // home button
            middleAction: MobileShell.NavigationPanelAction {
                enabled: true
                iconSource: "start-here-kde"
                onTriggered: root.hide()
            }

            // close app/keyboard button
            rightAction: MobileShell.NavigationPanelAction {
                enabled: true
                iconSource: "mobile-close-app"
                shrinkSize: 4

                onTriggered: {
                    taskList.getTaskAt(root.state.currentTaskIndex).closeApp();
                }
            }

            rightCornerAction: MobileShell.NavigationPanelAction {
                visible: false
            }
        }
    }

    Loader {
        id: panelLoader
        z: !root.taskSwitcherHelpers.currentlyBeingClosed ? 1 : 0
        sourceComponent: {
            if (ShellSettings.Settings.navigationPanelEnabled) {
                return navigationPanelComponent;
            } else if (ShellSettings.Settings.gesturePanelEnabled) {
                return gesturePanelComponent;
            } else {
                return null;
            }
        }
    }

    states: [
        State {
            name: "landscape"
            when: MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
            AnchorChanges {
                target: panelLoader
                anchors {
                    right: root.right
                    top: root.top
                    bottom: root.bottom
                    left: undefined
                }
            }
            PropertyChanges {
                target: panelLoader
                width: navRightMargin
                anchors.topMargin: root.topMargin
            }
        },
        State {
            name: "portrait"
            when: !MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
            AnchorChanges {
                target: panelLoader
                anchors {
                    top: undefined
                    right: root.right
                    left: root.left
                    bottom: root.bottom
                }
            }
            PropertyChanges {
                target: panelLoader
                height: navBottomMargin
            }
        }
    ]

    // task list
    Item {
        id: container

        // provide shell margins
        anchors.fill: root
        anchors.leftMargin: root.leftMargin
        anchors.rightMargin: root.rightMargin
        anchors.bottomMargin: root.bottomMargin
        anchors.topMargin: root.topMargin

        NumberAnimation on opacity {
            id: closeAnim
            running: false
            to: 0
            duration: Kirigami.Units.longDuration
            easing.type: Easing.InOutQuad

            onFinished: {
                closeAllButton.closeRequested = false;
            }
        }

        // placeholder message
        ColumnLayout {
            id: placeholder
            spacing: Kirigami.Units.gridUnit

            opacity: {
                let baseOpacity = ((root.tasksCount === 0 && !root.taskSwitcherHelpers.currentlyBeingClosed) ? 0.9 : 0);
                return root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.TaskSwitcher ? baseOpacity : 0;
            }
            Behavior on opacity { NumberAnimation { duration: Kirigami.Units.veryLongDuration } }

            anchors.centerIn: container

            Kirigami.Icon {
                id: icon
                Layout.alignment: Qt.AlignHCenter
                implicitWidth: Kirigami.Units.iconSizes.large
                implicitHeight: Kirigami.Units.iconSizes.large
                source: "edit-none-symbolic"
                color: "white"
            }

            Kirigami.Heading {
                Layout.fillWidth: true
                Layout.maximumWidth: root.width * 0.75
                Layout.alignment: Qt.AlignHCenter
                color: "white"
                level: 3
                wrapMode: Text.Wrap
                horizontalAlignment: Text.AlignHCenter
                text: i18n("No applications are running.")
            }
        }

        RowLayout {
            id: scrubIconList
            opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0
            Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration } }

            anchors.bottom: container.bottom
            anchors.right: container.horizontalCenter
            anchors.bottomMargin: root.taskSwitcherHelpers.scrubModeBottomMargin

            anchors.rightMargin: {
                let size = Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing * 2;
                let offset = (root.state.currentTaskIndex + 0.5) * size;
                return -offset;
            }
            Behavior on anchors.rightMargin {
                NumberAnimation {
                    duration: root.taskSwitcherHelpers.xAnimDuration;
                    easing.type: root.taskSwitcherHelpers.xAnimEasingType;
                }
            }

            spacing: Kirigami.Units.largeSpacing * 2

            layoutDirection: Qt.RightToLeft

            Repeater {
                model: root.tasksModel

                delegate: Kirigami.Icon {
                    id: iconDelegate

                    required property QtObject window
                    required property int index

                    readonly property bool isCenteredIcon: iconDelegate.index === root.state.currentTaskIndex;
                    Layout.preferredHeight: isCenteredIcon ? Kirigami.Units.iconSizes.huge : Kirigami.Units.iconSizes.large
                    Layout.preferredWidth: isCenteredIcon ? Kirigami.Units.iconSizes.huge : Kirigami.Units.iconSizes.large
                    Layout.alignment: Qt.AlignVCenter
                    source: iconDelegate.window.icon
                }
            }
        }

        RowLayout {
            id: scrubIndicator
            opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0
            Behavior on opacity { NumberAnimation { duration: 200 } }

            anchors.bottom: container.bottom
            anchors.horizontalCenter: container.horizontalCenter
            anchors.bottomMargin: root.taskSwitcherHelpers.openedYPosition * 1 / 4

            Kirigami.Icon {
                id: iconScrubBack
                opacity: root.state.currentTaskIndex == 0 ? 0.3 : 1
                Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration * 2; easing.type: Easing.OutExpo } }
                Layout.alignment: Qt.AlignHCenter
                implicitWidth: Kirigami.Units.iconSizes.medium
                implicitHeight: Kirigami.Units.iconSizes.medium
                source: "draw-arrow-back"
                color: "white"
            }

            Item {
                width: root.taskSwitcherHelpers.windowWidth / 4
            }

            Kirigami.Icon {
                id: iconScrubFront
                opacity: root.state.currentTaskIndex == root.tasksCount - 1 ? 0.3 : 1
                Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration * 2; easing.type: Easing.OutExpo } }
                Layout.alignment: Qt.AlignHCenter
                implicitWidth: Kirigami.Units.iconSizes.medium
                implicitHeight: Kirigami.Units.iconSizes.medium
                source: "draw-arrow-forward"
                color: "white"
            }
        }

        // flicking area for task switcher
        FlickContainer {
            id: flickable
            anchors.fill: container

            taskSwitcherState: root.state
            taskSwitcherHelpers: root.taskSwitcherHelpers
            tasksCount: root.tasksCount

            // don't allow FlickContainer to steal from swiping on tasks
            interactive: taskList.taskInteractingCount === 0

            // the item is effectively anchored to the flickable bounds
            TaskList {
                id: taskList
                taskSwitcher: root
                shellTopMargin: root.topMargin
                shellBottomMargin: root.bottomMargin


                x: flickable.contentX
                width: flickable.width
                height: flickable.height
            }

            PlasmaComponents.ToolButton {
                id: closeAllButton
                property bool closeRequested: false
                visible: root.tasksCount !== 0 && !root.taskSwitcherHelpers.isInTaskScrubMode
                enabled: !root.taskSwitcherHelpers.currentlyBeingClosed && !root.state.gestureInProgress

                Kirigami.Theme.inherit: false
                Kirigami.Theme.colorSet: Kirigami.Theme.Complementary

                anchors {
                    bottom: taskList.bottom
                    bottomMargin: (taskList.taskYBase) * 0.75
                    horizontalCenter: taskList.horizontalCenter
                }

                opacity: (root.taskSwitcherHelpers.currentlyBeingClosed || root.state.gestureInProgress || !root.taskSwitcherHelpers.taskDrawerOpened) ? 0.0 : 1.0
                Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }

                icon.name: "edit-clear-history"
                font.bold: true

                text: closeRequested ? i18n("Confirm Close All") : i18n("Close All")

                onClicked: {
                    if (closeRequested) {
                        taskList.closeAll();
                    } else {
                        closeRequested = true;
                    }
                }
            }
        }
    }
}

