mirror of
https://github.com/slint-ui/slint.git
synced 2025-12-23 09:19:32 +00:00
318 lines
8.4 KiB
Text
318 lines
8.4 KiB
Text
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import "./01 Digitall.ttf";
|
|
|
|
component CarButton inherits Rectangle {
|
|
callback clicked <=> touch-area.clicked;
|
|
in property <string> text;
|
|
in property <bool> enabled: true;
|
|
property <angle> touch-animation: touch-area.pressed ? 180deg : 0deg;
|
|
animate touch-animation { duration: 500ms; }
|
|
property <angle> stop1: 67deg + touch-animation;
|
|
property <angle> stop2: 121deg + touch-animation;
|
|
property <angle> stop3: 167deg + touch-animation;
|
|
property <angle> stop4: 256deg + touch-animation;
|
|
property <angle> stop5: 302deg + touch-animation;
|
|
property <angle> stop6: 351deg + touch-animation;
|
|
|
|
width: 80px;
|
|
height: 30px;
|
|
border-radius: self.height / 2;
|
|
border-width: 1px;
|
|
border-color: @conic-gradient(#020414 stop1, #ff0000 stop2, #020414 stop3, #020414 stop4, #ff0000 stop5, #000000 stop6);
|
|
background: @linear-gradient(45deg + touch-animation, #000000 0%, #4d0404 100%);
|
|
opacity: enabled ? 1 : 0.5;
|
|
|
|
label := Text {
|
|
text: text;
|
|
font-family: "01 Digitall";
|
|
font-size: 16px;
|
|
color: touch-area.pressed ? #ff0404 : #ffffff;
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
touch-area := TouchArea {
|
|
enabled: root.enabled;
|
|
}
|
|
}
|
|
|
|
component MiniDot {
|
|
width: 0;
|
|
height: 0;
|
|
Rectangle {
|
|
y: 160px;
|
|
x: -(self.width / 2);
|
|
width: 3px;
|
|
height: 6px;
|
|
background: #47505b;
|
|
border-radius: self.width / 2;
|
|
}
|
|
}
|
|
|
|
component MediumDot {
|
|
width: 0;
|
|
height: 0;
|
|
Rectangle {
|
|
y: 158px;
|
|
x: -(self.width / 2);
|
|
width: 4px;
|
|
height: 10px;
|
|
background: #505b63;
|
|
border-radius: self.width / 2;
|
|
}
|
|
}
|
|
|
|
component BigDot {
|
|
width: 0;
|
|
height: 0;
|
|
Rectangle {
|
|
y: 154px;
|
|
x: -(self.width / 2);
|
|
width: 5px;
|
|
height: 14px;
|
|
background: #b2c7d8;
|
|
border-radius: self.width / 2;
|
|
}
|
|
}
|
|
|
|
component KPHText {
|
|
in property <int> current-speed;
|
|
in property <bool> current-engine-running;
|
|
property <bool> showing: false;
|
|
width: 0;
|
|
height: 0;
|
|
opacity: 0;
|
|
|
|
|
|
states [
|
|
visible when showing : {
|
|
opacity: 1;
|
|
in-out {
|
|
animate opacity { easing: ease-out; duration: 250ms; }
|
|
}
|
|
}
|
|
]
|
|
|
|
changed current-speed => {
|
|
if current-speed >= (root.transform-rotation / 1deg).floor() && current-engine-running {
|
|
showing = true;
|
|
}
|
|
}
|
|
|
|
changed current-engine-running => {
|
|
if (!current-engine-running) {
|
|
showing = false;
|
|
}
|
|
}
|
|
|
|
Text {
|
|
y: 122px;
|
|
width: 36px;
|
|
color: white;
|
|
text: (root.transform-rotation / 1deg).floor();
|
|
font-size: 18px;
|
|
transform-rotation: -root.transform-rotation - 50deg;
|
|
font-family: "01 Digitall";
|
|
horizontal-alignment: left;
|
|
}
|
|
}
|
|
|
|
component SpeedTextDigit inherits Text {
|
|
in property <bool> active: true;
|
|
in property <int> digit;
|
|
text: active ? digit : 0;
|
|
color: active ? white : #392e2e;
|
|
width: 36px;
|
|
font-size: 52px;
|
|
font-family: "01 Digitall";
|
|
}
|
|
|
|
component SpeedText {
|
|
in property <int> speed;
|
|
HorizontalLayout {
|
|
property <length> default-width: 18px;
|
|
SpeedTextDigit {
|
|
digit: (speed / 100).floor();
|
|
active: speed > 99;
|
|
}
|
|
|
|
SpeedTextDigit {
|
|
digit: (speed.mod(100) / 10).floor();
|
|
active: speed > 9;
|
|
}
|
|
|
|
SpeedTextDigit {
|
|
digit: speed.mod(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
export component MainWindow inherits Window {
|
|
property <color> background-color: #020414;
|
|
property <int> speed: 0;
|
|
property <int> max-speed: 260;
|
|
property <int> total-mini-dots: max-speed / 2;
|
|
property <int> total-medium-dots: total-mini-dots / 10;
|
|
property <int> total-big-dots: total-mini-dots / 10;
|
|
property <int> kph-indicators: total-mini-dots / 10;
|
|
property <bool> engine-running: false;
|
|
property <bool> starting-up: true;
|
|
|
|
width: 500px;
|
|
height: 500px;
|
|
background: background-color;
|
|
title: "Sci-Fi Speedometer";
|
|
|
|
animate speed {
|
|
duration: starting-up ? 750ms : 500ms;
|
|
easing: ease-in-out;
|
|
}
|
|
|
|
changed engine-running => {
|
|
if (engine-running) {
|
|
speed = 260;
|
|
}
|
|
}
|
|
|
|
changed speed => {
|
|
if (speed == 260 && starting-up) {
|
|
speed = 0;
|
|
}
|
|
if (speed == 0 && starting-up) {
|
|
starting-up = false;
|
|
}
|
|
}
|
|
|
|
property <angle> stop1: speed < 100 ? 0deg : (speed - 100) * 1deg;
|
|
property <angle> stop2: speed * 1deg;
|
|
property <angle> stop3: speed * 1deg + 1deg;
|
|
property <angle> stop4: 360deg;
|
|
|
|
Rectangle {
|
|
width: 315px;
|
|
height: self.width;
|
|
border-radius: self.width / 2;
|
|
background: @conic-gradient(background-color stop1, red stop2, background-color stop3, background-color stop4);
|
|
transform-rotation: -130deg;
|
|
}
|
|
|
|
Rectangle {
|
|
dial := Rectangle {
|
|
width: 0px;
|
|
height: 0px;
|
|
transform-rotation: 50deg;
|
|
|
|
for i in total-mini-dots: MiniDot {
|
|
transform-rotation: 260deg * (i / total-mini-dots);
|
|
}
|
|
for i in total-medium-dots: MediumDot {
|
|
transform-rotation: 260deg * (i / total-medium-dots) + 10deg;
|
|
}
|
|
for i in total-big-dots + 1: BigDot {
|
|
transform-rotation: 260deg * (i / total-big-dots);
|
|
}
|
|
}
|
|
|
|
holder := Rectangle {
|
|
width: 0px;
|
|
height: 0px;
|
|
background: red;
|
|
transform-rotation: (speed * 1deg) - 130deg;
|
|
needle := Image {
|
|
x: -self.width / 2;
|
|
y: -self.height;
|
|
source: @image-url("needle.png");
|
|
transform-scale: 0.51;
|
|
transform-scale-x: 0.3;
|
|
transform-origin: { x: self.width / 2, y: self.height };
|
|
}
|
|
}
|
|
|
|
inner-circle := Rectangle {
|
|
width: 180px;
|
|
height: self.width;
|
|
border-radius: self.width / 2;
|
|
border-width: 1px;
|
|
border-color: @conic-gradient(#020414 67.5deg, #ff0000 121deg, #020414 167deg, #020414 256deg, #ff0000 302deg, #000000 351deg);
|
|
background: background-color;
|
|
drop-shadow-blur: 25px;
|
|
drop-shadow-color: #fe5a5a.with-alpha(0.4 + (0.2 * (speed / 260)));
|
|
|
|
Rectangle {
|
|
opacity: engine-running && !starting-up ? 1 : 0;
|
|
animate opacity {
|
|
duration: 500ms;
|
|
easing: ease-in-out;
|
|
}
|
|
SpeedText {
|
|
speed: speed;
|
|
y: parent.height / 2 - self.height / 2 - 20px;
|
|
}
|
|
|
|
kmh-label := Text {
|
|
text: "km/h";
|
|
font-size: 18px;
|
|
color: white;
|
|
x: parent.width / 2 - self.width / 2;
|
|
y: parent.height / 2 + 20px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
transform-rotation: 50deg;
|
|
for i in kph-indicators + 1: KPHText {
|
|
current-speed: speed;
|
|
current-engine-running: root.engine-running;
|
|
transform-rotation: 260deg * (i / kph-indicators);
|
|
}
|
|
}
|
|
|
|
control-buttons := HorizontalLayout {
|
|
spacing: 20px;
|
|
alignment: center;
|
|
height: 50px;
|
|
y: 420px;
|
|
|
|
CarButton {
|
|
text: root.engine-running ? "STOP" : "START";
|
|
clicked => {
|
|
if !engine-running {
|
|
engine-running = true;
|
|
starting-up = true;
|
|
speed = 260;
|
|
} else {
|
|
engine-running = false;
|
|
starting-up = false;
|
|
speed = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
CarButton {
|
|
text: "DRIVE";
|
|
enabled: !starting-up && engine-running;
|
|
clicked => {
|
|
speed = 60;
|
|
}
|
|
}
|
|
|
|
CarButton {
|
|
text: "FAST";
|
|
enabled: !starting-up && engine-running;
|
|
clicked => {
|
|
speed = 120;
|
|
}
|
|
}
|
|
|
|
CarButton {
|
|
text: "TURBO";
|
|
enabled: !starting-up && engine-running;
|
|
clicked => {
|
|
speed = 260;
|
|
}
|
|
}
|
|
}
|
|
}
|