slint/examples/dial/dial.slint
2024-09-17 09:05:38 +02:00

139 lines
4.3 KiB
Text

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
export global AppState {
out property <int> totalLights: 35;
property <angle> degreesFilledWithLights: 360deg - (startAngle - endAngle);
in-out property <int> volume: (normalizeAngle(angle - startAngle) / degreesFilledWithLights) * totalLights;
out property <angle> startAngle: 120deg;
out property <angle> endAngle: 60deg;
in-out property <angle> angle: startAngle;
in-out property <length> elementRadius: 185px;
pure public function normalizeAngle(angle: angle) -> angle {
return (angle + 360deg).mod(360deg);
}
}
export component Light {
in property <int> index;
property <angle> gap: (360deg - (AppState.startAngle - AppState.endAngle)) / AppState.totalLights;
property <angle> angle: (index * gap) + AppState.startAngle;
property <bool> lightOn: index <= AppState.volume;
x: AppState.elementRadius * angle.cos();
y: AppState.elementRadius * angle.sin();
width: 0;
height: 0;
states [
lightOff when !root.lightOn: {
blueLed.opacity: 0;
}
lightOn when root.lightOn: {
blueLed.opacity: 0.6;
in {
animate blueLed.opacity {
duration: 100ms;
easing: ease-in-sine;
}
}
out {
animate blueLed.opacity {
duration: 600ms;
easing: ease-out-sine;
}
}
}
]
Rectangle {
Image {
source: @image-url("images/light-hole.png");
}
blueLed := Image {
source: @image-url("images/light.png");
opacity: 0;
}
}
}
export component AppWindow inherits Window {
preferred-width: 500px;
preferred-height: 500px;
background: #1e1d27;
Rectangle {
width: 425px;
height: 427px;
background: #1e1d27;
knob := Rectangle {
base := Image {
source: @image-url("images/dial-frame.png");
ta := TouchArea {
property <length> centerX: self.width / 2;
property <length> centerY: self.height / 2;
property <length> relativeX;
property <length> relativeY;
property <angle> newAngle;
property <angle> deltaDegrees;
property <bool> firstTouch: false;
width: parent.width;
height: parent.height;
clicked => {
firstTouch = false;
}
moved => {
relativeX = ta.mouse-x - centerX;
relativeY = ta.mouse-y - centerY;
newAngle = AppState.normalizeAngle(atan2(relativeY / 1px, relativeX / 1px));
// on first touch work out what angle the dial is at. Then use this to create a delta
// So further movement will be relative to this angle.
if !firstTouch {
firstTouch = true;
deltaDegrees = AppState.normalizeAngle(AppState.angle - newAngle);
} else {
AppState.angle = AppState.normalizeAngle(deltaDegrees + newAngle);
}
}
}
}
}
Rectangle {
width: 1px;
height: 1px;
x: 212px;
y: 210px;
metalKnob := Image {
source: @image-url("images/metal-dial.png");
rotation-angle: AppState.angle;
}
Image {
source: @image-url("images/metal-lights.png");
}
Image {
x: 120px * AppState.angle.cos();
y: 120px * AppState.angle.sin();
source: @image-url("images/indicator.png");
}
Image {
source: @image-url("images/dial-trim.png");
}
lightHolder := Rectangle {
x: 0px;
y: 2px;
for i in AppState.totalLights + 1: Light {
index: i;
}
}
}
}
}