/* * SPDX-FileCopyrightText: 2022 Devin Lin * SPDX-FileCopyrightText: 2025 Luis Büchi * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Controls 2.15 as Controls import org.kde.kirigami as Kirigami import org.kde.kcmutils as KCM import org.kde.kirigamiaddons.formcard 1.0 as FormCard import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings KCM.SimpleKCM { id: root title: i18n("Navigation") topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 ColumnLayout { FormCard.FormHeader { title: i18nc("@label", "Navigation Mode") } FormCard.FormCard { id: gestureSelectionCard Controls.ButtonGroup { id: positionGroup buttons: [navPanelRadio, gesturesRadio] } FormCard.FormRadioDelegate { id: navPanelRadio text: i18nc("Nav Panel Navigation Mode", "Panel") checked: ShellSettings.Settings.navigationPanelEnabled onClicked: ShellSettings.Settings.navigationPanelEnabled = true; } FormCard.FormRadioDelegate { id: gesturesRadio text: i18nc("Gestures Navigation Mode", "Gestures") checked: !ShellSettings.Settings.navigationPanelEnabled onClicked: ShellSettings.Settings.navigationPanelEnabled = false; } FormCard.FormDelegateSeparator { visible: !ShellSettings.Settings.navigationPanelEnabled above: gesturesRadio below: tutorialContainer } Item { id: tutorialContainer visible: !ShellSettings.Settings.navigationPanelEnabled property int phoneWidth: Math.min(Window.width * 0.4, gestureSelectionCard.maximumWidth * 0.9) property int phoneHeight: phoneWidth * Window.height / Window.width property int count: 3 property int fingerSize: 20 Layout.alignment: Qt.AlignCenter Layout.preferredWidth: phoneWidth Layout.preferredHeight: phoneHeight + fingerSize / 2 Layout.topMargin: Kirigami.Units.largeSpacing * 2 clip: true MouseArea { id: tutorialSwitcherInput anchors.fill: tutorialContainer preventStealing: true property int activeTutorialIndex: 0 property real lastX property real lastDelta onPressedChanged: { if (pressed) { lastX = mouseX; } else { let moveOffset = lastDelta > 0 ? -0.45 : 0.45 let candidateIndex = Math.round(-tutorialLayout.offset / (tutorialLayout.spacing + tutorialContainer.phoneWidth) + moveOffset); activeTutorialIndex = Math.max(0, Math.min(tutorialContainer.count - 1, candidateIndex)); updateActiveTutorial(); releaseAnim.start() } } function updateActiveTutorial(): void { switch (activeTutorialIndex) { case 0: switchTutorial.loopSwitcherAnimation(); flickTutorial.stopAnimation(); scrubTutorial.stopAnimation(); break; case 1: switchTutorial.stopAnimation(); flickTutorial.loopFlickAnimation(); scrubTutorial.stopAnimation(); break; case 2: switchTutorial.stopAnimation(); flickTutorial.stopAnimation(); scrubTutorial.loopScrubAnimation(); break; } } onPositionChanged: (mouse) => { let delta = mouse.x - lastX; let endOffset = -(tutorialContainer.count - 1) * (tutorialContainer.phoneWidth + tutorialLayout.spacing); if (activeTutorialIndex == 0 && tutorialLayout.offset > 0 && delta > 0) { // handle overshoot on the left delta /= tutorialLayout.x; } else if (activeTutorialIndex == tutorialContainer.count - 1 && tutorialLayout.offset < endOffset) { // handle overhoot on the right delta /= Math.abs(tutorialLayout.x - endOffset); } tutorialLayout.offset += delta lastDelta = delta; lastX = mouse.x } NumberAnimation { id: releaseAnim target: tutorialLayout property: "offset" to: -tutorialSwitcherInput.activeTutorialIndex * (tutorialContainer.phoneWidth + tutorialLayout.spacing) duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } Item { Layout.alignment: Qt.AlignCenter Layout.preferredWidth: tutorialContainer.phoneWidth Layout.preferredHeight: tutorialContainer.phoneHeight + tutorialContainer.fingerSize / 2 RowLayout { id: tutorialLayout property int offset: 0 x: offset width: tutorialContainer.phoneWidth * tutorialContainer.count + Kirigami.Units.largeSpacing * (tutorialContainer.count - 1) height: tutorialContainer.phoneHeight spacing: Kirigami.Units.largeSpacing TutorialPhone { id: switchTutorial phoneWidth: tutorialContainer.phoneWidth phoneHeight: tutorialContainer.phoneHeight fingerSize: tutorialContainer.fingerSize showBackground: false Layout.alignment: Qt.AlignCenter Component.onCompleted: { loopSwitcherAnimation(); } } TutorialPhone { id: flickTutorial phoneWidth: tutorialContainer.phoneWidth phoneHeight: tutorialContainer.phoneHeight fingerSize: tutorialContainer.fingerSize Layout.alignment: Qt.AlignCenter } TutorialPhone { id: scrubTutorial phoneWidth: tutorialContainer.phoneWidth phoneHeight: tutorialContainer.phoneHeight fingerSize: tutorialContainer.fingerSize showBackground: false Layout.alignment: Qt.AlignCenter } } } } Controls.PageIndicator { visible: !ShellSettings.Settings.navigationPanelEnabled currentIndex: tutorialSwitcherInput.activeTutorialIndex count: tutorialContainer.count Layout.alignment: Qt.AlignCenter } FormCard.FormSectionText { visible: !ShellSettings.Settings.navigationPanelEnabled && tutorialSwitcherInput.activeTutorialIndex == 0 text: i18n("Swipe up, hold then release to enter Task Switcher") } FormCard.FormSectionText { visible: !ShellSettings.Settings.navigationPanelEnabled && tutorialSwitcherInput.activeTutorialIndex == 1 text: i18n("Swipe up and release to go to Home Screen") } FormCard.FormSectionText { visible: !ShellSettings.Settings.navigationPanelEnabled && tutorialSwitcherInput.activeTutorialIndex == 2 text: i18n("Swipe left and right near the bottom screen edge to scrub through open tasks. Release to select a task to focus") } FormCard.FormDelegateSeparator { visible: keyboardToggleDelegate.visible; above: gesturesRadio; below: keyboardToggleDelegate } FormCard.FormSwitchDelegate { id: keyboardToggleDelegate visible: ShellSettings.Settings.navigationPanelEnabled text: i18n("Always show keyboard toggle") description: i18n("Whether to always show the keyboard toggle button on the navigation panel.") checked: ShellSettings.Settings.alwaysShowKeyboardToggleOnNavigationPanel onCheckedChanged: { if (checked != ShellSettings.Settings.alwaysShowKeyboardToggleOnNavigationPanel) { ShellSettings.Settings.alwaysShowKeyboardToggleOnNavigationPanel = checked; } } } FormCard.FormDelegateSeparator { visible: gesturePanelToggleDelegate.visible } FormCard.FormSwitchDelegate { id: gesturePanelToggleDelegate visible: !ShellSettings.Settings.navigationPanelEnabled text: i18n("Show gesture handle") description: i18n("Whether to add a panel on the bottom with a gesture handle.") checked: ShellSettings.Settings.gesturePanelEnabled onCheckedChanged: { if (checked != ShellSettings.Settings.gesturePanelEnabled) { ShellSettings.Settings.gesturePanelEnabled = checked; } } } } } }