C

Qt Quick Ultralite Automotive Cluster Demo

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

import QtQuick 2.15
import QtQuickUltralite.Extras 2.0
import Automotive 1.0

Item {
    id: root

    property alias selected: proxy.selected;

    NormalModeContentItem  {
        // FIXME: This strange construct is a workaround for missing Item Layers.
        // We must prevent phone list mask opacity changes propagated from root item.
        // (mask becomes translucent during transitions and reveals items below)
        id: proxy
    }

    Item  {
        id: phone
        visible: proxy.visible;
        height: middle.height
        anchors.fill: parent;
        property int currentTab: PhoneModel.contactTabIndex;
        property bool calling: PhoneModel.inCall;
        property bool connected: false
        property int callDuration: 0
        property int targetContactIndex: PhoneModel.currentContactIndex

        property int fav_contacts_size: 3
        property int recent_contacts_size: 2
        property int all_contacts_size: 5

        onCallingChanged: {
            if (calling) {
                connectingTimer.interval = 3000 + Math.round(Math.random() * 1000 )
                phone.connected = false
                phone.callDuration = 0
                connectingTimer.start()
            }
        }

        readonly property list contacts_photos: [
            "images/photos/aryn.png",
            "images/photos/caspar.png",
            "images/photos/beatriz.png",
            "images/photos/joslin.png",
            "images/photos/hirini.png"
        ]

        readonly property list contacts_names: [
            "Aryn Jacobssen",
            "Caspar Sawrey",
            "Beatriz Brito",
            "Joslin Rodgers",
            "Hirini Hakopa"
        ]

        readonly property list contacts_numbers: [
            "+1 121 743 852",
            "+1 121 743 789",
            "+1 121 521 711",
            "+1 121 581 321",
            "+1 121 488 300"
        ]

        readonly property list contacts_last_calls: [ "Yesterday at 17:06", "Yesterday at 07:59", "", "", ""]

        readonly property list contacts_fav_indices: [-1, 0, 1, -1, 2]

        readonly property list contacts_rec_indices: [-1, 0, 1, -1, -1]

        Item {
            id: middle
            anchors { horizontalCenter: parent.horizontalCenter; top: tabs.bottom }
            width: 264
            height: 210 - 13
            clip: true

            Item {
                opacity: proxy.opacity * (phone.calling ? 0 : 1);
                id: middleSliding;
                height: parent.height;
                width: parent.width * 3;
                x: - parent.width * phone.currentTab;
                y: 75
                Behavior on x { NumberAnimation { duration: PhoneModel.contactTabSwitchDuration } }
                Behavior on opacity { NumberAnimation { } }

                Column {
                    id: favourites
                    width: middle.width
                    spacing: -8
                    y: - 75 * PhoneModel.favContactsIndex
                    Behavior on y { NumberAnimation { duration: PhoneModel.contactScrollDuration } }
                    Repeater {
                        model: [1, 2, 4] // favorite contacts indices
                        delegate: Row {
                            width: middle.width;
                            Item { width: 20; height: 1 }
                            Image {
                                width: 75; height: 75
                                source: phone.contacts_photos[modelData]
                            }
                            Item { width: 22; height: 1 }
                            Column {
                                anchors.verticalCenter: parent.verticalCenter
                                Text {
                                    text: phone.contacts_names[modelData];
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                    font.bold: true;
                                }
                                Text {
                                    text: phone.contacts_numbers[modelData]
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                }
                            }
                        }
                    }
                }

                Column {
                    id: recent
                    x: favourites.x + width;
                    width: middle.width;
                    spacing: -8
                    y: - 75 * PhoneModel.recentContactsIndex
                    Behavior on y { NumberAnimation { duration: PhoneModel.contactScrollDuration } }
                    Repeater {
                        model: [1, 2] // recent_contacts indecies
                        delegate: Row {
                            width: middle.width;
                            Item { width: 20; height: 1 }
                            Image {
                                width: 75; height: 75
                                source: phone.contacts_photos[modelData]
                            }
                            Item { width: 22; height: 1 }
                            Column {
                                anchors.verticalCenter: parent.verticalCenter
                                spacing: -1
                                Text {
                                    text: phone.contacts_names[modelData];
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                    font.bold: true;
                                }
                                Text {
                                    text: phone.contacts_numbers[modelData];
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                }
                                Text {
                                    text: phone.contacts_last_calls[modelData];
                                    font.pixelSize: 12;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                    font.bold: true;
                                }
                            }
                        }
                    }
                }

                Column {
                    id: contacts
                    x: favourites.x + width * 2;
                    width: middle.width;
                    spacing: -8
                    y: - 75 * PhoneModel.allContactsIndex
                    Behavior on y { NumberAnimation { duration: PhoneModel.contactScrollDuration } }
                    Repeater {
                        model: phone.contacts_names.length // all contacts
                        delegate: Row {
                            width: middle.width;
                            Item { width: 20; height: 1 }
                            Image {
                                width: 75; height: 75
                                source: phone.contacts_photos[index]
                            }
                            Item { width: 22; height: 1 }
                            Column {
                                anchors.verticalCenter: parent.verticalCenter
                                Text {
                                    text:  phone.contacts_names[index];
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                    font.bold: true;
                                }
                                Text {
                                    text: phone.contacts_numbers[index];
                                    font.pixelSize: 14;
                                    color: Style.lightPeriwinkle;
                                    font.family: "Sarabun";
                                }
                            }
                        }
                    }
                }
            }

            Image {
                opacity: 1 // Keep opacity constant, so nothing is revealed
                source: "images/assets-phone-list-pseudo-mask.png"
            }

            Image {
                opacity: 1 // Keep opacity constant, so nothing is revealed
                source: "images/pseudo-mask-vertical.png"
            }
        }

        Row {
            id: tabs
            opacity: proxy.opacity * (phone.calling ? 0 : 1);
            spacing: 23
            y: 74

            anchors.horizontalCenter: parent.horizontalCenter

            Repeater {
                model: ["Favourites", "Recent", "Contacts"];

                delegate: Text {
                    text: modelData;
                    color: Style.lightPeriwinkle;
                    font.pixelSize: 12;
                    font.family: "Sarabun";
                    opacity: phone.currentTab == index ? 1 : 0.2;
                    Behavior on opacity { NumberAnimation { } }
                }
            }
        }

        Item {
            anchors.fill: middle
            opacity: proxy.opacity * (phone.calling ? 1 : 0);
            Behavior on opacity { NumberAnimation { } }

            Repeater {
                model: phone.contacts_names.length // all contacts
                delegate: Column {
                    id: phoneTextCol
                    anchors.horizontalCenter: parent.horizontalCenter;
                    visible: {
                        if (phone.currentTab == 0) {
                            phone.targetContactIndex == phone.contacts_fav_indices[index]
                        } else if (phone.currentTab == 1) {
                            phone.targetContactIndex == phone.contacts_rec_indices[index]
                        } else if (phone.currentTab == 2) {
                            phone.targetContactIndex == index
                        }
                    }
                    Row {
                        anchors.horizontalCenter: parent.horizontalCenter
                        spacing: 5
                        ColorizedImage {
                            anchors.verticalCenter: parent.verticalCenter
                            width: 24; height: width
                            color: "green"
                            source: "images/phone.png"
                        }
                        Column {
                            Text {
                                anchors.horizontalCenter: parent.horizontalCenter
                                text: phone.contacts_names[index]
                                font.bold: true
                                font.pixelSize: 14
                                font.family: "Sarabun"
                                color: Style.lightPeriwinkle
                            }
                            Text {
                                anchors.horizontalCenter: parent.horizontalCenter
                                text: phone.contacts_numbers[index]
                                font.bold: false;
                                font.pixelSize: 12
                                font.family: "Sarabun"
                                color: Style.lightPeriwinkle
                            }
                        }
                    }
                    Image {
                        source: phone.contacts_photos[index]
                        width: 154
                        height: width
                    }
                }
            }

            Text {
                anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom  }
                id: callingLabel
                visible: !phone.connected && phone.calling
                text: "Calling..."
                font.pixelSize: 14
                font.family: "Sarabun"
                color: Style.lightPeriwinkle

                SequentialAnimation {
                    id: callingLabelAnimation
                    loops: Animation.Infinite
                    alwaysRunToEnd: true
                    running: callingLabel.visible
                    PropertyAnimation {
                        target: callingLabel
                        property: "opacity"
                        duration: 400
                        from: 1.0
                        to: 0.0
                    }

                    PauseAnimation {
                        duration: 100
                    }

                    PropertyAnimation {
                        target: callingLabel
                        property: "opacity"
                        duration: 400
                        from: 0.0
                        to: 1.0
                    }
                }
            }

            Row {
                anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom }

                visible: phone.connected && phone.calling
                Text {
                    text: Math.floor(phone.callDuration / 60)
                    font.pixelSize: 14
                    font.family: "Sarabun"
                    color: Style.lightPeriwinkle
                }

                Text {
                    text: (phone.callDuration % 60) < 10 ? ":0" : ":"
                    font.pixelSize: 14
                    font.family: "Sarabun"
                    color: Style.lightPeriwinkle
                }
                Text {
                    text: Math.floor(phone.callDuration % 60)
                    font.pixelSize: 14
                    font.family: "Sarabun"
                    color: Style.lightPeriwinkle
                }
            }
        }

        Timer {
            id: connectingTimer
            repeat: false
            onTriggered: { phone.connected = true }
        }

        Timer {
            id: callProgressTimer
            running: phone.connected
            repeat: true
            interval: 1000
            onTriggered: { phone.callDuration += 1 }
        }
    }
}