C

Qt Cluster: Rendering and Recovery from Main UI Failure

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

// This file is part of the Qt Safe Renderer module

import QtQuick
import ClusterDemo

DashboardSportForm {
    id: main //the id is used in e.g. VehicleInfoNote

    //Start animating gauges after both are loaded
    function showGauges() {
        if (speedoMeter.status === Loader.Ready
                && flipable.rpm.status === Loader.Ready) {
            startupAnimation.start()
        }
    }

    property var component: [
        "../MapView.qml",
        "../MediaPlayerView.qml",
        "../ConsumptionView.qml",
        "", // VideoView.qml
        "../CarParking.qml",
        "" // CarView.qml
    ]

    property string mapPositionImage: "image://etc/MapLocationSport.png"

    property int videoviewindex: 3
    property int parkingviewindex: 4
    property int carviewindex: 5

    property int preReversingCenterView: -1
    property int preReversingRightView

    // CarModel animations
    property int carModelHighlightType: 0

    property bool doorAction: false
    property bool actionInProgress: false
    property bool loadingInProgress: false
    property bool isReversing: false

    property int doorsOpen: ValueSource.frontLeftOpen + ValueSource.frontRightOpen
                            + ValueSource.hoodOpen + ValueSource.trunkOpen
    property bool flatTire: ValueSource.flatTire
    property bool lightFailure: ValueSource.lightFailure
    property int gear: ValueSource ? ValueSource.gear : "4"

    property var cameraView: camera
    property bool viewChanged: ValueSource.viewChange

    property color iconRed: "#e41e25"
    property color iconGreen: "#5caa15"
    property color iconYellow: "#face20"
    property color iconDark: "#444444"

    speedText.text: ValueSource.kph.toFixed().toString()
    iconCoolant.color: main.iconDark
    iconBattery.color: main.iconDark
    iconFuel.color: main.iconDark
    iconParkingBrake.color: main.iconDark
    iconLights.color: main.iconDark
    iconLowbeam.color: main.iconDark
    iconTyre.color: main.iconDark
    iconLamp.color: main.iconDark
    iconSeatbelt.color: main.iconDark

    function forceCarView() {
        actionInProgress = true
        // Make CarView visible before activating the animations
        if (car.item && car.item.hidden) {
            if (camera.visible)
                camera.visible = false
            car.opacity = 1.0
            centerStack.visible = false
            car.visible = true
            car.item.hidden = false
        }
    }

    function loadCenterView(nextView, allowParking) {
        loadingInProgress = true
        var previousViewIndex = centerStack.viewIndex

        if (preReversingCenterView != -1 && !allowParking) {
            if (centerStack.viewIndex !== preReversingCenterView) {
                centerStack.viewIndex = preReversingCenterView
                if (centerStack.viewIndex < 0)
                    centerStack.viewIndex = 5
            }
        }
        else {
            centerStack.viewIndex = getViewIndex(centerStack.viewIndex, nextView, allowParking)
        }
        loadingInProgress = false

        if (previousViewIndex === carviewindex)
            centerStack.fadeOutCenter.target = car
        else if (previousViewIndex === videoviewindex)
            centerStack.fadeOutCenter.target = camera
        else
            centerStack.fadeOutCenter.target = centerStack.loader
        centerStack.fadeOutCenter.start()
    }

    function getViewIndex(viewindex, nextView, allowParking) {
        if (allowParking) {
            return videoviewindex
        }

        if (nextView) {
            viewindex++
            if (viewindex === parkingviewindex) {
                viewindex++
            }
            if (viewindex > 5)
                viewindex = 0
        } else {
            viewindex--
            if (viewindex === parkingviewindex) {
                viewindex--
            }
            if (viewindex < 0)
                viewindex = 5
        }
        return viewindex
    }

    onDoorsOpenChanged: {
        if (actionInProgress && !doorAction)
            return

        // Check all doors & parse a correct value from them
        var doors = 0
        if (ValueSource.frontLeftOpen)
            doors ^= 1
        if (ValueSource.frontRightOpen)
            doors ^= 2
        if (ValueSource.trunkOpen)
            doors ^= 4
        if (ValueSource.hoodOpen)
            doors ^= 8

        if (doors != 0) {
            forceCarView()
            if (car.item)
                car.item.highlightDoors(doors)
            carModelHighlightType = -1
        }
    }

    onFlatTireChanged: {
        if (!actionInProgress && flatTire) {
            forceCarView()
            carModelHighlightType = car.item.highlightTire()
        }
    }

    onLightFailureChanged: {
        if (!actionInProgress && lightFailure) {
            forceCarView()
            carModelHighlightType = car.item.highlightLamp()
        }
    }

    onGearChanged: {
        if (gear === -1)
            reversing()
        else if (gear >= 0)
            returnFromReversing()
    }

    onViewChangedChanged: changeView(viewChanged)

    function reversing() {
        isReversing = true
        // Car backing up, trigger rear camera view and proximity sensor view
        preReversingCenterView = centerStack.viewIndex
        loadCenterView(0, true)
        flipable.flipped = !flipable.flipped
    }

    function returnFromReversing() {
        if (!isReversing)
            return
        loadCenterView(true, false)
        preReversingCenterView = -1
        flipable.flipped = !flipable.flipped
        isReversing = false
    }

    function changeView(nextView) {
        if (isReversing)
            return
        if (actionInProgress || loadingInProgress)
            return
        if (nextView)
            loadCenterView(nextView)
    }

    function stopAll() {
        returnView.stop()
        startupAnimation.stop()
        centerStack.fadeOutCenter.stop()
        centerStack.fadeInCenter.stop()
        doorAction = false
        actionInProgress = false
        loadingInProgress = false
        isReversing = false
    }

    Timer {
        id: returnView
        interval: 1000
        running: false
        onTriggered: {
            if (camera.x === centerStack.x)
                camera.visible = true
            car.item.hidden = true
            car.visible = false
            car.opacity = 0.0
            centerStack.visible = true
        }
    }

    SequentialAnimation {
        id: startupAnimation
        ParallelAnimation {
            SmoothedAnimation {
                target: speedoMeter.rotation
                property: "angle"
                from: 90
                to: 0
                duration: 300
            }
            SmoothedAnimation {
                target: flipable.flipRotation
                property: "angle"
                from: 90
                to: 0
                duration: 300
            }
        }

        ParallelAnimation {
            SequentialAnimation {
                id: rpmAnimation
                SmoothedAnimation {
                    target: flipable.rpm.item
                    property: "rpmValue"
                    to: flipable.rpm.item.maxValue
                    duration: gaugeDemoTime
                    easing.type: Easing.InQuint
                }
                SmoothedAnimation {
                    target: flipable.rpm.item
                    property: "rpmValue"
                    to: ValueSource.rpm
                    duration: gaugeDemoTime
                    easing.type: Easing.OutQuint
                }
                ScriptAction {
                    script: flipable.rpm.item.rpmValue = ValueSource.rpm
                }
            }

            SequentialAnimation {
                id: speedAnimation
                SmoothedAnimation {
                    target: speedoMeter.item
                    property: "speedValue"
                    to: speedoMeter.item.maxValue
                    duration: gaugeDemoTime
                    easing.type: Easing.InQuint
                }
                SmoothedAnimation {
                    target: speedoMeter.item
                    property: "speedValue"
                    to: ValueSource.kph // TODO: Not entirely accurate this way, fix
                    duration: gaugeDemoTime
                    easing.type: Easing.OutQuint
                }
                ScriptAction {
                    script: startupAnimationStopped = true
                }
            }
        }
    }

    speedoMeter.onLoaded: showGauges()
    flipable.onLoaded:  showGauges()
}