// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT import { Palette } from "../../common.slint"; export global DialState { out property totalLights: 60; out property degreesFilledWithLights: 360deg - (startAngle - endAngle); out property startAngle: 104deg; out property endAngle: -startAngle; in-out property elementRadius: 120px; } export component Dial { pure public function normalizeAngle(angle: angle) -> angle { return (angle + 360deg).mod(360deg); } in property interactive: true; property moving: ta.firstTouch; in-out property dialAngle: DialState.startAngle; out property volume: ((dialAngle - DialState.startAngle) / DialState.degreesFilledWithLights) * DialState.totalLights; width: 212px; height: 213px; knob := Rectangle { base := Rectangle { Image { x: 0px; y: 9px; source: @image-url("../../images/dial-frame.png"); } Image { source: @image-url("../../images/lines.png"); colorize: Palette.dark-color-scheme ? #fff : #000; rotation-angle: root.dialAngle; width: self.source.width * 0.55 * 1px; height: self.source.height * 0.55 * 1px; opacity: 0.03; } ta := TouchArea { property centerX: self.width / 2; property centerY: self.height / 2; property relativeX; property relativeY; property newAngle; property deltaDegrees; property firstTouch: false; width: parent.width; height: parent.height; enabled: root.interactive; changed pressed => { if !self.pressed { firstTouch = false; } } moved => { relativeX = ta.mouse-x - centerX; relativeY = ta.mouse-y - centerY; newAngle = normalizeAngle(atan2(relativeY / 1px, relativeX / 1px)); if !firstTouch { firstTouch = true; deltaDegrees = normalizeAngle(root.dialAngle - newAngle); } else { root.dialAngle = normalizeAngle(deltaDegrees + newAngle).clamp(DialState.startAngle, 260deg); } } } } } Rectangle { width: 1px; height: 1px; x: 106px; y: 105px; Rectangle { width: 0px; height: 0px; x: 55px * root.dialAngle.cos(); y: 55px * root.dialAngle.sin(); Image { source: @image-url("../../images/notch.png"); } } } }