launcher: Enable scrolling for the GridView to display more apps 65/27165/2 13.93.0 needlefish/13.93.0 needlefish_13.93.0
authorMarius Vlad <marius.vlad@collabora.com>
Fri, 11 Feb 2022 15:05:40 +0000 (17:05 +0200)
committerMarius Vlad <marius.vlad@collabora.com>
Fri, 18 Feb 2022 10:58:06 +0000 (12:58 +0200)
This enables the GridView to allow scrolling and implicitly be able to
display more than a dozen of applications. The interactive mode controls
whether this is enabled or not, if the items is higher than what the grid
can contain (which is 4x3 in our current item cell dimension). If
interactive is enabled we use vertical scrolling, with some commented
out examples on how to enable horizontal scrolling. In tests,
horizontal scrolling caused some animation artefacts while vertical
seemed to behave much more reliable.

With it, we simplify the QML considerably by removing IconItem. Most of
the functionality has been retained, being copy-pasted from IconItem
like displaying icons, or using the first letter of the application name
in case there's no icon.

Note that this change removes The icon movement entirely, which seemed
to cause quite a bit of issues while draging/dropping scrolling in the
the grid itself. This seems to remove one feature for another, but by
just enabling interactive resulted in a jerky inconsistent animation.

Bug-AGL: SPEC-3028

Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: I64810dbd7ef014ed0473fd92df67807d10cc36ba

launcher/qml/IconItem.qml [deleted file]
launcher/qml/Launcher.qml
launcher/qml/qml.qrc
launcher/src/homescreenhandler.cpp
launcher/src/homescreenhandler.h
launcher/src/main.cpp

diff --git a/launcher/qml/IconItem.qml b/launcher/qml/IconItem.qml
deleted file mode 100644 (file)
index 5207196..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 The Qt Company Ltd.
- * Copyright (c) 2018 TOYOTA MOTOR CORPORATION
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import QtQuick 2.0
-import QtQuick.Controls 2.0
-import QtGraphicalEffects 1.0
-
-Item {
-    id: main
-    width: 320
-    height: 320
-    property string icon: model.icon
-
-    Item {
-        id: container
-        parent: loc
-        x: main.x
-        y: main.y
-        width: main.width
-        height: main.height
-
-        Image {
-            id: item
-            anchors.top: parent.top
-            anchors.topMargin: 20
-            anchors.horizontalCenter: parent.horizontalCenter
-            width: 220
-            height: width
-            source: main.icon
-            antialiasing: item.state !== ''
-
-            property string initial: model.name.substring(0,1).toUpperCase()
-
-            Item {
-                id: title
-                width: 125
-                height: 125
-                anchors.centerIn: parent
-                Repeater {
-                    delegate: Label {
-                        style: Text.Outline
-                        styleColor: 'red'
-                        color: 'transparent'
-                        font.pixelSize: 125
-                        anchors.centerIn: parent
-                        anchors.horizontalCenterOffset: model.index / 3 - 1
-                        anchors.verticalCenterOffset: model.index % 3 - 1
-                        text: item.initial
-                    }
-                    model: main.icon === 'blank' ? 9 : 0
-                }
-                layer.enabled: true
-                layer.effect: LinearGradient {
-                    gradient: Gradient {
-                        GradientStop { position: -0.5; color: "#6BFBFF" }
-                        GradientStop { position: +1.5; color: "#00ADDC" }
-                    }
-                }
-            }
-        }
-        Label {
-            id: name
-            anchors.top: item.bottom
-            anchors.left: parent.left
-            anchors.right: parent.right
-            anchors.margins: 20
-            font.pixelSize: 25
-            font.letterSpacing: 5
-            wrapMode: Text.WordWrap
-            horizontalAlignment: Text.AlignHCenter
-            color: "white"
-            text: qsTr(model.name.toUpperCase())
-        }
-
-        Behavior on x { enabled: item.state !== 'active'; NumberAnimation { duration: 400; easing.type: Easing.OutCubic } }
-        Behavior on y { enabled: item.state !== 'active'; NumberAnimation { duration: 400; easing.type: Easing.OutCubic } }
-        SequentialAnimation on rotation {
-            NumberAnimation { to:  5; duration: 100 }
-            NumberAnimation { to: -5; duration: 200 }
-            NumberAnimation { to:  0; duration: 100 }
-            running: loc.currentId !== '' && item.state !== 'active'
-            loops: Animation.Infinite; alwaysRunToEnd: true
-        }
-        states: [
-            State {
-                name: 'active'
-                when: loc.currentId === model.id
-                PropertyChanges {
-                    target: container
-                    x: loc.mouseX - width/2
-                    y: loc.mouseY - height/2
-                    scale: 1.15
-                    z: 10
-                }
-            },
-            State {
-                when: loc.currentId !== ''
-                PropertyChanges {
-                    target: container
-                    scale: 0.85
-                    opacity: 0.75
-                }
-            }
-        ]
-        transitions: Transition { NumberAnimation { properties: 'scale, opacity, x, y'; duration: 150; easing.type: Easing.OutCubic} }
-    }
-}
index 3c948dd..894ff98 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import QtQuick 2.6
+import QtQuick 2.13
 import QtQuick.Layouts 1.1
 import QtQuick.Controls 2.0
 import QtQuick.Window 2.13
