From 576da3e087cbdb3cbe6ec32ab553311af535fa40 Mon Sep 17 00:00:00 2001
From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com>
Date: Mon, 2 Aug 2021 14:52:08 -0400
Subject: [PATCH] wizardview: add key navigations

Gitlab: #470

Change-Id: I2ec5c4a4b490895ea32fef64baf1e7c5b85cf9d5
---
 qml.qrc                                       |  1 +
 src/commoncomponents/BackButton.qml           |  7 ++
 src/commoncomponents/JamiSwitch.qml           | 62 ++++++++++++++
 src/commoncomponents/MaterialButton.qml       | 12 ++-
 src/commoncomponents/PhotoboothView.qml       | 84 ++++++++++++++++++-
 src/commoncomponents/PushButton.qml           |  2 +-
 src/constant/JamiTheme.qml                    | 12 +++
 src/mainview/components/ContactSearchBar.qml  |  7 +-
 src/wizardview/components/BackupKeyPage.qml   | 23 ++++-
 .../ConnectToAccountManagerPage.qml           | 49 +++++++++--
 .../components/CreateAccountPage.qml          | 53 +++++++++++-
 .../components/CreateSIPAccountPage.qml       | 24 ++++++
 .../components/ImportFromBackupPage.qml       | 49 +++++++++++
 .../components/ImportFromDevicePage.qml       | 26 ++++++
 src/wizardview/components/ProfilePage.qml     | 36 ++++++++
 src/wizardview/components/WelcomePage.qml     | 48 ++++++++++-
 16 files changed, 479 insertions(+), 16 deletions(-)
 create mode 100644 src/commoncomponents/JamiSwitch.qml

diff --git a/qml.qrc b/qml.qrc
index 297d67ee8..c6396fa30 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -159,5 +159,6 @@
         <file>src/constant/JamiResources.qml</file>
         <file>src/commoncomponents/BubbleLabel.qml</file>
         <file>src/commoncomponents/BackButton.qml</file>
+        <file>src/commoncomponents/JamiSwitch.qml</file>
     </qresource>
 </RCC>
diff --git a/src/commoncomponents/BackButton.qml b/src/commoncomponents/BackButton.qml
index 9f523b009..71eea2764 100644
--- a/src/commoncomponents/BackButton.qml
+++ b/src/commoncomponents/BackButton.qml
@@ -29,4 +29,11 @@ PushButton {
 
     source: JamiResources.ic_arrow_back_24dp_svg
     toolTipText: JamiStrings.back
+
+    Keys.onPressed: function (keyEvent) {
+        if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+            clicked()
+            keyEvent.accepted = true
+        }
+    }
 }
diff --git a/src/commoncomponents/JamiSwitch.qml b/src/commoncomponents/JamiSwitch.qml
new file mode 100644
index 000000000..055919efd
--- /dev/null
+++ b/src/commoncomponents/JamiSwitch.qml
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+
+import net.jami.Constants 1.0
+
+Switch {
+    id: root
+
+    indicator: Rectangle {
+        implicitWidth: JamiTheme.switchPreferredWidth
+        implicitHeight: JamiTheme.switchPreferredHeight
+
+        x: root.leftPadding
+        y: parent.height / 2 - height / 2
+
+        radius: JamiTheme.switchIndicatorRadius
+
+        color: root.checked ? JamiTheme.switchBackgroundCheckedColor : JamiTheme.whiteColor
+        border.color: root.checked ? JamiTheme.switchBackgroundCheckedColor :
+                                     JamiTheme.switchBackgroundBorderColor
+
+        Rectangle {
+            x: root.checked ? parent.width - width : 0
+            y: parent.height / 2 - height / 2
+
+            width: JamiTheme.switchIndicatorPreferredWidth
+            height: JamiTheme.switchIndicatorPreferredHeight
+
+            radius: JamiTheme.switchIndicatorRadius
+
+            color: (root.down || root.focus) ? Qt.darker(JamiTheme.switchBackgroundBorderColor, 1.2) :
+                                               JamiTheme.whiteColor
+            border.color: root.checked ? JamiTheme.switchBackgroundCheckedColor :
+                                         JamiTheme.switchIndicatorBorderColor
+        }
+    }
+
+    Keys.onPressed: function (keyEvent) {
+        if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+            checked = !checked
+            keyEvent.accepted = true
+        }
+    }
+}
diff --git a/src/commoncomponents/MaterialButton.qml b/src/commoncomponents/MaterialButton.qml
index 578c9922b..2a83d8b64 100644
--- a/src/commoncomponents/MaterialButton.qml
+++ b/src/commoncomponents/MaterialButton.qml
@@ -32,6 +32,7 @@ Button {
     property var color: "transparent"
     property var hoveredColor: undefined
     property var pressedColor: undefined
+    property var keysNavigationFocusColor: Qt.darker(hoveredColor, 2)
     property var outlined: false
     property string animatedImageSource: ""
 
@@ -149,7 +150,7 @@ Button {
                 return root.hoveredColor
             if (checked && root.pressedColor)
                 return root.pressedColor
-            return root.color
+            return root.focus ? root.keysNavigationFocusColor : root.color
         }
         border.color: {
             if (!outlined)
@@ -158,8 +159,15 @@ Button {
                 return root.hoveredColor
             if (checked && root.pressedColor)
                 return root.pressedColor
-            return root.color
+            return root.focus ? root.keysNavigationFocusColor : root.color
         }
         radius: 4
     }
+
+    Keys.onPressed: function (keyEvent) {
+        if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+            clicked()
+            keyEvent.accepted = true
+        }
+    }
 }
