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 bool active: false;
    visible: false

    Component.onCompleted: {
        if(menu == NormalModeModel.CarStatusMenu) {
            leftGauge.opacity = 0
            rightGauge.opacity = 0
            topLine.opacity = 0
            menuChanger.opacity = 0
            leftGauge.x = root.width / 3
            rightGauge.x = root.width - rightGauge.width - root.width / 3
            menuChanger.source = "CarStatus.qml"
        }

        active = true
        visible = true
    }

    Connections {
        target: MainModel
        function onClusterModeChanged(clusterMode: int) {
            active = false
        }
    }

    property int menu: NormalModeModel.menu;
    property bool naviMode: active && menu == NormalModeModel.NavigationMenu;
    property real scale: NormalModeModel.scale;

    Image {
        id: topLine;
        source: "images/top-line.png";
        anchors.horizontalCenter: parent.horizontalCenter;
        y: 62;
    }

    Loader {
        id: menuChanger
        anchors.fill: parent
        source: "MediaPlayer.qml"
    }

    Connections {
        target: NormalModeModel
        function onMenuChanged(menu: int) {
            loadNewMenu.start()
            if (menu === NormalModeModel.NavigationMenu) {
                NormalModeModel.scale = 1.25
            } else {
                NormalModeModel.scale = 1
            }
        }
    }

    Timer {
        id: loadNewMenu
        interval: 250
        onTriggered: {
            switch(menu) {
                case NormalModeModel.MediaPlayerMenu:
                    menuChanger.source = "MediaPlayer.qml"
                    break;
                case NormalModeModel.NavigationMenu:
                    menuChanger.source = "Navi.qml"
                    break;
                case NormalModeModel.PhoneMenu:
                    menuChanger.source = "Phone.qml"
                    break;
                case NormalModeModel.CarStatusMenu:
                    menuChanger.source = "CarStatus.qml"
                    break;
            }
        }
    }

    Item {
        id: gauges
        opacity: MainModel.gaugesOpacity;

        Gauge {
            id: leftGauge;
            x: 20;
            y: 44;
            left: true;
            value: Units.kilometersToLongDistanceUnit(MainModel.speed)
            maxValue: Units.maximumSpeed
            textLabel: Units.speedUnit

            SpeedWarningIndicator {
                id: speedWarningIndicator
                x: 218 - width/2
                y: 271 - height/2
                transform: Scale {
                    origin.x: 60 - speedWarningIndicator.x
                    origin.y: 340 - speedWarningIndicator.y
                    xScale: leftGauge.scale
                    yScale: leftGauge.scale
                }
            }
        }

        Gauge {
            id: rightGauge;
            x: root.width - rightGauge.width - 20;
            y: 44;
            value: MainModel.rpm / 1000;
            valueText: MainModel.gearShiftText
            maxValue: MainModel.maxRpm / 1000;
            maxAngle: 180
            textLabel: ""

            StaticText {
                id: rpmLabel
                anchors.centerIn: parent
                anchors.horizontalCenterOffset: -35
                anchors.verticalCenterOffset: 100

                opacity: 0.2
                horizontalAlignment: Text.AlignRight
                text: "x1000\n    RPM"
                color: Style.lightPeriwinkle;
                font.pixelSize: 10
                font.family: "Sarabun"

                transform: Scale {
                    origin.x: rightGauge.transformOriginX - rpmLabel.x
                    origin.y: 340 - rpmLabel.y
                    xScale: rightGauge.scale
                    yScale: rightGauge.scale
                }
            }
        }

        Behavior on opacity { NumberAnimation { duration: MainModel.gaugesOpacityChangeDuration; } }
    }

    Menu {
        id: normalMenu;
        opacity: topLine.opacity;
        anchors.horizontalCenter: parent.horizontalCenter;
        y: 293;
        currentIndex: menu;
        onClicked: menu = index;
    }

    Menu {
        id: navModeMenu;
        opacity: 0;
        anchors.horizontalCenter: parent.horizontalCenter;
        y: 87;
        currentIndex: 1;
        onClicked: menu = index;
    }

    states: [
        State {
            name: "inactive"
            when: !active
            PropertyChanges {
                target: menuChanger
                opacity: 0
            }
            PropertyChanges {
                target: topLine
                opacity: 0
            }
            PropertyChanges {
                target: leftGauge
                opacity: 0
                x: root.width / 3;
            }
            PropertyChanges {
                target: rightGauge
                opacity: 0
                x: root.width - rightGauge.width - root.width / 3;
            }
        },
        State {
            name: "navi"
            when: naviMode;
            PropertyChanges { target: navModeMenu; opacity: topLine.opacity; }
            PropertyChanges { target: normalMenu; opacity: 0; }
            PropertyChanges {
                target: leftGauge;
                scale: 0.8
            }
            PropertyChanges {
                target: rightGauge;
                scale: 0.8
            }
        }
    ]

    // FIXME! need to have exact easing curve and timeing from the design
    transitions: [
        Transition {
            from: "inactive"
            SequentialAnimation {
                NumberAnimation {
                    target: menuChanger
                    duration: 350;
                }
                ScriptAction {
                    script: SportModeModel.menuActive = false
                }
                ParallelAnimation {
                    NumberAnimation {
                        easing.type: Easing.OutQuad
                        target: leftGauge;
                        duration: 350;
                    }
                    NumberAnimation {
                        easing.type: Easing.OutQuad
                        target: rightGauge;
                        duration: 350;
                    }
                    NumberAnimation {
                        easing.type: Easing.InQuad
                        target: topLine
                        duration: 400;
                    }
                }
            }
        },
        Transition {
            to: "inactive"
            SequentialAnimation {
                ParallelAnimation {
                    NumberAnimation {
                        target: menuChanger
                        duration: 250;
                    }
                    NumberAnimation {
                        target: topLine
                        duration: 250;
                    }
                }
                ParallelAnimation {
                    NumberAnimation {
                        easing.type: Easing.OutQuad
                        target: leftGauge;
                        duration: 350;
                    }
                    NumberAnimation {
                        easing.type: Easing.OutQuad
                        target: rightGauge;
                        duration: 350;
                    }
                }
            }
        },
        Transition {
            to: "navi"
            NumberAnimation {
                properties: "scale";
                duration: 500;
            }
            SequentialAnimation {
                NumberAnimation {
                    target: normalMenu;
                    duration: 250;
                }
                PauseAnimation { duration: 300; }
                NumberAnimation {
                    target: navModeMenu
                    duration: 300;
                    easing.type: Easing.InCubic;
                }
            }
        },
        Transition {
            from: "navi"
            NumberAnimation {
                properties: "scale";
                duration: 500;
            }
            SequentialAnimation {
                NumberAnimation {
                    target: navModeMenu;
                    duration: 250;
                }
                PauseAnimation { duration: 300; }
                NumberAnimation {
                    target: normalMenu
                    duration: 300;
                    easing.type: Easing.InCubic;
                }
            }
        }
    ]
}