+import QtGraphicalEffects 1.0
 import AppModel 1.0
 
 ApplicationWindow {
 
-    id: root 
+    id: root
     //width: container.width
     //height: container.height
     flags: Qt.FramelessWindowHint
@@ -36,77 +37,120 @@ ApplicationWindow {
         height: Window.height
 
         Image {
-          anchors.centerIn: parent
-          source: './images/AGL_HMI_Blue_Background_Car-01.png'
+            anchors.centerIn: parent
+            source: './images/AGL_HMI_Blue_Background_Car-01.png'
         }
 
-    GridView {
-        id: grid
-        anchors {
-          topMargin: 60; bottomMargin: 0 
-          leftMargin: 60; rightMargin: 60
-          fill: parent
-        }
-        contentHeight: 320
-        flickableDirection: Flickable.AutoFlickDirection
-        snapMode: GridView.SnapOneRow
-        visible: true
-        cellWidth: 320
-        cellHeight: 320
-        interactive: false
+        GridView {
+            id: grid
+            anchors {
+                topMargin: 60; bottomMargin: 60
+                leftMargin: 60; rightMargin: 60
+                fill: parent
+            }
+            contentHeight: 320
+            // change this HorizontalFlick or see Flickable documentation
+            // for other possible combinations
+            flickableDirection: Flickable.VerticalFlick
+            snapMode: GridView.SnapOneRow
+            visible: true
+            cellWidth: 320
+            cellHeight: 320
+            interactive: apps_len > 12 ? true : false
 
-        model: ApplicationModel { id: applicationModel }
-        delegate: IconItem {
-            width: grid.cellWidth
-            height: grid.cellHeight
-        }
+            // the follow makes it display from left to right to allow
+            // horizontal scrolling to work
+            //verticalLayoutDirection: Grid.TopToBottom
+            //layoutDirection: Qt.LeftToRight
+            //flow: Grid.TopToBottom
 
-        Connections {
-            target: homescreenHandler
-            onAppListUpdate: {
-                console.warn("applist update in Launcher.qml")
-                applicationModel.updateApplist(info);
-            }
-        }
-        Connections {
-            target: homescreenHandler
-            onInitAppList: {
-                console.warn("applist init in Launcher.qml")
-                applicationModel.initAppList(data);
-            }
-        }
+            // uncomment this out if you want to highlight the currently selected item
+            //highlight: Rectangle { width: 80; height: 80; color: "steelblue"; opacity: 0.3 }
 
-        MouseArea {
-            id: loc
-            anchors.fill: parent
-            property string currentId: ''
-            property int newIndex: -1
-            property int index: grid.indexAt(loc.mouseX, loc.mouseY)
-            x: 62
-            y: 264
-            onPressAndHold: currentId = applicationModel.id(newIndex = index)
-            onReleased: {
-                if(loc.index < 0) {
-                    return
+            model: ApplicationModel { id: applicationModel }
+            delegate: Item {
+                width: grid.cellWidth
+                height: grid.cellHeight
+
+                Text {
+                    color: "white"
+                    anchors.top: myIcon.bottom
+                    anchors.left: parent.left
+                    anchors.right: parent.right
+                    anchors.margins: 20
+                    font.pixelSize: 25
+                    font.letterSpacing: 5
+                    wrapMode: Text.WordWrap
+                    horizontalAlignment: Text.AlignHCenter
+                    text: qsTr(model.name.toUpperCase())
                 }
 
-               //if (applicationModel.appid(loc.index) === 'tbtnavi' ||
-               //    applicationModel.appid(loc.index) === 'hvac') {
-               //      output_screen = 'Virtual-1'
-               //}
-                if (currentId === '') {
-                    homescreenHandler.tapShortcut(applicationModel.appid(loc.index))
-                } else {
-                    currentId = ''
+                Image {
+                    id: myIcon
+                    anchors.top: parent.top
+                    anchors.topMargin: 20
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    // make the image/icons smaller than the grid cell size as
+                    // the text below/above current cell not be on top of the
+                    // current icon
+                    width: 220
+                    height: 220
+                    source: model.icon
+                    antialiasing: true
+                    property string initial: model.name.substring(0,1).toUpperCase()
+
+                    Item {
+                        id: title
+                        width: 125
+                        height: 125
+                        anchors.centerIn: parent
+                        Label {
+                            style: Text.Outline
+                            styleColor: '#00ADDC'
+                            color: 'transparent'
+                            font.pixelSize: 125
+                            anchors.centerIn: parent
+                            anchors.horizontalCenterOffset: model.index / 3 - 1
+                            anchors.verticalCenterOffset: model.index % 3 - 1
+                            text: model.icon === 'blank' ? myIcon.initial : ''
+                        }
+
+                        layer.enabled: true
+                        layer.effect: LinearGradient {
+                            gradient: Gradient {
+                                GradientStop { position: -0.5; color: "#6BFBFF" }
+                                GradientStop { position: +1.5; color: "#00ADDC" }
+                            }
+                        }
+                    }
+                }
+
+                MouseArea {
+                    id: loc
+                    anchors.fill: parent
+                    property string currentApp: ''
+                    onClicked: {
+                        parent.GridView.view.currentIndex = index
+                        currentApp = applicationModel.appid(parent.GridView.view.currentIndex)
+                        homescreenHandler.tapShortcut(currentApp)
+                    }
+                }
+            }
+
+            Connections {
+                target: homescreenHandler
+                onAppListUpdate: {
+                    console.warn("applist update in Launcher.qml")
+                    applicationModel.updateApplist(info);
                 }
             }
-            onPositionChanged: {
-                if (loc.currentId === '') return
-                if (index < 0) return
-                if (index === newIndex) return
-                    applicationModel.move(newIndex, newIndex = index)
+            Connections {
+                target: homescreenHandler
+                onInitAppList: {
+                    console.warn("applist init in Launcher.qml")
+                    applicationModel.initAppList(data);
+                }
             }
         }
     }
 }
-}
index d132fed..3094f71 100644 (file)
@@ -1,6 +1,5 @@
 <RCC>
     <qresource prefix="/">
         <file>Launcher.qml</file>
-        <file>IconItem.qml</file>
     </qresource>
 </RCC>
index 9cf7e61..33987dd 100644 (file)
@@ -47,10 +47,30 @@ void HomescreenHandler::tapShortcut(QString application_id)
     }
 }
 
+int HomescreenHandler::getRunnablesCount(void)
+{
+    int apps = 0;
+
+    QDBusPendingReply<QVariantList> reply = applaunch_iface->listApplications(true);
+    reply.waitForFinished();
+
+    if (reply.isError()) {
+        HMI_ERROR("Launcher","Unable to retrieve application list: %s",
+                  reply.error().message().toStdString().c_str());
+        return apps;
+    } else {
+        QVariantList applist_variant = reply.value();
+        for (auto &v: applist_variant)
+           apps++;
+    }
+
+    return apps;
+}
+
 void HomescreenHandler::getRunnables(void)
 {
-       struct json_object *json_applist;
-       QString applist;
+    struct json_object *json_applist;
+    QString applist;
     QStringList apps;
 
     QDBusPendingReply<QVariantList> reply = applaunch_iface->listApplications(true);
index 616f816..7392992 100644 (file)
@@ -38,6 +38,7 @@ public:
 
     Q_INVOKABLE void tapShortcut(QString application_id);
     Q_INVOKABLE void getRunnables(void);
+    int getRunnablesCount(void);
 
     void onRep(struct json_object* reply_contents);
 
index 563e883..77dee27 100644 (file)
@@ -44,7 +44,9 @@ int main(int argc, char *argv[])
 
     // mail.qml loading
     QQmlApplicationEngine engine;
+    int apps = homescreenHandler->getRunnablesCount();
     engine.rootContext()->setContextProperty(QStringLiteral("homescreenHandler"), homescreenHandler);
+    engine.rootContext()->setContextProperty(QStringLiteral("apps_len"), apps);
     engine.load(QUrl(QStringLiteral("qrc:/Launcher.qml")));
     homescreenHandler->getRunnables();