From fdc8262c86a108d4ac53264cae3b843778bddf5a Mon Sep 17 00:00:00 2001 From: Trist4n Date: Tue, 18 Aug 2026 15:43:42 +0200 Subject: [PATCH 1/4] fix(example): ReferenceError: root is not defined in 007 signals example --- examples/007-signals/main.qml | 135 ++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/examples/007-signals/main.qml b/examples/007-signals/main.qml index e4717dc..61fcf25 100644 --- a/examples/007-signals/main.qml +++ b/examples/007-signals/main.qml @@ -2,77 +2,88 @@ import QtQuick import QtQuick.Window Window { - width: 400 - height: 350 - title: "Signals" - visible: true + width: 400 + height: 350 + title: "Signals" + visible: true - Rectangle { - anchors.fill: parent - color: "#1e1e2e" + Rectangle { + id: root - property int clickCount: 0 + property int clickCount: 0 - Rectangle { - id: button - anchors.centerIn: parent - width: 180 - height: 60 - color: "#89b4fa" - radius: 10 - border.color: "#7a8cd6" - border.width: 2 - - Text { - id: buttonLabel - anchors.centerIn: parent - text: "Click me!" + anchors.fill: parent color: "#1e1e2e" - font.pixelSize: 18 - font.bold: true - } - MouseArea { - anchors.fill: parent - onClicked: { - parent.clicked() + Rectangle { + id: button + + signal clicked() + + anchors.centerIn: parent + width: 180 + height: 60 + color: "#89b4fa" + radius: 10 + border.color: "#7a8cd6" + border.width: 2 + onClicked: { + root.clickCount += 1; + buttonLabel.text = "Clicked " + root.clickCount; + if (root.clickCount === 1) + buttonLabel.text = "Clicked once"; + + } + + Text { + id: buttonLabel + + anchors.centerIn: parent + text: "Click me!" + color: "#1e1e2e" + font.pixelSize: 18 + font.bold: true + } + + MouseArea { + anchors.fill: parent + onClicked: { + parent.clicked(); + } + } + } - } - signal clicked() + Rectangle { + id: indicator - onClicked: { - root.clickCount += 1 - buttonLabel.text = "Clicked " + root.clickCount - if (clickCount === 1) buttonLabel.text = "Clicked once" - } - } + anchors.top: button.bottom + anchors.topMargin: 24 + anchors.horizontalCenter: parent.horizontalCenter + width: 12 + height: 12 + radius: 6 + color: "#a6e3a1" - Rectangle { - id: indicator - anchors.top: button.bottom - anchors.topMargin: 24 - anchors.horizontalCenter: parent.horizontalCenter - width: 12 - height: 12 - radius: 6 - color: "#a6e3a1" - - PropertyAnimation on color { - id: flashAnim - from: "#f38ba8" - to: "#a6e3a1" - duration: 300 - } - } + PropertyAnimation on color { + id: flashAnim + + from: "#f38ba8" + to: "#a6e3a1" + duration: 300 + } + + } + + Text { + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + text: "Signals connect events to behavior" + color: "#a6adc8" + font.pixelSize: 13 + } - Text { - anchors.bottom: parent.bottom - anchors.bottomMargin: 20 - anchors.horizontalCenter: parent.horizontalCenter - text: "Signals connect events to behavior" - color: "#a6adc8" - font.pixelSize: 13 } - } + } From 42713c3e1e5b342f455603a6ce20901c09ae521a Mon Sep 17 00:00:00 2001 From: Trist4n Date: Tue, 18 Aug 2026 19:50:39 +0200 Subject: [PATCH 2/4] fix(examples [002-026]): Migration of examples to Quickshell v0.3 --- examples/009-calculator/calculator.qml | 315 ++++++++++++++++------- examples/010-first-shell/shell.qml | 32 +-- examples/011-shellroot/Theme.qml | 11 +- examples/011-shellroot/shell.qml | 61 +++-- examples/012-config-structure/Panel.qml | 57 ++-- examples/020-panel-window/shell.qml | 34 +-- examples/021-popup-window/shell.qml | 129 ++++++---- examples/022-floating-window/shell.qml | 25 +- examples/023-anchors-margins/shell.qml | 79 +++--- examples/024-exclusive-zones/shell.qml | 76 +++--- examples/025-multi-monitor/shell.qml | 50 ++-- examples/026-transparency-blur/shell.qml | 61 +++-- 12 files changed, 564 insertions(+), 366 deletions(-) diff --git a/examples/009-calculator/calculator.qml b/examples/009-calculator/calculator.qml index b6aeb25..ca22c96 100644 --- a/examples/009-calculator/calculator.qml +++ b/examples/009-calculator/calculator.qml @@ -1,108 +1,229 @@ import QtQuick +import QtQuick.Layouts import QtQuick.Window Window { - width: 300 - height: 400 - title: "Calculator" - visible: true - - Rectangle { - anchors.fill: parent - color: "#1e1e2e" - - property string displayText: "0" - property real leftOperand: 0 - property string operator: "" - property bool freshInput: true - - function digitPressed(d) { - if (freshInput) { - displayText = d - freshInput = false - } else { - displayText = displayText === "0" ? d : displayText + d - } - } + width: 300 + height: 400 + title: "Calculator" + visible: true + + Rectangle { + id: mainRect + + property string displayText: "0" + property real leftOperand: 0 + property string operator: "" + property bool freshInput: true + + function digitPressed(d) { + if (freshInput) { + displayText = d; + freshInput = false; + } else { + if (displayText === "0" && d === "0") + return ; + + displayText = displayText === "0" ? d : displayText + d; + } + } - function operatorPressed(op) { - leftOperand = parseFloat(displayText) - operator = op - freshInput = true - } + function operatorPressed(op) { + leftOperand = parseFloat(displayText); + operator = op; + freshInput = true; + } - function equalsPressed() { - var right = parseFloat(displayText) - var result = 0 - switch (operator) { - case "+": result = leftOperand + right; break - case "−": result = leftOperand - right; break - case "×": result = leftOperand * right; break - case "÷": result = right !== 0 ? leftOperand / right : 0; break - default: result = right - } - displayText = String(result) - operator = "" - freshInput = true - } + function equalsPressed() { + var right = parseFloat(displayText); + var result = 0; + switch (operator) { + case "+": + result = leftOperand + right; + break; + case "−": + result = leftOperand - right; + break; + case "×": + result = leftOperand * right; + break; + case "÷": + result = right !== 0 ? leftOperand / right : 0; + break; + default: + result = right; + } + displayText = String(result); + operator = ""; + freshInput = true; + } - function clearPressed() { - displayText = "0" - leftOperand = 0 - operator = "" - freshInput = true - } + function clearPressed() { + displayText = "0"; + leftOperand = 0; + operator = ""; + freshInput = true; + } + + anchors.fill: parent + color: "#1e1e2e" + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 12 + + // Display + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 64 + color: "#181825" + radius: 8 + + Text { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: mainRect.displayText + color: "#cdd6f4" + font.pixelSize: 32 + } + + } + + // Keypad Grid + GridLayout { + columns: 4 + rowSpacing: 6 + columnSpacing: 6 + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + + // Row 1 + CalcButton { + text: "C" + bgColor: "#f38ba8" + textColor: "#1e1e2e" + onClicked: mainRect.clearPressed() + } + + CalcButton { + text: "÷" + bgColor: "#45475a" + onClicked: mainRect.operatorPressed("÷") + } + + CalcButton { + text: "×" + bgColor: "#45475a" + onClicked: mainRect.operatorPressed("×") + } + + CalcButton { + text: "⌫" + bgColor: "#45475a" + onClicked: { + if (mainRect.freshInput) + return ; + + mainRect.displayText = mainRect.displayText.length > 1 ? mainRect.displayText.slice(0, -1) : "0"; + } + } + + // Row 2 + CalcButton { + text: "7" + onClicked: mainRect.digitPressed("7") + } + + CalcButton { + text: "8" + onClicked: mainRect.digitPressed("8") + } + + CalcButton { + text: "9" + onClicked: mainRect.digitPressed("9") + } + + CalcButton { + text: "−" + bgColor: "#45475a" + onClicked: mainRect.operatorPressed("−") + } + + // Row 3 + CalcButton { + text: "4" + onClicked: mainRect.digitPressed("4") + } + + CalcButton { + text: "5" + onClicked: mainRect.digitPressed("5") + } + + CalcButton { + text: "6" + onClicked: mainRect.digitPressed("6") + } + + CalcButton { + text: "+" + bgColor: "#45475a" + onClicked: mainRect.operatorPressed("+") + } + + // Row 4 + CalcButton { + text: "1" + onClicked: mainRect.digitPressed("1") + } + + CalcButton { + text: "2" + onClicked: mainRect.digitPressed("2") + } + + CalcButton { + text: "3" + onClicked: mainRect.digitPressed("3") + } + + CalcButton { + text: "±" + bgColor: "#45475a" + onClicked: mainRect.displayText = String(-parseFloat(mainRect.displayText)) + } + + // Row 5 + CalcButton { + text: "0" + onClicked: mainRect.digitPressed("0") + } + + CalcButton { + text: "." + onClicked: { + if (!mainRect.displayText.includes(".")) + mainRect.displayText += "."; + + } + } + + CalcButton { + text: "=" + bgColor: "#89b4fa" + textColor: "#1e1e2e" + Layout.columnSpan: 2 + Layout.fillWidth: true + onClicked: mainRect.equalsPressed() + } + + } - Column { - anchors.fill: parent - anchors.margins: 16 - spacing: 8 - - Rectangle { - width: parent.width - height: 64 - color: "#181825" - radius: 8 - - Text { - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: displayText - color: "#cdd6f4" - font.pixelSize: 32 } - } - - Grid { - columns: 4 - spacing: 6 - width: parent.width - - CalcButton { text: "C"; bgColor: "#f38ba8"; textColor: "#1e1e2e"; onClicked: clearPressed() } - CalcButton { text: "÷"; bgColor: "#45475a"; onClicked: operatorPressed("÷") } - CalcButton { text: "×"; bgColor: "#45475a"; onClicked: operatorPressed("×") } - CalcButton { text: "⌫"; bgColor: "#45475a"; onClicked: { displayText = displayText.slice(0, -1) || "0" } } - - CalcButton { text: "7"; onClicked: digitPressed("7") } - CalcButton { text: "8"; onClicked: digitPressed("8") } - CalcButton { text: "9"; onClicked: digitPressed("9") } - CalcButton { text: "−"; bgColor: "#45475a"; onClicked: operatorPressed("−") } - - CalcButton { text: "4"; onClicked: digitPressed("4") } - CalcButton { text: "5"; onClicked: digitPressed("5") } - CalcButton { text: "6"; onClicked: digitPressed("6") } - CalcButton { text: "+"; bgColor: "#45475a"; onClicked: operatorPressed("+") } - - CalcButton { text: "1"; onClicked: digitPressed("1") } - CalcButton { text: "2"; onClicked: digitPressed("2") } - CalcButton { text: "3"; onClicked: digitPressed("3") } - CalcButton { text: "="; bgColor: "#89b4fa"; textColor: "#1e1e2e"; onClicked: equalsPressed() } - - CalcButton { text: "0"; width: 134; onClicked: digitPressed("0") } - CalcButton { text: "."; onClicked: { if (!displayText.includes(".")) displayText += "." } } - CalcButton { text: "±"; onClicked: { displayText = String(-parseFloat(displayText)) } } - } + } - } + } diff --git a/examples/010-first-shell/shell.qml b/examples/010-first-shell/shell.qml index d366455..942bf52 100644 --- a/examples/010-first-shell/shell.qml +++ b/examples/010-first-shell/shell.qml @@ -1,21 +1,23 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - PanelWindow { - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" + PanelWindow { + implicitHeight: 48 + color: "#1e1e2e" + + anchors { + top: true + left: true + right: true + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "My first shell" + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "My first shell" } - } + } diff --git a/examples/011-shellroot/Theme.qml b/examples/011-shellroot/Theme.qml index 2c87889..57409c2 100644 --- a/examples/011-shellroot/Theme.qml +++ b/examples/011-shellroot/Theme.qml @@ -1,10 +1,9 @@ -pragma Singleton - import QtQuick +pragma Singleton QtObject { - readonly property color bgColor: "#1e1e2e" - readonly property color accent: "#89b4fa" - readonly property color fgColor: "#cdd6f4" - readonly property int panelHeight: 48 + readonly property color bgColor: "#1e1e2e" + readonly property color accent: "#89b4fa" + readonly property color fgColor: "#cdd6f4" + readonly property int panelHeight: 48 } diff --git a/examples/011-shellroot/shell.qml b/examples/011-shellroot/shell.qml index fe9c2fc..de98b93 100644 --- a/examples/011-shellroot/shell.qml +++ b/examples/011-shellroot/shell.qml @@ -1,28 +1,43 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - PanelWindow { - anchors { - top: true - left: true - right: true - } - height: Theme.panelHeight - color: Theme.bgColor - - Row { - anchors { - left: parent.left - leftMargin: 12 - verticalCenter: parent.verticalCenter - } - spacing: 16 - - Text { color: Theme.accent; text: "Apps" } - Text { color: Theme.fgColor; text: "Terminal" } - Text { color: Theme.fgColor; text: "Settings" } + PanelWindow { + implicitHeight: Theme.panelHeight + color: Theme.bgColor + + anchors { + top: true + left: true + right: true + } + + Row { + spacing: 16 + + anchors { + left: parent.left + leftMargin: 12 + verticalCenter: parent.verticalCenter + } + + Text { + color: Theme.accent + text: "Apps" + } + + Text { + color: Theme.fgColor + text: "Terminal" + } + + Text { + color: Theme.fgColor + text: "Settings" + } + + } + } - } + } diff --git a/examples/012-config-structure/Panel.qml b/examples/012-config-structure/Panel.qml index f04bd34..bbe8093 100644 --- a/examples/012-config-structure/Panel.qml +++ b/examples/012-config-structure/Panel.qml @@ -1,26 +1,45 @@ -import Quickshell.Window import QtQuick +import Quickshell PanelWindow { - anchors { - top: true - left: true - right: true - } - height: Theme.panelHeight - color: Theme.bgColor - - Row { + implicitHeight: Theme.panelHeight + color: Theme.bgColor + anchors { - left: parent.left - leftMargin: 12 - verticalCenter: parent.verticalCenter + top: true + left: true + right: true + } + + Row { + spacing: 16 + + anchors { + left: parent.left + leftMargin: 12 + verticalCenter: parent.verticalCenter + } + + Text { + color: Theme.accent + text: " Launcher" + } + + Text { + color: Theme.fgColor + text: " Terminal" + } + + Item { + width: 1 + height: 1 + } + + Text { + color: Theme.fgColor + text: " 12:00" + } + } - spacing: 16 - Text { color: Theme.accent; text: " Launcher" } - Text { color: Theme.fgColor; text: " Terminal" } - Item { width: 1; height: 1 } - Text { color: Theme.fgColor; text: " 12:00" } - } } diff --git a/examples/020-panel-window/shell.qml b/examples/020-panel-window/shell.qml index 06ece49..29735a0 100644 --- a/examples/020-panel-window/shell.qml +++ b/examples/020-panel-window/shell.qml @@ -1,22 +1,24 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - PanelWindow { - anchors { - bottom: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" - exclusiveZone: 48 + PanelWindow { + implicitHeight: 48 + color: "#1e1e2e" + exclusiveZone: 48 + + anchors { + bottom: true + left: true + right: true + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Bottom panel with exclusive zone" + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Bottom panel with exclusive zone" } - } + } diff --git a/examples/021-popup-window/shell.qml b/examples/021-popup-window/shell.qml index 725300f..fd86de3 100644 --- a/examples/021-popup-window/shell.qml +++ b/examples/021-popup-window/shell.qml @@ -1,62 +1,87 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - PanelWindow { - id: panel - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" - - Rectangle { - id: button - anchors.centerIn: parent - width: 120 - height: 32 - radius: 6 - color: "#313244" - - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Click for popup" - } - - MouseArea { - anchors.fill: parent - onClicked: popup.visible = !popup.visible - } - } - - PopupWindow { - id: popup - width: 200 - height: 150 - color: "#1e1e2e" - visible: false + PanelWindow { + id: panel - property var anchor: Qt.point( - panel.x + button.x + button.width / 2 - width / 2, - panel.y + panel.height - ) + implicitHeight: 48 + color: "#1e1e2e" - Column { anchors { - top: parent.top - topMargin: 8 - horizontalCenter: parent.horizontalCenter + top: true + left: true + right: true + } + + Rectangle { + id: button + + anchors.centerIn: parent + width: 120 + height: 32 + radius: 6 + color: "#313244" + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Click for popup" + } + + MouseArea { + anchors.fill: parent + onClicked: popup.visible = !popup.visible + } + + } + + PopupWindow { + id: popup + + implicitWidth: 200 + implicitHeight: 150 + color: "#1e1e2e" + visible: false + + anchor { + window: panel + rect.x: button.x + rect.y: button.y + rect.width: button.width + rect.height: button.height + edges: Edges.Bottom + gravity: Edges.Bottom + } + + Column { + spacing: 8 + + anchors { + top: parent.top + topMargin: 8 + horizontalCenter: parent.horizontalCenter + } + + Text { + color: "#cdd6f4" + text: "Item 1" + } + + Text { + color: "#cdd6f4" + text: "Item 2" + } + + Text { + color: "#cdd6f4" + text: "Item 3" + } + + } + } - spacing: 8 - Text { color: "#cdd6f4"; text: "Item 1" } - Text { color: "#cdd6f4"; text: "Item 2" } - Text { color: "#cdd6f4"; text: "Item 3" } - } } - } + } diff --git a/examples/022-floating-window/shell.qml b/examples/022-floating-window/shell.qml index 6744383..91b70be 100644 --- a/examples/022-floating-window/shell.qml +++ b/examples/022-floating-window/shell.qml @@ -1,18 +1,19 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - FloatingWindow { - title: "Floating Window" - width: 400 - height: 300 - color: "#1e1e2e" + FloatingWindow { + title: "Floating Window" + implicitWidth: 400 + implicitHeight: 300 + color: "#1e1e2e" + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "I float freely" + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "I float freely" } - } + } diff --git a/examples/023-anchors-margins/shell.qml b/examples/023-anchors-margins/shell.qml index ac82099..2d50377 100644 --- a/examples/023-anchors-margins/shell.qml +++ b/examples/023-anchors-margins/shell.qml @@ -1,43 +1,50 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - // Full-width top panel - PanelWindow { - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" + // Full-width top panel + PanelWindow { + implicitHeight: 48 + color: "#1e1e2e" + + anchors { + top: true + left: true + right: true + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Top panel (anchors top + left + right)" + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Top panel (anchors top + left + right)" - } - } - - // Bottom panel with margins on each side - PanelWindow { - anchors { - bottom: true - left: true - right: true - leftMargin: 64 - rightMargin: 64 - bottomMargin: 8 } - height: 48 - color: "#313244" - exclusiveZone: 48 - - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Bottom panel with margins" + + // Bottom panel with margins on each side + PanelWindow { + implicitHeight: 48 + color: "#313244" + exclusiveZone: 48 + + anchors { + bottom: true + left: true + right: true + } + + margins { + bottom: 8 + left: 64 + right: 64 + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Bottom panel with margins" + } + } - } + } diff --git a/examples/024-exclusive-zones/shell.qml b/examples/024-exclusive-zones/shell.qml index 718f5b1..c7eee1a 100644 --- a/examples/024-exclusive-zones/shell.qml +++ b/examples/024-exclusive-zones/shell.qml @@ -1,41 +1,47 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell +import Quickshell.Wayland ShellRoot { - PanelWindow { - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" - // Reserve 48px at the top so maximized windows avoid this area - exclusiveZone: 48 - - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "exclusiveZone: 48 — maximized windows avoid me" - } - } - - // A panel without exclusiveZone — windows can overlap it - PanelWindow { - anchors { - bottom: true - left: true - right: true + PanelWindow { + implicitHeight: 48 + color: "#1e1e2e" + // Reserve 48px at the top so maximized windows avoid this area + exclusiveZone: 48 + WlrLayershell.layer: WlrLayer.Bottom + + anchors { + top: true + left: true + right: true + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "exclusiveZone: 48 — maximized windows avoid me" + } + } - height: 48 - color: "#313244" - // exclusiveZone omitted — windows may cover this panel - - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "No exclusiveZone — windows can overlap" + + // A panel without exclusiveZone — windows can overlap it + PanelWindow { + // exclusiveZone omitted — windows may cover this panel + implicitHeight: 48 + color: "#313244" + + anchors { + bottom: true + left: true + right: true + } + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "No exclusiveZone — windows can overlap" + } + } - } + } diff --git a/examples/025-multi-monitor/shell.qml b/examples/025-multi-monitor/shell.qml index 4111ea9..c6d5ee5 100644 --- a/examples/025-multi-monitor/shell.qml +++ b/examples/025-multi-monitor/shell.qml @@ -1,35 +1,31 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - // Iterate over every connected screen - Instantiator { - model: Quickshell.screens + Instantiator { + model: Quickshell.screens - PanelWindow { - // Assign each window to its corresponding screen variant - screen: VariantList { - Variant { - screen: modelData - value: modelData - } - } + PanelWindow { + screen: modelData + implicitHeight: 48 + color: "#1e1e2e" + exclusiveZone: 48 + + anchors { + top: true + left: true + right: true + } - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" - exclusiveZone: 48 + // Assign each window to its corresponding screen variant + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Screen: " + modelData.name + " (" + modelData.width + "x" + modelData.height + ")" + } + + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Screen: " + modelData.name + " (" + modelData.width + "x" + modelData.height + ")" - } } - } + } diff --git a/examples/026-transparency-blur/shell.qml b/examples/026-transparency-blur/shell.qml index e27bcb2..7eb7e36 100644 --- a/examples/026-transparency-blur/shell.qml +++ b/examples/026-transparency-blur/shell.qml @@ -1,35 +1,40 @@ -import Quickshell -import Quickshell.Window -import QtQuick import Qt5Compat.GraphicalEffects +import QtQuick +import Quickshell ShellRoot { - PanelWindow { - anchors { - top: true - left: true - right: true - } - height: 48 - transparent: true - - // Enable the layer and apply a blur for a glass-like effect - layer.enabled: true - layer.effect: GaussianBlur { - radius: 8 - samples: 17 - } + PanelWindow { + implicitHeight: 48 + // Apply a blur for a glass-like effect + color: "transparent" - Rectangle { - anchors.fill: parent - color: "#1e1e2e" - opacity: 0.85 + anchors { + top: true + left: true + right: true + } + + Rectangle { + id: background + + anchors.fill: parent + color: "#1e1e2e" + opacity: 0.85 + layer.enabled: true + + Text { + anchors.centerIn: parent + color: "#cdd6f4" + text: "Transparent panel with blur effect" + } + + layer.effect: GaussianBlur { + radius: 8 + samples: 17 + } + + } - Text { - anchors.centerIn: parent - color: "#cdd6f4" - text: "Transparent panel with blur effect" - } } - } + } From b74e90c7ebb956b930b2cacbb3140398efc7c1c8 Mon Sep 17 00:00:00 2001 From: Trist4n Date: Wed, 19 Aug 2026 10:04:12 +0200 Subject: [PATCH 3/4] fix(examples [030-091]): Migration of examples to Quickshell v0.3 --- examples/030-top-panel/MainPanel.qml | 66 +++-- examples/030-top-panel/WorkspaceDots.qml | 75 +++-- examples/030-top-panel/shell.qml | 19 +- examples/040-widgets/BatteryWidget.qml | 36 ++- examples/040-widgets/CalendarWidget.qml | 46 +-- examples/040-widgets/ClockWidget.qml | 13 +- examples/040-widgets/CpuWidget.qml | 20 +- examples/040-widgets/DiskWidget.qml | 20 +- examples/040-widgets/NetworkWidget.qml | 44 ++- examples/040-widgets/RamWidget.qml | 20 +- examples/040-widgets/SystemTray.qml | 12 +- examples/040-widgets/VolumeWidget.qml | 24 +- examples/040-widgets/WeatherWidget.qml | 11 +- examples/090-app-launcher/AppLauncher.qml | 312 +++++++++++--------- examples/091-power-menu/PowerMenu.qml | 340 +++++++++++++--------- 15 files changed, 641 insertions(+), 417 deletions(-) diff --git a/examples/030-top-panel/MainPanel.qml b/examples/030-top-panel/MainPanel.qml index c90abc7..b16b6b8 100644 --- a/examples/030-top-panel/MainPanel.qml +++ b/examples/030-top-panel/MainPanel.qml @@ -1,41 +1,49 @@ -import Quickshell.Window import QtQuick +import Quickshell PanelWindow { - id: panel - anchors { - top: true - left: true - right: true - } - height: 48 - color: "#1e1e2e" - exclusiveZone: 48 - - Row { + id: panel + + implicitHeight: 48 + color: "#1e1e2e" + exclusiveZone: 48 + anchors { - left: parent.left - leftMargin: 12 - verticalCenter: parent.verticalCenter + top: true + left: true + right: true } - spacing: 12 - LauncherButton { iconText: "" } + Row { + spacing: 12 + + anchors { + left: parent.left + leftMargin: 12 + verticalCenter: parent.verticalCenter + } + + LauncherButton { + iconText: "" + } + + WorkspaceDots { + } - WorkspaceDots { - currentWorkspace: 0 - workspaceCount: 5 } - } - Row { - anchors { - right: parent.right - rightMargin: 12 - verticalCenter: parent.verticalCenter + Row { + spacing: 12 + + anchors { + right: parent.right + rightMargin: 12 + verticalCenter: parent.verticalCenter + } + + ClockWidget { + } + } - spacing: 12 - ClockWidget { } - } } diff --git a/examples/030-top-panel/WorkspaceDots.qml b/examples/030-top-panel/WorkspaceDots.qml index 80e0714..2171515 100644 --- a/examples/030-top-panel/WorkspaceDots.qml +++ b/examples/030-top-panel/WorkspaceDots.qml @@ -1,30 +1,55 @@ import QtQuick +import Quickshell +import Quickshell.Hyprland Item { - id: root - height: 36 - - property int workspaceCount: 5 - property int currentWorkspace: 0 - property color activeColor: "#89b4fa" - property color inactiveColor: "#585b70" - property int dotSize: 10 - property int spacing: 8 - - Row { - anchors.centerIn: parent - spacing: root.spacing - - Repeater { - model: root.workspaceCount - - Rectangle { - width: root.dotSize - height: root.dotSize - radius: root.dotSize / 2 - color: index === root.currentWorkspace ? root.activeColor : root.inactiveColor - Behavior on color { ColorAnimation { duration: 150 } } - } + id: root + + property int workspaceCount: 5 + property int currentWorkspace: 0 + property color activeColor: "#89b4fa" + property color inactiveColor: "#585b70" + property int dotSize: 10 + property int spacing: 8 + + implicitWidth: row.implicitWidth + implicitHeight: 36 + + Row { + id: row + anchors.centerIn: parent + spacing: root.spacing + + Repeater { + + model: root.workspaceCount + + Rectangle { + readonly property int workspaceId: index + 1 + readonly property bool isFocused: Hyprland.focusedWorkspace?.id === workspaceId + + width: root.dotSize + height: root.dotSize + radius: root.dotSize / 2 + color: isFocused ? root.activeColor : root.inactiveColor + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: Hyprland.dispatch("hl.dsp.focus({ workspace = " + workspaceId + " })") + } + + Behavior on color { + ColorAnimation { + duration: 150 + } + + } + + } + + } + } - } + } diff --git a/examples/030-top-panel/shell.qml b/examples/030-top-panel/shell.qml index 43ca162..47dc428 100644 --- a/examples/030-top-panel/shell.qml +++ b/examples/030-top-panel/shell.qml @@ -1,19 +1,14 @@ -import Quickshell -import Quickshell.Window import QtQuick +import Quickshell ShellRoot { - // Spawn a MainPanel on every connected screen - Instantiator { - model: Quickshell.screens + // Spawn a MainPanel on every connected screen + Instantiator { + model: Quickshell.screens - MainPanel { - screen: VariantList { - Variant { - screen: modelData - value: modelData + MainPanel { } - } + } - } + } diff --git a/examples/040-widgets/BatteryWidget.qml b/examples/040-widgets/BatteryWidget.qml index dd6e086..a56aaa4 100644 --- a/examples/040-widgets/BatteryWidget.qml +++ b/examples/040-widgets/BatteryWidget.qml @@ -13,10 +13,9 @@ import QtQuick.Layouts Item { id: root - /*! Battery charge level from 0 to 100. */ + //! Battery charge level from 0 to 100. property int percent: 0 - - /*! Whether the battery is currently charging. */ + //! Whether the battery is currently charging. property bool charging: false implicitWidth: batteryRow.implicitWidth @@ -24,21 +23,36 @@ Item { RowLayout { id: batteryRow + anchors.centerIn: parent spacing: 4 Text { text: { - if (root.charging) return "\u26A1"; - if (root.percent <= 10) return "\u26A1"; - if (root.percent <= 25) return "\u25D4"; - if (root.percent <= 50) return "\u25D3"; - if (root.percent <= 75) return "\u25D2"; + if (root.charging) + return "\u26A1"; + + if (root.percent <= 10) + return "\u26A1"; + + if (root.percent <= 25) + return "\u25D4"; + + if (root.percent <= 50) + return "\u25D3"; + + if (root.percent <= 75) + return "\u25D2"; + return "\u25D1"; } color: { - if (root.charging) return "#a6e3a1"; - if (root.percent <= 15) return "#f38ba8"; + if (root.charging) + return "#a6e3a1"; + + if (root.percent <= 15) + return "#f38ba8"; + return "#cdd6f4"; } font.pixelSize: 14 @@ -49,5 +63,7 @@ Item { color: "#cdd6f4" font.pixelSize: 12 } + } + } diff --git a/examples/040-widgets/CalendarWidget.qml b/examples/040-widgets/CalendarWidget.qml index 794d612..c180456 100644 --- a/examples/040-widgets/CalendarWidget.qml +++ b/examples/040-widgets/CalendarWidget.qml @@ -14,22 +14,17 @@ import QtQuick.Layouts Item { id: root - /*! The date used to determine the displayed month and year. */ + //! The date used to determine the displayed month and year. property date date: new Date() - - /*! The first day of the displayed month. */ + //! The first day of the displayed month. readonly property date monthStart: new Date(date.getFullYear(), date.getMonth(), 1) - - /*! The last day of the displayed month. */ + //! The last day of the displayed month. readonly property date monthEnd: new Date(date.getFullYear(), date.getMonth() + 1, 0) - - /*! Number of days in the month. */ + //! Number of days in the month. readonly property int daysInMonth: monthEnd.getDate() - - /*! Weekday of the first day (0=Sun .. 6=Sat). */ + //! Weekday of the first day (0=Sun .. 6=Sat). readonly property int startDayOfWeek: monthStart.getDay() - - /*! Total cells needed to fill the grid (leading blanks + days). */ + //! Total cells needed to fill the grid (leading blanks + days). readonly property int totalCells: startDayOfWeek + daysInMonth implicitWidth: calendarColumn.implicitWidth @@ -37,13 +32,13 @@ Item { ColumnLayout { id: calendarColumn + anchors.centerIn: parent spacing: 4 Text { text: { - var months = ["January","February","March","April","May","June", - "July","August","September","October","November","December"]; + var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; return months[root.date.getMonth()] + " " + root.date.getFullYear(); } color: "#cdd6f4" @@ -63,7 +58,7 @@ Item { model: 7 Text { - text: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][model.index] + text: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][model.index] color: "#585b70" font.pixelSize: 10 font.bold: true @@ -72,6 +67,7 @@ Item { width: 28 height: 20 } + } Repeater { @@ -88,20 +84,20 @@ Item { return (day >= 1 && day <= root.daysInMonth) ? day : ""; } color: { - if (text === "") return "transparent"; + if (text === "") + return "transparent"; + var today = new Date(); - var isToday = root.date.getFullYear() === today.getFullYear() - && root.date.getMonth() === today.getMonth() - && parseInt(text) === today.getDate(); + var isToday = root.date.getFullYear() === today.getFullYear() && root.date.getMonth() === today.getMonth() && parseInt(text) === today.getDate(); return isToday ? "#f38ba8" : "#cdd6f4"; } font.pixelSize: 11 font.bold: { - if (text === "") return false; + if (text === "") + return false; + var today = new Date(); - return root.date.getFullYear() === today.getFullYear() - && root.date.getMonth() === today.getMonth() - && parseInt(text) === today.getDate(); + return root.date.getFullYear() === today.getFullYear() && root.date.getMonth() === today.getMonth() && parseInt(text) === today.getDate(); } horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -115,9 +111,15 @@ Item { border.width: parent.text !== "" && parent.font.bold ? 1 : 0 visible: parent.font.bold } + } + } + } + } + } + } diff --git a/examples/040-widgets/ClockWidget.qml b/examples/040-widgets/ClockWidget.qml index ae52a39..5cb8ca1 100644 --- a/examples/040-widgets/ClockWidget.qml +++ b/examples/040-widgets/ClockWidget.qml @@ -13,13 +13,11 @@ import QtQuick.Layouts Item { id: root - /*! The current time as a formatted string (HH:mm:ss). */ + //! The current time as a formatted string (HH:mm:ss). readonly property string timeString: new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss") - - /*! The current date as a formatted string. */ + //! The current date as a formatted string. readonly property string dateString: new Date().toLocaleDateString(Qt.locale(), "ddd MMM d yyyy") - - /*! Whether the date label is visible. */ + //! Whether the date label is visible. property bool showDate: false implicitWidth: clockLayout.implicitWidth @@ -27,11 +25,13 @@ Item { ColumnLayout { id: clockLayout + anchors.centerIn: parent spacing: 2 Text { id: timeText + text: root.timeString color: "#cdd6f4" font.pixelSize: 14 @@ -42,6 +42,7 @@ Item { Text { id: dateText + text: root.dateString color: "#89b4fa" font.pixelSize: 11 @@ -49,6 +50,7 @@ Item { visible: root.showDate Layout.fillWidth: true } + } MouseArea { @@ -65,4 +67,5 @@ Item { dateText.text = root.dateString; } } + } diff --git a/examples/040-widgets/CpuWidget.qml b/examples/040-widgets/CpuWidget.qml index 55fd1f5..404aac2 100644 --- a/examples/040-widgets/CpuWidget.qml +++ b/examples/040-widgets/CpuWidget.qml @@ -12,14 +12,15 @@ import QtQuick.Layouts Item { id: root - /*! CPU usage as a float from 0.0 to 1.0. */ - property real usage: 0.0 + //! CPU usage as a float from 0.0 to 1.0. + property real usage: 0 implicitWidth: cpuRow.implicitWidth implicitHeight: cpuRow.implicitHeight RowLayout { id: cpuRow + anchors.centerIn: parent spacing: 6 @@ -32,6 +33,7 @@ Item { Rectangle { id: barBg + width: 60 height: 8 radius: 4 @@ -41,13 +43,21 @@ Item { Rectangle { id: barFill - width: barBg.width * Math.min(Math.max(root.usage, 0.0), 1.0) + + width: barBg.width * Math.min(Math.max(root.usage, 0), 1) height: parent.height radius: 4 color: root.usage > 0.8 ? "#f38ba8" : "#a6e3a1" - Behavior on width { SmoothedAnimation { velocity: 200 } } + Behavior on width { + SmoothedAnimation { + velocity: 200 + } + + } + } + } Text { @@ -55,5 +65,7 @@ Item { color: "#cdd6f4" font.pixelSize: 11 } + } + } diff --git a/examples/040-widgets/DiskWidget.qml b/examples/040-widgets/DiskWidget.qml index 6e70253..57c496e 100644 --- a/examples/040-widgets/DiskWidget.qml +++ b/examples/040-widgets/DiskWidget.qml @@ -12,14 +12,15 @@ import QtQuick.Layouts Item { id: root - /*! Disk usage as a float from 0.0 to 1.0. */ - property real usage: 0.0 + //! Disk usage as a float from 0.0 to 1.0. + property real usage: 0 implicitWidth: diskRow.implicitWidth implicitHeight: diskRow.implicitHeight RowLayout { id: diskRow + anchors.centerIn: parent spacing: 6 @@ -32,6 +33,7 @@ Item { Rectangle { id: barBg + width: 60 height: 8 radius: 4 @@ -41,13 +43,21 @@ Item { Rectangle { id: barFill - width: barBg.width * Math.min(Math.max(root.usage, 0.0), 1.0) + + width: barBg.width * Math.min(Math.max(root.usage, 0), 1) height: parent.height radius: 4 color: root.usage > 0.8 ? "#f38ba8" : "#89b4fa" - Behavior on width { SmoothedAnimation { velocity: 200 } } + Behavior on width { + SmoothedAnimation { + velocity: 200 + } + + } + } + } Text { @@ -55,5 +65,7 @@ Item { color: "#cdd6f4" font.pixelSize: 11 } + } + } diff --git a/examples/040-widgets/NetworkWidget.qml b/examples/040-widgets/NetworkWidget.qml index 1a07189..3cfb8e2 100644 --- a/examples/040-widgets/NetworkWidget.qml +++ b/examples/040-widgets/NetworkWidget.qml @@ -13,10 +13,9 @@ import QtQuick.Layouts Item { id: root - /*! Signal strength from 0 to 100. */ + //! Signal strength from 0 to 100. property int strength: 0 - - /*! The SSID of the connected network. */ + //! The SSID of the connected network. property string ssid: "" implicitWidth: netRow.implicitWidth @@ -24,23 +23,42 @@ Item { RowLayout { id: netRow + anchors.centerIn: parent spacing: 4 Text { text: { - if (root.ssid === "") return "\u2262"; - if (root.strength <= 0) return "\uD83D\uDDA4"; - if (root.strength <= 25) return "\uD83D\uDD36"; - if (root.strength <= 50) return "\uD83D\uDFE8"; - if (root.strength <= 75) return "\uD83D\uDFE0"; + if (root.ssid === "") + return "\u2262"; + + if (root.strength <= 0) + return "\uD83D\uDDA4"; + + if (root.strength <= 25) + return "\uD83D\uDD36"; + + if (root.strength <= 50) + return "\uD83D\uDFE8"; + + if (root.strength <= 75) + return "\uD83D\uDFE0"; + return "\uD83D\uDFE2"; } color: { - if (root.ssid === "") return "#585b70"; - if (root.strength <= 25) return "#f38ba8"; - if (root.strength <= 50) return "#fab387"; - if (root.strength <= 75) return "#f9e2af"; + if (root.ssid === "") + return "#585b70"; + + if (root.strength <= 25) + return "#f38ba8"; + + if (root.strength <= 50) + return "#fab387"; + + if (root.strength <= 75) + return "#f9e2af"; + return "#a6e3a1"; } font.pixelSize: 14 @@ -53,5 +71,7 @@ Item { elide: Text.ElideRight maximumLineCount: 1 } + } + } diff --git a/examples/040-widgets/RamWidget.qml b/examples/040-widgets/RamWidget.qml index 67f9f05..09c9344 100644 --- a/examples/040-widgets/RamWidget.qml +++ b/examples/040-widgets/RamWidget.qml @@ -12,14 +12,15 @@ import QtQuick.Layouts Item { id: root - /*! RAM usage as a float from 0.0 to 1.0. */ - property real usage: 0.0 + //! RAM usage as a float from 0.0 to 1.0. + property real usage: 0 implicitWidth: ramRow.implicitWidth implicitHeight: ramRow.implicitHeight RowLayout { id: ramRow + anchors.centerIn: parent spacing: 6 @@ -32,6 +33,7 @@ Item { Rectangle { id: barBg + width: 60 height: 8 radius: 4 @@ -41,13 +43,21 @@ Item { Rectangle { id: barFill - width: barBg.width * Math.min(Math.max(root.usage, 0.0), 1.0) + + width: barBg.width * Math.min(Math.max(root.usage, 0), 1) height: parent.height radius: 4 color: root.usage > 0.8 ? "#f38ba8" : "#89b4fa" - Behavior on width { SmoothedAnimation { velocity: 200 } } + Behavior on width { + SmoothedAnimation { + velocity: 200 + } + + } + } + } Text { @@ -55,5 +65,7 @@ Item { color: "#cdd6f4" font.pixelSize: 11 } + } + } diff --git a/examples/040-widgets/SystemTray.qml b/examples/040-widgets/SystemTray.qml index b4f2002..3b88929 100644 --- a/examples/040-widgets/SystemTray.qml +++ b/examples/040-widgets/SystemTray.qml @@ -14,7 +14,7 @@ import QtQuick.Layouts Item { id: root - /*! Number of placeholder tray items to display. */ + //! Number of placeholder tray items to display. property int trayItemCount: 4 implicitWidth: trayRow.implicitWidth @@ -22,6 +22,7 @@ Item { Row { id: trayRow + anchors.centerIn: parent spacing: 6 @@ -33,8 +34,7 @@ Item { height: 12 radius: 6 color: { - var colors = ["#89b4fa", "#a6e3a1", "#f9e2af", "#f38ba8", - "#cba6f7", "#94e2d5", "#fab387", "#74c7ec"]; + var colors = ["#89b4fa", "#a6e3a1", "#f9e2af", "#f38ba8", "#cba6f7", "#94e2d5", "#fab387", "#74c7ec"]; return colors[index % colors.length]; } @@ -42,9 +42,13 @@ Item { anchors.fill: parent hoverEnabled: true onEntered: parent.opacity = 0.6 - onExited: parent.opacity = 1.0 + onExited: parent.opacity = 1 } + } + } + } + } diff --git a/examples/040-widgets/VolumeWidget.qml b/examples/040-widgets/VolumeWidget.qml index f3db757..f23b3ba 100644 --- a/examples/040-widgets/VolumeWidget.qml +++ b/examples/040-widgets/VolumeWidget.qml @@ -13,10 +13,9 @@ import QtQuick.Layouts Item { id: root - /*! Volume level from 0 to 100. */ + //! Volume level from 0 to 100. property int volume: 0 - - /*! Whether the audio output is muted. */ + //! Whether the audio output is muted. property bool muted: false implicitWidth: volumeRow.implicitWidth @@ -24,15 +23,24 @@ Item { RowLayout { id: volumeRow + anchors.centerIn: parent spacing: 4 Text { text: { - if (root.muted) return "\uD83D\uDD07"; - if (root.volume <= 0) return "\uD83D\uDD07"; - if (root.volume <= 33) return "\uD83D\uDD08"; - if (root.volume <= 66) return "\uD83D\uDD09"; + if (root.muted) + return "\uD83D\uDD07"; + + if (root.volume <= 0) + return "\uD83D\uDD07"; + + if (root.volume <= 33) + return "\uD83D\uDD08"; + + if (root.volume <= 66) + return "\uD83D\uDD09"; + return "\uD83D\uDD0A"; } color: root.muted ? "#f38ba8" : "#cdd6f4" @@ -44,5 +52,7 @@ Item { color: root.muted ? "#585b70" : "#cdd6f4" font.pixelSize: 12 } + } + } diff --git a/examples/040-widgets/WeatherWidget.qml b/examples/040-widgets/WeatherWidget.qml index 9847a52..5ea03d5 100644 --- a/examples/040-widgets/WeatherWidget.qml +++ b/examples/040-widgets/WeatherWidget.qml @@ -13,13 +13,11 @@ import QtQuick.Layouts Item { id: root - /*! City name. */ + //! City name. property string city: "" - - /*! Temperature string (e.g. "24°C"). */ + //! Temperature string (e.g. "24°C"). property string temperature: "" - - /*! Weather condition description (e.g. "Partly Cloudy"). */ + //! Weather condition description (e.g. "Partly Cloudy"). property string condition: "" implicitWidth: weatherColumn.implicitWidth @@ -27,6 +25,7 @@ Item { ColumnLayout { id: weatherColumn + anchors.centerIn: parent spacing: 1 @@ -55,5 +54,7 @@ Item { horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true } + } + } diff --git a/examples/090-app-launcher/AppLauncher.qml b/examples/090-app-launcher/AppLauncher.qml index e445628..3b26e17 100644 --- a/examples/090-app-launcher/AppLauncher.qml +++ b/examples/090-app-launcher/AppLauncher.qml @@ -1,150 +1,188 @@ -import Quickshell -import Quickshell.Window import QtQuick -import QtQuick.Controls import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland + +PanelWindow { + id: root + + property var appsList: [{ + "name": "Firefox", + "exec": "firefox", + "icon": "󰈹" + }, { + "name": "Terminal", + "exec": "kitty", + "icon": "󰞷" + }, { + "name": "Code", + "exec": "vscodium", + "icon": "󰨞" + }, { + "name": "Files", + "exec": "nautilus", + "icon": "󰉋" + }, { + "name": "Settings", + "exec": "gnome-control-center", + "icon": "󰒓" + }, { + "name": "Calculator", + "exec": "qalculate-gtk", + "icon": "󰪚" + }] + property string filterQuery: searchInput.text.toLowerCase() + property var filteredApps: appsList.filter((app) => { + return app.name.toLowerCase().includes(filterQuery); + }) + + function launchCurrent() { + if (filteredApps.length > 0 && appListView.currentIndex >= 0) { + let app = filteredApps[appListView.currentIndex]; + appProcess.command = ["bash", "-c", app.exec]; + appProcess.running = true; + root.visible = false; + } + } -PopupWindow { - id: root - - width: 500 - height: 400 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - property ListModel allApps: ListModel { - ListElement { name: "Firefox"; exec: "firefox"; icon: "firefox" } - ListElement { name: "Terminal"; exec: "kitty"; icon: "terminal" } - ListElement { name: "Code"; exec: "code"; icon: "code" } - ListElement { name: "Files"; exec: "nautilus"; icon: "folder" } - ListElement { name: "Settings"; exec: "gnome-control-center"; icon: "settings" } - ListElement { name: "Calculator"; exec: "qalculate-gtk"; icon: "calculator" } - ListElement { name: "Calendar"; exec: "gnome-calendar"; icon: "calendar" } - ListElement { name: "Music"; exec: "spotify"; icon: "spotify" } - ListElement { name: "Clock"; exec: "gnome-clocks"; icon: "clock" } - ListElement { name: "System Monitor"; exec: "gnome-system-monitor"; icon: "system-monitor" } - } - - property string searchText: "" - property var filteredApps: [] - - function filterApps() { - if (root.searchText === "") { - root.filteredApps = []; - for (let i = 0; i < root.allApps.count; i++) - root.filteredApps.push(root.allApps.get(i)); - } else { - root.filteredApps = []; - for (let i = 0; i < root.allApps.count; i++) { - let app = root.allApps.get(i); - if (app.name.toLowerCase().includes(root.searchText.toLowerCase())) - root.filteredApps.push(app); - } + implicitWidth: 500 + implicitHeight: 400 + color: "transparent" + visible: true + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + Component.onCompleted: searchInput.forceActiveFocus() + + anchors { + top: false + bottom: false + left: false + right: false } - appListView.currentIndex = 0; - } - - function launchSelected() { - if (root.filteredApps.length === 0) return; - let app = root.filteredApps[appListView.currentIndex]; - Process.exec("bash", ["-c", app.exec]); - root.visible = false; - } - - Shortcut { - sequence: "Escape" - onActivated: root.visible = false - } - - Shortcut { - sequence: "Return" - onActivated: root.launchSelected() - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - Rectangle { - Layout.fillWidth: true - height: 40 - radius: 8 - color: "#313244" + Process { + id: appProcess + } - TextField { - id: searchField - anchors.fill: parent - anchors.margins: 4 - placeholderText: "Search applications..." - placeholderTextColor: "#6c7086" - color: "#cdd6f4" - font.pixelSize: 14 - background: null - - Keys.onDownPressed: appListView.incrementCurrentIndex() - Keys.onUpPressed: appListView.decrementCurrentIndex() - - onTextChanged: { - root.searchText = text; - root.filterApps(); - } - } + Shortcut { + sequence: "Escape" + onActivated: root.visible = false } - ListView { - id: appListView - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - model: root.filteredApps - currentIndex: 0 - - delegate: Rectangle { - width: appListView.width - height: 40 - radius: 6 - color: ListView.isCurrentItem ? "#45475a" : "transparent" - - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 10 - - Text { - text: modelData.icon || "" - font.pixelSize: 16 - color: "#89b4fa" - } - - Text { - text: modelData.name - font.pixelSize: 14 - color: "#cdd6f4" - Layout.fillWidth: true - } - - Text { - text: "↵" - font.pixelSize: 12 - color: "#585b70" - visible: ListView.isCurrentItem - } - } + Shortcut { + sequence: "Return" + onActivated: root.launchCurrent() + } + + Rectangle { + anchors.fill: parent + color: "#1e1e2e" + radius: 12 + border.color: "#313244" + border.width: 1 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + // Barre de recherche + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 42 + color: "#313244" + radius: 8 + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + spacing: 8 + + Text { + text: "󰍉" + color: "#89b4fa" + font.pixelSize: 16 + } + + TextInput { + id: searchInput + + Layout.fillWidth: true + color: "#cdd6f4" + font.pixelSize: 15 + clip: true + focus: true + onTextChanged: appListView.currentIndex = 0 + Keys.onDownPressed: appListView.incrementCurrentIndex() + Keys.onUpPressed: appListView.decrementCurrentIndex() + + Text { + text: "Search applications..." + color: "#6c7086" + font.pixelSize: 15 + visible: searchInput.text === "" + } + + } + + } + + } + + ListView { + id: appListView + + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: root.filteredApps + currentIndex: 0 + + delegate: Rectangle { + required property var modelData + required property int index + + width: appListView.width + height: 42 + radius: 6 + color: index === appListView.currentIndex ? "#45475a" : "transparent" + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + spacing: 12 + + Text { + text: modelData.icon + font.pixelSize: 18 + color: "#89b4fa" + } + + Text { + text: modelData.name + font.pixelSize: 14 + color: "#cdd6f4" + Layout.fillWidth: true + } + + } + + MouseArea { + anchors.fill: parent + onClicked: { + appListView.currentIndex = index; + root.launchCurrent(); + } + } + + } + + } - MouseArea { - anchors.fill: parent - onClicked: { - appListView.currentIndex = index; - root.launchSelected(); - } } - } + } - } - Component.onCompleted: { - root.filterApps(); - searchField.forceActiveFocus(); - } } diff --git a/examples/091-power-menu/PowerMenu.qml b/examples/091-power-menu/PowerMenu.qml index be57c47..08d9ab8 100644 --- a/examples/091-power-menu/PowerMenu.qml +++ b/examples/091-power-menu/PowerMenu.qml @@ -1,163 +1,229 @@ -import Quickshell -import Quickshell.Window -import Quickshell.Io import QtQuick import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland + +PanelWindow { + id: root + + property string shutdownIcon: "⏻" + property real shutdownHoldProgress: 0 + property bool shutdownConfirming: false + + function execute(command) { + cmdProcess.command = ["bash", "-c", command]; + cmdProcess.running = true; + root.visible = false; + } -PopupWindow { - id: root - - width: 400 - height: 280 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - property string shutdownIcon: "⏻" - - property real shutdownHoldProgress: 0 - property bool shutdownConfirming: false - - function execute(command) { - Process.exec("bash", ["-c", command]); - root.visible = false; - } - - function startShutdownTimer() { - if (root.shutdownConfirming) return; - root.shutdownConfirming = true; - root.shutdownHoldProgress = 0; - shutdownTimer.start(); - } - - function cancelShutdown() { - root.shutdownConfirming = false; - root.shutdownHoldProgress = 0; - shutdownTimer.stop(); - } - - Timer { - id: shutdownTimer - interval: 50 - repeat: true - onTriggered: { - root.shutdownHoldProgress += 0.025; - if (root.shutdownHoldProgress >= 1) { + function startShutdownTimer() { + if (root.shutdownConfirming) + return ; + + root.shutdownConfirming = true; root.shutdownHoldProgress = 0; + shutdownTimer.start(); + } + + function cancelShutdown() { root.shutdownConfirming = false; + root.shutdownHoldProgress = 0; shutdownTimer.stop(); - root.execute("systemctl poweroff"); - } } - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 - - Text { - text: "Power Menu" - font.pixelSize: 18 - font.bold: true - color: "#cdd6f4" - Layout.alignment: Qt.AlignHCenter + + implicitWidth: 380 + implicitHeight: 260 + color: "transparent" + visible: true + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + + anchors { + top: false + bottom: false + left: false + right: false + } + + Process { + id: cmdProcess } - GridLayout { - columns: 3 - columnSpacing: 10 - rowSpacing: 10 - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - - Repeater { - model: ListModel { - ListElement { label: "Lock"; icon: ""; cmd: "loginctl lock-session" } - ListElement { label: "Log Out"; icon: ""; cmd: "loginctl terminate-user $USER" } - ListElement { label: "Sleep"; icon: ""; cmd: "systemctl suspend" } - ListElement { label: "Restart"; icon: ""; cmd: "systemctl reboot" } - ListElement { label: "Shutdown"; icon: "⏻"; cmd: "systemctl poweroff" } + Timer { + id: shutdownTimer + + interval: 50 + repeat: true + onTriggered: { + root.shutdownHoldProgress += 0.025; + if (root.shutdownHoldProgress >= 1) { + root.shutdownHoldProgress = 0; + root.shutdownConfirming = false; + shutdownTimer.stop(); + root.execute("systemctl poweroff"); + } } + } - delegate: Rectangle { - width: 100 - height: 80 - radius: 10 - color: { - if (label === "Shutdown" && root.shutdownConfirming) - return Qt.rgba(1, 0, 0, root.shutdownHoldProgress); - return "#313244"; - } - border.color: { - if (label === "Shutdown" && root.shutdownConfirming) - return "#f38ba8"; - return "transparent"; - } - border.width: root.shutdownConfirming && label === "Shutdown" ? 2 : 0 - - ColumnLayout { + Shortcut { + sequence: "Escape" + onActivated: { + root.cancelShutdown(); + root.visible = false; + } + } + + // Main window container + Rectangle { + anchors.fill: parent + color: "#1e1e2e" + radius: 12 + border.color: "#313244" + border.width: 1 + + ColumnLayout { anchors.fill: parent - anchors.margins: 8 - spacing: 4 + anchors.margins: 20 + spacing: 16 Text { - text: icon - font.pixelSize: 22 - color: "#89b4fa" - Layout.alignment: Qt.AlignHCenter + text: "Power Menu" + font.pixelSize: 18 + font.bold: true + color: "#cdd6f4" + Layout.alignment: Qt.AlignHCenter } - Text { - text: label - font.pixelSize: 11 - color: "#cdd6f4" - Layout.alignment: Qt.AlignHCenter + GridLayout { + columns: 3 + columnSpacing: 10 + rowSpacing: 10 + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + + Repeater { + + model: ListModel { + ListElement { + label: "Lock" + icon: "" + cmd: "loginctl lock-session" + } + + ListElement { + label: "Log Out" + icon: "" + cmd: "loginctl terminate-user $USER" + } + + ListElement { + label: "Sleep" + icon: "" + cmd: "systemctl suspend" + } + + ListElement { + label: "Restart" + icon: "" + cmd: "systemctl reboot" + } + + ListElement { + label: "Shutdown" + icon: "⏻" + cmd: "systemctl poweroff" + } + + } + + delegate: Rectangle { + required property string label + required property string icon + required property string cmd + + width: 100 + height: 75 + radius: 10 + color: { + if (label === "Shutdown" && root.shutdownConfirming) + return Qt.rgba(0.95, 0.54, 0.66, root.shutdownHoldProgress * 0.4); + + return "#313244"; + } + border.color: (label === "Shutdown" && root.shutdownConfirming) ? "#f38ba8" : "transparent" + border.width: (label === "Shutdown" && root.shutdownConfirming) ? 2 : 0 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 4 + + Text { + text: parent.parent.icon + font.pixelSize: 20 + color: (parent.parent.label === "Shutdown" && root.shutdownConfirming) ? "#f38ba8" : "#89b4fa" + Layout.alignment: Qt.AlignHCenter + } + + Text { + text: parent.parent.label + font.pixelSize: 12 + color: "#cdd6f4" + Layout.alignment: Qt.AlignHCenter + } + + // Hold progress bar for shutdown confirmation + Rectangle { + Layout.fillWidth: true + height: 3 + radius: 2 + color: "#45475a" + visible: parent.parent.label === "Shutdown" && root.shutdownConfirming + + Rectangle { + width: parent.width * root.shutdownHoldProgress + height: parent.height + radius: 2 + color: "#f38ba8" + } + + } + + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (label === "Shutdown") { + if (!root.shutdownConfirming) + root.startShutdownTimer(); + else + root.cancelShutdown(); + } else { + root.execute(cmd); + } + } + } + + } + + } + } - Rectangle { - Layout.fillWidth: true - height: 3 - radius: 2 - color: "#585b70" - visible: label === "Shutdown" && root.shutdownConfirming - - Rectangle { - width: parent.width * root.shutdownHoldProgress - height: parent.height - radius: 2 - color: "#f38ba8" - } + Item { + Layout.fillHeight: true } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (label === "Shutdown") - root.startShutdownTimer(); - else - root.execute(cmd); + Text { + text: "Esc to close" + font.pixelSize: 11 + color: "#585b70" + Layout.alignment: Qt.AlignHCenter } - } - } - } - } - Item { Layout.fillHeight: true } + } - Text { - text: "Esc to close" - font.pixelSize: 11 - color: "#585b70" - Layout.alignment: Qt.AlignHCenter } - } - Shortcut { - sequence: "Escape" - onActivated: { - root.cancelShutdown(); - root.visible = false; - } - } } From 6851487717bf3f9a6f3f8c6bf5dd24d468dd68bb Mon Sep 17 00:00:00 2001 From: Trist4n Date: Wed, 19 Aug 2026 11:09:16 +0200 Subject: [PATCH 4/4] fix(examples [093-900]): Migration of examples to Quickshell v0.3 --- examples/093-media-player/MediaPlayer.qml | 323 +++++++------ .../ClipboardManager.qml | 330 ++++++++------ .../NotificationCenter.qml | 321 +++++++------ examples/097-control-center/ControlCenter.qml | 156 +++---- examples/111-top-bar/shell.qml | 244 +++++----- examples/112-dock/shell.qml | 187 ++++---- examples/113-launcher/shell.qml | 271 +++++++---- examples/900-complete-shell/Theme.qml | 52 +-- examples/900-complete-shell/shell.qml | 428 +++++++++++++----- 9 files changed, 1411 insertions(+), 901 deletions(-) diff --git a/examples/093-media-player/MediaPlayer.qml b/examples/093-media-player/MediaPlayer.qml index 289ed3b..fe563ad 100644 --- a/examples/093-media-player/MediaPlayer.qml +++ b/examples/093-media-player/MediaPlayer.qml @@ -1,164 +1,215 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Controls import QtQuick.Layouts +import Quickshell PopupWindow { - id: root - - width: 360 - height: 440 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - property string currentPlayer: "spotify" - property bool isPlaying: false - property real progress: 0.45 - property var players: ["spotify", "firefox", "vlc"] - - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 - - RowLayout { - Layout.fillWidth: true - spacing: 8 - - Text { - text: "Now Playing" - font.pixelSize: 16 - font.bold: true - color: "#cdd6f4" - Layout.fillWidth: true - } - - ComboBox { - model: root.players - currentIndex: root.players.indexOf(root.currentPlayer) - font.pixelSize: 12 - contentItem: Text { - text: parent.currentText - color: "#cdd6f4" - font.pixelSize: 12 - } - background: Rectangle { - radius: 6 - color: "#313244" - } - indicator: Text { - x: parent.width - width - 8 - y: parent.height / 2 - height / 2 - text: "▼" - color: "#585b70" - font.pixelSize: 8 - } - onActivated: root.currentPlayer = root.players[index] - } - } - - Rectangle { - Layout.preferredWidth: 200 - Layout.preferredHeight: 200 - Layout.alignment: Qt.AlignHCenter - radius: 12 - color: "#313244" - - Text { - anchors.centerIn: parent - text: "♫" - font.pixelSize: 64 - color: "#45475a" - } - } + id: root - Text { - text: "Bohemian Rhapsody" - font.pixelSize: 16 - font.bold: true - color: "#cdd6f4" - Layout.alignment: Qt.AlignHCenter - elide: Text.ElideRight - } + property string currentPlayer: "spotify" + property bool isPlaying: false + property real progress: 0.45 + property var players: ["spotify", "firefox", "vlc"] - Text { - text: "Queen" - font.pixelSize: 14 - color: "#a6adc8" - Layout.alignment: Qt.AlignHCenter - } + width: 360 + height: 440 + color: "#1e1e2e" ColumnLayout { - Layout.fillWidth: true - spacing: 4 + anchors.fill: parent + anchors.margins: 20 + spacing: 16 + + RowLayout { + Layout.fillWidth: true + spacing: 8 + + Text { + text: "Now Playing" + font.pixelSize: 16 + font.bold: true + color: "#cdd6f4" + Layout.fillWidth: true + } + + ComboBox { + model: root.players + currentIndex: root.players.indexOf(root.currentPlayer) + font.pixelSize: 12 + onActivated: root.currentPlayer = root.players[index] + + contentItem: Text { + text: parent.currentText + color: "#cdd6f4" + font.pixelSize: 12 + } + + background: Rectangle { + radius: 6 + color: "#313244" + } + + indicator: Text { + x: parent.width - width - 8 + y: parent.height / 2 - height / 2 + text: "▼" + color: "#585b70" + font.pixelSize: 8 + } + + } - Rectangle { - Layout.fillWidth: true - height: 6 - radius: 3 - color: "#313244" + } Rectangle { - width: parent.width * root.progress - height: parent.height - radius: 3 - color: "#cba6f7" + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + Layout.alignment: Qt.AlignHCenter + radius: 12 + color: "#313244" + + Text { + anchors.centerIn: parent + text: "♫" + font.pixelSize: 64 + color: "#45475a" + } + } - } - RowLayout { - Layout.fillWidth: true + Text { + text: "Bohemian Rhapsody" + font.pixelSize: 16 + font.bold: true + color: "#cdd6f4" + Layout.alignment: Qt.AlignHCenter + elide: Text.ElideRight + } Text { - text: "1:23" - font.pixelSize: 11 - color: "#585b70" + text: "Queen" + font.pixelSize: 14 + color: "#a6adc8" + Layout.alignment: Qt.AlignHCenter } - Item { Layout.fillWidth: true } + ColumnLayout { + Layout.fillWidth: true + spacing: 4 + + Rectangle { + Layout.fillWidth: true + height: 6 + radius: 3 + color: "#313244" + + Rectangle { + width: parent.width * root.progress + height: parent.height + radius: 3 + color: "#cba6f7" + } + + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: "1:23" + font.pixelSize: 11 + color: "#585b70" + } + + Item { + Layout.fillWidth: true + } + + Text { + text: "3:04" + font.pixelSize: 11 + color: "#585b70" + } + + } - Text { - text: "3:04" - font.pixelSize: 11 - color: "#585b70" } - } - } - RowLayout { - Layout.alignment: Qt.AlignHCenter - spacing: 20 + RowLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 20 + + Rectangle { + width: 40 + height: 40 + radius: 20 + color: "#313244" + + Text { + anchors.centerIn: parent + text: "❮" + color: "#cdd6f4" + font.pixelSize: 16 + } + + MouseArea { + anchors.fill: parent + onClicked: { + } + } + + } + + Rectangle { + width: 52 + height: 52 + radius: 26 + color: "#cba6f7" + + Text { + anchors.centerIn: parent + text: root.isPlaying ? "⏸" : "▶" + color: "#1e1e2e" + font.pixelSize: 18 + } + + MouseArea { + anchors.fill: parent + onClicked: root.isPlaying = !root.isPlaying + } + + } + + Rectangle { + width: 40 + height: 40 + radius: 20 + color: "#313244" + + Text { + anchors.centerIn: parent + text: "❯" + color: "#cdd6f4" + font.pixelSize: 16 + } + + MouseArea { + anchors.fill: parent + onClicked: { + } + } + + } - Rectangle { - width: 40; height: 40; radius: 20; color: "#313244" - Text { anchors.centerIn: parent; text: "❮"; color: "#cdd6f4"; font.pixelSize: 16 } - MouseArea { anchors.fill: parent; onClicked: {} } - } + } - Rectangle { - width: 52; height: 52; radius: 26; color: "#cba6f7" Text { - anchors.centerIn: parent - text: root.isPlaying ? "⏸" : "▶" - color: "#1e1e2e" - font.pixelSize: 18 + text: root.isPlaying ? "Playing" : "Paused" + font.pixelSize: 11 + color: "#585b70" + Layout.alignment: Qt.AlignHCenter } - MouseArea { anchors.fill: parent; onClicked: root.isPlaying = !root.isPlaying } - } - - Rectangle { - width: 40; height: 40; radius: 20; color: "#313244" - Text { anchors.centerIn: parent; text: "❯"; color: "#cdd6f4"; font.pixelSize: 16 } - MouseArea { anchors.fill: parent; onClicked: {} } - } - } - Text { - text: root.isPlaying ? "Playing" : "Paused" - font.pixelSize: 11 - color: "#585b70" - Layout.alignment: Qt.AlignHCenter } - } + } diff --git a/examples/095-clipboard-manager/ClipboardManager.qml b/examples/095-clipboard-manager/ClipboardManager.qml index 9bbebef..8037dc1 100644 --- a/examples/095-clipboard-manager/ClipboardManager.qml +++ b/examples/095-clipboard-manager/ClipboardManager.qml @@ -1,147 +1,217 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland + +PanelWindow { + id: root + + property int maxHistory: 20 + property bool ignoreNextChange: false + + function addEntry(text) { + if (!text || text.trim() === "") + return ; + + for (let i = 0; i < clipboardModel.count; i++) { + if (clipboardModel.get(i).text === text) { + clipboardModel.move(i, 0, 1); + return ; + } + } + clipboardModel.insert(0, { + "text": text + }); + while (clipboardModel.count > root.maxHistory) + clipboardModel.remove(clipboardModel.count - 1); -PopupWindow { - id: root - - width: 420 - height: 500 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - property int maxHistory: 20 - property bool ignoreNextChange: false - - ListModel { - id: clipboardModel - - ListElement { text: "https://github.com/Quickshell/quickshell" } - ListElement { text: "qml: warning: unknown property" } - ListElement { text: "systemctl --user status" } - ListElement { text: "Hello, World!" } - ListElement { text: "#include " } - ListElement { text: "catppuccin/mocha" } - ListElement { text: "192.168.1.1" } - ListElement { text: "Lorem ipsum dolor sit amet" } - } - - function addEntry(text) { - if (text.trim() === "") return; - - for (let i = 0; i < clipboardModel.count; i++) { - if (clipboardModel.get(i).text === text) { - clipboardModel.move(i, 0, 1); - return; - } } - clipboardModel.insert(0, { text: text }); - while (clipboardModel.count > root.maxHistory) - clipboardModel.remove(clipboardModel.count - 1); - } - - function copyToClipboard(text) { - root.ignoreNextChange = true; - clipboardDebounceTimer.restart(); - Process.exec("bash", ["-c", `printf '%s' "${text}" | wl-copy`]); - root.visible = false; - } - - Timer { - id: clipboardDebounceTimer - interval: 300 - onTriggered: root.ignoreNextChange = false - } - - ClipboardsWatcher { - id: watcher - onClipboardChanged: { - if (root.ignoreNextChange) return; - root.addEntry(watcher.clipboard); + function copyToClipboard(text) { + root.ignoreNextChange = true; + clipboardDebounceTimer.restart(); + copyProcess.command = ["bash", "-c", `printf '%s' "${text.replace(/"/g, '\\"')}" | wl-copy`]; + copyProcess.running = true; + root.visible = false; } - } - - Shortcut { - sequence: "Escape" - onActivated: root.visible = false - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Clipboard History" - font.pixelSize: 16 - font.bold: true - color: "#cdd6f4" - Layout.fillWidth: true - } - - Text { - text: clipboardModel.count + " items" - font.pixelSize: 11 - color: "#585b70" - } + + // Window dimensions + implicitWidth: 420 + implicitHeight: 500 + color: "transparent" + visible: true + // Enable Wayland LayerShell focus + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + Component.onCompleted: { + if (Quickshell.clipboard && Quickshell.clipboard.text) + root.addEntry(Quickshell.clipboard.text); + } - Rectangle { - Layout.fillWidth: true - height: 1 - color: "#313244" + anchors { + top: false + bottom: false + left: false + right: false } - ListView { - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - model: clipboardModel - - delegate: Rectangle { - width: parent.width - height: 44 - radius: 6 - color: mouse.containsMouse ? "#45475a" : "transparent" - - RowLayout { - anchors.fill: parent - anchors.margins: 10 - spacing: 8 - - Text { - text: "" - font.pixelSize: 14 - color: "#89b4fa" - } - - Text { - text: model.text - font.pixelSize: 13 - color: "#cdd6f4" - elide: Text.ElideRight - Layout.fillWidth: true - } + // Process instance for wl-copy execution + Process { + id: copyProcess + } + + ListModel { + id: clipboardModel + + ListElement { + text: "https://github.com/Quickshell/quickshell" + } + + ListElement { + text: "qml: warning: unknown property" + } + + ListElement { + text: "systemctl --user status" } - property alias mouse: mouseArea + ListElement { + text: "Hello, World!" + } - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: root.copyToClipboard(model.text) + ListElement { + text: "#include " } - } + + ListElement { + text: "catppuccin/mocha" + } + + ListElement { + text: "192.168.1.1" + } + + ListElement { + text: "Lorem ipsum dolor sit amet" + } + + } + + Timer { + id: clipboardDebounceTimer + + interval: 300 + onTriggered: root.ignoreNextChange = false + } + + // Native Quickshell Clipboard observer + Connections { + function onTextChanged() { + if (root.ignoreNextChange) + return ; + + root.addEntry(Quickshell.clipboard.text); + } + + target: Quickshell.clipboard + } + + Shortcut { + sequence: "Escape" + onActivated: root.visible = false + } + + // Main window frame + Rectangle { + anchors.fill: parent + color: "#1e1e2e" + radius: 12 + border.color: "#313244" + border.width: 1 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 8 + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Clipboard History" + font.pixelSize: 16 + font.bold: true + color: "#cdd6f4" + Layout.fillWidth: true + } + + Text { + text: clipboardModel.count + " items" + font.pixelSize: 11 + color: "#585b70" + } + + } + + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#313244" + } + + ListView { + id: listView + + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: clipboardModel + + delegate: Rectangle { + required property string text + required property int index + + width: listView.width + height: 44 + radius: 6 + color: mouseArea.containsMouse ? "#45475a" : "transparent" + + RowLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 8 + + Text { + text: "" + font.pixelSize: 14 + color: "#89b4fa" + } + + Text { + text: parent.parent.text + font.pixelSize: 13 + color: "#cdd6f4" + elide: Text.ElideRight + Layout.fillWidth: true + } + + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + onClicked: root.copyToClipboard(parent.text) + } + + } + + } + + } + } - } - Component.onCompleted: { - root.addEntry(watcher.clipboard); - } } diff --git a/examples/096-notification-center/NotificationCenter.qml b/examples/096-notification-center/NotificationCenter.qml index afa7497..d4ddfeb 100644 --- a/examples/096-notification-center/NotificationCenter.qml +++ b/examples/096-notification-center/NotificationCenter.qml @@ -1,168 +1,215 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts +import Quickshell PopupWindow { - id: root - - width: 380 - height: 520 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - ListModel { - id: notifModel - - ListElement { appName: "Firefox"; summary: "Download complete"; body: "quickshell-book.pdf has finished downloading."; timestamp: "2m ago" } - ListElement { appName: "Spotify"; summary: "Now Playing"; body: "Bohemian Rhapsody - Queen"; timestamp: "5m ago" } - ListElement { appName: "System"; summary: "Updates available"; body: "3 package updates are available."; timestamp: "15m ago" } - ListElement { appName: "Discord"; summary: "Message from @user"; body: "Hey, are you coming to the meeting?"; timestamp: "1h ago" } - ListElement { appName: "Thunderbird"; summary: "New email"; body: "Re: Project update - Please review the attached files."; timestamp: "2h ago" } - ListElement { appName: "Slack"; summary: "Channel notification"; body: "New message in #general"; timestamp: "3h ago" } - } - - function dismiss(index) { - notifModel.remove(index, 1); - } - - function clearAll() { - notifModel.clear(); - } - - Shortcut { - sequence: "Escape" - onActivated: root.visible = false - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Notifications" - font.pixelSize: 16 - font.bold: true - color: "#cdd6f4" - Layout.fillWidth: true - } - - Rectangle { - height: 28 - width: 80 - radius: 6 - color: "#f38ba8" + id: root - Text { - anchors.centerIn: parent - text: "Clear All" - font.pixelSize: 11 - color: "#1e1e2e" + function dismiss(index) { + notifModel.remove(index, 1); + } + + function clearAll() { + notifModel.clear(); + } + + implicitWidth: 380 + implicitHeight: 520 + color: "#1e1e2e" + + ListModel { + id: notifModel + + ListElement { + appName: "Firefox" + summary: "Download complete" + body: "quickshell-book.pdf has finished downloading." + timestamp: "2m ago" } - MouseArea { - anchors.fill: parent - onClicked: root.clearAll() + ListElement { + appName: "Spotify" + summary: "Now Playing" + body: "Bohemian Rhapsody - Queen" + timestamp: "5m ago" } - } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: "#313244" - } + ListElement { + appName: "System" + summary: "Updates available" + body: "3 package updates are available." + timestamp: "15m ago" + } - ListView { - id: notifList - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - model: notifModel - spacing: 6 + ListElement { + appName: "Discord" + summary: "Message from @user" + body: "Hey, are you coming to the meeting?" + timestamp: "1h ago" + } - delegate: Rectangle { - width: parent.width - height: 80 - radius: 8 - color: "#313244" + ListElement { + appName: "Thunderbird" + summary: "New email" + body: "Re: Project update - Please review the attached files." + timestamp: "2h ago" + } - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 10 + ListElement { + appName: "Slack" + summary: "Channel notification" + body: "New message in #general" + timestamp: "3h ago" + } - Rectangle { - width: 36 - height: 36 - radius: 8 - color: "#45475a" + } - Text { - anchors.centerIn: parent - text: model.appName.charAt(0).toUpperCase() - font.bold: true - color: "#89b4fa" - font.pixelSize: 14 - } - } + Shortcut { + sequence: "Escape" + onActivated: root.visible = false + } - ColumnLayout { - Layout.fillWidth: true - spacing: 2 + ColumnLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 8 - RowLayout { - Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true - Text { - text: model.appName - font.pixelSize: 12 + Text { + text: "Notifications" + font.pixelSize: 16 font.bold: true color: "#cdd6f4" - } + Layout.fillWidth: true + } - Item { Layout.fillWidth: true } + Rectangle { + height: 28 + width: 80 + radius: 6 + color: "#f38ba8" - Text { - text: model.timestamp - font.pixelSize: 10 - color: "#585b70" - } - } + Text { + anchors.centerIn: parent + text: "Clear All" + font.pixelSize: 11 + color: "#1e1e2e" + } + + MouseArea { + anchors.fill: parent + onClicked: root.clearAll() + } - Text { - text: model.summary - font.pixelSize: 12 - color: "#a6adc8" - elide: Text.ElideRight } - Text { - text: model.body - font.pixelSize: 11 - color: "#6c7086" - elide: Text.ElideRight - maximumLineCount: 1 + } + + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#313244" + } + + ListView { + id: notifList + + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: notifModel + spacing: 6 + + delegate: Rectangle { + width: parent.width + height: 80 + radius: 8 + color: "#313244" + + RowLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + Rectangle { + width: 36 + height: 36 + radius: 8 + color: "#45475a" + + Text { + anchors.centerIn: parent + text: model.appName.charAt(0).toUpperCase() + font.bold: true + color: "#89b4fa" + font.pixelSize: 14 + } + + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + RowLayout { + Layout.fillWidth: true + + Text { + text: model.appName + font.pixelSize: 12 + font.bold: true + color: "#cdd6f4" + } + + Item { + Layout.fillWidth: true + } + + Text { + text: model.timestamp + font.pixelSize: 10 + color: "#585b70" + } + + } + + Text { + text: model.summary + font.pixelSize: 12 + color: "#a6adc8" + elide: Text.ElideRight + } + + Text { + text: model.body + font.pixelSize: 11 + color: "#6c7086" + elide: Text.ElideRight + maximumLineCount: 1 + } + + } + + } + + MouseArea { + anchors.fill: parent + onClicked: root.dismiss(index) + } + } - } + } - MouseArea { - anchors.fill: parent - onClicked: root.dismiss(index) + Text { + text: "Click a notification to dismiss" + font.pixelSize: 10 + color: "#585b70" + Layout.alignment: Qt.AlignHCenter } - } - } - Text { - text: "Click a notification to dismiss" - font.pixelSize: 10 - color: "#585b70" - Layout.alignment: Qt.AlignHCenter } - } + } diff --git a/examples/097-control-center/ControlCenter.qml b/examples/097-control-center/ControlCenter.qml index 5dc3b9a..57af07e 100644 --- a/examples/097-control-center/ControlCenter.qml +++ b/examples/097-control-center/ControlCenter.qml @@ -1,94 +1,94 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts +import Quickshell PopupWindow { - id: root - - width: 360 - height: 300 - backgroundColor: "#1e1e2e" - color: "#1e1e2e" - - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 - - Text { - text: "Control Center" - font.pixelSize: 16 - font.bold: true - color: "#cdd6f4" - Layout.alignment: Qt.AlignHCenter - } - - GridLayout { - columns: 2 - columnSpacing: 16 - rowSpacing: 16 - Layout.alignment: Qt.AlignHCenter - - ToggleCard { - icon: "" - label: "Wi-Fi" - property var process: null - - onToggledChanged: { - if (toggled) - Process.exec("bash", ["-c", "nmcli radio wifi on"]); - else - Process.exec("bash", ["-c", "nmcli radio wifi off"]); + id: root + + implicitWidth: 360 + implicitHeight: 300 + color: "#1e1e2e" + + ColumnLayout { + anchors.fill: parent + anchors.margins: 20 + spacing: 16 + + Text { + text: "Control Center" + font.pixelSize: 16 + font.bold: true + color: "#cdd6f4" + Layout.alignment: Qt.AlignHCenter } - } - - ToggleCard { - icon: "" - label: "Bluetooth" - toggled: false - - onToggledChanged: { - if (toggled) - Process.exec("bash", ["-c", "bluetoothctl power on"]); - else - Process.exec("bash", ["-c", "bluetoothctl power off"]); - } - } - ToggleCard { - icon: "" - label: "Dark Mode" - toggled: true + GridLayout { + columns: 2 + columnSpacing: 16 + rowSpacing: 16 + Layout.alignment: Qt.AlignHCenter + + ToggleCard { + property var process: null + + icon: "" + label: "Wi-Fi" + onToggledChanged: { + if (toggled) + Process.exec("bash", ["-c", "nmcli radio wifi on"]); + else + Process.exec("bash", ["-c", "nmcli radio wifi off"]); + } + } + + ToggleCard { + icon: "" + label: "Bluetooth" + toggled: false + onToggledChanged: { + if (toggled) + Process.exec("bash", ["-c", "bluetoothctl power on"]); + else + Process.exec("bash", ["-c", "bluetoothctl power off"]); + } + } + + ToggleCard { + icon: "" + label: "Dark Mode" + toggled: true + onToggledChanged: { + Process.exec("bash", ["-c", `gsettings set org.gnome.desktop.interface color-scheme '${toggled ? "prefer-dark" : "prefer-light"}'`]); + } + } + + ToggleCard { + icon: "" + label: "DND" + toggled: false + onToggledChanged: { + Process.exec("bash", ["-c", `dunstctl set ${toggled ? "true" : "false"}`]); + } + } - onToggledChanged: { - Process.exec("bash", ["-c", `gsettings set org.gnome.desktop.interface color-scheme '${toggled ? "prefer-dark" : "prefer-light"}'`]); } - } - ToggleCard { - icon: "" - label: "DND" - toggled: false + Item { + Layout.fillHeight: true + } - onToggledChanged: { - Process.exec("bash", ["-c", `dunstctl set ${toggled ? "true" : "false"}`]); + Text { + text: "Esc to close" + font.pixelSize: 11 + color: "#585b70" + Layout.alignment: Qt.AlignHCenter } - } - } - Item { Layout.fillHeight: true } + } - Text { - text: "Esc to close" - font.pixelSize: 11 - color: "#585b70" - Layout.alignment: Qt.AlignHCenter + Shortcut { + sequence: "Escape" + onActivated: root.visible = false } - } - Shortcut { - sequence: "Escape" - onActivated: root.visible = false - } } diff --git a/examples/111-top-bar/shell.qml b/examples/111-top-bar/shell.qml index a0d4b6b..2b1950f 100644 --- a/examples/111-top-bar/shell.qml +++ b/examples/111-top-bar/shell.qml @@ -1,158 +1,178 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland -// Catppuccin Mocha palette -pragma Singleton -QtObject { - id: theme - property color base: "#1e1e2e" - property color text: "#cdd6f4" - property color accent: "#89b4fa" - property color green: "#a6e3a1" - property color red: "#f38ba8" - property color yellow: "#f9e2af" - property color purple: "#cba6f7" - property int slimHeight: 36 - property int expandedHeight: 48 - property int spacing: 8 - property int radius: 6 -} - -// Multi-monitor support: one top bar per screen ShellRoot { - Variants { - Quickshell.screens { - onScreensChanged: { - for (const screen of screens) { - if (!screenComponents[screen.name]) { - var component = Qt.createComponent("shell.qml") - screenComponents[screen.name] = component.createObject(null, {screen: screen}) - } - } - } - } + id: root + + QtObject { + id: theme + + property color base: "#1e1e2e" + property color text: "#cdd6f4" + property color accent: "#89b4fa" + property color green: "#a6e3a1" + property color red: "#f38ba8" + property color yellow: "#f9e2af" + property color purple: "#cba6f7" + property int slimHeight: 36 + property int expandedHeight: 48 + property int spacing: 8 + property int radius: 6 } - property var screenComponents: ({}) - - Component { - id: panelDelegate + Variants { + model: Quickshell.screens - PanelWindow { + delegate: PanelWindow { id: panel - screen: screen + + required property var modelData + + screen: modelData + WlrLayershell.layer: WlrLayer.Top + implicitHeight: theme.slimHeight + color: theme.base + anchors { top: true left: true right: true } - height: theme.slimHeight - color: theme.base - exclusionMode: ExclusionMode.Exclusive - // Hover-to-expand MouseArea { anchors.fill: parent hoverEnabled: true - onEntered: panel.height = theme.expandedHeight - onExited: panel.height = theme.slimHeight - Behavior on height { NumberAnimation { duration: 150 } } + onEntered: panel.implicitHeight = theme.expandedHeight + onExited: panel.implicitHeight = theme.slimHeight + + Behavior on height { + NumberAnimation { + duration: 150 + } + + } + } RowLayout { anchors.fill: parent - anchors.margins: 4 + anchors.leftMargin: 8 + anchors.rightMargin: 8 spacing: theme.spacing - // Left section - Row { + RowLayout { Layout.alignment: Qt.AlignVCenter spacing: theme.spacing - WorkspaceIndicator {} - LauncherButton {} + // Workspace Indicator + Rectangle { + implicitWidth: 24 + implicitHeight: 24 + radius: theme.radius + color: theme.accent + + Text { + anchors.centerIn: parent + text: "1" + color: theme.base + font.bold: true + } + + } + + // Launcher Button + Rectangle { + implicitWidth: 24 + implicitHeight: 24 + radius: theme.radius + color: theme.purple + + Text { + anchors.centerIn: parent + text: "" + color: theme.base + } + + } + + } + + Item { + Layout.fillWidth: true } - // Center section - Item { Layout.fillWidth: true } + Text { + id: clockText - ClockWidget { Layout.alignment: Qt.AlignCenter + text: new Date().toLocaleTimeString(Qt.locale(), "hh:mm") + color: theme.text + font.pixelSize: 14 + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: clockText.text = new Date().toLocaleTimeString(Qt.locale(), "hh:mm") + } + } - // Right section - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } - Row { + RowLayout { Layout.alignment: Qt.AlignVCenter spacing: theme.spacing - VolumeIcon {} - BatteryIcon {} - SystemTray {} - } - } - } - } + // Volume Icon + Rectangle { + implicitWidth: 24 + implicitHeight: 24 + radius: theme.radius + color: theme.green - // --- Widget stubs --- + Text { + anchors.centerIn: parent + text: "" + color: theme.base + } - component WorkspaceIndicator: Rectangle { - width: 24; height: 24; radius: theme.radius - color: theme.accent - Text { - anchors.centerIn: parent - text: "1" - color: theme.base - font.bold: true - } - } + } - component LauncherButton: Rectangle { - width: 24; height: 24; radius: theme.radius - color: theme.purple - Text { - anchors.centerIn: parent - text: "" - color: theme.base - } - } + // Battery Icon + Rectangle { + implicitWidth: 24 + implicitHeight: 24 + radius: theme.radius + color: theme.yellow - component ClockWidget: Text { - text: new Date().toLocaleTimeString(Qt.locale(), "hh:mm") - color: theme.text - font.pixelSize: 14 - Timer { - interval: 1000; running: true; repeat: true - onTriggered: parent.text = new Date().toLocaleTimeString(Qt.locale(), "hh:mm") - } - } + Text { + anchors.centerIn: parent + text: "" + color: theme.base + } - component VolumeIcon: Rectangle { - width: 24; height: 24; radius: theme.radius - color: theme.green - Text { - anchors.centerIn: parent - text: "" - color: theme.base - } - } + } + + // System Tray Placeholder + Rectangle { + implicitWidth: 60 + implicitHeight: 24 + radius: theme.radius + color: theme.text + opacity: 0.2 + } + + } + + } - component BatteryIcon: Rectangle { - width: 24; height: 24; radius: theme.radius - color: theme.yellow - Text { - anchors.centerIn: parent - text: "" - color: theme.base } - } - component SystemTray: Rectangle { - width: 60; height: 24; radius: theme.radius - color: theme.text - opacity: 0.2 } + } diff --git a/examples/112-dock/shell.qml b/examples/112-dock/shell.qml index ccb9bf8..9a4dc70 100644 --- a/examples/112-dock/shell.qml +++ b/examples/112-dock/shell.qml @@ -1,58 +1,67 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts +import Quickshell -// Catppuccin Mocha palette -pragma Singleton -QtObject { - id: theme - property color base: "#1e1e2e" - property color text: "#cdd6f4" - property color accent: "#89b4fa" - property color green: "#a6e3a1" - property color red: "#f38ba8" - property color yellow: "#f9e2af" - property color purple: "#cba6f7" - property int spacing: 8 - property int radius: 8 - property int iconSize: 48 -} +ShellRoot { + // Pinned applications + property var pinnedApps: [{ + "name": "Browser", + "icon": "🌐" + }, { + "name": "Terminal", + "icon": "" + }, { + "name": "Files", + "icon": "" + }, { + "name": "Settings", + "icon": "" + }, { + "name": "Code", + "icon": "" + }] + // Currently running apps (mocked state) + property var runningApps: ["Browser", "Terminal"] + + // Launch or focus target application + function launchOrFocus(appName) { + if (runningApps.indexOf(appName) >= 0) { + console.log("Focusing " + appName); + } else { + console.log("Launching " + appName); + runningApps.push(appName); + runningAppsChanged(); + } + } -// Pinned applications -property var pinnedApps: [ - { name: "Browser", icon: "🌐" }, - { name: "Terminal", icon: "" }, - { name: "Files", icon: "" }, - { name: "Settings", icon: "" }, - { name: "Code", icon: "" } -] - -// Currently "running" apps (mocked) -property var runningApps: ["Browser", "Terminal"] - -// Mock launch or focus function -function launchOrFocus(appName) { - if (runningApps.indexOf(appName) >= 0) { - console.log("Focusing " + appName) - } else { - console.log("Launching " + appName) - runningApps.push(appName) - runningAppsChanged() + QtObject { + id: theme + + // Catppuccin Mocha color palette + property color base: "#1e1e2e" + property color text: "#cdd6f4" + property color accent: "#89b4fa" + property color green: "#a6e3a1" + property color red: "#f38ba8" + property color yellow: "#f9e2af" + property color purple: "#cba6f7" + property int spacing: 8 + property int radius: 8 + property int iconSize: 48 } -} -ShellRoot { PanelWindow { id: dock + + implicitHeight: theme.iconSize + 16 + color: theme.base + exclusionMode: ExclusionMode.Normal + anchors { bottom: true left: true right: true } - height: theme.iconSize + 16 - color: theme.base - exclusionMode: ExclusionMode.Exclusive RowLayout { anchors.centerIn: parent @@ -62,18 +71,20 @@ ShellRoot { model: pinnedApps delegate: Item { + id: delegateItem + width: theme.iconSize + 8 height: theme.iconSize + 8 - // App icon button + // App icon item container Rectangle { id: iconBg + anchors.centerIn: parent width: theme.iconSize height: theme.iconSize radius: theme.radius - color: mouseArea.containsMouse ? theme.accent : Qt.rgba(255, 255, 255, 0.1) - Behavior on color { ColorAnimation { duration: 100 } } + color: mouseArea.containsMouse ? theme.accent : Qt.rgba(1, 1, 1, 0.1) Text { anchors.centerIn: parent @@ -84,57 +95,75 @@ ShellRoot { MouseArea { id: mouseArea + anchors.fill: parent hoverEnabled: true onClicked: launchOrFocus(modelData.name) + } + + Behavior on color { + ColorAnimation { + duration: 100 + } + + } - // Fake preview window on hover - onEntered: { - if (!fakeWindow) { - var comp = Qt.createComponent("FakeWindow.qml") - fakeWindow = comp.createObject(dock, {appName: modelData.name}) - } - fakeWindow.visible = true + } + + // Native Quickshell popup window for hover preview + PopupWindow { + id: previewPopup + + visible: mouseArea.containsMouse + width: 200 + height: 120 + color: "transparent" + + // Anchor popup to the top of the icon item + anchor { + window: dock + rect.x: delegateItem.x + (delegateItem.width / 2) - 100 + rect.y: dock.y - 130 + } + + Rectangle { + anchors.fill: parent + radius: 10 + color: theme.base + opacity: 0.95 + + border { + color: theme.accent + width: 2 } - onExited: { - if (fakeWindow) - fakeWindow.visible = false + + Text { + anchors.centerIn: parent + text: modelData.name + color: theme.text + font.pixelSize: 18 } + } - property var fakeWindow: null } - // Running indicator dot + // Active window indicator dot Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - width: 6; height: 6; radius: 3 - color: runningApps.indexOf(modelData.name) >= 0 ? theme.green : Qt.rgba(255, 255, 255, 0.2) + width: 6 + height: 6 + radius: 3 + color: runningApps.indexOf(modelData.name) >= 0 ? theme.green : Qt.rgba(1, 1, 1, 0.2) } - } - } - } - } - // Inline FakeWindow component for preview - component FakeWindow: Rectangle { - id: fw - property string appName: "" - width: 200; height: 120; radius: 10 - color: theme.base - border { color: theme.accent; width: 2 } - visible: false - opacity: 0.95 + } - x: dock.x + dock.width / 2 - width / 2 - y: dock.y - height - 10 + } - Text { - anchors.centerIn: parent - text: fw.appName - color: theme.text - font.pixelSize: 18 } + } + } diff --git a/examples/113-launcher/shell.qml b/examples/113-launcher/shell.qml index ad4b649..cc42e31 100644 --- a/examples/113-launcher/shell.qml +++ b/examples/113-launcher/shell.qml @@ -1,79 +1,136 @@ -import Quickshell -import Quickshell.Window import QtQuick +import QtQuick.Controls import QtQuick.Layouts +import Quickshell -// Catppuccin Mocha palette -pragma Singleton -QtObject { - id: theme - property color base: "#1e1e2e" - property color surface: "#313244" - property color text: "#cdd6f4" - property color accent: "#89b4fa" - property color green: "#a6e3a1" - property color red: "#f38ba8" - property color yellow: "#f9e2af" - property color purple: "#cba6f7" - property int radius: 8 - property int spacing: 6 -} +ShellRoot { + // Hardcoded application database -// Hardcoded application database -property var allApps: [] -function loadApps() { - allApps = [ - { name: "Browser", icon: "🌐", exec: "browser" }, - { name: "Terminal", icon: "", exec: "terminal" }, - { name: "Files", icon: "", exec: "files" }, - { name: "Settings", icon: "", exec: "settings" }, - { name: "Code", icon: "", exec: "code" }, - { name: "Calculator", icon: "", exec: "calculator" }, - { name: "Calendar", icon: "", exec: "calendar" }, - { name: "Mail", icon: "", exec: "mail" }, - { name: "Music", icon: "", exec: "music" }, - { name: "Photos", icon: "", exec: "photos" } - ] -} + property var allApps: [] + property var filteredApps: allApps -// Fuzzy scoring mock — returns 0..1 match score -function fuzzyScore(query, target) { - if (!query) return 1 - var q = query.toLowerCase() - var t = target.toLowerCase() - if (t.indexOf(q) >= 0) return 1 - // Simple character-by-character fuzzy - var qi = 0 - for (var ti = 0; ti < t.length && qi < q.length; ti++) { - if (q[qi] === t[ti]) qi++ + function loadApps() { + allApps = [{ + "name": "Browser", + "icon": "🌐", + "exec": "browser" + }, { + "name": "Terminal", + "icon": "", + "exec": "terminal" + }, { + "name": "Files", + "icon": "", + "exec": "files" + }, { + "name": "Settings", + "icon": "", + "exec": "settings" + }, { + "name": "Code", + "icon": "", + "exec": "code" + }, { + "name": "Calculator", + "icon": "", + "exec": "calculator" + }, { + "name": "Calendar", + "icon": "", + "exec": "calendar" + }, { + "name": "Mail", + "icon": "", + "exec": "mail" + }, { + "name": "Music", + "icon": "", + "exec": "music" + }, { + "name": "Photos", + "icon": "", + "exec": "photos" + }]; } - return qi === q.length ? 0.5 : 0 -} -function filterApps(query) { - var scored = allApps.map(function(app) { - return { app: app, score: fuzzyScore(query, app.name) } - }) - scored.sort(function(a, b) { return b.score - a.score }) - return scored.filter(function(item) { return item.score > 0 }).map(function(item) { return item.app }) -} + // Fuzzy scoring mock — returns 0..1 match score + function fuzzyScore(query, target) { + if (!query) + return 1; -property var filteredApps: allApps + var q = query.toLowerCase(); + var t = target.toLowerCase(); + if (t.indexOf(q) >= 0) + return 1; + + // Simple character-by-character fuzzy + var qi = 0; + for (var ti = 0; ti < t.length && qi < q.length; ti++) { + if (q[qi] === t[ti]) + qi++; + + } + return qi === q.length ? 0.5 : 0; + } + + function filterApps(query) { + var scored = allApps.map(function(app) { + return { + "app": app, + "score": fuzzyScore(query, app.name) + }; + }); + scored.sort(function(a, b) { + return b.score - a.score; + }); + return scored.filter(function(item) { + return item.score > 0; + }).map(function(item) { + return item.app; + }); + } + + Component.onCompleted: { + loadApps(); + filteredApps = allApps; + } + + QtObject { + // Catppuccin Mocha palette + + id: theme + + property color base: "#1e1e2e" + property color surface: "#313244" + property color text: "#cdd6f4" + property color accent: "#89b4fa" + property color green: "#a6e3a1" + property color red: "#f38ba8" + property color yellow: "#f9e2af" + property color purple: "#cba6f7" + property int radius: 8 + property int spacing: 6 + } -ShellRoot { PopupWindow { + // Toggle with Meta/Super key via a Quickshell shortcut binding + id: launcher - width: 500 - height: 400 + + implicitWidth: 500 + implicitHeight: 400 color: theme.base visible: false - // Toggle with Meta/Super key via a Quickshell shortcut binding Rectangle { anchors.fill: parent radius: theme.radius color: theme.base - border { color: theme.accent; width: 2 } + + border { + color: theme.accent + width: 2 + } ColumnLayout { anchors.fill: parent @@ -87,59 +144,82 @@ ShellRoot { radius: theme.radius color: theme.surface - TextInput { + TextField { id: searchInput + anchors.fill: parent anchors.margins: 10 color: theme.text font.pixelSize: 16 placeholderText: "Search applications..." placeholderTextColor: Qt.rgba(1, 1, 1, 0.4) - + // Remove background styling from default TextField control + background: null onTextChanged: { - filteredApps = filterApps(text) + filteredApps = filterApps(text); } - Keys.onEscapePressed: { - launcher.visible = false - searchInput.text = "" + launcher.visible = false; + searchInput.text = ""; } - Keys.onReturnPressed: { if (resultsListView.currentIndex >= 0 && resultsListView.currentIndex < resultsList.count) { - var app = resultsList.get(resultsListView.currentIndex).modelData - console.log("Launching: " + app.name + " (" + app.exec + ")") - launcher.visible = false - searchInput.text = "" + var app = resultsList.get(resultsListView.currentIndex).modelData; + console.log("Launching: " + app.name + " (" + app.exec + ")"); + launcher.visible = false; + searchInput.text = ""; } } - Keys.onUpPressed: { if (resultsListView.currentIndex > 0) - resultsListView.currentIndex-- - } + resultsListView.currentIndex--; + } Keys.onDownPressed: { if (resultsListView.currentIndex < resultsList.count - 1) - resultsListView.currentIndex++ + resultsListView.currentIndex++; + } } + } // Results list ListView { id: resultsListView + + // Sync filteredApps into the ListModel + property var filteredApps: filteredApps + Layout.fillWidth: true Layout.fillHeight: true clip: true - model: ListModel { id: resultsList } + onFilteredAppsChanged: { + resultsList.clear(); + for (var i = 0; i < filteredApps.length; i++) { + resultsList.append({ + "modelData": filteredApps[i] + }); + } + } + Component.onCompleted: { + resultsList.clear(); + for (var i = 0; i < filteredApps?.length; i++) { + resultsList.append({ + "modelData": filteredApps[i] + }); + } + } + + model: ListModel { + id: resultsList + } delegate: Rectangle { width: resultsListView.width height: 40 radius: theme.radius color: ListView.isCurrentItem ? theme.accent : "transparent" - Behavior on color { ColorAnimation { duration: 50 } } RowLayout { anchors.fill: parent @@ -150,50 +230,49 @@ ShellRoot { text: modelData.icon font.pixelSize: 18 } + Text { text: modelData.name color: theme.text font.pixelSize: 14 } - Item { Layout.fillWidth: true } + + Item { + Layout.fillWidth: true + } + Text { text: modelData.exec color: Qt.rgba(1, 1, 1, 0.4) font.pixelSize: 12 } + } MouseArea { anchors.fill: parent onClicked: { - console.log("Launching: " + modelData.name + " (" + modelData.exec + ")") - launcher.visible = false - searchInput.text = "" + console.log("Launching: " + modelData.name + " (" + modelData.exec + ")"); + launcher.visible = false; + searchInput.text = ""; } } - } - // Sync filteredApps into the ListModel - property var filteredApps: filteredApps - onFilteredAppsChanged: { - resultsList.clear() - for (var i = 0; i < filteredApps.length; i++) { - resultsList.append({modelData: filteredApps[i]}) - } - } - Component.onCompleted: { - resultsList.clear() - for (var i = 0; i < filteredApps.length; i++) { - resultsList.append({modelData: filteredApps[i]}) + Behavior on color { + ColorAnimation { + duration: 50 + } + } + } + } + } + } - } - Component.onCompleted: { - loadApps() - filteredApps = allApps } + } diff --git a/examples/900-complete-shell/Theme.qml b/examples/900-complete-shell/Theme.qml index 4af90f9..2ec0304 100644 --- a/examples/900-complete-shell/Theme.qml +++ b/examples/900-complete-shell/Theme.qml @@ -1,12 +1,12 @@ -import QtQml +import QtQuick -// Shared Theme singleton for the complete desktop shell. +// Shared Theme component for the complete desktop shell. // Provides colors, spacing, and font definitions consumed by all widgets. QtObject { // --- Catppuccin Mocha palette --- - readonly property color base: "#1e1e2e" + readonly property color base: "#1e1e2e" readonly property color mantle: "#181825" - readonly property color crust: "#11111b" + readonly property color crust: "#11111b" readonly property color surface0: "#313244" readonly property color surface1: "#45475a" readonly property color surface2: "#585b70" @@ -14,29 +14,27 @@ QtObject { readonly property color overlay1: "#7f849c" readonly property color subtext0: "#a6adc8" readonly property color subtext1: "#bac2de" - readonly property color text: "#cdd6f4" - readonly property color accent: "#89b4fa" - readonly property color green: "#a6e3a1" - readonly property color red: "#f38ba8" - readonly property color yellow: "#f9e2af" - readonly property color purple: "#cba6f7" - readonly property color teal: "#94e2d5" - readonly property color pink: "#f5c2e7" - + readonly property color text: "#cdd6f4" + readonly property color accent: "#89b4fa" + readonly property color green: "#a6e3a1" + readonly property color red: "#f38ba8" + readonly property color yellow: "#f9e2af" + readonly property color purple: "#cba6f7" + readonly property color teal: "#94e2d5" + readonly property color pink: "#f5c2e7" // --- Dimensions --- - readonly property int topBarHeight: 48 - readonly property int dockHeight: 64 - readonly property int iconSize: 48 - readonly property int spacingSmall: 4 - readonly property int spacingMedium: 8 - readonly property int spacingLarge: 12 - readonly property int radiusSmall: 4 - readonly property int radiusMedium: 8 - readonly property int radiusLarge: 12 - + readonly property int topBarHeight: 48 + readonly property int dockHeight: 64 + readonly property int iconSize: 48 + readonly property int spacingSmall: 4 + readonly property int spacingMedium: 8 + readonly property int spacingLarge: 12 + readonly property int radiusSmall: 4 + readonly property int radiusMedium: 8 + readonly property int radiusLarge: 12 // --- Typography --- - readonly property string fontFamily: "sans-serif" - readonly property int fontSizeSmall: 12 - readonly property int fontSizeMedium: 14 - readonly property int fontSizeLarge: 18 + readonly property string fontFamily: "sans-serif" + readonly property int fontSizeSmall: 12 + readonly property int fontSizeMedium: 14 + readonly property int fontSizeLarge: 18 } diff --git a/examples/900-complete-shell/shell.qml b/examples/900-complete-shell/shell.qml index 1e797ca..5549a1f 100644 --- a/examples/900-complete-shell/shell.qml +++ b/examples/900-complete-shell/shell.qml @@ -1,11 +1,6 @@ -import Quickshell -import Quickshell.Window import QtQuick import QtQuick.Layouts - -// Shared Theme — must be imported first by the runtime -import "Theme.qml" as ThemeModule -property ThemeModule.Theme theme: ThemeModule.Theme {} +import Quickshell // ────────────────────────────────────────────── // Shell root — top-level container for the shell @@ -13,17 +8,23 @@ property ThemeModule.Theme theme: ThemeModule.Theme {} ShellRoot { id: shell + // Instantiate Theme directly inside the root component + Theme { + id: theme + } + // ── Top Bar ──────────────────────────────── PanelWindow { id: topBar + + implicitHeight: theme.topBarHeight + color: theme.base + anchors { top: true left: true right: true } - height: theme.topBarHeight - color: theme.base - exclusionMode: ExclusionMode.Exclusive RowLayout { anchors.fill: parent @@ -36,37 +37,50 @@ ShellRoot { spacing: theme.spacingMedium Rectangle { - width: 32; height: 32; radius: theme.radiusSmall + width: 32 + height: 32 + radius: theme.radiusSmall color: theme.accent + Text { anchors.centerIn: parent text: "1" color: theme.base font.bold: true } + MouseArea { anchors.fill: parent onClicked: console.log("Workspace switched") } + } Rectangle { - width: 32; height: 32; radius: theme.radiusSmall + width: 32 + height: 32 + radius: theme.radiusSmall color: theme.purple + Text { anchors.centerIn: parent text: "" color: theme.base font.pixelSize: 16 } + MouseArea { anchors.fill: parent onClicked: launcher.visible = !launcher.visible } + } + } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } // Center: clock Text { @@ -74,13 +88,19 @@ ShellRoot { text: new Date().toLocaleTimeString(Qt.locale(), "HH:mm") color: theme.text font.pixelSize: theme.fontSizeLarge + Timer { - interval: 1000; running: true; repeat: true + interval: 1000 + running: true + repeat: true onTriggered: parent.text = new Date().toLocaleTimeString(Qt.locale(), "HH:mm") } + } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } // Right: system indicators Row { @@ -88,83 +108,111 @@ ShellRoot { spacing: theme.spacingMedium Rectangle { - width: 32; height: 32; radius: theme.radiusSmall + width: 32 + height: 32 + radius: theme.radiusSmall color: theme.green + Text { anchors.centerIn: parent text: "" color: theme.base } + MouseArea { anchors.fill: parent onClicked: controlCenter.visible = !controlCenter.visible } + } Rectangle { - width: 32; height: 32; radius: theme.radiusSmall + width: 32 + height: 32 + radius: theme.radiusSmall color: theme.yellow + Text { anchors.centerIn: parent text: "" color: theme.base } + } Rectangle { - width: 32; height: 32; radius: theme.radiusSmall + width: 32 + height: 32 + radius: theme.radiusSmall color: theme.red + Text { anchors.centerIn: parent text: "" color: theme.base } + MouseArea { anchors.fill: parent onClicked: notificationCenter.visible = !notificationCenter.visible } + } Rectangle { - width: 60; height: 28; radius: theme.radiusSmall + width: 60 + height: 28 + radius: theme.radiusSmall color: theme.surface0 } + } + } + } // ── Dock ─────────────────────────────────── PanelWindow { id: dock - anchors { - bottom: true - left: true - right: true - } - height: theme.dockHeight - color: theme.mantle - exclusionMode: ExclusionMode.Exclusive - - property var pinnedApps: [ - { name: "Browser", icon: "🌐" }, - { name: "Terminal", icon: "" }, - { name: "Files", icon: "" }, - { name: "Settings", icon: "" }, - { name: "Code", icon: "" } - ] + property var pinnedApps: [{ + "name": "Browser", + "icon": "🌐" + }, { + "name": "Terminal", + "icon": "" + }, { + "name": "Files", + "icon": "" + }, { + "name": "Settings", + "icon": "" + }, { + "name": "Code", + "icon": "" + }] property var runningApps: ["Browser", "Terminal"] function launchOrFocus(appName) { if (runningApps.indexOf(appName) >= 0) { - console.log("Focusing " + appName) + console.log("Focusing " + appName); } else { - console.log("Launching " + appName) - runningApps.push(appName) - runningAppsChanged() + console.log("Launching " + appName); + runningApps.push(appName); + runningAppsChanged(); } } + implicitHeight: theme.dockHeight + color: theme.mantle + + anchors { + bottom: true + left: true + right: true + } + RowLayout { anchors.centerIn: parent spacing: theme.spacingMedium @@ -181,7 +229,6 @@ ShellRoot { height: theme.iconSize radius: theme.radiusMedium color: mouseArea.containsMouse ? theme.accent : Qt.rgba(1, 1, 1, 0.08) - Behavior on color { ColorAnimation { duration: 100 } } Text { anchors.centerIn: parent @@ -192,27 +239,43 @@ ShellRoot { MouseArea { id: mouseArea + anchors.fill: parent hoverEnabled: true onClicked: dock.launchOrFocus(modelData.name) } + + Behavior on color { + ColorAnimation { + duration: 100 + } + + } + } Rectangle { anchors.horizontalCenter: parent.horizontalCenter - width: 6; height: 6; radius: 3 + width: 6 + height: 6 + radius: 3 color: dock.runningApps.indexOf(modelData.name) >= 0 ? theme.green : Qt.rgba(1, 1, 1, 0.15) } + } + } + } + } // ── Launcher ──────────────────────────────── PopupWindow { id: launcher - width: 520 - height: 440 + + implicitWidth: 520 + implicitHeight: 440 color: "transparent" visible: false @@ -220,7 +283,11 @@ ShellRoot { anchors.fill: parent radius: theme.radiusLarge color: theme.base - border { color: theme.accent; width: 2 } + + border { + color: theme.accent + width: 2 + } ColumnLayout { anchors.fill: parent @@ -235,41 +302,112 @@ ShellRoot { TextInput { id: searchInput + anchors.fill: parent anchors.margins: 12 color: theme.text font.pixelSize: theme.fontSizeLarge - placeholderText: "Search applications..." - placeholderTextColor: theme.overlay0 - Keys.onEscapePressed: { - launcher.visible = false - searchInput.text = "" + launcher.visible = false; + searchInput.text = ""; } Keys.onReturnPressed: { - console.log("Launch selected") - launcher.visible = false - searchInput.text = "" + console.log("Launch selected"); + launcher.visible = false; + searchInput.text = ""; + } + + // Custom placeholder overlay for standard TextInput + Text { + anchors.fill: parent + text: "Search applications..." + color: theme.overlay0 + font.pixelSize: parent.font.pixelSize + visible: !searchInput.text && !searchInput.activeFocus } + } + } ListView { id: launcherList + Layout.fillWidth: true Layout.fillHeight: true clip: true + Keys.onUpPressed: { + if (currentIndex > 0) + currentIndex--; + + } + Keys.onDownPressed: { + if (currentIndex < count - 1) + currentIndex++; + + } + model: ListModel { - ListElement { name: "Browser"; icon: "🌐"; exec: "browser" } - ListElement { name: "Terminal"; icon: ""; exec: "terminal" } - ListElement { name: "Files"; icon: ""; exec: "files" } - ListElement { name: "Settings"; icon: ""; exec: "settings" } - ListElement { name: "Code"; icon: ""; exec: "code" } - ListElement { name: "Calculator"; icon: ""; exec: "calculator" } - ListElement { name: "Calendar"; icon: ""; exec: "calendar" } - ListElement { name: "Mail"; icon: ""; exec: "mail" } - ListElement { name: "Music"; icon: ""; exec: "music" } - ListElement { name: "Photos"; icon: ""; exec: "photos" } + ListElement { + name: "Browser" + icon: "🌐" + exec: "browser" + } + + ListElement { + name: "Terminal" + icon: "" + exec: "terminal" + } + + ListElement { + name: "Files" + icon: "" + exec: "files" + } + + ListElement { + name: "Settings" + icon: "" + exec: "settings" + } + + ListElement { + name: "Code" + icon: "" + exec: "code" + } + + ListElement { + name: "Calculator" + icon: "" + exec: "calculator" + } + + ListElement { + name: "Calendar" + icon: "" + exec: "calendar" + } + + ListElement { + name: "Mail" + icon: "" + exec: "mail" + } + + ListElement { + name: "Music" + icon: "" + exec: "music" + } + + ListElement { + name: "Photos" + icon: "" + exec: "photos" + } + } delegate: Rectangle { @@ -277,53 +415,78 @@ ShellRoot { height: 40 radius: theme.radiusSmall color: launcherList.currentIndex === index ? theme.accent : "transparent" - Behavior on color { ColorAnimation { duration: 50 } } RowLayout { anchors.fill: parent anchors.margins: theme.spacingMedium spacing: theme.spacingLarge - Text { text: icon; font.pixelSize: 18 } - Text { text: name; color: theme.text; font.pixelSize: theme.fontSizeMedium } - Item { Layout.fillWidth: true } - Text { text: exec; color: theme.overlay0; font.pixelSize: theme.fontSizeSmall } + + Text { + text: icon + font.pixelSize: 18 + } + + Text { + text: name + color: theme.text + font.pixelSize: theme.fontSizeMedium + } + + Item { + Layout.fillWidth: true + } + + Text { + text: exec + color: theme.overlay0 + font.pixelSize: theme.fontSizeSmall + } + } MouseArea { anchors.fill: parent onClicked: { - console.log("Launch: " + name) - launcher.visible = false + console.log("Launch: " + name); + launcher.visible = false; + } + } + + Behavior on color { + ColorAnimation { + duration: 50 } + } + } - Keys.onUpPressed: { if (currentIndex > 0) currentIndex-- } - Keys.onDownPressed: { if (currentIndex < count - 1) currentIndex++ } } + } + } + } // ── Notification Center ───────────────────── PopupWindow { id: notificationCenter - width: 360 - height: 480 + + implicitWidth: 360 + implicitHeight: 480 color: "transparent" visible: false - anchors { - right: true - top: true - topMargin: theme.topBarHeight + theme.spacingMedium - rightMargin: theme.spacingMedium - } Rectangle { anchors.fill: parent radius: theme.radiusLarge color: theme.mantle - border { color: theme.surface1; width: 1 } + + border { + color: theme.surface1 + width: 1 + } ColumnLayout { anchors.fill: parent @@ -349,11 +512,19 @@ ShellRoot { spacing: theme.spacingMedium Repeater { - model: [ - { app: "Mail", summary: "New email", body: "You have 3 unread messages" }, - { app: "Calendar", summary: "Meeting at 3pm", body: "Standup in 15 minutes" }, - { app: "Music", summary: "Now Playing", body: "Song Title — Artist" } - ] + model: [{ + "app": "Mail", + "summary": "New email", + "body": "You have 3 unread messages" + }, { + "app": "Calendar", + "summary": "Meeting at 3pm", + "body": "Standup in 15 minutes" + }, { + "app": "Music", + "summary": "Now Playing", + "body": "Song Title — Artist" + }] delegate: Rectangle { Layout.fillWidth: true @@ -372,40 +543,48 @@ ShellRoot { font.pixelSize: theme.fontSizeSmall font.bold: true } + Text { text: modelData.body color: theme.subtext0 font.pixelSize: theme.fontSizeSmall elide: Text.ElideRight } + } + } + } + } + } + } + } + } // ── Control Center ────────────────────────── PopupWindow { id: controlCenter - width: 320 - height: 320 + + implicitWidth: 320 + implicitHeight: 320 color: "transparent" visible: false - anchors { - right: true - top: true - topMargin: theme.topBarHeight + theme.spacingMedium - rightMargin: theme.spacingMedium - } Rectangle { anchors.fill: parent radius: theme.radiusLarge color: theme.mantle - border { color: theme.surface1; width: 1 } + + border { + color: theme.surface1 + width: 1 + } ColumnLayout { anchors.fill: parent @@ -426,97 +605,133 @@ ShellRoot { columnSpacing: theme.spacingMedium Repeater { - model: [ - { label: "Wi-Fi", icon: "", active: true }, - { label: "Bluetooth", icon: "", active: false }, - { label: "DND", icon: "", active: false }, - { label: "VPN", icon: "", active: true } - ] + model: [{ + "label": "Wi-Fi", + "icon": "", + "active": true + }, { + "label": "Bluetooth", + "icon": "", + "active": false + }, { + "label": "DND", + "icon": "", + "active": false + }, { + "label": "VPN", + "icon": "", + "active": true + }] delegate: Rectangle { Layout.fillWidth: true height: 60 radius: theme.radiusMedium color: modelData.active ? theme.surface1 : theme.surface0 - border { color: modelData.active ? theme.accent : "transparent"; width: 1 } + + border { + color: modelData.active ? theme.accent : "transparent" + width: 1 + } ColumnLayout { anchors.centerIn: parent spacing: 4 + Text { - anchors.horizontalCenter: parent.horizontalCenter + Layout.alignment: Qt.AlignHCenter text: modelData.icon color: modelData.active ? theme.accent : theme.overlay0 font.pixelSize: 20 } + Text { - anchors.horizontalCenter: parent.horizontalCenter + Layout.alignment: Qt.AlignHCenter text: modelData.label color: modelData.active ? theme.text : theme.overlay0 font.pixelSize: theme.fontSizeSmall } + } MouseArea { anchors.fill: parent onClicked: { - modelData.active = !modelData.active - console.log("Toggle " + modelData.label + ": " + modelData.active) + modelData.active = !modelData.active; + console.log("Toggle " + modelData.label + ": " + modelData.active); } } + } + } + } - Item { Layout.fillHeight: true } + Item { + Layout.fillHeight: true + } // Volume slider ColumnLayout { Layout.fillWidth: true spacing: theme.spacingSmall + Text { text: "Volume" color: theme.subtext0 font.pixelSize: theme.fontSizeSmall } + Rectangle { Layout.fillWidth: true height: 6 radius: 3 color: theme.surface1 + Rectangle { width: parent.width * 0.7 height: parent.height radius: 3 color: theme.accent } + } + } // Brightness slider ColumnLayout { Layout.fillWidth: true spacing: theme.spacingSmall + Text { text: "Brightness" color: theme.subtext0 font.pixelSize: theme.fontSizeSmall } + Rectangle { Layout.fillWidth: true height: 6 radius: 3 - color: theme.surface1 + color: theme.yellow + Rectangle { width: parent.width * 0.85 height: parent.height radius: 3 color: theme.yellow } + } + } + } + } + } // ── Keyboard shortcut: Meta/Super to toggle launcher ── @@ -524,4 +739,5 @@ ShellRoot { sequence: "Meta+Space" onActivated: launcher.visible = !launcher.visible } + }