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

View File

@@ -2,6 +2,7 @@
// Copyright (C) 2026 Association Exergie <association.exergie@gmail.com> // Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint"; import { Palette } from "../palette.slint";
import { WindowHeader } from "window-header.slint";
struct DataItem { struct DataItem {
label: string, label: string,
value: float, value: float,
@@ -10,11 +11,7 @@ struct DataItem {
component DataColumn { component DataColumn {
in property <[DataItem]> data; in property <[DataItem]> data;
preferred-width: 100%;
preferred-height: 100%;
HorizontalLayout { HorizontalLayout {
width: 100%;
height: 100%;
VerticalLayout { VerticalLayout {
alignment: center; alignment: center;
@@ -54,44 +51,60 @@ component DataColumn {
export component DataWindow { export component DataWindow {
in property <[DataItem]> data1; in property <[DataItem]> data1;
in property <[DataItem]> data2; in property <[DataItem]> data2;
preferred-width: 100%;
preferred-height: 100%;
HorizontalLayout { HorizontalLayout {
padding: 12px; padding: 12px;
Rectangle { Rectangle {
area := TouchArea { area := TouchArea { }
mouse-cursor: pointer;
}
background: Palette.bg-main; background: Palette.bg-main;
border-color: area.has-hover ? Palette.border-focus : Palette.border;
border-width: 2px;
border-radius: 8px; border-radius: 8px;
drop-shadow-color: black; drop-shadow-color: black;
drop-shadow-offset-x: 6px; drop-shadow-offset-x: 6px;
drop-shadow-offset-y: 6px; drop-shadow-offset-y: 6px;
drop-shadow-blur: 10px; drop-shadow-blur: 10px;
HorizontalLayout { width: 500px;
DataColumn { vertical-stretch: 1;
data: data1; VerticalLayout {
visible: true;
alignment: start;
WindowHeader {
title: "Data";
} }
HorizontalLayout { HorizontalLayout {
height: 100%; padding-left: 10px;
width: 1px; padding-right: 10px;
padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
Rectangle { alignment: space-between;
DataColumn {
width: 218px;
data: data1;
}
HorizontalLayout {
vertical-stretch: 1;
width: 21px;
padding: 10px; padding: 10px;
width: 1px; Rectangle {
background: Palette.bg-secondary; width: 1px;
background: Palette.border;
}
}
DataColumn {
width: 218px;
data: data2;
} }
} }
}
DataColumn { Rectangle {
data: data2; 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> // Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import { Palette } from "../palette.slint"; import { Palette } from "../palette.slint";
import { WindowHeader } from "window-header.slint";
export component MapWindow { export component MapWindow {
in property <[float]> data; in property <[float]> data;
in property <[float]> rpm-axis; in property <[float]> rpm-axis;
@@ -9,19 +10,13 @@ export component MapWindow {
in property <float> data-min; in property <float> data-min;
in property <float> data-max; in property <float> data-max;
in property <string> name; in property <string> name;
preferred-width: 100%;
preferred-height: 100%;
VerticalLayout { VerticalLayout {
padding: 12px; padding: 12px;
Rectangle { Rectangle {
area := TouchArea { area := TouchArea { }
mouse-cursor: pointer;
}
background: Palette.bg-main; background: Palette.bg-main;
border-color: area.has-hover ? Palette.border-focus : Palette.border;
border-width: 2px;
border-radius: 8px; border-radius: 8px;
drop-shadow-color: black; drop-shadow-color: black;
drop-shadow-offset-x: 6px; drop-shadow-offset-x: 6px;
@@ -29,25 +24,59 @@ export component MapWindow {
drop-shadow-blur: 10px; drop-shadow-blur: 10px;
VerticalLayout { VerticalLayout {
HorizontalLayout { WindowHeader {
alignment: center; title: name;
padding-top: 6px;
Text {
text: name;
font-size: 20px;
color: Palette.text-primary;
}
} }
GridLayout { GridLayout {
width: 100%;
padding: 6px; padding: 6px;
vertical-stretch: 1; 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 { for i in rpm-axis.length: Rectangle {
border-color: black; border-color: black;
border-width: 1px; border-width: 1px;
width: (parent.width - 2 * parent.padding) / (rpm-axis.length + 1); width: 60px;
height: 60px;
property <int> r: 0; property <int> r: 0;
property <int> c: mod(i, rpm-axis.length) + 1; property <int> c: mod(i, rpm-axis.length) + 1;
@@ -64,7 +93,8 @@ export component MapWindow {
for i in map-axis.length: Rectangle { for i in map-axis.length: Rectangle {
border-color: black; border-color: black;
border-width: 1px; border-width: 1px;
width: (parent.width - 2 * parent.padding) / (rpm-axis.length + 1); width: 60px;
height: 30px;
property <int> r: i + 1; property <int> r: i + 1;
property <int> c: 0; property <int> c: 0;
@@ -82,7 +112,8 @@ export component MapWindow {
border-color: black; border-color: black;
border-width: 1px; border-width: 1px;
background: hsv((240deg - ((data[i] - data-min) / (data-max - data-min)) * 240deg), 0.8, 0.80); 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> r: i / rpm-axis.length + 1;
property <int> c: mod(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;
}
}
}
}
}
}
}