diff --git a/src/commoncomponents/PhotoboothView.qml b/src/commoncomponents/PhotoboothView.qml
index 7f1b33412..4593c808f 100644
--- a/src/commoncomponents/PhotoboothView.qml
+++ b/src/commoncomponents/PhotoboothView.qml
@@ -33,6 +33,9 @@ Item {
     property alias imageId: avatar.imageId
     required property real avatarSize
 
+    signal focusOnPreviousItem
+    signal focusOnNextItem
+
     width: avatarSize
     height: boothLayout.height
 
@@ -48,6 +51,14 @@ Item {
         isPreviewing = false
     }
 
+    function focusOnNextPhotoBoothItem () {
+        takePhotoButton.forceActiveFocus()
+    }
+
+    function focusOnPreviousPhotoBoothItem () {
+        importButton.forceActiveFocus()
+    }
+
     onVisibleChanged: {
         if (!visible) {
             stopBooth()
@@ -66,10 +77,28 @@ Item {
             qsTr("All files") + " (*)"
         ]
 
+        onVisibleChanged: {
+            if (!visible) {
+                rejected()
+            }
+        }
+
         onAccepted: {
+            if (importButton.focusAfterFileDialogClosed) {
+                importButton.focusAfterFileDialogClosed = false
+                importButton.forceActiveFocus()
+            }
+
             var filePath = UtilsAdapter.getAbsPath(file)
             AccountAdapter.setCurrentAccountAvatarFile(filePath)
         }
+
+        onRejected: {
+            if (importButton.focusAfterFileDialogClosed) {
+                importButton.focusAfterFileDialogClosed = false
+                importButton.forceActiveFocus()
+            }
+        }
     }
 
     ColumnLayout {
@@ -153,6 +182,7 @@ Item {
                 id: takePhotoButton
 
                 Layout.alignment: Qt.AlignHCenter
+
                 radius: JamiTheme.primaryRadius
                 imageColor: JamiTheme.textColor
                 toolTipText: JamiStrings.takePhoto
@@ -160,6 +190,23 @@ Item {
                             JamiResources.round_add_a_photo_24dp_svg :
                             JamiResources.baseline_camera_alt_24dp_svg
 
+                Keys.onPressed: function (keyEvent) {
+                    if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+                        clicked()
+                        keyEvent.accepted = true
+                    } else if (keyEvent.matches(StandardKey.MoveToPreviousLine)) {
+                        root.focusOnPreviousItem()
+                        keyEvent.accepted = true
+                    }
+                }
+
+                KeyNavigation.tab: {
+                    if (clearButton.visible)
+                        return clearButton
+                    return importButton
+                }
+                KeyNavigation.down: KeyNavigation.tab
+
                 onClicked: {
                     if (isPreviewing) {
                         flashAnimation.start()
@@ -176,13 +223,27 @@ Item {
             PushButton {
                 id: clearButton
 
-                visible: LRCInstance.currentAccountAvatarSet
                 Layout.alignment: Qt.AlignHCenter
+
+                visible: LRCInstance.currentAccountAvatarSet
+
                 radius: JamiTheme.primaryRadius
                 source: JamiResources.round_close_24dp_svg
                 toolTipText: JamiStrings.clearAvatar
                 imageColor: JamiTheme.textColor
 
+                KeyNavigation.tab: importButton
+                KeyNavigation.up: takePhotoButton
+                KeyNavigation.down: KeyNavigation.tab
+
+                Keys.onPressed: function (keyEvent) {
+                    if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+                        clicked()
+                        takePhotoButton.forceActiveFocus()
+                        keyEvent.accepted = true
+                    }
+                }
+
                 onClicked: {
                     stopBooth()
                     AccountAdapter.setCurrentAccountAvatarBase64()
@@ -192,12 +253,33 @@ Item {
             PushButton {
                 id: importButton
 
+                property bool focusAfterFileDialogClosed: false
+
                 Layout.alignment: Qt.AlignHCenter
+
                 radius: JamiTheme.primaryRadius
                 source: JamiResources.round_folder_24dp_svg
                 toolTipText: JamiStrings.importFromFile
                 imageColor: JamiTheme.textColor
 
+                Keys.onPressed: function (keyEvent) {
+                    if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
+                        focusAfterFileDialogClosed = true
+                        clicked()
+                        keyEvent.accepted = true
+                    } else if (keyEvent.matches(StandardKey.MoveToNextLine) ||
+                               keyEvent.key === Qt.Key_Tab) {
+                        root.focusOnNextItem()
+                        keyEvent.accepted = true
+                    }
+                }
+
+                KeyNavigation.up: {
+                    if (clearButton.visible)
+                        return clearButton
+                    return takePhotoButton
+                }
+
                 onClicked: {
                     stopBooth()
                     importFromFileDialog.open()
diff --git a/src/commoncomponents/PushButton.qml b/src/commoncomponents/PushButton.qml
index ef9bbb011..8706e4338 100644
--- a/src/commoncomponents/PushButton.qml
+++ b/src/commoncomponents/PushButton.qml
@@ -153,7 +153,7 @@ AbstractButton {
                 PropertyChanges { target: background; color: pressedColor }
             },
             State {
-                name: "hovered"; when: hovered
+                name: "hovered"; when: hovered || root.focus
                 PropertyChanges { target: background; color: hoveredColor }
             },
             State {
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index 9958f963d..381a8728b 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -97,6 +97,11 @@ Item {
 
     property color closeButtonLighterBlack: "#4c4c4c"
 
+    // Jami switch
+    property color switchBackgroundBorderColor: "#cccccc"
+    property color switchBackgroundCheckedColor: primaryBackgroundColor
+    property color switchIndicatorBorderColor: "#999999"
+
     // Call buttons
     property color acceptButtonGreen: "#4caf50"
     property color acceptButtonHoverGreen: "#5db761"
@@ -254,6 +259,13 @@ Item {
     property real lineEditContextMenuItemsWidth: 100
     property real lineEditContextMenuSeparatorsHeight: 2
 
+    // Jami switch
+    property real switchIndicatorRadius: 13
+    property real switchPreferredHeight: 18
+    property real switchPreferredWidth: 48
+    property real switchIndicatorPreferredHeight: 26
+    property real switchIndicatorPreferredWidth: 26
+
     // Modal Popup
     property real modalPopupRadius: 4
     property real modalPopupDropShadowSamples: 16
diff --git a/src/mainview/components/ContactSearchBar.qml b/src/mainview/components/ContactSearchBar.qml
index 7eccf510a..c67912cbe 100644
--- a/src/mainview/components/ContactSearchBar.qml
+++ b/src/mainview/components/ContactSearchBar.qml
@@ -133,12 +133,11 @@ Rectangle {
         onActivated: contactSearchBar.forceActiveFocus()
     }
 
-    Shortcut {
-        sequence: "Return"
-        context: Qt.ApplicationShortcut
-        onActivated: {
+    Keys.onPressed: function (keyEvent) {
+        if (keyEvent.matches(StandardKey.InsertParagraphSeparator)) {
             if (contactSearchBar.text !== "") {
                 returnPressedWhileSearching()
+                keyEvent.accepted = true
             }
         }
     }
diff --git a/src/wizardview/components/BackupKeyPage.qml b/src/wizardview/components/BackupKeyPage.qml
index 49fde1695..af1455110 100644
--- a/src/wizardview/components/BackupKeyPage.qml
+++ b/src/wizardview/components/BackupKeyPage.qml
@@ -95,6 +95,10 @@ Rectangle {
                 rejected()
             }
         }
+
+        onRejected: {
+            backupBtn.forceActiveFocus()
+        }
     }
 
     color: JamiTheme.backgroundColor
@@ -157,10 +161,17 @@ Rectangle {
                 font.pointSize: JamiTheme.textFontSize
             }
 
-            Switch {
+            JamiSwitch {
                 id: passwordSwitch
+
                 Layout.alignment: Qt.AlignRight
 
+                focus: visible
+
+                KeyNavigation.tab: backupBtn
+                KeyNavigation.up: skipBackupBtn
+                KeyNavigation.down: KeyNavigation.tab
+
                 onToggled: AppSettingsManager.setValue(Settings.NeverShowMeAgain, checked)
             }
         }
@@ -177,10 +188,16 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedGreyHovered
             pressedColor: JamiTheme.buttonTintedGreyPressed
 
+            KeyNavigation.tab: skipBackupBtn
+            KeyNavigation.up: passwordSwitch
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: exportDialog.open()
         }
 
         MaterialButton {
+            id: skipBackupBtn
+
             Layout.alignment: Qt.AlignCenter
             Layout.bottomMargin: JamiTheme.wizardViewPageBackButtonMargins
             Layout.preferredWidth: preferredWidth
@@ -192,6 +209,10 @@ Rectangle {
             pressedColor: JamiTheme.buttonTintedGreyPressed
             outlined: true
 
+            KeyNavigation.tab: passwordSwitch
+            KeyNavigation.up: backupBtn
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.nextStep()
         }
     }
diff --git a/src/wizardview/components/ConnectToAccountManagerPage.qml b/src/wizardview/components/ConnectToAccountManagerPage.qml
index 2eda7a3af..7645c6c7c 100644
--- a/src/wizardview/components/ConnectToAccountManagerPage.qml
+++ b/src/wizardview/components/ConnectToAccountManagerPage.qml
@@ -62,11 +62,6 @@ Rectangle {
 
     color: JamiTheme.backgroundColor
 
-    onVisibleChanged: {
-        if (visible)
-            accountManagerEdit.focus = true
-    }
-
     ColumnLayout {
         id: connectToAccountManagerPageColumnLayout
 
@@ -104,6 +99,8 @@ Rectangle {
             Layout.preferredWidth: connectBtn.width
             Layout.alignment: Qt.AlignCenter
 
+            focus: visible
+
             selectByMouse: true
             placeholderText: JamiStrings.jamiManagementServerURL
             font.pointSize: JamiTheme.textFontSize
@@ -111,6 +108,16 @@ Rectangle {
 
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: usernameManagerEdit
+            KeyNavigation.up: {
+                if (backButton.visible)
+                    return backButton
+                else if (connectBtn.enabled)
+                    return connectBtn
+                return passwordManagerEdit
+            }
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -140,6 +147,10 @@ Rectangle {
 
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: passwordManagerEdit
+            KeyNavigation.up: accountManagerEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -158,6 +169,16 @@ Rectangle {
             echoMode: TextInput.Password
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: {
+                if (connectBtn.enabled)
+                    return connectBtn
+                else if (backButton.visible)
+                    return backButton
+                return accountManagerEdit
+            }
+            KeyNavigation.up: usernameManagerEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -177,7 +198,17 @@ Rectangle {
                      && passwordManagerEdit.text.length !== 0
                      && !spinnerTriggered
 
+            KeyNavigation.tab: {
+                if (backButton.visible)
+                    return backButton
+                return accountManagerEdit
+            }
+            KeyNavigation.up: passwordManagerEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
+                if (connectBtn.focus)
+                    accountManagerEdit.forceActiveFocus()
                 spinnerTriggered = true
 
                 WizardViewStepModel.accountCreationInfo =
@@ -216,6 +247,14 @@ Rectangle {
 
         preferredSize: JamiTheme.wizardViewPageBackButtonSize
 
+        KeyNavigation.tab: accountManagerEdit
+        KeyNavigation.up: {
+            if (connectBtn.enabled)
+                return connectBtn
+            return passwordManagerEdit
+        }
+        KeyNavigation.down: KeyNavigation.tab
+
         onClicked: WizardViewStepModel.previousStep()
     }
 }
diff --git a/src/wizardview/components/CreateAccountPage.qml b/src/wizardview/components/CreateAccountPage.qml
index 843f430d2..2788bfa2a 100644
--- a/src/wizardview/components/CreateAccountPage.qml
+++ b/src/wizardview/components/CreateAccountPage.qml
@@ -131,6 +131,11 @@ Rectangle {
 
                     focus: visible
 
+                    KeyNavigation.tab: chooseUsernameButton.enabled ? chooseUsernameButton :
+                                                                      skipButton
+                    KeyNavigation.up: backButton
+                    KeyNavigation.down: KeyNavigation.tab
+
                     placeholderText: isRendezVous ? JamiStrings.chooseAName :
                                                     JamiStrings.chooseYourUserName
                 }
@@ -174,6 +179,10 @@ Rectangle {
                     hoveredColor: JamiTheme.buttonTintedBlueHovered
                     pressedColor: JamiTheme.buttonTintedBluePressed
 
+                    KeyNavigation.tab: skipButton
+                    KeyNavigation.up: usernameEdit
+                    KeyNavigation.down: KeyNavigation.tab
+
                     onClicked: WizardViewStepModel.nextStep()
                 }
 
@@ -190,6 +199,11 @@ Rectangle {
                     pressedColor: JamiTheme.buttonTintedGreyPressed
                     outlined: true
 
+                    KeyNavigation.tab: backButton
+                    KeyNavigation.up: chooseUsernameButton.enabled ? chooseUsernameButton :
+                                                                     usernameEdit
+                    KeyNavigation.down: KeyNavigation.tab
+
                     onClicked: {
                         usernameEdit.clear()
                         WizardViewStepModel.nextStep()
@@ -215,8 +229,14 @@ Rectangle {
 
             property int stackIndex: 1
 
+            focus: visible
+
             color: JamiTheme.backgroundColor
 
+            KeyNavigation.tab: passwordSwitch
+            KeyNavigation.up: passwordSwitch
+            KeyNavigation.down: passwordSwitch
+
             ColumnLayout {
                 id: passwordColumnLayout
 
@@ -239,7 +259,7 @@ Rectangle {
                         font.pointSize: JamiTheme.textFontSize + 3
                     }
 
-                    Switch {
+                    JamiSwitch {
                         id: passwordSwitch
 
                         objectName: "passwordSwitch"
@@ -247,6 +267,10 @@ Rectangle {
                         Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
                         Layout.leftMargin: -JamiTheme.wizardViewPageLayoutSpacing
                         Layout.topMargin: 5
+
+                        KeyNavigation.tab: checked ? passwordEdit : createAccountButton
+                        KeyNavigation.up: backButton
+                        KeyNavigation.down: KeyNavigation.tab
                     }
 
                     BubbleLabel {
@@ -274,6 +298,10 @@ Rectangle {
                     placeholderText: JamiStrings.password
                     font.pointSize: JamiTheme.textFontSize
                     font.kerning: true
+
+                    KeyNavigation.tab: passwordConfirmEdit
+                    KeyNavigation.up: passwordSwitch
+                    KeyNavigation.down: KeyNavigation.tab
                 }
 
                 MaterialLineEdit {
@@ -292,6 +320,11 @@ Rectangle {
                     placeholderText: JamiStrings.confirmPassword
                     font.pointSize: JamiTheme.textFontSize
                     font.kerning: true
+
+                    KeyNavigation.tab: createAccountButton.enabled ? createAccountButton :
+                                                                     backButton
+                    KeyNavigation.up: passwordEdit
+                    KeyNavigation.down: KeyNavigation.tab
                 }
 
                 Label {
@@ -328,6 +361,10 @@ Rectangle {
                     hoveredColor: JamiTheme.buttonTintedBlueHovered
                     pressedColor: JamiTheme.buttonTintedBluePressed
 
+                    KeyNavigation.tab: backButton
+                    KeyNavigation.up: passwordSwitch.checked ? passwordConfirmEdit : passwordSwitch
+                    KeyNavigation.down: KeyNavigation.tab
+
                     onClicked: {
                         WizardViewStepModel.accountCreationInfo =
                                 JamiQmlUtils.setUpAccountCreationInputPara(
@@ -361,6 +398,20 @@ Rectangle {
 
         preferredSize: JamiTheme.wizardViewPageBackButtonSize
 
+        KeyNavigation.tab: {
+            if (createAccountStack.currentIndex === nameRegistrationPage.stackIndex)
+                return usernameEdit
+            else
+                return passwordSwitch
+        }
+        KeyNavigation.up: {
+            if (createAccountStack.currentIndex === nameRegistrationPage.stackIndex)
+                return skipButton
+            else
+                return createAccountButton.enabled ? createAccountButton : passwordConfirmEdit
+        }
+        KeyNavigation.down: KeyNavigation.tab
+
         onClicked: WizardViewStepModel.previousStep()
     }
 }
diff --git a/src/wizardview/components/CreateSIPAccountPage.qml b/src/wizardview/components/CreateSIPAccountPage.qml
index d60b0bf0f..4c2346cc8 100644
--- a/src/wizardview/components/CreateSIPAccountPage.qml
+++ b/src/wizardview/components/CreateSIPAccountPage.qml
@@ -98,6 +98,10 @@ Rectangle {
             placeholderText: JamiStrings.server
             font.pointSize: JamiTheme.textFontSize
             font.kerning: true
+
+            KeyNavigation.tab: sipProxyEdit
+            KeyNavigation.up: backButton
+            KeyNavigation.down: KeyNavigation.tab
         }
 
         MaterialLineEdit {
@@ -113,6 +117,10 @@ Rectangle {
             placeholderText: JamiStrings.proxy
             font.pointSize: JamiTheme.textFontSize
             font.kerning: true
+
+            KeyNavigation.tab: sipUsernameEdit
+            KeyNavigation.up: sipServernameEdit
+            KeyNavigation.down: KeyNavigation.tab
         }
 
         MaterialLineEdit {
@@ -128,6 +136,10 @@ Rectangle {
             placeholderText: JamiStrings.username
             font.pointSize: JamiTheme.textFontSize
             font.kerning: true
+
+            KeyNavigation.tab: sipPasswordEdit
+            KeyNavigation.up: sipProxyEdit
+            KeyNavigation.down: KeyNavigation.tab
         }
 
         MaterialLineEdit {
@@ -144,6 +156,10 @@ Rectangle {
             placeholderText: JamiStrings.password
             font.pointSize: JamiTheme.textFontSize
             font.kerning: true
+
+            KeyNavigation.tab: createAccountButton
+            KeyNavigation.up: sipUsernameEdit
+            KeyNavigation.down: KeyNavigation.tab
         }
 
         MaterialButton {
@@ -161,6 +177,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: backButton
+            KeyNavigation.up: sipPasswordEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
                 WizardViewStepModel.accountCreationInfo =
                         JamiQmlUtils.setUpAccountCreationInputPara(
@@ -182,6 +202,10 @@ Rectangle {
 
         preferredSize: JamiTheme.wizardViewPageBackButtonSize
 
+        KeyNavigation.tab: sipServernameEdit
+        KeyNavigation.up: createAccountButton
+        KeyNavigation.down: KeyNavigation.tab
+
         onClicked: WizardViewStepModel.previousStep()
     }
 }
diff --git a/src/wizardview/components/ImportFromBackupPage.qml b/src/wizardview/components/ImportFromBackupPage.qml
index 7ffffb038..f6fdf0f79 100644
--- a/src/wizardview/components/ImportFromBackupPage.qml
+++ b/src/wizardview/components/ImportFromBackupPage.qml
@@ -42,6 +42,7 @@ Rectangle {
     function clearAllTextFields() {
         connectBtn.spinnerTriggered = false
         passwordFromBackupEdit.clear()
+        filePath = ""
         errorText = ""
         fileImportBtnText = JamiStrings.archive
     }
@@ -75,6 +76,16 @@ Rectangle {
 
         nameFilters: [JamiStrings.jamiArchiveFiles + " (*.gz)", JamiStrings.allFiles + " (*)"]
 
+        onVisibleChanged: {
+            if (!visible) {
+                rejected()
+            }
+        }
+
+        onRejected: {
+            fileImportBtn.forceActiveFocus()
+        }
+
         onAccepted: {
             filePath = file
             if (file.length !== "") {
@@ -116,6 +127,16 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedGreyHovered
             pressedColor: JamiTheme.buttonTintedGreyPressed
 
+            KeyNavigation.tab: passwordFromBackupEdit
+            KeyNavigation.up: {
+                if (backButton.visible)
+                    return backButton
+                else if (connectBtn.enabled)
+                    return connectBtn
+                return passwordFromBackupEdit
+            }
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
                 errorText = ""
                 importFromFileDialog.open()
@@ -161,6 +182,16 @@ Rectangle {
             echoMode: TextInput.Password
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: {
+                if (connectBtn.enabled)
+                    return connectBtn
+                else if (backButton.visible)
+                    return backButton
+                return fileImportBtn
+            }
+            KeyNavigation.up: fileImportBtn
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -185,7 +216,17 @@ Rectangle {
                 return false
             }
 
+            KeyNavigation.tab: {
+                if (backButton.visible)
+                    return backButton
+                return fileImportBtn
+            }
+            KeyNavigation.up: passwordFromBackupEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
+                if (connectBtn.focus)
+                    fileImportBtn.forceActiveFocus()
                 spinnerTriggered = true
 
                 WizardViewStepModel.accountCreationInfo =
@@ -225,6 +266,14 @@ Rectangle {
 
         preferredSize: JamiTheme.wizardViewPageBackButtonSize
 
+        KeyNavigation.tab: fileImportBtn
+        KeyNavigation.up: {
+            if (connectBtn.enabled)
+                return connectBtn
+            return passwordFromBackupEdit
+        }
+        KeyNavigation.down: KeyNavigation.tab
+
         onClicked: WizardViewStepModel.previousStep()
     }
 }
diff --git a/src/wizardview/components/ImportFromDevicePage.qml b/src/wizardview/components/ImportFromDevicePage.qml
index b343aa3e9..049934bcd 100644
--- a/src/wizardview/components/ImportFromDevicePage.qml
+++ b/src/wizardview/components/ImportFromDevicePage.qml
@@ -95,6 +95,14 @@ Rectangle {
             echoMode: TextInput.Password
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: pinFromDevice
+            KeyNavigation.up: {
+                if (backButton.visible)
+                    return backButton
+                return pinFromDevice
+            }
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -133,6 +141,16 @@ Rectangle {
 
             borderColorMode: MaterialLineEdit.NORMAL
 
+            KeyNavigation.tab: {
+                if (connectBtn.enabled)
+                    return connectBtn
+                else if (connectBtn.spinnerTriggered)
+                    return passwordFromDevice
+                return backButton
+            }
+            KeyNavigation.up: passwordFromDevice
+            KeyNavigation.down: KeyNavigation.tab
+
             onTextChanged: errorText = ""
         }
 
@@ -149,6 +167,10 @@ Rectangle {
 
             enabled: pinFromDevice.text.length !== 0 && !spinnerTriggered
 
+            KeyNavigation.tab: backButton
+            KeyNavigation.up: pinFromDevice
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
                 spinnerTriggered = true
 
@@ -186,6 +208,10 @@ Rectangle {
 
         visible: !connectBtn.spinnerTriggered
 
+        KeyNavigation.tab: passwordFromDevice
+        KeyNavigation.up: connectBtn
+        KeyNavigation.down: KeyNavigation.tab
+
         preferredSize: JamiTheme.wizardViewPageBackButtonSize
 
         onClicked: WizardViewStepModel.previousStep()
diff --git a/src/wizardview/components/ProfilePage.qml b/src/wizardview/components/ProfilePage.qml
index 7d9e06151..1292916d7 100644
--- a/src/wizardview/components/ProfilePage.qml
+++ b/src/wizardview/components/ProfilePage.qml
@@ -104,6 +104,14 @@ Rectangle {
             imageId: createdAccountId
             avatarSize: 200
 
+            onFocusOnPreviousItem: {
+                skipProfileSavingButton.forceActiveFocus()
+            }
+
+            onFocusOnNextItem: {
+                aliasEdit.forceActiveFocus()
+            }
+
             onVisibleChanged: {
                 if (visible)
                     LRCInstance.currentAccountAvatarSet = false
@@ -138,6 +146,16 @@ Rectangle {
 
             fieldLayoutWidth: saveProfileBtn.width
 
+            KeyNavigation.tab: saveProfileBtn
+            KeyNavigation.down: KeyNavigation.tab
+
+            Keys.onPressed: function (keyEvent) {
+                if (keyEvent.matches(StandardKey.MoveToPreviousLine)) {
+                    setAvatarWidget.focusOnPreviousPhotoBoothItem()
+                    keyEvent.accepted = true
+                }
+            }
+
             onTextEdited: {
                 if (LRCInstance.currentAccountAvatarSet)
                     return
@@ -170,6 +188,10 @@ Rectangle {
                     return JamiStrings.creatingAccount
             }
 
+            KeyNavigation.tab: skipProfileSavingButton
+            KeyNavigation.up: aliasEdit
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
                 AccountAdapter.setCurrAccDisplayName(aliasEdit.text)
                 WizardViewStepModel.nextStep()
@@ -177,6 +199,10 @@ Rectangle {
         }
 
         MaterialButton {
+            id: skipProfileSavingButton
+
+            objectName: "skipProfileSavingButton"
+
             Layout.alignment: Qt.AlignCenter
             Layout.preferredWidth: preferredWidth
             Layout.preferredHeight: preferredHeight
@@ -188,6 +214,16 @@ Rectangle {
             pressedColor: JamiTheme.buttonTintedGreyPressed
             outlined: true
 
+            KeyNavigation.up: saveProfileBtn
+
+            Keys.onPressed: function (keyEvent) {
+                if (keyEvent.matches(StandardKey.MoveToNextLine) ||
+                        keyEvent.key === Qt.Key_Tab) {
+                    setAvatarWidget.focusOnNextPhotoBoothItem()
+                    keyEvent.accepted = true
+                }
+            }
+
             onClicked: {
                 AccountAdapter.setCurrentAccountAvatarBase64()
                 aliasEdit.clear()
diff --git a/src/wizardview/components/WelcomePage.qml b/src/wizardview/components/WelcomePage.qml
index 4557e6ae5..0d86072a6 100644
--- a/src/wizardview/components/WelcomePage.qml
+++ b/src/wizardview/components/WelcomePage.qml
@@ -48,6 +48,16 @@ Rectangle {
         }
     }
 
+    // Make sure that welcomePage grab activeFocus initially (when there is no account)
+    onVisibleChanged: {
+        if (visible)
+            forceActiveFocus()
+    }
+
+    KeyNavigation.tab: newAccountButton
+    KeyNavigation.up: newAccountButton
+    KeyNavigation.down: KeyNavigation.tab
+
     ColumnLayout {
         id: welcomePageColumnLayout
 
@@ -98,6 +108,13 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: newRdvButton
+            KeyNavigation.up: backButton.visible ? backButton :
+                                                   (showAdvancedButton.showAdvanced ?
+                                                        newSIPAccountButton :
+                                                        showAdvancedButton)
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.CreateJamiAccount)
         }
@@ -117,6 +134,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: fromDeviceButton
+            KeyNavigation.up: newAccountButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.CreateRendezVous)
         }
@@ -136,6 +157,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: fromBackupButton
+            KeyNavigation.up: newRdvButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.ImportFromDevice)
         }
@@ -155,6 +180,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: showAdvancedButton
+            KeyNavigation.up: fromDeviceButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.ImportFromBackup)
         }
@@ -181,8 +210,13 @@ Rectangle {
 
             hoverEnabled: true
 
+            KeyNavigation.tab: showAdvanced ? connectAccountManagerButton :
+                                              (backButton.visible ? backButton : newAccountButton)
+            KeyNavigation.up: fromBackupButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: {
-                showAdvanced =  !showAdvanced
+                showAdvanced = !showAdvanced
                 connectAccountManagerButton.visible = showAdvanced
                 newSIPAccountButton.visible = showAdvanced
             }
@@ -205,6 +239,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: newSIPAccountButton
+            KeyNavigation.up: showAdvancedButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.ConnectToAccountManager)
         }
@@ -227,6 +265,10 @@ Rectangle {
             hoveredColor: JamiTheme.buttonTintedBlueHovered
             pressedColor: JamiTheme.buttonTintedBluePressed
 
+            KeyNavigation.tab: backButton.visible ? backButton : newAccountButton
+            KeyNavigation.up: connectAccountManagerButton
+            KeyNavigation.down: KeyNavigation.tab
+
             onClicked: WizardViewStepModel.startAccountCreationFlow(
                            WizardViewStepModel.AccountCreationOption.CreateSipAccount)
         }
@@ -253,6 +295,10 @@ Rectangle {
 
         visible: UtilsAdapter.getAccountListSize()
 
+        KeyNavigation.tab: newAccountButton
+        KeyNavigation.up: newSIPAccountButton
+        KeyNavigation.down: KeyNavigation.tab
+
         onClicked: WizardViewStepModel.previousStep()
     }
 }
-- 
GitLab