import QtQuick import Quickshell import Quickshell.Io import Quickshell.Widgets PanelWindow { id: launcher required property bool open required property string repoRoot required property string flavorName signal closeRequested() visible: open focusable: true aboveWindows: true color: "transparent" anchors { top: true right: true bottom: true left: true } FileView { id: flavorFile path: launcher.repoRoot + "/theme/flavors/" + launcher.flavorName + "/launcher.json" blockLoading: true watchChanges: true onFileChanged: reload() } readonly property var flavor: JSON.parse(flavorFile.text()) Process { id: appProcess } function fileUrl(path) { if (!path || path.length === 0) return "" if (path.startsWith("/")) return "file://" + path return "file://" + launcher.repoRoot + "/" + path } function launchProgram(program) { launcher.closeRequested() appProcess.command = program.command appProcess.startDetached() } Rectangle { anchors.fill: parent color: launcher.flavor.colors.overlay MouseArea { anchors.fill: parent onClicked: launcher.closeRequested() } Item { id: dial property real r: launcher.flavor.radius property real appR: launcher.flavor.appRadius property real orbit: launcher.flavor.orbitMultiplier * r width: orbit * 2 + appR * 2 height: orbit * 2 + appR * 2 anchors.centerIn: parent Rectangle { id: center width: dial.r * 2 height: dial.r * 2 radius: dial.r anchors.centerIn: parent color: launcher.flavor.colors.centerBackground border.color: launcher.flavor.colors.centerBorder border.width: 2 clip: true Image { anchors.fill: parent anchors.margins: 6 source: launcher.fileUrl(launcher.flavor.centerIcon) fillMode: Image.PreserveAspectFit smooth: true } } Repeater { model: launcher.flavor.programs delegate: Item { id: appButton required property int index required property var modelData property real angle: (index * 45 - 90) * Math.PI / 180 width: dial.appR * 2 height: dial.appR * 2 x: dial.width / 2 + Math.cos(angle) * dial.orbit - width / 2 y: dial.height / 2 + Math.sin(angle) * dial.orbit - height / 2 Rectangle { anchors.fill: parent radius: width / 2 color: mouse.containsMouse ? launcher.flavor.colors.appHoverBackground : launcher.flavor.colors.appBackground border.color: launcher.flavor.colors.appBorder border.width: 2 clip: true Image { anchors.fill: parent anchors.margins: 7 source: launcher.fileUrl(appButton.modelData.icon) fillMode: Image.PreserveAspectFit smooth: true } MouseArea { id: mouse anchors.fill: parent hoverEnabled: true onClicked: launcher.launchProgram(appButton.modelData) } } } } } } }