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 }
}
}
}