Compare commits

3 Commits
main ... dev

Author SHA1 Message Date
bda1465422 Tweaked hover effects 2026-03-10 14:03:36 +01:00
4ccd49b11b Added Tab Bar and animations 2026-03-10 13:52:05 +01:00
8faa56c522 Added Window Header and minor tweaks 2026-03-10 13:18:28 +01:00
5 changed files with 442 additions and 369 deletions

View File

@@ -6,6 +6,7 @@ import { VerticalBox, Button } from "std-widgets.slint";
import { Palette } from "palette.slint";
import { DataWindow } from "components/data-window.slint";
import { MapWindow } from "components/map-window.slint";
import { MinimizedTab } from "components/minimized-tab.slint";
component StatusBadge {
in property <string> label;
@@ -43,6 +44,8 @@ component MenuButton {
border-radius: 8px;
background: area.pressed ? Palette.brand : area.has-hover ? Palette.bg-hover : Palette.bg-main;
animate background { duration: 100ms; }
animate border-color { duration: 100ms; }
HorizontalLayout {
padding-left: 12px;
@@ -123,335 +126,245 @@ export component AppWindow inherits Window {
background: Palette.bg-secondary;
horizontal-stretch: 1;
vertical-stretch: 1;
Rectangle {
width: parent.width / 3;
height: parent.height / 2;
x: 0px;
y: 0px;
DataWindow {
data1: [
{ label: "Engine Temp", value: 97, unit: " °C" },
{ label: "Engine RPM", value: 2573, unit: " RPM" },
{ label: "Engine Map", value: 7, unit: "" },
{ label: "Fuel Rate", value: 0.62, unit: " L/h" },
{ label: "Injector PW", value: 2.7, unit: " ms" },
{ label: "Ignition Adv", value: 21, unit: " °" },
];
data2: [
{ label: "GPS Speed", value: 38.9, unit: " km/h" },
{ label: "GPS Satellites", value: 14, unit: "" },
{ label: "Tank Pressure", value: 101.7, unit: " kPa" },
{ label: "Air Temp", value: 24, unit: " °C" },
{ label: "Humidity", value: 42, unit: " %" },
{ label: "Barometric", value: 1012, unit: " hPa" }
];
VerticalLayout {
padding-top: 4px;
height: 100%;
width: 28px;
x: parent.width - self.width + 2px;
alignment: start;
MinimizedTab {
name: "Spark Advance";
}
MinimizedTab {
name: "Voltage Graph";
}
}
Rectangle {
width: 2 * parent.width / 3;
height: 2 * parent.height / 3;
x: parent.width / 3;
y: 0px;
MapWindow {
data-min: 2200;
data-max: 12000;
name: "MAP 1";
map-axis: [
0.0,
0.0667,
0.1333,
0.2,
0.2667,
0.3333,
0.4,
0.4667,
0.5333,
0.6,
0.6667,
0.7333,
0.8,
0.8667,
0.9333,
1.0
];
rpm-axis: [
250,
500,
750,
1000,
1250,
1500,
1750,
2000,
2250,
2500,
2750,
3000,
3250,
3500,
3750,
4000
];
data: [
2200,
2300,
2400,
2500,
2600,
2700,
2800,
2900,
3000,
3100,
3200,
3300,
3400,
3500,
3600,
3700,
2400,
2500,
2600,
2700,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4200,
4400,
4600,
4800,
5000,
2600,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4200,
4400,
4600,
4800,
5000,
5200,
5400,
5600,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4300,
4600,
4900,
5200,
5500,
5800,
6100,
6400,
6700,
3000,
3200,
3400,
3600,
3800,
4100,
4400,
4700,
5000,
5300,
5600,
5900,
6200,
6500,
6800,
7100,
3300,
3500,
3700,
3900,
4200,
4500,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
3600,
3800,
4000,
4200,
4500,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
7800,
3900,
4100,
4300,
4600,
4900,
5200,
5500,
5800,
6100,
6400,
6700,
7000,
7300,
7600,
7900,
8200,
4200,
4400,
4700,
5000,
5300,
5600,
5900,
6200,
6500,
6800,
7100,
7400,
7700,
8000,
8300,
8600,
4600,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
7800,
8100,
8400,
8700,
9000,
5000,
5200,
5500,
5800,
6100,
6400,
6700,
7000,
7300,
7600,
7900,
8200,
8500,
8800,
9100,
9400,
5400,
5600,
5900,
6200,
6500,
6800,
7100,
7400,
7700,
8000,
8300,
8600,
8900,
9200,
9500,
9800,
5800,
6000,
6300,
6600,
6900,
7200,
7500,
7800,
8100,
8400,
8700,
9000,
9300,
9600,
9900,
10200,
6200,
6500,
6800,
7100,
7400,
7700,
8000,
8300,
8600,
8900,
9200,
9500,
9800,
10100,
10400,
10700,
6800,
7100,
7400,
7700,
8000,
8300,
8600,
8900,
9200,
9500,
9800,
10100,
10400,
10700,
11000,
11300,
7400,
7700,
8000,
8300,
8600,
8900,
9200,
9500,
9800,
10100,
10400,
10700,
11000,
11300,
11600,
12000
];
}
DataWindow {
x: 0;
y: 0;
data1: [
{ label: "Engine Temp", value: 97, unit: " °C" },
{ label: "Engine RPM", value: 2573, unit: " RPM" },
{ label: "Engine Map", value: 7, unit: "" },
{ label: "Fuel Rate", value: 0.62, unit: " L/h" },
{ label: "Injector PW", value: 2.7, unit: " ms" },
{ label: "Ignition Adv", value: 21, unit: " °" },
];
data2: [
{ label: "GPS Speed", value: 38.9, unit: " km/h" },
{ label: "GPS Satellites", value: 14, unit: "" },
{ label: "Tank Pressure", value: 101.7, unit: " kPa" },
{ label: "Air Temp", value: 24, unit: " °C" },
{ label: "Humidity", value: 42, unit: " %" },
{ label: "Barometric", value: 1012, unit: " hPa" }
];
}
MapWindow {
x: 512px;
y: 0;
data-min: 2200;
data-max: 9400;
name: "Injection Period";
map-axis: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
rpm-axis: [
250,
500,
750,
1000,
1250,
1500,
1750,
2000,
2250,
2500,
2750,
3000,
3250,
3500,
3750,
4000
];
data: [
2200,
2300,
2400,
2500,
2600,
2700,
2800,
2900,
3000,
3100,
3200,
3300,
3400,
3500,
3600,
3700,
2400,
2500,
2600,
2700,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4200,
4400,
4600,
4800,
5000,
2600,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4200,
4400,
4600,
4800,
5000,
5200,
5400,
5600,
2800,
3000,
3200,
3400,
3600,
3800,
4000,
4300,
4600,
4900,
5200,
5500,
5800,
6100,
6400,
6700,
3000,
3200,
3400,
3600,
3800,
4100,
4400,
4700,
5000,
5300,
5600,
5900,
6200,
6500,
6800,
7100,
3300,
3500,
3700,
3900,
4200,
4500,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
3600,
3800,
4000,
4200,
4500,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
7800,
3900,
4100,
4300,
4600,
4900,
5200,
5500,
5800,
6100,
6400,
6700,
7000,
7300,
7600,
7900,
8200,
4200,
4400,
4700,
5000,
5300,
5600,
5900,
6200,
6500,
6800,
7100,
7400,
7700,
8000,
8300,
8600,
4600,
4800,
5100,
5400,
5700,
6000,
6300,
6600,
6900,
7200,
7500,
7800,
8100,
8400,
8700,
9000,
5000,
5200,
5500,
5800,
6100,
6400,
6700,
7000,
7300,
7600,
7900,
8200,
8500,
8800,
9100,
9400,
];
}
}

View File

@@ -2,6 +2,7 @@
// Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint";
import { WindowHeader } from "window-header.slint";
struct DataItem {
label: string,
value: float,
@@ -10,11 +11,7 @@ struct DataItem {
component DataColumn {
in property <[DataItem]> data;
preferred-width: 100%;
preferred-height: 100%;
HorizontalLayout {
width: 100%;
height: 100%;
VerticalLayout {
alignment: center;
@@ -54,44 +51,60 @@ component DataColumn {
export component DataWindow {
in property <[DataItem]> data1;
in property <[DataItem]> data2;
preferred-width: 100%;
preferred-height: 100%;
HorizontalLayout {
padding: 12px;
Rectangle {
area := TouchArea {
mouse-cursor: pointer;
}
area := TouchArea { }
background: Palette.bg-main;
border-color: area.has-hover ? Palette.border-focus : Palette.border;
border-width: 2px;
border-radius: 8px;
drop-shadow-color: black;
drop-shadow-offset-x: 6px;
drop-shadow-offset-y: 6px;
drop-shadow-blur: 10px;
HorizontalLayout {
DataColumn {
data: data1;
width: 500px;
vertical-stretch: 1;
VerticalLayout {
visible: true;
alignment: start;
WindowHeader {
title: "Data";
}
HorizontalLayout {
height: 100%;
width: 1px;
padding-top: 10px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
Rectangle {
alignment: space-between;
DataColumn {
width: 218px;
data: data1;
}
HorizontalLayout {
vertical-stretch: 1;
width: 21px;
padding: 10px;
width: 1px;
background: Palette.bg-secondary;
Rectangle {
width: 1px;
background: Palette.border;
}
}
DataColumn {
width: 218px;
data: data2;
}
}
}
DataColumn {
data: data2;
}
Rectangle {
background: transparent;
border-color: Palette.border;
border-width: 2px;
border-radius: 8px;
vertical-stretch: 1;
animate border-color { duration: 200ms; }
}
}
}

View File

@@ -2,6 +2,7 @@
// Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint";
import { WindowHeader } from "window-header.slint";
export component MapWindow {
in property <[float]> data;
in property <[float]> rpm-axis;
@@ -9,19 +10,13 @@ export component MapWindow {
in property <float> data-min;
in property <float> data-max;
in property <string> name;
preferred-width: 100%;
preferred-height: 100%;
VerticalLayout {
padding: 12px;
Rectangle {
area := TouchArea {
mouse-cursor: pointer;
}
area := TouchArea { }
background: Palette.bg-main;
border-color: area.has-hover ? Palette.border-focus : Palette.border;
border-width: 2px;
border-radius: 8px;
drop-shadow-color: black;
drop-shadow-offset-x: 6px;
@@ -29,25 +24,59 @@ export component MapWindow {
drop-shadow-blur: 10px;
VerticalLayout {
HorizontalLayout {
alignment: center;
padding-top: 6px;
Text {
text: name;
font-size: 20px;
color: Palette.text-primary;
}
WindowHeader {
title: name;
}
GridLayout {
width: 100%;
padding: 6px;
vertical-stretch: 1;
Rectangle {
border-color: black;
border-width: 1px;
width: 60px;
height: 60px;
col: 0;
row: 0;
clip: true;
Rectangle {
height: 2 * (parent.height);
width: 2px;
background: white;
transform-rotation: -45deg;
}
Text {
text: "RPM";
font-size: 14px;
color: Palette.text-primary;
transform-origin: { x: self.width / 2, y: self.height / 2 };
transform-rotation: 45deg;
x: parent.width / 2 + 18px * sin(45deg) - self.width / 2;
y: parent.height / 2 - 18px * sin(45deg) - self.height / 2;
}
Text {
text: "Map";
font-size: 14px;
color: Palette.text-primary;
transform-origin: { x: self.width / 2, y: self.height / 2 };
transform-rotation: 45deg;
x: parent.width / 2 - 18px * sin(45deg) - self.width / 2;
y: parent.height / 2 + 18px * sin(45deg) - self.height / 2;
}
}
for i in rpm-axis.length: Rectangle {
border-color: black;
border-width: 1px;
width: (parent.width - 2 * parent.padding) / (rpm-axis.length + 1);
width: 60px;
height: 60px;
property <int> r: 0;
property <int> c: mod(i, rpm-axis.length) + 1;
@@ -64,7 +93,8 @@ export component MapWindow {
for i in map-axis.length: Rectangle {
border-color: black;
border-width: 1px;
width: (parent.width - 2 * parent.padding) / (rpm-axis.length + 1);
width: 60px;
height: 30px;
property <int> r: i + 1;
property <int> c: 0;
@@ -82,7 +112,8 @@ export component MapWindow {
border-color: black;
border-width: 1px;
background: hsv((240deg - ((data[i] - data-min) / (data-max - data-min)) * 240deg), 0.8, 0.80);
width: (parent.width - 2 * parent.padding) / (rpm-axis.length + 1);
width: 60px;
height: 30px;
property <int> r: i / rpm-axis.length + 1;
property <int> c: mod(i, rpm-axis.length) + 1;
@@ -99,6 +130,15 @@ export component MapWindow {
}
}
}
Rectangle {
background: transparent;
border-color: Palette.border;
border-width: 2px;
border-radius: 8px;
vertical-stretch: 1;
animate border-color { duration: 200ms; }
}
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
// Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint";
export component MinimizedTab {
in property <string> name;
height: label.width + 20px;
VerticalLayout {
padding-bottom: 4px;
Rectangle {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
border-width: 2px;
border-color: area.has-hover ? Palette.brand : Palette.border;
background: area.pressed ? Palette.brand : area.has-hover ? Palette.bg-hover : Palette.bg-main;
animate background { duration: 100ms; }
animate border-color { duration: 100ms; }
label := Text {
text: name;
transform-origin: { x: self.width / 2, y: self.height / 2 };
transform-rotation: -90deg;
color: Palette.text-primary;
font-size: 14px;
horizontal-alignment: center;
vertical-alignment: center;
x: parent.width / 2 - self.width / 2 + 1px;
y: parent.height / 2 - self.height / 2;
}
area := TouchArea {
mouse-cursor: pointer;
}
}
}
}

View File

@@ -0,0 +1,67 @@
// Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
// Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint";
export component WindowHeader {
in property <string> title;
HorizontalLayout {
horizontal-stretch: 1;
Rectangle {
border-top-left-radius: 8px;
border-top-right-radius: 8px;
background: Palette.border;
HorizontalLayout {
VerticalLayout {
alignment: center;
padding-left: 6px;
Rectangle {
vertical-stretch: 1;
Text {
text: title;
color: Palette.text-primary;
horizontal-alignment: center;
font-size: 14px;
}
}
}
Rectangle {
horizontal-stretch: 1;
}
HorizontalLayout {
padding-right: 4px;
padding-top: 4px;
padding-bottom: 4px;
Rectangle {
width: 12px;
height: 12px;
background: Palette.warning;
border-radius: self.width / 2;
y: (parent.height - self.height) / 2;
TouchArea {
mouse-cursor: pointer;
}
}
}
HorizontalLayout {
padding-right: 4px;
padding-top: 4px;
padding-bottom: 4px;
Rectangle {
width: 12px;
height: 12px;
background: Palette.error;
border-radius: self.width / 2;
y: (parent.height - self.height) / 2;
TouchArea {
mouse-cursor: pointer;
}
}
}
}
}
}
}