[plasma/plasma-mobile] kcms/navigation/ui: kcms/navigation: Navigation Buttons Animations and UI Improvements
Devin Lin <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 6e43a6d5bda88c7f24d9e90b02dcd92ad25533e8 by Devin Lin, on behalf of Micah Stanley.
Committed on 22/07/2026 at 03:51.
Pushed by devinlin into branch 'master'.
kcms/navigation: Navigation Buttons Animations and UI Improvements
- Add tutorial animations for the navbar.
- Move the tutorial pages up top and simplify the code by utilizing a ListView.
- Give the tutorial labels a fixed height so the settings do not move around when changing the tutorial page.
- Make the tutorial pages extend further out and fade out the edges, making it easier to swipe between the pages.
- Move the images used to a sub folder for cleaner file organization.
- Separate the display navbar code and extracted the dummy window code to separate files to help shrink the amount of code contained within `TutorialPhone.qml`.
{width=203 height=405}
{width=203 height=405}
M +7 -1 kcms/navigation/ui/AnimationHandler.qml
A +47 -0 kcms/navigation/ui/DummyWindow.qml [License: GPL(v2.0+)]
A +175 -0 kcms/navigation/ui/NavigationBar.qml [License: GPL(v2.0+)]
M +385 -250 kcms/navigation/ui/TutorialPhone.qml
R +- -- kcms/navigation/ui/images/Mascot_konqi-base-plasma.png [from: kcms/navigation/ui/Mascot_konqi-base-plasma.png - 100% similarity]
R +0 -0 kcms/navigation/ui/images/Mascot_konqi-base-plasma.png.license [from: kcms/navigation/ui/Mascot_konqi-base-plasma.png.license - 100% similarity]
R +- -- kcms/navigation/ui/images/katie.png [from: kcms/navigation/ui/katie.png - 100% similarity]
R +0 -0 kcms/navigation/ui/images/katie.png.license [from: kcms/navigation/ui/katie.png.license - 100% similarity]
R +- -- kcms/navigation/ui/images/konqi_kde.png [from: kcms/navigation/ui/konqi_kde.png - 100% similarity]
R +0 -0 kcms/navigation/ui/images/konqi_kde.png.license [from: kcms/navigation/ui/konqi_kde.png.license - 100% similarity]
M +127 -165 kcms/navigation/ui/main.qml
https://invent.kde.org/plasma/plasma-mobile/-/commit/6e43a6d5bda88c7f24d9e90b02dcd92ad25533e8
diff --git a/kcms/navigation/ui/AnimationHandler.qml b/kcms/navigation/ui/AnimationHandler.qml
index c610fa079..b7bf1f334 100644
--- a/kcms/navigation/ui/AnimationHandler.qml
+++ b/kcms/navigation/ui/AnimationHandler.qml
@@ -51,14 +51,20 @@ Item {
if (!running) {
return;
}
+
+ running = false;
loopTimer.stop();
stepTimer.stop();
- animations[_step].stop();
+
+ if (_step >= 0 && _step < animations.length) {
+ animations[_step].stop();
+ }
}
function body(): void {
if (_step >= animations.length) {
hasFinished = true;
+ running = false;
finished();
return;
}
diff --git a/kcms/navigation/ui/DummyWindow.qml b/kcms/navigation/ui/DummyWindow.qml
new file mode 100644
index 000000000..cd8ab726b
--- /dev/null
+++ b/kcms/navigation/ui/DummyWindow.qml
@@ -0,0 +1,47 @@
+/*
+ SPDX-FileCopyrightText: 2025 Luis Büchi <[email protected]>
+ SPDX-FileCopyrightText: 2026 Micah Stanley <[email protected]>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import org.kde.kirigami as Kirigami
+
+Rectangle {
+ id: root
+
+ property real windowScale: 1
+ property real offset: 0
+ property string imageSource: ""
+
+ property real baseWidth: 0
+ property real baseHeight: 0
+ property real phoneRadius: 0
+ property real phoneBorderWidth: 0
+
+ Kirigami.Theme.inherit: false
+ Kirigami.Theme.colorSet: Kirigami.Theme.Window
+
+ color: Kirigami.Theme.backgroundColor
+ width: Math.round(baseWidth * windowScale)
+ height: Math.round(baseHeight * windowScale)
+ radius: Math.max(0, phoneRadius - phoneBorderWidth)
+
+ Image {
+ source: root.imageSource
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+
+ width: {
+ if (root.width > root.height * 0.8) {
+ return Math.round(root.height * 0.6)
+ }
+ return Math.round(root.width * 0.75)
+ }
+ fillMode: Image.PreserveAspectFit
+ }
+}
diff --git a/kcms/navigation/ui/NavigationBar.qml b/kcms/navigation/ui/NavigationBar.qml
new file mode 100644
index 000000000..acffaba9f
--- /dev/null
+++ b/kcms/navigation/ui/NavigationBar.qml
@@ -0,0 +1,175 @@
+/*
+ SPDX-FileCopyrightText: 2026 Micah Stanley <[email protected]>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import org.kde.kirigami as Kirigami
+
+Item {
+ id: root
+
+ property bool taskSwitcher: false
+ property real phoneRadius: 0
+ property real phoneBorderWidth: 0
+
+ function animateTaskSwitcher() {
+ taskSwitcherButtonTouchFeedbackAnimation.restart();
+ }
+
+ function animateHome() {
+ homeButtonTouchFeedbackAnimation.restart();
+ }
+
+ function animateClose() {
+ closeButtonTouchFeedbackAnimation.restart();
+ }
+
+ function stopAnimations() {
+ taskSwitcherButtonTouchFeedbackAnimation.stop();
+ homeButtonTouchFeedbackAnimation.stop();
+ closeButtonTouchFeedbackAnimation.stop();
+ taskSwitcherButtonTouchFeedback.opacity = 0;
+ homeButtonTouchFeedback.opacity = 0;
+ closeButtonTouchFeedback.opacity = 0;
+ }
+
+ Rectangle {
+ id: navbarBase
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: Kirigami.Units.largeSpacing * 2
+
+ radius: root.phoneRadius - root.phoneBorderWidth
+ color: root.taskSwitcher ? "black" : Kirigami.Theme.backgroundColor
+ opacity: root.taskSwitcher ? 0.1 : 1
+
+ layer.enabled: true
+
+ Rectangle {
+ width: navbarBase.width
+ height: navbarBase.radius
+ color: navbarBase.color
+ }
+ }
+
+ RowLayout {
+ id: navbarButtons
+ anchors.top: navbarBase.top
+ anchors.bottom: navbarBase.bottom
+ anchors.horizontalCenter: navbarBase.horizontalCenter
+
+ width: Math.min(navbarBase.width * 0.7, navbarBase.height * 9)
+ uniformCellSizes: true
+
+ // task switcher button
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ Rectangle {
+ id: taskSwitcherButtonTouchFeedback
+ radius: height * 0.5
+ anchors.fill: parent
+ anchors.leftMargin: -radius
+ anchors.rightMargin: -radius
+ opacity: 0
+ color: Kirigami.Theme.textColor
+ }
+
+ NumberAnimation {
+ id: taskSwitcherButtonTouchFeedbackAnimation
+ target: taskSwitcherButtonTouchFeedback
+ property: "opacity"
+ from: 0.1
+ to: 0
+ duration: Kirigami.Units.shortDuration
+ easing.type: Easing.InOutQuad
+ }
+
+ Kirigami.Icon {
+ anchors.centerIn: parent
+ implicitHeight: navbarButtons.height * 0.4
+ implicitWidth: navbarButtons.height * 0.4
+ width: implicitWidth
+ height: implicitHeight
+ source: "mobile-task-switcher"
+ }
+ }
+
+ // home button
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ Rectangle {
+ id: homeButtonTouchFeedback
+ radius: height * 0.5
+ anchors.fill: parent
+ anchors.leftMargin: -radius
+ anchors.rightMargin: -radius
+ opacity: 0
+ color: Kirigami.Theme.textColor
+ }
+
+ NumberAnimation {
+ id: homeButtonTouchFeedbackAnimation
+ target: homeButtonTouchFeedback
+ property: "opacity"
+ from: 0.1
+ to: 0
+ duration: Kirigami.Units.shortDuration
+ easing.type: Easing.InOutQuad
+ }
+
+ Kirigami.Icon {
+ anchors.centerIn: parent
+ implicitHeight: navbarButtons.height * 0.4
+ implicitWidth: navbarButtons.height * 0.4
+ width: implicitWidth
+ height: implicitHeight
+ source: "start-here-kde"
+ }
+ }
+
+ // close app button
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ Rectangle {
+ id: closeButtonTouchFeedback
+ radius: height * 0.5
+ anchors.fill: parent
+ anchors.leftMargin: -radius
+ anchors.rightMargin: -radius
+ opacity: 0
+ color: Kirigami.Theme.textColor
+ }
+
+ NumberAnimation {
+ id: closeButtonTouchFeedbackAnimation
+ target: closeButtonTouchFeedback
+ property: "opacity"
+ from: 0.1
+ to: 0
+ duration: Kirigami.Units.shortDuration
+ easing.type: Easing.InOutQuad
+ }
+
+ Kirigami.Icon {
+ anchors.centerIn: parent
+ implicitHeight: navbarButtons.height * 0.4
+ implicitWidth: navbarButtons.height * 0.4
+ width: implicitWidth
+ height: implicitHeight
+ source: "mobile-close-app"
+ }
+ }
+ }
+}
diff --git a/kcms/navigation/ui/TutorialPhone.qml b/kcms/navigation/ui/TutorialPhone.qml
index 52014b70a..09fc90119 100644
--- a/kcms/navigation/ui/TutorialPhone.qml
+++ b/kcms/navigation/ui/TutorialPhone.qml
@@ -5,6 +5,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
+import QtQuick.Effects
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kirigami as Kirigami
@@ -18,16 +19,101 @@ Item {
Layout.preferredWidth: phoneWidth
Layout.preferredHeight: phoneHeight
- property bool showBackground: true
property int fingerSize: 20
property int _endTimeout: 2000
+ property bool _gesturesTutorial: true
+ property int _activeIndex: 0
+
+ function playTutorial(gestureMode: bool, index: int): void {
+ _gesturesTutorial = gestureMode;
+ stopAllAnimations(gestureMode);
+
+ if (_gesturesTutorial) {
+ playGestureTutorial(index);
+ } else {
+ playButtonTutorial(index);
+ }
+ }
+
+ function stopAllAnimations(gestureMode: bool): void {
+ _gesturesTutorial = gestureMode;
+
+ switcherAnimation.finished.disconnect(loopCurrentAnimation);
+ flickAnimation.finished.disconnect(loopCurrentAnimation);
+ scrubAnimation.finished.disconnect(loopCurrentAnimation);
+ buttonsTaskSwitcherAnimation.finished.disconnect(loopCurrentAnimation);
+ buttonsHomeAnimation.finished.disconnect(loopCurrentAnimation);
+ buttonsCloseAnimation.finished.disconnect(loopCurrentAnimation);
+
+ switcherAnimation.stop();
+ windowSwitcherAnimation.stop();
+ flickAnimation.stop();
+ windowFlickAnimation.stop();
+ scrubAnimation.stop();
+ windowScrubAnimation.stop();
+ buttonsTaskSwitcherAnimation.stop();
+ buttonsHomeAnimation.stop();
+ buttonsCloseAnimation.stop();
+
+ touchOnAnim.stop();
+ touchOffAnim.stop();
+ homeScreenBackgroundOpacityAnimation.stop();
+ homeScreenBackgroundScaleAnimation.stop();
+
+ navigationBar.stopAnimations();
+
+ switcherAnimation.reset();
+ windowSwitcherAnimation.reset();
+ flickAnimation.reset();
+ windowFlickAnimation.reset();
+ scrubAnimation.reset();
+ windowScrubAnimation.reset();
+ buttonsTaskSwitcherAnimation.reset();
+ buttonsHomeAnimation.reset();
+ buttonsCloseAnimation.reset();
+ }
+
+ function loopCurrentAnimation(): void {
+ playTutorial(_gesturesTutorial, _activeIndex);
+ }
+
+ function playGestureTutorial(index: int): void {
+ _activeIndex = index;
+ if (index === 0) {
+ switcherAnimation.finished.connect(loopCurrentAnimation);
+ switcherAnimation.start();
+ windowSwitcherAnimation.start();
+ } else if (index === 1) {
+ flickAnimation.finished.connect(loopCurrentAnimation);
+ flickAnimation.start();
+ windowFlickAnimation.start();
+ } else if (index === 2) {
+ scrubAnimation.finished.connect(loopCurrentAnimation);
+ scrubAnimation.start();
+ windowScrubAnimation.start();
+ }
+ }
+
+ function playButtonTutorial(index: int): void {
+ _activeIndex = index;
+
+ if (index === 0) {
+ buttonsTaskSwitcherAnimation.finished.connect(loopCurrentAnimation);
+ buttonsTaskSwitcherAnimation.start();
+ } else if (index === 1) {
+ buttonsHomeAnimation.finished.connect(loopCurrentAnimation);
+ buttonsHomeAnimation.start();
+ } else if (index === 2) {
+ buttonsCloseAnimation.finished.connect(loopCurrentAnimation);
+ buttonsCloseAnimation.start();
+ }
+ }
Rectangle {
id: phone
width: root.phoneWidth
height: root.phoneHeight
-
border.color: {
let color = Kirigami.Theme.textColor
// note: luminance calculation from https://en.wikipedia.org/wiki/Relative_luminance
@@ -42,144 +128,123 @@ Item {
radius: Kirigami.Units.largeSpacing
Kirigami.Icon {
- visible: root.showBackground
+ id: homeScreenBackground
+ opacity: 0
source: "start-here-kde"
smooth: true
anchors.verticalCenter: phone.verticalCenter
anchors.horizontalCenter: phone.horizontalCenter
+
+ NumberAnimation {
+ id: homeScreenBackgroundOpacityAnimation
+ target: homeScreenBackground
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.InOutQuad
+ }
+
+ NumberAnimation {
+ id: homeScreenBackgroundScaleAnimation
+ target: homeScreenBackground
+ property: "scale"
+ from: 1.5
+ to: 1
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.InOutQuad
+ }
+
+ function homeScreenBackgroundAnimation() {
+ homeScreenBackgroundOpacityAnimation.restart();
+ homeScreenBackgroundScaleAnimation.restart();
+ }
}
Item {
id: phoneContent
-
clip: true
-
- anchors.horizontalCenter: phone.horizontalCenter
- anchors.verticalCenter: phone.verticalCenter
-
+ anchors.centerIn: parent
width: phone.width - phone.border.width * 2
height: phone.height - phone.border.width * 2
- Rectangle {
+ DummyWindow {
id: dummyWindow
-
- Kirigami.Theme.inherit: false
- Kirigami.Theme.colorSet: Kirigami.Theme.Window
-
- property real scale: 1
- property real offset: 0
-
- color: Kirigami.Theme.backgroundColor
- width: Math.round(phoneContent.width * scale)
- height: Math.round(phoneContent.height * scale)
- radius: Math.max(0, phone.radius - phone.border.width)
+ imageSource: "images/konqi_kde.png"
+ baseWidth: phoneContent.width
+ baseHeight: phoneContent.height
+ phoneRadius: phone.radius
+ phoneBorderWidth: phone.border.width
anchors.verticalCenter: phoneContent.verticalCenter
anchors.horizontalCenter: phoneContent.horizontalCenter
anchors.horizontalCenterOffset: Math.round(phoneContent.width * offset)
-
- Image {
- source: "konqi_kde.png"
-
- anchors.horizontalCenter: dummyWindow.horizontalCenter
- anchors.verticalCenter: dummyWindow.verticalCenter
-
- width: {
- if (dummyWindow.width > dummyWindow.height * 0.8) {
- return Math.round(dummyWindow.height * 0.6)
- }
- return Math.round(dummyWindow.width * 0.75)
- }
- fillMode: Image.PreserveAspectFit
- }
}
- Rectangle {
+ DummyWindow {
id: dummyWindow2
-
- Kirigami.Theme.inherit: false
- Kirigami.Theme.colorSet: Kirigami.Theme.Window
-
- property real scale: dummyWindow.scale
- property real offset: 0.5
-
- color: Kirigami.Theme.backgroundColor
- width: phoneContent.width * scale
- height: phoneContent.height * scale
- radius: Math.max(0, phone.radius - phone.border.width)
+ windowScale: dummyWindow.windowScale
+ offset: 0.5
+ imageSource: "images/katie.png"
+ baseWidth: phoneContent.width
+ baseHeight: phoneContent.height
+ phoneRadius: phone.radius
+ phoneBorderWidth: phone.border.width
anchors.verticalCenter: phoneContent.verticalCenter
anchors.horizontalCenter: phoneContent.horizontalCenter
anchors.horizontalCenterOffset: phoneContent.width * (-offset - 0.6 + dummyWindow.offset)
-
- Image {
- source: "katie.png"
-
- anchors.horizontalCenter: dummyWindow2.horizontalCenter
- anchors.verticalCenter: dummyWindow2.verticalCenter
-
- width: {
- if (dummyWindow.width > dummyWindow.height * 0.8) {
- return Math.round(dummyWindow.height * 0.6)
- }
- return Math.round(dummyWindow.width * 0.75)
- }
- fillMode: Image.PreserveAspectFit
- }
}
- Rectangle {
+ DummyWindow {
id: dummyWindow3
-
- Kirigami.Theme.inherit: false
- Kirigami.Theme.colorSet: Kirigami.Theme.Window
-
- property real scale: dummyWindow.scale
- property real offset: 1.2
-
- color: Kirigami.Theme.backgroundColor
- width: phoneContent.width * scale
- height: phoneContent.height * scale
- radius: Math.max(0, phone.radius - phone.border.width)
+ windowScale: dummyWindow.windowScale
+ offset: 1.2
+ imageSource: "images/Mascot_konqi-base-plasma.png"
+ baseWidth: phoneContent.width
+ baseHeight: phoneContent.height
+ phoneRadius: phone.radius
+ phoneBorderWidth: phone.border.width
anchors.verticalCenter: phoneContent.verticalCenter
anchors.horizontalCenter: phoneContent.horizontalCenter
anchors.horizontalCenterOffset: phoneContent.width * (-offset + dummyWindow.offset - dummyWindow2.offset)
+ }
- Image {
- source: "Mascot_konqi-base-plasma.png"
+ NavigationBar {
+ id: navigationBar
+ anchors.fill: parent
+ visible: !root._gesturesTutorial
+ phoneRadius: phone.radius
+ phoneBorderWidth: phone.border.width
+ }
- anchors.horizontalCenter: dummyWindow3.horizontalCenter
- anchors.verticalCenter: dummyWindow3.verticalCenter
+ // phone border to overlay over any imperfections
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: -phone.border.width
- width: {
- if (dummyWindow.width > dummyWindow.height * 0.8) {
- return Math.round(dummyWindow.height * 0.6)
- }
- return Math.round(dummyWindow.width * 0.75)
- }
- fillMode: Image.PreserveAspectFit
- }
+ border.color: phone.border.color
+ border.width: phone.border.width
+ color: "transparent"
+ radius: Kirigami.Units.largeSpacing
}
}
Rectangle {
id: touchPoint
-
property int size: root.fingerSize
+ property real yPosition: 0
+ property real xPosition: 0
width: size
height: size
radius: size / 2
- property real yPosition: 0
- property real xPosition: 0
-
anchors.verticalCenter: phone.bottom
anchors.verticalCenterOffset: Math.round(-yPosition * root.phoneHeight / 6)
-
anchors.horizontalCenter: phone.horizontalCenter
anchors.horizontalCenterOffset: Math.round(xPosition * root.phoneWidth * 0.3)
@@ -189,7 +254,7 @@ Item {
}
}
- // into task switcher animation
+ // gestures - into task switcher animation
AnimationHandler {
id: switcherAnimation
@@ -198,53 +263,47 @@ Item {
function reset(): void {
touchPoint.yPosition = 0;
touchPoint.xPosition = 0;
+ touchPoint.visible = true;
}
animations: [
NumberAnimation {
target: touchPoint
property: "yPosition"
-
from: 0
to: 1
+ duration: 1500
+ easing.type: Easing.InOutQuad
onStarted: {
switcherAnimation.reset();
- root.touchOnAnim.start()
+ root.touchOnAnim.start();
}
-
- duration: 1500
- easing.type: Easing.InOutQuad
},
NumberAnimation {
target: touchPoint
property: "size"
-
to: 0
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
},
NumberAnimation {
target: touchPoint
property: "xPosition"
-
from: 0
to: 1.5
+ duration: 500
+ easing.type: Easing.InOutQuad
onStarted: {
touchPoint.yPosition = 2;
- root.touchOnAnim.start()
+ root.touchOnAnim.start();
}
- duration: 500
- easing.type: Easing.InOutQuad
},
NumberAnimation {
target: touchPoint
property: "size"
-
to: 0
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
}
@@ -252,60 +311,51 @@ Item {
delays: [
500,
500,
- -Kirigami.Units.longDuration * 2,
+ -Kirigami.Units.longDuration * 2
]
}
AnimationHandler {
id: windowSwitcherAnimation
-
endTimeout: root._endTimeout
-
function reset(): void {
dummyWindow.offset = 0;
- dummyWindow.scale = 1;
+ dummyWindow.windowScale = 1;
dummyWindow2.offset = 0.5;
+ homeScreenBackground.opacity = 0;
}
animations: [
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
from: 1
to: 0.5
+ duration: switcherAnimation.animations[0].duration
+ easing.type: switcherAnimation.animations[0].easing.type
onStarted: {
windowSwitcherAnimation.reset();
}
-
- duration: switcherAnimation.animations[0].duration
- easing.type: switcherAnimation.animations[0].easing.type
},
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
to: 0.55
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
},
NumberAnimation {
target: dummyWindow2
property: "offset"
-
to: 0
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
},
NumberAnimation {
target: dummyWindow
property: "offset"
-
to: 0.6
-
duration: switcherAnimation.animations[2].duration
easing.type: switcherAnimation.animations[2].easing.type
}
@@ -313,154 +363,138 @@ Item {
delays: [
switcherAnimation.delays[0],
immediate,
- switcherAnimation.delays[1],
+ switcherAnimation.delays[1]
]
}
- // flick to home animation
+ // gestures - flick to home animation
AnimationHandler {
id: flickAnimation
-
endTimeout: root._endTimeout
-
function reset(): void {
touchPoint.yPosition = 0;
+ touchPoint.visible = true;
}
animations: [
NumberAnimation {
target: touchPoint
property: "yPosition"
-
from: 0
to: 1
+ duration: 900
+ easing.type: Easing.InQuart
onStarted: {
- root.touchOnAnim.start()
+ flickAnimation.reset();
+ root.touchOnAnim.start();
}
-
- duration: 900
- easing.type: Easing.InQuart
},
NumberAnimation {
target: touchPoint
property: "size"
-
to: 0
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
}
]
delays: [
- -Kirigami.Units.longDuration,
+ -Kirigami.Units.longDuration
]
}
AnimationHandler {
id: windowFlickAnimation
-
endTimeout: root._endTimeout
-
function reset(): void {
dummyWindow.offset = 0;
dummyWindow.opacity = 1;
- dummyWindow.scale = 1;
+ dummyWindow.windowScale = 1;
+ homeScreenBackground.opacity = 0;
}
animations: [
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
from: 1
to: 0.5
+ duration: flickAnimation.animations[0].duration
+ easing.type: flickAnimation.animations[0].easing.type
onStarted: {
windowFlickAnimation.reset();
dummyWindow2.offset = 0.5;
}
-
- duration: flickAnimation.animations[0].duration
- easing.type: flickAnimation.animations[0].easing.type
},
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
to: 0.1
-
duration: 300
easing.type: Easing.InQuad
+
+ onFinished: {
+ homeScreenBackground.homeScreenBackgroundAnimation();
+ }
},
NumberAnimation {
target: dummyWindow
property: "opacity"
-
to: 0
-
duration: 300
easing.type: Easing.Linear
}
]
delays: [
1, // for some reason setting this to 0 creates a runtime error
- immediate,
+ immediate
]
}
- // scrub animation
+ // gestures - scrub animation
AnimationHandler {
id: scrubAnimation
-
endTimeout: root._endTimeout
-
function reset(): void {
touchPoint.yPosition = 0;
touchPoint.xPosition = 0;
+ touchPoint.visible = true;
}
animations: [
NumberAnimation {
target: touchPoint
property: "yPosition"
-
from: 0
to: 0.2
+ duration: 900
+ easing.type: Easing.InOutQuart
onStarted: {
scrubAnimation.reset();
- root.touchOnAnim.start()
+ root.touchOnAnim.start();
}
-
- duration: 900
- easing.type: Easing.InOutQuart
},
NumberAnimation {
target: touchPoint
property: "xPosition"
-
from: 0
to: 1
-
duration: 1500
easing.type: Easing.InOutQuart
},
NumberAnimation {
target: touchPoint
property: "xPosition"
-
to: 0.5
-
duration: 700
easing.type: Easing.InOutCubic
},
NumberAnimation {
target: touchPoint
property: "size"
-
to: 0
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
}
@@ -474,14 +508,13 @@ Item {
AnimationHandler {
id: windowScrubAnimation
-
endTimeout: root._endTimeout
-
function reset(): void {
- dummyWindow.scale = 1;
+ dummyWindow.windowScale = 1;
dummyWindow.offset = 0;
dummyWindow2.offset = 0.5
dummyWindow3.offset = 1.2;
+ homeScreenBackground.opacity = 0;
}
animations: [
@@ -489,85 +522,66 @@ Item {
// this is just to add some delay at the start when starting at the same time as the touch point animation
target: dummyWindow
property: "opacity"
-
to: 1
+ duration: 0
onStarted: {
windowScrubAnimation.reset();
}
-
- duration: 0
},
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
to: 0.55
-
-
duration: 300
easing.type: Easing.InOutQuad
},
NumberAnimation {
target: dummyWindow2
property: "offset"
-
to: 0
-
duration: 300
easing.type: Easing.InOutQuad
},
NumberAnimation {
target: dummyWindow
property: "offset"
-
to: 1.2
-
duration: scrubAnimation.animations[1].duration
easing.type: Easing.InOutQuint
},
NumberAnimation {
target: dummyWindow
property: "offset"
-
to: 0.6
-
duration: scrubAnimation.animations[2].duration
easing.type: Easing.InOutQuart
},
NumberAnimation {
target: dummyWindow
- property: "scale"
-
+ property: "windowScale"
to: 1
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
},
NumberAnimation { // move leftmost window out of the way, otherwise it overlaps
target: dummyWindow3
property: "offset"
-
to: 1.8
-
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
},
NumberAnimation { // move middle window (that's to be focused) a bit to the side to counteract moving of the first window
target: dummyWindow2
property: "offset"
-
to: 0.4
-
duration: Kirigami.Units.longDuration
easing.type: Easing.Linear
},
NumberAnimation { // move first (rightmost) window to get a bit more space between it and the middle one during the animation
target: dummyWindow
property: "offset"
-
to: 1
-
duration: Kirigami.Units.longDuration
easing.type: Easing.Linear
}
@@ -584,88 +598,209 @@ Item {
]
}
- function stopAnimation(): void {
- switcherAnimation.finished.disconnect(startSwitcherAnimation);
- flickAnimation.finished.disconnect(startFlickAnimation);
- scrubAnimation.finished.disconnect(startScrubAnimation);
-
- switcherAnimation.stop();
- flickAnimation.stop();
- scrubAnimation.stop();
+ NumberAnimation {
+ id: touchOffAnim
+ target: touchPoint
+ property: "size"
+ to: 0
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.InQuad
}
- function startSwitcherAnimation(): void {
- switcherAnimation.start();
- windowSwitcherAnimation.start();
+ property var touchOnAnim: NumberAnimation {
+ target: touchPoint
+ property: "size"
+ to: root.fingerSize
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.OutQuad
}
- function loopSwitcherAnimation(): void {
- switcherAnimation.finished.connect(startSwitcherAnimation);
- startSwitcherAnimation();
- }
+ // buttons - open task switcher animation
+ AnimationHandler {
+ id: buttonsTaskSwitcherAnimation
+ endTimeout: root._endTimeout
+ function reset(): void {
+ dummyWindow.windowScale = 1;
+ dummyWindow.offset = 0;
+ dummyWindow2.offset = 0.5
+ dummyWindow3.offset = 1.2;
+ homeScreenBackground.opacity = 0;
+ navigationBar.taskSwitcher = false;
+ touchPoint.visible = false;
+ }
- function startFlickAnimation(): void {
- flickAnimation.start();
- windowFlickAnimation.start();
- }
+ animations: [
+ NumberAnimation {
+ // this is just to add some delay at the start when starting at the same time as the touch point animation
+ target: dummyWindow
+ property: "opacity"
+ to: 1
+ duration: 0
- function loopFlickAnimation(): void {
- flickAnimation.finished.connect(startFlickAnimation);
- startFlickAnimation();
+ onStarted: {
+ buttonsTaskSwitcherAnimation.reset();
+ }
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "windowScale"
+ to: 0.55
+ duration: 300
+ easing.type: Easing.InOutQuad
- }
+ onStarted: {
+ navigationBar.taskSwitcher = true;
+ navigationBar.animateTaskSwitcher();
- function startScrubAnimation(): void {
- scrubAnimation.start();
- windowScrubAnimation.start();
- }
+ }
+ },
+ NumberAnimation {
+ target: dummyWindow2
+ property: "offset"
+ to: 0
+ duration: 300
+ easing.type: Easing.InOutQuad
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "offset"
+ to: 0.6
+ duration: 700
+ easing.type: Easing.InOutQuint
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "offset"
+ to: 0
+ duration: 700
+ easing.type: Easing.InOutQuart
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "windowScale"
+ to: 1
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.InOutQuad
- function loopScrubAnimation(): void {
- scrubAnimation.finished.connect(startScrubAnimation);
- startScrubAnimation();
+ onFinished: {
+ navigationBar.taskSwitcher = false;
+ }
+ },
+ NumberAnimation {
+ target: dummyWindow3
+ property: "offset"
+ to: 1.2
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.InOutQuad
+ },
+ NumberAnimation {
+ target: dummyWindow2
+ property: "offset"
+ to: 0.5
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.Linear
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "offset"
+ to: 0
+ duration: Kirigami.Units.longDuration
+ easing.type: Easing.Linear
+ }
+ ]
+ delays: [
+ 400,
+ immediate,
+ 200,
+ 700,
+ 700,
+ immediate,
+ immediate,
+ immediate
+ ]
}
- function startCyclingAnimations(): void {
- switcherAnimation.finished.connect(() => {
- switcherAnimation.reset();
- windowSwitcherAnimation.reset();
-
- startFlickAnimation();
- });
- flickAnimation.finished.connect(() => {
- flickAnimation.reset();
- windowFlickAnimation.reset();
+ // buttons - return home animation
+ AnimationHandler {
+ id: buttonsHomeAnimation
+ endTimeout: root._endTimeout
+ function reset(): void {
+ dummyWindow.offset = 0;
+ dummyWindow.opacity = 1;
+ dummyWindow.windowScale = 1;
+ homeScreenBackground.opacity = 0;
+ navigationBar.taskSwitcher = false;
+ touchPoint.visible = false;
+ }
- startScrubAnimation();
- });
- scrubAnimation.finished.connect(() => {
- scrubAnimation.reset();
- windowScrubAnimation.reset();
+ animations: [
+ NumberAnimation {
+ // this is just to add some delay at the start when starting at the same time as the touch point animation
+ target: dummyWindow
+ property: "opacity"
+ to: 1
+ duration: 0
- startSwitcherAnimation();
- });
+ onStarted: {
+ buttonsHomeAnimation.reset();
+ }
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "opacity"
+ to: 0
+ duration: 0
- startSwitcherAnimation();
+ onStarted: {
+ navigationBar.animateHome();
+ homeScreenBackground.homeScreenBackgroundAnimation();
+ }
+ }
+ ]
+ delays: [
+ 500
+ ]
}
- NumberAnimation {
- id: touchOffAnim
- target: touchPoint
- property: "size"
-
- to: 0
-
- duration: Kirigami.Units.longDuration
- easing.type: Easing.InQuad
- }
+ // buttons - close app animation
+ AnimationHandler {
+ id: buttonsCloseAnimation
+ endTimeout: root._endTimeout
+ function reset(): void {
+ dummyWindow.offset = 0;
+ dummyWindow.opacity = 1;
+ dummyWindow.windowScale = 1;
+ homeScreenBackground.opacity = 0;
+ navigationBar.taskSwitcher = false;
+ touchPoint.visible = false;
+ }
- property var touchOnAnim: NumberAnimation {
- target: touchPoint
- property: "size"
+ animations: [
+ NumberAnimation {
+ // this is just to add some delay at the start when starting at the same time as the touch point animation
+ target: dummyWindow
+ property: "opacity"
+ to: 1
+ duration: 0
- to: root.fingerSize
+ onStarted: {
+ buttonsCloseAnimation.reset();
+ }
+ },
+ NumberAnimation {
+ target: dummyWindow
+ property: "opacity"
+ to: 0
+ duration: 0
- duration: Kirigami.Units.longDuration
- easing.type: Easing.OutQuad
+ onStarted: {
+ navigationBar.animateClose();
+ homeScreenBackground.homeScreenBackgroundAnimation();
+ }
+ }
+ ]
+ delays: [
+ 500
+ ]
}
}
diff --git a/kcms/navigation/ui/Mascot_konqi-base-plasma.png b/kcms/navigation/ui/images/Mascot_konqi-base-plasma.png
similarity index 100%
rename from kcms/navigation/ui/Mascot_konqi-base-plasma.png
rename to kcms/navigation/ui/images/Mascot_konqi-base-plasma.png
diff --git a/kcms/navigation/ui/Mascot_konqi-base-plasma.png.license b/kcms/navigation/ui/images/Mascot_konqi-base-plasma.png.license
similarity index 100%
rename from kcms/navigation/ui/Mascot_konqi-base-plasma.png.license
rename to kcms/navigation/ui/images/Mascot_konqi-base-plasma.png.license
diff --git a/kcms/navigation/ui/katie.png b/kcms/navigation/ui/images/katie.png
similarity index 100%
rename from kcms/navigation/ui/katie.png
rename to kcms/navigation/ui/images/katie.png
diff --git a/kcms/navigation/ui/katie.png.license b/kcms/navigation/ui/images/katie.png.license
similarity index 100%
rename from kcms/navigation/ui/katie.png.license
rename to kcms/navigation/ui/images/katie.png.license
diff --git a/kcms/navigation/ui/konqi_kde.png b/kcms/navigation/ui/images/konqi_kde.png
similarity index 100%
rename from kcms/navigation/ui/konqi_kde.png
rename to kcms/navigation/ui/images/konqi_kde.png
diff --git a/kcms/navigation/ui/konqi_kde.png.license b/kcms/navigation/ui/images/konqi_kde.png.license
similarity index 100%
rename from kcms/navigation/ui/konqi_kde.png.license
rename to kcms/navigation/ui/images/konqi_kde.png.license
diff --git a/kcms/navigation/ui/main.qml b/kcms/navigation/ui/main.qml
index 64ec62a82..defb5243c 100644
--- a/kcms/navigation/ui/main.qml
+++ b/kcms/navigation/ui/main.qml
@@ -4,9 +4,12 @@
* 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 QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls as Controls
+import QtQuick.Effects
+
+import Qt5Compat.GraphicalEffects
import org.kde.kirigami as Kirigami
import org.kde.kcmutils as KCM
@@ -24,205 +27,164 @@ KCM.SimpleKCM {
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
- }
+ ListView {
+ id: tutorialView
- Item {
- id: tutorialContainer
- visible: !ShellSettings.Settings.navigationPanelEnabled
+ property int phoneWidth: Math.min(Screen.width * 0.25, gestureSelectionCard.maximumWidth * 0.65)
+ property int phoneHeight: phoneWidth * Screen.height / Screen.width
+ property int count: 3
+ property int fingerSize: 20
- 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.fillWidth: true
+ Layout.preferredHeight: phoneHeight + fingerSize / 2
+ Layout.topMargin: Kirigami.Units.largeSpacing * 2
+ Layout.leftMargin: Kirigami.Units.largeSpacing * 2
+ Layout.rightMargin: Kirigami.Units.largeSpacing * 2
- Layout.alignment: Qt.AlignCenter
- Layout.preferredWidth: phoneWidth
- Layout.preferredHeight: phoneHeight + fingerSize / 2
- Layout.topMargin: Kirigami.Units.largeSpacing * 2
+ clip: true
+ orientation: ListView.Horizontal
+ spacing: Math.min(Math.round((tutorialView.width - phoneWidth) * 0.5), Kirigami.Units.largeSpacing * 6)
- clip: true
+ snapMode: ListView.SnapOneItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ preferredHighlightBegin: width / 2 - phoneWidth / 2
+ preferredHighlightEnd: width / 2 + phoneWidth / 2
- MouseArea {
- id: tutorialSwitcherInput
+ property int activeTutorialIndex: currentIndex
- anchors.fill: tutorialContainer
+ model: tutorialView.count
+ delegate: Item {
+ id: delegateItem
- preventStealing: true
+ required property int index
- property int activeTutorialIndex: 0
+ width: tutorialView.phoneWidth
+ height: tutorialView.phoneHeight
- property real lastX
- property real lastDelta
+ property bool isCurrentItem: ListView.isCurrentItem
+ property bool isGestureMode: !ShellSettings.Settings.navigationPanelEnabled
- 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()
- }
+ onIsCurrentItemChanged: {
+ if (isCurrentItem) {
+ tutorialPhone.playTutorial(isGestureMode, delegateItem.index);
+ } else {
+ tutorialPhone.stopAllAnimations(isGestureMode);
}
+ }
- 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;
- }
+ onIsGestureModeChanged: {
+ if (isCurrentItem) {
+ tutorialPhone.playTutorial(isGestureMode, delegateItem.index);
+ } else {
+ tutorialPhone.stopAllAnimations(isGestureMode);
}
+ }
- 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);
+ function playTutorial(gestureMode, idx) {
+ tutorialPhone.playTutorial(gestureMode, idx);
+ }
+
+ TutorialPhone {
+ id: tutorialPhone
+ anchors.fill: parent
+ phoneWidth: delegateItem.width
+ phoneHeight: delegateItem.height
+ fingerSize: tutorialView.fingerSize
+
+ Component.onCompleted: {
+ if (delegateItem.index === tutorialView.activeTutorialIndex) {
+ tutorialPhone.playTutorial(isGestureMode, delegateItem.index);
+ } else {
+ tutorialPhone.stopAllAnimations(isGestureMode);
}
- tutorialLayout.offset += delta
- lastDelta = delta;
- lastX = mouse.x
}
+ }
+ }
- NumberAnimation {
- id: releaseAnim
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ id: maskRect
+ width: tutorialView.width
+ height: tutorialView.height
- target: tutorialLayout
- property: "offset"
+ property real gradientBoundaries: Math.max((tutorialView.width * 0.5) - (tutorialView.phoneWidth * 0.5) - (Kirigami.Units.gridUnit * 6), 0) / Math.max(1, width)
+ property real gradientDistance: Math.max(((tutorialView.width * 0.5) - (tutorialView.phoneWidth * 0.5)) / Math.max(1, width), maskRect.gradientBoundaries)
- to: -tutorialSwitcherInput.activeTutorialIndex * (tutorialContainer.phoneWidth + tutorialLayout.spacing)
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
- duration: Kirigami.Units.longDuration
- easing.type: Easing.InOutQuad
+ GradientStop { position: maskRect.gradientBoundaries; color: 'transparent' }
+ GradientStop { position: 0 + maskRect.gradientDistance; color: 'white' }
+ GradientStop { position: 1.0 - maskRect.gradientDistance; color: 'white' }
+ GradientStop { position: 1.0 - maskRect.gradientBoundaries; color: 'transparent' }
}
}
+ }
+ }
- 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
+ Controls.PageIndicator {
+ currentIndex: tutorialView.activeTutorialIndex
+ count: tutorialView.count
- phoneWidth: tutorialContainer.phoneWidth
- phoneHeight: tutorialContainer.phoneHeight
- fingerSize: tutorialContainer.fingerSize
+ Layout.alignment: Qt.AlignCenter
+ }
- Layout.alignment: Qt.AlignCenter
- }
+ FormCard.FormSectionText {
+ id: tutorialText
+ verticalAlignment: Text.AlignTop
+ Layout.preferredHeight: textMetrics.lineSpacing * 2
- TutorialPhone {
- id: scrubTutorial
+ property bool isGestureMode: !ShellSettings.Settings.navigationPanelEnabled
- phoneWidth: tutorialContainer.phoneWidth
- phoneHeight: tutorialContainer.phoneHeight
- fingerSize: tutorialContainer.fingerSize
- showBackground: false
+ FontMetrics {
+ id: textMetrics
+ font: tutorialText.font
+ }
- Layout.alignment: Qt.AlignCenter
- }
+ text: {
+ if (isGestureMode) {
+ switch (tutorialView.activeTutorialIndex) {
+ case 0: return i18n("Swipe up, hold, then release to enter the Task Switcher.");
+ case 1: return i18n("Swipe up from the bottom to return to Home Screen.");
+ case 2: return i18n("Swipe horizontally near the bottom to scrub through open tasks. Release to select.");
+ default: return "";
+ }
+ } else {
+ switch (tutorialView.activeTutorialIndex) {
+ case 0: return i18n("Tap the square button to enter the Task Switcher. Double tap to switch to the last used application.");
+ case 1: return i18n("Tap the center button to return to the Home Screen.");
+ case 2: return i18n("Tap the X button to close the current application.");
+ default: return "";
}
}
}
+ }
- 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
+ FormCard.FormHeader {
+ title: i18nc("@label", "Navigation Mode")
+ }
+ FormCard.FormCard {
+ id: gestureSelectionCard
- text: i18n("Swipe up, hold then release to enter Task Switcher")
+ Controls.ButtonGroup {
+ id: positionGroup
+ buttons: [navPanelRadio, gesturesRadio]
}
- FormCard.FormSectionText {
- visible: !ShellSettings.Settings.navigationPanelEnabled && tutorialSwitcherInput.activeTutorialIndex == 1
-
- text: i18n("Swipe up and release to go to Home Screen")
+ FormCard.FormRadioDelegate {
+ id: navPanelRadio
+ text: i18nc("Nav Panel Navigation Mode", "Buttons")
+ checked: ShellSettings.Settings.navigationPanelEnabled
+ onClicked: ShellSettings.Settings.navigationPanelEnabled = true;
}
- 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.FormRadioDelegate {
+ id: gesturesRadio
+ text: i18nc("Gestures Navigation Mode", "Swipe Gestures")
+ checked: !ShellSettings.Settings.navigationPanelEnabled
+ onClicked: ShellSettings.Settings.navigationPanelEnabled = false;
}
FormCard.FormDelegateSeparator { visible: keyboardToggleDelegate.visible; above: gesturesRadio; below: keyboardToggleDelegate }