From 1356456ab0c6e8dffc10d3639bbeba720ffaaedb Mon Sep 17 00:00:00 2001 From: voidarc Date: Thu, 11 Jun 2026 16:09:16 +0100 Subject: [PATCH] battery stuff and fixed the weird workspace nonsense --- .nvim.lua | 24 ++ .qmlformat.ini | 12 + Bar.qml | 127 ++++----- Components/Utils.qml | 60 +++-- Components/Utils/Battery.qml | 46 +++- Components/Utils/Container.qml | 64 +++++ Components/Workspace.qml | 465 +++++++++++++++++++-------------- 7 files changed, 507 insertions(+), 291 deletions(-) create mode 100644 .qmlformat.ini create mode 100644 Components/Utils/Container.qml diff --git a/.nvim.lua b/.nvim.lua index 62ba8b5..bf8ae53 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -1,2 +1,26 @@ require("nvim-treesitter").install({ "qmljs" }) vim.lsp.enable("qmlls") + +-- Disable semantic tokens when any LSP client attaches to a buffer +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + if client then + client.server_capabilities.semanticTokensProvider = nil + end + end, +}) + +vim.api.nvim_create_autocmd("FileType", { + pattern = { "qml" }, + callback = function() + vim.treesitter.start() + end, +}) + +require("conform").setup({ + formatters_by_ft = { + qml = { "qmlformat" }, + }, + format_on_save = true, +}) diff --git a/.qmlformat.ini b/.qmlformat.ini new file mode 100644 index 0000000..d090a20 --- /dev/null +++ b/.qmlformat.ini @@ -0,0 +1,12 @@ +[General] +FunctionsSpacing=true +GroupAttributesTogether=false +IndentWidth=2 +MaxColumnWidth=100 +NewlineType=unix +NormalizeOrder=true +ObjectsSpacing=true +SemicolonRule=always +SingleLineEmptyObjects=true +SortImports=true +UseTabs=true diff --git a/Bar.qml b/Bar.qml index 3018b26..dd10837 100644 --- a/Bar.qml +++ b/Bar.qml @@ -1,77 +1,86 @@ -import Quickshell -import Quickshell.Hyprland - import QtQuick import QtQuick.Effects - -import "Components" +import Quickshell +import Quickshell.Hyprland import "Components/Color.js" as Colors +import "Components" + Scope { - Variants { - model: Quickshell.screens + Variants { + model: Quickshell.screens - delegate: Component { - PanelWindow { - id: win - required property var modelData + delegate: Component { + PanelWindow { + id: win - screen: modelData + required property var modelData + property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) - Behavior on implicitHeight { - NumberAnimation { duration: 0; easing.type: Easing.InOutQuad } - } + color: "transparent" + exclusionMode: ExclusionMode.Normal + exclusiveZone: 20 + implicitHeight: 1000 + screen: modelData - property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) + Behavior on implicitHeight { + NumberAnimation { + duration: 0 + easing.type: Easing.InOutQuad + } + } + mask: Region { + Region { + item: workspaceSwitcher + } - color: "transparent" + Region { + item: pillItem + } - exclusionMode: ExclusionMode.Normal + Region { + item: utilsItem + } + } - mask: Region { - Region { - item: workspaceSwitcher - } - Region { - item: pillItem - } - } + anchors { + left: true + right: true + top: true + } - anchors { - top: true - left: true - right: true - } + Workspace { + id: workspaceSwitcher - implicitHeight: 1000 - exclusiveZone: 20 + monitor: win.monitor + } - Workspace { - id: workspaceSwitcher - monitor: win.monitor - } + RectangularShadow { + anchors.fill: pillItem + blur: 20 + color: Colors.crust + offset.x: 3 + offset.y: 3 + radius: pillItem.radius + spread: 7 + } - RectangularShadow { - anchors.fill: pillItem - offset.x: 3 - offset.y: 3 - radius: pillItem.radius - blur: 20 - spread: 7 - color: Colors.crust - } - Pill { - id: pillItem - monitor: win.monitor - } + Pill { + id: pillItem - Item { - anchors.fill: parent - Utils { - monitor: win.monitor - } - } - } - } - } + monitor: win.monitor + } + + Item { + anchors.fill: parent + + Utils { + id: utilsItem + + monitor: win.monitor + } + } + } + } + } } diff --git a/Components/Utils.qml b/Components/Utils.qml index 13a11f0..0002578 100644 --- a/Components/Utils.qml +++ b/Components/Utils.qml @@ -1,41 +1,45 @@ - pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts import Quickshell import Quickshell.Hyprland -import QtQuick -import QtQuick.Layouts import "./Utils" RowLayout { - id: utilsContainer - required property HyprlandMonitor monitor - state: "" + id: utilsContainer - anchors { - right: parent.right - top: parent.top - } - - states: [ - State { - name: "" - } - ] + required property HyprlandMonitor monitor + property bool shown: monitor !== null && monitor.focused - Rectangle { - color: "red" - implicitWidth: 40 - implicitHeight: 40 - } - Rectangle { - color: "blue" - implicitWidth: 40 - implicitHeight: 40 - } + opacity: shown ? 1 : 0 + state: "" - // Battery {} - // Mpris {} + Behavior on anchors.topMargin { + SmoothedAnimation { + duration: 300 + } + } + Behavior on opacity { + NumberAnimation { + duration: 50 + } + } + states: [ + State { + name: "" + } + ] + + anchors { + right: parent.right + rightMargin: 7 + top: parent.top + topMargin: shown ? -33 : -40 + } + + Battery {} + // Mpris {} } diff --git a/Components/Utils/Battery.qml b/Components/Utils/Battery.qml index 67e59f5..ed377b5 100644 --- a/Components/Utils/Battery.qml +++ b/Components/Utils/Battery.qml @@ -1,8 +1,46 @@ -import Quickshell import QtQuick +import Quickshell +import Quickshell.Services.UPower -Item { - Text { - text: "some" +import "../Color.js" as Colors + +import "./" + +Container { + id: batteryRoot + + boxColor: Colors.green + boxState: "" + sourceComponent: batteryClosed + visible: UPower.devices[0] ? true : false + + MouseArea { + anchors.fill: parent + enabled: true + hoverEnabled: true + propagateComposedEvents: true + + onClicked: batteryRoot.boxState == "" ? batteryRoot.boxState = "open" : batteryRoot.boxState = "" + onExited: batteryRoot.boxState = "" + } + + Component { + id: batteryClosed + + Item { + id: batteryRoot + + anchors.fill: parent + + Text { + color: Colors.base + text: "something" + + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + } } + } } diff --git a/Components/Utils/Container.qml b/Components/Utils/Container.qml new file mode 100644 index 0000000..c10edf1 --- /dev/null +++ b/Components/Utils/Container.qml @@ -0,0 +1,64 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell + +Item { + id: container + + required property color boxColor + property double boxHeight + property double boxRadius + required property string boxState + property double boxWidth + property double openHeight + property double openWidth + required property Component sourceComponent + + Layout.topMargin: 0 + implicitHeight: boxHeight || 50 + implicitWidth: boxWidth || 30 + state: boxState + + Behavior on anchors.topMargin { + SmoothAnim {} + } + Behavior on implicitHeight { + SmoothAnim {} + } + Behavior on implicitWidth { + SmoothAnim {} + } + states: [ + State { + name: "" + }, + State { + name: "open" + + PropertyChanges { + container.Layout.topMargin: 35 + container.implicitHeight: openHeight || 150 + container.implicitWidth: openWidth || 200 + } + } + ] + + Rectangle { + id: mainBackground + + anchors.fill: parent + color: container.boxColor + radius: container.boxRadius || 10 + + Loader { + anchors.fill: parent + sourceComponent: container.sourceComponent + } + } + + component SmoothAnim: SpringAnimation { + damping: 0.18 + mass: 0.5 + spring: 3 + } +} diff --git a/Components/Workspace.qml b/Components/Workspace.qml index e5d98ca..d718117 100644 --- a/Components/Workspace.qml +++ b/Components/Workspace.qml @@ -1,225 +1,290 @@ pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts import Quickshell import Quickshell.Hyprland -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects import "Color.js" as Colors Item { - id: workspaceRowContainer - required property HyprlandMonitor monitor - state: "" + id: workspaceRowContainer - function getNextEmptyWorkspaceId() { - let maxId = 0; - let workspaces = Hyprland.workspaces.values; + required property HyprlandMonitor monitor + property int textBottomMargin: 0 - for (let i = 0; i < workspaces.length; i++) { - let ws = workspaces[i]; - // Look at all numerical workspaces globally (ignoring special workspaces like scratchpads) - if (ws.id > maxId && ws.id < 99) { - maxId = ws.id; + function getNextEmptyWorkspaceId() { + let maxId = 0; + let workspaces = Hyprland.workspaces.values; + + for (let i = 0; i < workspaces.length; i++) { + let ws = workspaces[i]; + // Look at all numerical workspaces globally (ignoring special workspaces like scratchpads) + if (ws.id > maxId && ws.id < 99) { + maxId = ws.id; + } + } + + // Returns the next global workspace ID (e.g., if max is 3, returns 4) + console.log(maxId + 1); + return maxId === 0 ? 1 : maxId + 1; + } + + implicitHeight: 65 + implicitWidth: 200 + state: "" + + Behavior on anchors.topMargin { + NumberAnimation { + duration: 100 + } + } + states: [ + State { + name: "" + // When not hovered, stay at default + when: !workspaceHoverHandler.hovered + + PropertyChanges { + workspaceRowContainer.anchors.topMargin: -33 + } + }, + State { + name: "hovered" + // Keep the workspace dropped whenever hovered is true + when: workspaceHoverHandler.hovered + + PropertyChanges { + workspaceRowContainer.anchors.topMargin: -20 + } + + PropertyChanges { + workspaceRowContainer.textBottomMargin: 7 + } + } + ] + Behavior on textBottomMargin { + NumberAnimation { + duration: 80 + } + } + + anchors { + left: parent.left + leftMargin: 15 + top: parent.top + topMargin: -33 + } + + HoverHandler { + id: workspaceHoverHandler + } + + Row { + id: workspaceRow + + spacing: 5 + + Repeater { + model: Hyprland.workspaces.values + + Item { + id: selectorContainer + + property HyprlandWorkspace activeWorkspace: workspaceRowContainer.monitor.activeWorkspace + property HyprlandWorkspace currentWorkspace: Hyprland.workspaces.values[index] + property HyprlandWorkspace globalFocusedWorkspace: Hyprland.focusedWorkspace + required property int index + + function getImplicitWidth() { + let implicitWidth = 25; + + if (workspaceRowContainer.state == "hovered") { + implicitWidth += 5; + if (currentWorkspace.focused) { + implicitWidth += 20; } + } + return implicitWidth; } - // Returns the next global workspace ID (e.g., if max is 3, returns 4) - console.log(maxId + 1) - return maxId === 0 ? 1 : maxId + 1; - } + function shouldBeVisible() { + let isSpecialWorkspace = !selectorContainer.currentWorkspace.name.includes("special:"); + let isCurrentMonitor = Hyprland.workspaces.values[index].monitor + == workspaceRowContainer.monitor; + return isSpecialWorkspace && isCurrentMonitor; + } - anchors { - top: parent.top - left: parent.left - leftMargin: 15 - topMargin: -33 - } + implicitHeight: 50 + implicitWidth: getImplicitWidth() + visible: shouldBeVisible() - HoverHandler { - id: workspaceHoverHandler - } - - Behavior on anchors.topMargin { - NumberAnimation { duration: 100 } - } - - property int textBottomMargin: 0 - - Behavior on textBottomMargin { - NumberAnimation {duration: 80} - } - - states: [ - State { + Behavior on anchors.topMargin { + NumberAnimation { + duration: 100 + } + } + Behavior on implicitWidth { + NumberAnimation { + duration: 100 + } + } + states: [ + State { name: "" - // When not hovered, stay at default - when: !workspaceHoverHandler.hovered - PropertyChanges { workspaceRowContainer.anchors.topMargin: -33 } - }, - State { - name: "hovered" - // Keep the workspace dropped whenever hovered is true - when: workspaceHoverHandler.hovered - PropertyChanges { workspaceRowContainer.anchors.topMargin: -20 } - PropertyChanges { workspaceRowContainer.textBottomMargin: 7 } - } - ] + when: !selectorHoverHandler.hovered - implicitWidth: 200 - implicitHeight: 65 - - Row { - id: workspaceRow - spacing: 5 - - - Repeater { - model: Hyprland.workspaces.values - - Item { - id: selectorContainer - required property int index - property HyprlandWorkspace currentWorkspace: Hyprland.workspaces.values[index] - property HyprlandWorkspace globalFocusedWorkspace: Hyprland.focusedWorkspace - implicitHeight: 50 - implicitWidth: currentWorkspace.focused && workspaceRowContainer.state == "hovered" ? 50 : workspaceRowContainer.state == "hovered" ? 30 : 25 - - onGlobalFocusedWorkspaceChanged: { - if (globalFocusedWorkspace.id === currentWorkspace.id) { - selectorContainer.state = "workspaceSwitched" - switchFeedbackTimer.restart() - } - } - - Timer { - id: switchFeedbackTimer - interval: 200 - repeat: false - onTriggered: selectorContainer.state = "" - } - - anchors { - top: parent.top - topMargin: 0 - } - - HoverHandler { - id: selectorHoverHandler - } - - MouseArea { - id: selectorMouseArea - anchors.fill: parent - hoverEnabled: true - propagateComposedEvents: true - - onClicked: selectorContainer.currentWorkspace.activate() - } - - Behavior on anchors.topMargin { - NumberAnimation {duration: 100} - } - - Behavior on implicitWidth { - NumberAnimation {duration: 100} - } - - states: [ - State { - name: "" - when: !selectorHoverHandler.hovered - PropertyChanges { selectorContainer.anchors.topMargin: 0 } - }, - State { - name: "individualHovered" - when: selectorHoverHandler.hovered - PropertyChanges { selectorContainer.anchors.topMargin: 10 } - }, - State { - name: "workspaceSwitched" - PropertyChanges { selectorContainer.anchors.topMargin: 5 } - } - ] - - visible: Hyprland.workspaces.values[index].monitor == monitor && index > 0 && selectorContainer.currentWorkspace.name != "special:scratch" && selectorContainer.currentWorkspace.name != "special:music" - - RectangularShadow { - anchors.fill: selector - offset.x: 3 - offset.y: 3 - radius: selector.radius - blur: 20 - z: -1 - spread: 7 - color: Colors.crust - } - Rectangle { - z: 1 - id: selector - state: "" - - color: selectorContainer.currentWorkspace.focused ? Colors.mauve : workspaceRowContainer.monitor.activeWorkspace.id == selectorContainer.currentWorkspace.id ? Colors.lavender : Colors.overlay1 - radius: 8 - anchors.fill: parent - - Behavior on color { - ColorAnimation {duration: 100} - } - - Text { - id: workspaceIdentifier - text: selectorContainer.currentWorkspace.name - font.family: "FiraMono Nerd Font" - font.pixelSize: workspaceRowContainer.state == "hovered" ? 15 : 13 - anchors { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - bottomMargin: workspaceRowContainer.textBottomMargin - } - } - - } + PropertyChanges { + selectorContainer.anchors.topMargin: 0 } + }, + State { + name: "individualHovered" + when: selectorHoverHandler.hovered + + PropertyChanges { + selectorContainer.anchors.topMargin: 10 + } + }, + State { + name: "workspaceSwitched" + + PropertyChanges { + selectorContainer.anchors.topMargin: 5 + } + } + ] + + // onActiveWorkspaceChanged: { + // if (workspaceRowContainer.monitor.activeWorkspace.id == "1") {} + // if (workspaceRowContainer.monitor.activeWorkspace.id == "4") {} + // } + onGlobalFocusedWorkspaceChanged: { + if (globalFocusedWorkspace.name === currentWorkspace.name && globalFocusedWorkspace.monitor + === workspaceRowContainer.monitor && workspaceRowContainer.monitor.activeWorkspace.name + === currentWorkspace.name) { + selectorContainer.state = "workspaceSwitched"; + switchFeedbackTimer.restart(); + } } - Item { - implicitHeight: 40 - implicitWidth: 20 - + + Timer { + id: switchFeedbackTimer + + interval: 200 + repeat: false + + onTriggered: selectorContainer.state = "" + } + + anchors { + top: parent.top + topMargin: 0 + } + + HoverHandler { + id: selectorHoverHandler + } + + MouseArea { + id: selectorMouseArea + + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + + onClicked: selectorContainer.currentWorkspace.activate() + } + + RectangularShadow { + anchors.fill: selector + blur: 20 + color: Colors.crust + offset.x: 3 + offset.y: 3 + radius: selector.radius + spread: 7 + z: -1 + } + + Rectangle { + id: selector + + anchors.fill: parent + color: selectorContainer.currentWorkspace.focused ? Colors.mauve : + workspaceRowContainer.monitor.activeWorkspace + == selectorContainer.currentWorkspace ? Colors.lavender : Colors.overlay1 + radius: 8 + state: "" + z: 1 + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + + Text { + id: workspaceIdentifier + + font.family: "FiraMono Nerd Font" + font.pixelSize: workspaceRowContainer.state == "hovered" ? 15 : 13 + text: selectorContainer.currentWorkspace.name anchors { - top: parent.top - topMargin: workspaceRowContainer.state == "hovered" ? 0 : -10 - } - - Behavior on anchors.topMargin { - NumberAnimation {duration: 100} - } - - MouseArea { - id: newWorkspaceButton - anchors.fill: parent - - onClicked: Hyprland.dispatch(`hl.dsp.focus({workspace = ${workspaceRowContainer.getNextEmptyWorkspaceId()}})`) - } - Rectangle { - color: Colors.surface2 - anchors.fill: parent - radius: 10 - border.color: Colors.text - border.width: 2 - Text { - text: "+" - color: Colors.text - font.family: "FiraMono Nerd Font" - font.pixelSize: 20 - anchors { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - } + bottom: parent.bottom + bottomMargin: workspaceRowContainer.textBottomMargin + horizontalCenter: parent.horizontalCenter } + } } + } } + + Item { + implicitHeight: 40 + implicitWidth: 20 + + Behavior on anchors.topMargin { + NumberAnimation { + duration: 100 + } + } + + anchors { + top: parent.top + topMargin: workspaceRowContainer.state == "hovered" ? 0 : -10 + } + + MouseArea { + id: newWorkspaceButton + + anchors.fill: parent + + onClicked: Hyprland.dispatch(`hl.dsp.focus({workspace = +${workspaceRowContainer.getNextEmptyWorkspaceId()}})`) + } + + Rectangle { + anchors.fill: parent + border.color: Colors.text + border.width: 2 + color: Colors.surface2 + radius: 10 + + Text { + color: Colors.text + font.family: "FiraMono Nerd Font" + font.pixelSize: 20 + text: "+" + + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + } + } + } + } }