From 7872fba6feab09757c89070641aaabbaf7b179c9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 4 Jan 2026 22:08:53 +0100 Subject: [PATCH 001/104] hefty: morphing thingy --- .../ii/modules/common/widgets/shapes | 2 +- .../hefty/topLayer/HAbstractMorphedPanel.qml | 32 +++++ .../ii/modules/hefty/topLayer/HBar.qml | 89 +++++++++++++ .../ii/modules/hefty/topLayer/HOverview.qml | 75 +++++++++++ .../ii/modules/hefty/topLayer/HTopLayer.qml | 15 +++ .../modules/hefty/topLayer/HTopLayerPanel.qml | 123 ++++++++++++++++++ .../ii/panelFamilies/HeftyHypeFamily.qml | 48 +++++++ dots/.config/quickshell/ii/shell.qml | 7 +- 8 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayer.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml create mode 100644 dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/shapes b/dots/.config/quickshell/ii/modules/common/widgets/shapes index 7f0f0709e..5916b5ae6 160000 --- a/dots/.config/quickshell/ii/modules/common/widgets/shapes +++ b/dots/.config/quickshell/ii/modules/common/widgets/shapes @@ -1 +1 @@ -Subproject commit 7f0f0709ec5bbe4c3158c7f5fc68fd890af46618 +Subproject commit 5916b5ae6f4a3d9c351ed5c78ae24ce4013c1764 diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml new file mode 100644 index 000000000..53b03769a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml @@ -0,0 +1,32 @@ +import QtQuick +import Quickshell + +/** + * Abstract morphed panel to be used in TopLayerPanel. + * Screen width and height are to be supplied when declared in the top layer panel + * Others are to be declared by panels deriving from this + * + * To make sure morph movements don't look weird: + * - Follow the convention of having points start from bottom-middle and go clockwise + * - Make sure the number of points is "balanced" in all directions + * - Tip: Sometimes symmetry is not enough. Try to have more intermediate points if ones you have are too spaced out and act funny. + */ +Item { + id: root + + // To be fed + required property int screenWidth + required property int screenHeight + + // Some info + property int reservedTop: 0 + property int reservedBottom: 0 + property int reservedLeft: 0 + property int reservedRight: 0 + + // Main stuff + property var backgroundPolygon + property Region maskRegion: Region { + item: root + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml new file mode 100644 index 000000000..cdfa7a05c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml @@ -0,0 +1,89 @@ +import QtQuick +import qs.modules.common +import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../common/widgets/shapes/geometry/offset.js" as Offset + +HAbstractMorphedPanel { + id: root + + // Own props + property int barHeight: Appearance.sizes.baseBarHeight + function getRounding(cornerStyle) { + switch(cornerStyle) { + case 0: return Appearance.rounding.screenRounding; + case 1: return Appearance.rounding.windowRounding; + case 2: return 0; + default: return Appearance.rounding.screenRounding; + } + } + function getEdgeGap(cornerStyle) { + switch(cornerStyle) { + case 0: return 0; + case 1: return Appearance.sizes.hyprlandGapsOut; + case 2: return 0; + default: return 0; + } + } + function getEdgeRounding(cornerStyle) { + switch(cornerStyle) { + case 0: return 0; + case 1: return Appearance.rounding.windowRounding; + case 2: return 0; + default: return Appearance.rounding.windowRounding; + } + } + function getHug(cornerStyle) { + return cornerStyle === 0; + } + + // Some info + reservedTop: barHeight + getEdgeGap(Config.options.bar.cornerStyle) + + // Background + backgroundPolygon: { + // It's certainly cleaner to have the below props declared outside, but we do this + // to make sure config change only makes this re-evaluate exactly once + const cornerStyle = Config.options.bar.cornerStyle + const rounding = root.getRounding(cornerStyle) + const edgeGap = root.getEdgeGap(cornerStyle) + const edgeRounding = root.getEdgeRounding(cornerStyle) + const hug = root.getHug(cornerStyle) + const points = [ + + // bottom-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, root.barHeight + edgeGap), new CornerRounding.CornerRounding(0)), + + // bottom-left /|| + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap + barHeight), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + barHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + barHeight + rounding * (hug ? 1 : -1)), new CornerRounding.CornerRounding(edgeRounding)), + // top-left |/- + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap), new CornerRounding.CornerRounding(0)), + + // top-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap), new CornerRounding.CornerRounding(0)), + + // top-right -\| + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(0)), + + // bottom-right ||\ + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, root.barHeight + edgeGap + rounding * (hug ? 1 : -1)), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, root.barHeight + edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, root.barHeight + edgeGap), new CornerRounding.CornerRounding(0)), + ] + return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + barHeight / 2)) + } + + // Content + implicitHeight: barHeight + getEdgeGap(Config.options.bar.cornerStyle) * 2 + anchors { + top: parent.top + left: parent.left + right: parent.right + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml new file mode 100644 index 000000000..960693fb1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml @@ -0,0 +1,75 @@ +import QtQuick +import Quickshell.Hyprland +import qs +import qs.modules.common +import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../common/widgets/shapes/geometry/offset.js" as Offset + +HAbstractMorphedPanel { + id: root + + // Own props + property int edgeGap: Appearance.sizes.hyprlandGapsOut + property real rounding: Appearance.rounding.windowRounding + property real contentHeight: 300 // For now + + // Background + backgroundPolygon: MaterialShapes.customPolygon([ + // bottom-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(0)), + // bottom-left + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), + // top-left + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), + // top-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap), new CornerRounding.CornerRounding(0)), + // top-right + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), + + // bottom-right + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + ], 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2)) + + // Keybinds + GlobalShortcut { + name: "searchToggle" + description: "Toggles search on press" + + onPressed: { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } + GlobalShortcut { + name: "searchToggleRelease" + description: "Toggles search on release" + + onPressed: { + GlobalStates.superReleaseMightTrigger = true; + } + + onReleased: { + if (!GlobalStates.superReleaseMightTrigger) { + GlobalStates.superReleaseMightTrigger = true; + return; + } + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } + GlobalShortcut { + name: "searchToggleReleaseInterrupt" + description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + + onPressed: { + GlobalStates.superReleaseMightTrigger = false; + } + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayer.qml new file mode 100644 index 000000000..3f30e2d78 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayer.qml @@ -0,0 +1,15 @@ +import QtQuick +import Quickshell + +// The stuff that sits on the "top" layer for layershells. Not to be confused with "toplevels" as in windows. +Scope { + id: root + + Variants { + model: Quickshell.screens + delegate: HTopLayerPanel { + required property var modelData + screen: modelData + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml new file mode 100644 index 000000000..9109d4769 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -0,0 +1,123 @@ +import QtQuick +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import "../../common" +import "../../common/widgets/shapes" as S +import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../common/widgets/shapes/geometry/offset.js" as Offset + +/** + * Fullscreen layer. Uses masking to not block clicks on windows n' stuff. + */ +PanelWindow { + id: root + + ///////////////// Window ////////////////// + color: "transparent" + WlrLayershell.namespace: "quickshell:topLayerPanel" + exclusionMode: ExclusionMode.Ignore + anchors { + top: true + left: true + right: true + bottom: true + } + + mask: Region { + item: root.currentPanel + } + // HyprlandWindow.visibleMask: mask // TODO: use this later to optimize hyprland's rendering + + ///////////////// Content ////////////////// + + property alias roundedPolygon: backgroundShape.roundedPolygon + S.ShapeCanvas { + id: backgroundShape + anchors.fill: parent + polygonIsNormalized: false + roundedPolygon: MaterialShapes.customPolygon([new MaterialShapes.PointNRound(new Offset.Offset(root.screen.width, 0), new CornerRounding.CornerRounding(9999)),]) + animation: NumberAnimation { + duration: 500 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + } + // animation: SpringAnimation { + // spring: 3.5 + // damping: 0.3 + // } + color: Appearance.colors.colLayer0 + borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1 + borderColor: Appearance.colors.colLayer0Border + visible: false // cuz there's already the shadow + // debug: true + } + DropShadow { + id: shadow + source: backgroundShape + anchors.fill: backgroundShape + radius: 10 + samples: radius * 2 + 1 // Ideally radius * 2 + 1, see qt docs + color: "#44000000" + } + + property HAbstractMorphedPanel currentPanel: bar + roundedPolygon: currentPanel.backgroundPolygon + + // Do we want to have reserved area always follow the bar or maybe differ per panel? + EdgeReservedArea { + anchors.top: true + exclusiveZone: bar.reservedTop + } + EdgeReservedArea { + anchors.bottom: true + exclusiveZone: bar.reservedBottom + } + EdgeReservedArea { + anchors.left: true + exclusiveZone: bar.reservedLeft + } + EdgeReservedArea { + anchors.right: true + exclusiveZone: bar.reservedRight + } + + ////////////// Content: Panels /////////////// + + HBar { + id: bar + screenWidth: root.width + screenHeight: root.height + } + + HOverview { + id: overview + screenWidth: root.width + screenHeight: root.height + } + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (GlobalStates.overviewOpen) { + currentPanel = overview; + } else { + currentPanel = bar; + } + } + } + + //////////////// Components ///////////////// + + component EdgeReservedArea: PanelWindow { + WlrLayershell.namespace: "quickshell:edgeReservedArea" + implicitWidth: 0 + implicitHeight: 0 + mask: Region { + item: null + } + } +} diff --git a/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml b/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml new file mode 100644 index 000000000..62e969f2f --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml @@ -0,0 +1,48 @@ +import QtQuick +import Quickshell + +import qs.modules.common +import qs.modules.ii.background +import qs.modules.ii.cheatsheet +import qs.modules.ii.dock +import qs.modules.ii.lock +import qs.modules.ii.mediaControls +import qs.modules.ii.notificationPopup +import qs.modules.ii.onScreenDisplay +import qs.modules.ii.onScreenKeyboard +import qs.modules.ii.overview +import qs.modules.ii.polkit +import qs.modules.ii.regionSelector +import qs.modules.ii.screenCorners +import qs.modules.ii.sessionScreen +import qs.modules.ii.sidebarLeft +import qs.modules.ii.sidebarRight +import qs.modules.ii.overlay +import qs.modules.ii.verticalBar +import qs.modules.ii.wallpaperSelector + +import qs.modules.hefty.topLayer + +Scope { + // New + PanelLoader { component: HTopLayer {} } + + // Fallbacks + PanelLoader { component: Background {} } + PanelLoader { component: Cheatsheet {} } + PanelLoader { extraCondition: Config.options.dock.enable; component: Dock {} } + PanelLoader { component: Lock {} } + PanelLoader { component: MediaControls {} } + PanelLoader { component: NotificationPopup {} } + PanelLoader { component: OnScreenDisplay {} } + PanelLoader { component: OnScreenKeyboard {} } + PanelLoader { component: Overlay {} } + // PanelLoader { component: Overview {} } + PanelLoader { component: Polkit {} } + PanelLoader { component: RegionSelector {} } + PanelLoader { component: ScreenCorners {} } + PanelLoader { component: SessionScreen {} } + PanelLoader { component: SidebarLeft {} } + PanelLoader { component: SidebarRight {} } + PanelLoader { component: WallpaperSelector {} } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index a1cb57baa..96d469c03 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -34,7 +34,7 @@ ShellRoot { // Panel families - property list families: ["ii", "waffle"] + property list families: ["ii", "waffle", "hefty"] function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) const nextIndex = (currentIndex + 1) % families.length @@ -57,6 +57,11 @@ ShellRoot { component: WaffleFamily {} } + PanelFamilyLoader { + identifier: "hefty" + component: HeftyHypeFamily {} + } + // Shortcuts IpcHandler { From d1988bef0222771ffda683ed9190d50e3f6d3c22 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:27:39 +0100 Subject: [PATCH 002/104] make bar work with bottom position --- .../hefty/topLayer/HAbstractMorphedPanel.qml | 8 +- .../ii/modules/hefty/topLayer/HBar.qml | 79 +++++++++++++++---- .../ii/modules/hefty/topLayer/HOverview.qml | 37 ++++++--- .../modules/hefty/topLayer/HTopLayerPanel.qml | 27 ++----- 4 files changed, 100 insertions(+), 51 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml index 53b03769a..2dd545f96 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml @@ -15,8 +15,12 @@ Item { id: root // To be fed - required property int screenWidth - required property int screenHeight + property int screenWidth: QsWindow.window.width + property int screenHeight: QsWindow.window.height + + // Signals + signal requestFocus() + signal dismissed() // Some info property int reservedTop: 0 diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml index cdfa7a05c..d3ba215da 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml @@ -36,45 +36,71 @@ HAbstractMorphedPanel { function getHug(cornerStyle) { return cornerStyle === 0; } + property int reservedArea: barHeight + getEdgeGap(Config.options.bar.cornerStyle) // Some info - reservedTop: barHeight + getEdgeGap(Config.options.bar.cornerStyle) + reservedTop: Config.options.bar.bottom ? 0 : reservedArea + reservedBottom: Config.options.bar.bottom ? reservedArea : 0 // Background backgroundPolygon: { // It's certainly cleaner to have the below props declared outside, but we do this - // to make sure config change only makes this re-evaluate exactly once + // to make sure a config change only makes this re-evaluate exactly once + const bottom = Config.options.bar.bottom const cornerStyle = Config.options.bar.cornerStyle const rounding = root.getRounding(cornerStyle) const edgeGap = root.getEdgeGap(cornerStyle) const edgeRounding = root.getEdgeRounding(cornerStyle) const hug = root.getHug(cornerStyle) - const points = [ + const xLeft = edgeGap + const xRight = root.screenWidth - edgeGap + const yTop = bottom ? (root.screenHeight - edgeGap - barHeight) : edgeGap + const yBottom = bottom ? (root.screenHeight - edgeGap) : (edgeGap + barHeight) + const topRounding = bottom ? rounding : edgeRounding + const bottomRounding = bottom ? edgeRounding : rounding + var topCornerDirection, bottomCornerDirection; + if (cornerStyle === 2) { // Rect + topCornerDirection = 0; + bottomCornerDirection = 0; + } else if (cornerStyle === 1) { // Rect + topCornerDirection = 1; + bottomCornerDirection = -1; + } else { // Hug + topCornerDirection = bottom ? -1 : 1; + bottomCornerDirection = bottom ? -1 : 1; + } + const points = [ // bottom-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, root.barHeight + edgeGap), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 1/2, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.1, yBottom), new CornerRounding.CornerRounding(0)), // bottom-left /|| - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap + barHeight), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + barHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + barHeight + rounding * (hug ? 1 : -1)), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom), new CornerRounding.CornerRounding(bottomRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom + rounding * bottomCornerDirection), new CornerRounding.CornerRounding(edgeRounding)), // top-left |/- - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap), new CornerRounding.CornerRounding(edgeRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + rounding * topCornerDirection), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop), new CornerRounding.CornerRounding(topRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding, yTop), new CornerRounding.CornerRounding(0)), // top-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.1, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 1/2, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.9, yTop), new CornerRounding.CornerRounding(0)), // top-right -\| - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap), new CornerRounding.CornerRounding(edgeRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight - rounding, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop), new CornerRounding.CornerRounding(topRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + rounding * topCornerDirection), new CornerRounding.CornerRounding(0)), // bottom-right ||\ - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, root.barHeight + edgeGap + rounding * (hug ? 1 : -1)), new CornerRounding.CornerRounding(edgeRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, root.barHeight + edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, root.barHeight + edgeGap), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom + rounding * bottomCornerDirection), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom), new CornerRounding.CornerRounding(bottomRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight - rounding, yBottom), new CornerRounding.CornerRounding(0)), + + // bottom-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.9, yBottom), new CornerRounding.CornerRounding(0)), ] return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + barHeight / 2)) } @@ -83,7 +109,26 @@ HAbstractMorphedPanel { implicitHeight: barHeight + getEdgeGap(Config.options.bar.cornerStyle) * 2 anchors { top: parent.top + bottom: undefined left: parent.left right: parent.right } + states: State { + name: "bottom" + when: Config.options.bar.bottom + AnchorChanges { + target: root + anchors.top: undefined + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + } + } + transitions: Transition { + AnchorAnimation { + duration: 500 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + } + } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml index 960693fb1..30c71b995 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml @@ -13,30 +13,32 @@ HAbstractMorphedPanel { property int edgeGap: Appearance.sizes.hyprlandGapsOut property real rounding: Appearance.rounding.windowRounding property real contentHeight: 300 // For now + property real contentWidth: root.screenWidth * 0.9 + property real horizontalGap: (root.screenWidth - contentWidth) / 2 // Background backgroundPolygon: MaterialShapes.customPolygon([ // bottom-middle new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(0)), // bottom-left - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), // top-left - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(edgeGap + rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), // top-middle new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap), new CornerRounding.CornerRounding(0)), // top-right - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), // bottom-right - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - edgeGap - rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), ], 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2)) // Keybinds @@ -72,4 +74,15 @@ HAbstractMorphedPanel { GlobalStates.superReleaseMightTrigger = false; } } + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (GlobalStates.overviewOpen) { + root.requestFocus(); + } else { + root.dismissed(); + } + } + } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index 9109d4769..526ed653a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -45,15 +45,11 @@ PanelWindow { easing.type: Easing.BezierSpline easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial } - // animation: SpringAnimation { - // spring: 3.5 - // damping: 0.3 - // } color: Appearance.colors.colLayer0 borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1 borderColor: Appearance.colors.colLayer0Border visible: false // cuz there's already the shadow - // debug: true + debug: true } DropShadow { id: shadow @@ -87,27 +83,18 @@ PanelWindow { ////////////// Content: Panels /////////////// + function dismiss() { + root.currentPanel = bar; + } + HBar { id: bar - screenWidth: root.width - screenHeight: root.height } HOverview { id: overview - screenWidth: root.width - screenHeight: root.height - } - - Connections { - target: GlobalStates - function onOverviewOpenChanged() { - if (GlobalStates.overviewOpen) { - currentPanel = overview; - } else { - currentPanel = bar; - } - } + onRequestFocus: root.currentPanel = overview; + onDismissed: root.dismiss(); } //////////////// Components ///////////////// From fb13cc1a2148545e18e06fabe782bacc97c2fd15 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 5 Jan 2026 23:23:10 +0100 Subject: [PATCH 003/104] hefty: make overview bg work with bottom position --- .../ii/modules/common/widgets/shapes | 2 +- .../ii/modules/hefty/topLayer/HOverview.qml | 50 +++++++++++-------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/shapes b/dots/.config/quickshell/ii/modules/common/widgets/shapes index 5916b5ae6..b62e4482a 160000 --- a/dots/.config/quickshell/ii/modules/common/widgets/shapes +++ b/dots/.config/quickshell/ii/modules/common/widgets/shapes @@ -1 +1 @@ -Subproject commit 5916b5ae6f4a3d9c351ed5c78ae24ce4013c1764 +Subproject commit b62e4482af56722840dd4b86c4dab41d5c9f2dda diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml index 30c71b995..1ebea2d10 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml @@ -17,29 +17,35 @@ HAbstractMorphedPanel { property real horizontalGap: (root.screenWidth - contentWidth) / 2 // Background - backgroundPolygon: MaterialShapes.customPolygon([ - // bottom-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(0)), - // bottom-left - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), - // top-left - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), - // top-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, edgeGap), new CornerRounding.CornerRounding(0)), - // top-right - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + rounding), new CornerRounding.CornerRounding(rounding)), + backgroundPolygon: { + const bottom = Config.options.bar.bottom + const topY = bottom ? (root.screenHeight - edgeGap - contentHeight) : edgeGap + const bottomY = bottom ? (root.screenHeight - edgeGap) : (edgeGap + contentHeight) + const points = [ + // bottom-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, bottomY), new CornerRounding.CornerRounding(0)), + // bottom-left + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, bottomY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, bottomY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, bottomY - rounding), new CornerRounding.CornerRounding(rounding)), + // top-left + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, topY + rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, topY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, topY), new CornerRounding.CornerRounding(rounding)), + // top-middle + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, topY), new CornerRounding.CornerRounding(0)), + // top-right + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, topY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, topY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, topY + rounding), new CornerRounding.CornerRounding(rounding)), - // bottom-right - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + root.contentHeight - rounding), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, edgeGap + root.contentHeight), new CornerRounding.CornerRounding(rounding)), - ], 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2)) + // bottom-right + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, bottomY - rounding), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, bottomY), new CornerRounding.CornerRounding(rounding)), + new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, bottomY), new CornerRounding.CornerRounding(rounding)), + ] + return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2)) + } // Keybinds GlobalShortcut { From 819fa81fc6377736d04ef096ffd5d284719b4eaf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 10 Jan 2026 10:42:53 +0100 Subject: [PATCH 004/104] make lock push hack work for multimonitor --- dots/.config/quickshell/ii/modules/ii/lock/Lock.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index adeaaa481..7cbd68030 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -16,7 +16,6 @@ LockScreen { } // Push everything down - property var lastWorkspaceId: 1 Variants { model: Quickshell.screens delegate: Scope { @@ -25,13 +24,16 @@ LockScreen { property string targetMonitorName: modelData.name property int verticalMovementDistance: modelData.height property int horizontalSqueeze: modelData.width * 0.2 + property int lastWorkspaceId onShouldPushChanged: { if (shouldPush) { - root.lastWorkspaceId = HyprlandData.activeWorkspace.id; + // Save workspace + print(targetMonitorName) + lastWorkspaceId = HyprlandData.monitors.find(m => m.name == targetMonitorName).activeWorkspace.id // Set anim to vertical and move to very very big workspace for a sliding up effect - Quickshell.execDetached(["hyprctl", "--batch", "keyword animation workspaces,1,7,menu_decel,slidevert; dispatch workspace 2147483647"]); + Quickshell.execDetached(["hyprctl", "--batch", `keyword animation workspaces,1,7,menu_decel,slidevert; dispatch workspace ${2147483647 - lastWorkspaceId}`]); } else { - Quickshell.execDetached(["hyprctl", "--batch", `dispatch workspace ${root.lastWorkspaceId}; reload`]); + Quickshell.execDetached(["hyprctl", "--batch", `dispatch workspace ${lastWorkspaceId}; reload`]); } } } From 28e580c2b1f20d4ff163bf7d073a84f0f7e489db Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:47:08 +0100 Subject: [PATCH 005/104] changes from main n stuff --- dots/.config/quickshell/ii/GlobalStates.qml | 20 ---- .../quickshell/ii/modules/common/Config.qml | 26 +--- .../ii/modules/common/Directories.qml | 17 ++- .../ii/modules/common/config/HeftyConfig.qml | 11 ++ .../ii/modules/common/config/WaffleConfig.qml | 24 ++++ .../ii/modules/common/widgets/Colorizer.qml | 14 +++ .../modules/common/widgets/FadeLazyLoader.qml | 25 ++++ .../modules/common/widgets/FallbackLoader.qml | 25 ++++ .../common/widgets/NotificationGroup.qml | 3 +- .../hefty/topLayer/HAbstractMorphedPanel.qml | 4 +- .../modules/hefty/topLayer/HTopLayerPanel.qml | 33 +++++- .../modules/hefty/topLayer/{ => bar}/HBar.qml | 20 +++- .../hefty/topLayer/bar/HBarContent.qml | 39 ++++++ .../ii/modules/ii/bar/BarContent.qml | 6 +- .../ii/modules/ii/bar/LeftSidebarButton.qml | 5 + .../ii/modules/ii/bar/Workspaces.qml | 20 ++-- .../ii/modules/ii/dock/DockApps.qml | 2 +- .../ii/modules/ii/lock/LockSurface.qml | 7 +- .../ii/modules/ii/lock/PasswordChars.qml | 47 ++++++-- .../modules/ii/regionSelector/CursorGuide.qml | 112 ++++++++++++++++++ .../ii/regionSelector/OptionsToolbar.qml | 43 ------- .../RectCornersSelectionDetails.qml | 40 +++++-- .../ii/regionSelector/RegionSelection.qml | 12 +- .../ii/sidebarLeft/SidebarLeftContent.qml | 17 ++- .../ii/sidebarRight/BottomWidgetGroup.qml | 4 +- .../modules/ii/sidebarRight/SidebarRight.qml | 1 + .../bluetoothDevices/BluetoothDeviceItem.qml | 1 + .../wifiNetworks/WifiNetworkItem.qml | 1 + .../bluetooth/BluetoothDeviceItem.qml | 1 + .../actionCenter/wifi/WWifiNetworkItem.qml | 1 + .../quickshell/ii/services/Brightness.qml | 6 +- .../quickshell/ii/services/LauncherSearch.qml | 12 +- dots/.config/quickshell/ii/shell.qml | 4 +- 33 files changed, 450 insertions(+), 153 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml create mode 100644 dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/Colorizer.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/FadeLazyLoader.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/FallbackLoader.qml rename dots/.config/quickshell/ii/modules/hefty/topLayer/{ => bar}/HBar.qml (91%) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml create mode 100644 dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml diff --git a/dots/.config/quickshell/ii/GlobalStates.qml b/dots/.config/quickshell/ii/GlobalStates.qml index 85a0414d6..ba680220b 100644 --- a/dots/.config/quickshell/ii/GlobalStates.qml +++ b/dots/.config/quickshell/ii/GlobalStates.qml @@ -37,14 +37,6 @@ Singleton { } } - property real screenZoom: 1 - onScreenZoomChanged: { - Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]); - } - Behavior on screenZoom { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - GlobalShortcut { name: "workspaceNumber" description: "Hold to show workspace numbers, release to show icons" @@ -56,16 +48,4 @@ Singleton { root.superDown = false } } - - IpcHandler { - target: "zoom" - - function zoomIn() { - screenZoom = Math.min(screenZoom + 0.4, 3.0) - } - - function zoomOut() { - screenZoom = Math.max(screenZoom - 0.4, 1) - } - } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 4b4f54f92..fc418675d 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -3,7 +3,9 @@ pragma ComponentBehavior: Bound import QtQuick import Quickshell import Quickshell.Io -import qs.modules.common.functions + +import "functions" +import "config" Singleton { id: root @@ -603,26 +605,8 @@ Singleton { } } - property JsonObject waffles: JsonObject { - // Some spots are kinda janky/awkward. Setting the following to - // false will make (some) stuff also be like that for accuracy. - // Example: the right-click menu of the Start button - property JsonObject tweaks: JsonObject { - property bool switchHandlePositionFix: true - property bool smootherMenuAnimations: true - property bool smootherSearchBar: true - } - property JsonObject bar: JsonObject { - property bool bottom: true - property bool leftAlignApps: false - } - property JsonObject actionCenter: JsonObject { - property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ] - } - property JsonObject calendar: JsonObject { - property bool force2CharDayOfWeek: true - } - } + property JsonObject hefty: HeftyConfig {} + property JsonObject waffles: WaffleConfig {} } } } diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 78b0a9edb..f0dcc8a30 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -20,7 +20,8 @@ Singleton { readonly property string music: StandardPaths.standardLocations(StandardPaths.MusicLocation)[0] readonly property string videos: StandardPaths.standardLocations(StandardPaths.MoviesLocation)[0] - // Other dirs used by the shell, without "file://" + /////// Stuff below are without "file://" ///////// + // General property string assetsPath: Quickshell.shellPath("assets") property string scriptPath: Quickshell.shellPath("scripts") property string favicons: FileUtils.trimFileProtocol(`${Directories.cache}/media/favicons`) @@ -30,9 +31,6 @@ Singleton { property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework") property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️") property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/media/latex`) - property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`) - property string shellConfigName: "config.json" - property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}` property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`) property string notesPath: FileUtils.trimFileProtocol(`${Directories.state}/user/notes.txt`) property string conflictCachePath: FileUtils.trimFileProtocol(`${Directories.cache}/conflict-killer`) @@ -43,14 +41,21 @@ Singleton { property string screenshotTemp: "/tmp/quickshell/media/screenshot" property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`) property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts") - property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`) - property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`) property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`) property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`) property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`) property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`) property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`) property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`) + + // User + property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`) + property string shellConfigName: "config.json" + property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}` + property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`) + property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`) + property string userComponents: FileUtils.trimFileProtocol(`${Directories.shellConfig}/components`) + // Cleanup on init Component.onCompleted: { Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml new file mode 100644 index 000000000..57a18fe86 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -0,0 +1,11 @@ +import QtQuick +import Quickshell +import Quickshell.Io + +JsonObject { + property JsonObject bar: JsonObject { + property list leftWidgets: [] + property list centerWidgets: [] + property list rightWidgets: [] + } +} diff --git a/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml b/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml new file mode 100644 index 000000000..9509ff166 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml @@ -0,0 +1,24 @@ +import QtQuick +import Quickshell +import Quickshell.Io + +JsonObject { + // Some spots are kinda janky/awkward. Setting the following to + // false will make (some) stuff also be like that for accuracy. + // Example: the right-click menu of the Start button + property JsonObject tweaks: JsonObject { + property bool switchHandlePositionFix: true + property bool smootherMenuAnimations: true + property bool smootherSearchBar: true + } + property JsonObject bar: JsonObject { + property bool bottom: true + property bool leftAlignApps: false + } + property JsonObject actionCenter: JsonObject { + property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ] + } + property JsonObject calendar: JsonObject { + property bool force2CharDayOfWeek: true + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Colorizer.qml b/dots/.config/quickshell/ii/modules/common/widgets/Colorizer.qml new file mode 100644 index 000000000..032d59eb1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/Colorizer.qml @@ -0,0 +1,14 @@ +import QtQuick +import QtQuick.Effects +import qs.modules.common + +MultiEffect { + property color sourceColor: "black" + + colorization: 1 + brightness: 1 - sourceColor.hslLightness + + Behavior on colorizationColor { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FadeLazyLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/FadeLazyLoader.qml new file mode 100644 index 000000000..f826a9fbc --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/FadeLazyLoader.qml @@ -0,0 +1,25 @@ +import QtQuick +import Quickshell +import qs.modules.common + +Item { + id: root + + property alias load: loader.activeAsync + property bool shown: true // By default show immediately when loaded + property alias component: loader.component + + property alias fade: opacityBehavior.enabled + property alias animation: opacityBehavior.animation + + opacity: loader.active && shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + id: opacityBehavior + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + LazyLoader { + id: loader + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FallbackLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/FallbackLoader.qml new file mode 100644 index 000000000..0da12fbb7 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/FallbackLoader.qml @@ -0,0 +1,25 @@ +import QtQuick + +Loader { + id: root + + property int fallbackIndex: 0 + property list fallbacks: [] + property list fallbackComponents: [] + + onStatusChanged: { + if (status === Loader.Error && fallbackIndex < fallbacks.length) { + if (fallbacks[fallbackIndex]) { + source = fallbacks[fallbackIndex]; + if (fallbackComponents[fallbackIndex]) { + console.warn("[FallbackLoader] Both fallbacks urls and components are set, using url fallback"); + } + } else if (fallbackComponents[fallbackIndex]) { + sourceComponent = fallbackComponents[fallbackIndex]; + } else { + console.error("[FallbackLoader] Out of fallbacks, tried all", fallbackIndex); + } + fallbackIndex += 1; + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index e3d64027b..8802aaf69 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -85,7 +85,7 @@ MouseArea { // Notification group area automaticallyReset: false acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - onPressed: { + onPressed: (mouse) => { if (mouse.button === Qt.RightButton) root.toggleExpanded(); } @@ -102,6 +102,7 @@ MouseArea { // Notification group area } onDragDiffXChanged: () => { + if (!dragging) return; root.qmlParent.dragDistance = dragDiffX; } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml index 2dd545f96..5eb6e21aa 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml @@ -18,9 +18,11 @@ Item { property int screenWidth: QsWindow.window.width property int screenHeight: QsWindow.window.height - // Signals + // Signals & loading signal requestFocus() signal dismissed() + property bool load: true + property bool shown: true // Some info property int reservedTop: 0 diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index 526ed653a..a96129f7f 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -10,6 +10,8 @@ import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding import "../../common/widgets/shapes/geometry/offset.js" as Offset +import "bar" + /** * Fullscreen layer. Uses masking to not block clicks on windows n' stuff. */ @@ -35,15 +37,31 @@ PanelWindow { ///////////////// Content ////////////////// property alias roundedPolygon: backgroundShape.roundedPolygon + property bool finishedMorphing: true + onRoundedPolygonChanged: finishedMorphing = false + Connections { + target: backgroundShape + function onProgressChanged() { + // While it overshoots because of the spring animation, waiting for the bounce to finish entirely would be too slow + // ^ (totally not an excuse for my laziness) + if (backgroundShape.progress >= 1.0) { + root.finishedMorphing = true + } + } + } S.ShapeCanvas { id: backgroundShape anchors.fill: parent polygonIsNormalized: false roundedPolygon: MaterialShapes.customPolygon([new MaterialShapes.PointNRound(new Offset.Offset(root.screen.width, 0), new CornerRounding.CornerRounding(9999)),]) - animation: NumberAnimation { - duration: 500 - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + // animation: NumberAnimation { + // duration: 500 + // easing.type: Easing.BezierSpline + // easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + // } + animation: SpringAnimation { + spring: 3.5 + damping: 0.35 } color: Appearance.colors.colLayer0 borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1 @@ -60,7 +78,8 @@ PanelWindow { color: "#44000000" } - property HAbstractMorphedPanel currentPanel: bar + property HAbstractMorphedPanel currentPanel: null + Component.onCompleted: currentPanel = bar roundedPolygon: currentPanel.backgroundPolygon // Do we want to have reserved area always follow the bar or maybe differ per panel? @@ -89,10 +108,14 @@ PanelWindow { HBar { id: bar + load: root.currentPanel === this + shown: root.finishedMorphing } HOverview { id: overview + load: root.currentPanel === this + shown: root.finishedMorphing onRequestFocus: root.currentPanel = overview; onDismissed: root.dismiss(); } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml similarity index 91% rename from dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml rename to dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml index d3ba215da..8da64c902 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HBar.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml @@ -1,8 +1,11 @@ +pragma ComponentBehavior: Bound import QtQuick import qs.modules.common -import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes -import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding -import "../../common/widgets/shapes/geometry/offset.js" as Offset +import qs.modules.common.widgets +import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../../common/widgets/shapes/geometry/offset.js" as Offset +import ".." HAbstractMorphedPanel { id: root @@ -131,4 +134,15 @@ HAbstractMorphedPanel { easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial } } + + FadeLazyLoader { + id: contentLoader + load: root.load + shown: root.shown + anchors.fill: parent + component: HBarContent { + parent: contentLoader + anchors.fill: parent + } + } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml new file mode 100644 index 000000000..6db44b69e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -0,0 +1,39 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common.widgets + +Item { + id: root + + Side { + id: leftSide + anchors.left: parent.left + width: (parent.width - centerSide.width) / 2 + } + + Side { + id: centerSide + anchors.horizontalCenter: parent.horizontalCenter + + FallbackLoader { + asynchronous: true + source: "/home/end/.config/quickshell/ii/modules/ii/bar/WrongModuleName" + fallbacks: ["/home/end/.config/quickshell/ii/modules/ii/bar/Workspaces.qml"] + } + } + + Side { + id: rightSide + anchors.right: parent.right + width: (parent.width - centerSide.width) / 2 + } + + component Side: RowLayout { + anchors { + top: parent.top + bottom: parent.bottom + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml index bcca26740..2a72e3382 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml @@ -80,19 +80,21 @@ Item { // Bar content region RowLayout { id: leftSectionRowLayout anchors.fill: parent - spacing: 10 + spacing: 0 LeftSidebarButton { // Left sidebar button + id: leftSidebarButton Layout.alignment: Qt.AlignVCenter Layout.leftMargin: Appearance.rounding.screenRounding colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) } ActiveWindow { - visible: root.useShortenedForm === 0 + Layout.leftMargin: 10 + (leftSidebarButton.visible ? 0 : Appearance.rounding.screenRounding) Layout.rightMargin: Appearance.rounding.screenRounding Layout.fillWidth: true Layout.fillHeight: true + visible: root.useShortenedForm === 0 } } } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml index 5ea1bf5a9..6c73ca72e 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml @@ -9,6 +9,11 @@ RippleButton { property bool showPing: false + property bool aiChatEnabled: Config.options.policies.ai !== 0 + property bool translatorEnabled: Config.options.sidebar.translator.enable + property bool animeEnabled: Config.options.policies.weeb !== 0 + visible: aiChatEnabled || translatorEnabled || animeEnabled + property real buttonPadding: 5 implicitWidth: distroIcon.width + buttonPadding * 2 implicitHeight: distroIcon.height + buttonPadding * 2 diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index f0a61ef23..0ac6e3ea7 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -181,6 +181,7 @@ Item { // Workspaces - numbers Grid { + id: wsNumbers z: 3 columns: root.vertical ? 1 : root.workspacesShown @@ -209,6 +210,11 @@ Item { property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue) property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + property color numberColor: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + Appearance.m3colors.m3onPrimary : + (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : + Appearance.colors.colOnLayer1Inactive) + StyledText { // Workspace number text opacity: root.showNumbers || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers)) @@ -225,10 +231,7 @@ Item { } text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue elide: Text.ElideRight - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? - Appearance.m3colors.m3onPrimary : - (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : - Appearance.colors.colOnLayer1Inactive) + color: workspaceButtonBackground.numberColor Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) @@ -245,10 +248,7 @@ Item { width: workspaceButtonWidth * 0.18 height: width radius: width / 2 - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? - Appearance.m3colors.m3onPrimary : - (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : - Appearance.colors.colOnLayer1Inactive) + color: workspaceButtonBackground.numberColor Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) @@ -308,12 +308,8 @@ Item { } } } - - } - } - } } diff --git a/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml b/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml index d575b7dd4..6e5e4cbc5 100644 --- a/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml +++ b/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml @@ -133,7 +133,7 @@ Item { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } clip: true - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer radius: Appearance.rounding.normal anchors.bottom: parent.bottom anchors.bottomMargin: Appearance.sizes.elevationMargin diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml index b2482132d..82d10260a 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml @@ -136,6 +136,8 @@ MouseArea { // Style clip: true font.pixelSize: Appearance.font.pixelSize.small + selectedTextColor: materialShapeChars ? "transparent" : Appearance.colors.colOnSecondaryContainer + selectionColor: materialShapeChars ? "transparent" : Appearance.colors.colSecondaryContainer // Password enabled: !root.context.unlockInProgress @@ -195,6 +197,9 @@ MouseArea { } sourceComponent: PasswordChars { length: root.context.currentText.length + selectionStart: passwordBox.selectionStart + selectionEnd: passwordBox.selectionEnd + cursorPosition: passwordBox.cursorPosition } } } @@ -215,7 +220,7 @@ MouseArea { iconSize: 24 text: { if (root.context.targetAction === LockContext.ActionEnum.Unlock) { - return root.ctrlHeld ? "emoji_food_beverage" : "arrow_right_alt"; + return root.ctrlHeld ? "coffee" : "arrow_right_alt"; } else if (root.context.targetAction === LockContext.ActionEnum.Poweroff) { return "power_settings_new"; } else if (root.context.targetAction === LockContext.ActionEnum.Reboot) { diff --git a/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml b/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml index 400c2495a..07c856c05 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml @@ -9,29 +9,62 @@ import Quickshell StyledFlickable { id: root + required property int length + property int selectionStart + property int selectionEnd + property int cursorPosition + + property color color: Appearance.colors.colPrimary + property color selectedTextColor: Appearance.colors.colOnSecondaryContainer + property color selectionColor: Appearance.colors.colSecondaryContainer + + property int charSize: 20 + contentWidth: dotsRow.implicitWidth contentX: (Math.max(contentWidth - width, 0)) Behavior on contentX { animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + + Rectangle { + id: cursor + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: root.charSize * root.cursorPosition + } + color: root.color + implicitWidth: 2 + implicitHeight: root.charSize + Behavior on anchors.leftMargin { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(cursor) + } + } + Row { id: dotsRow anchors { left: parent.left verticalCenter: parent.verticalCenter - leftMargin: 4 + leftMargin: 4 - 5 // -5 to account for spacing being simulated by char item width } - spacing: 10 + spacing: 0 + Repeater { - model: ScriptModel { + model: ScriptModel { // TODO: use proper custom object model to insert new char at the correct pos values: Array(root.length) } - delegate: Item { + + delegate: Rectangle { id: charItem required property int index - implicitWidth: 10 - implicitHeight: 10 + implicitWidth: root.charSize + implicitHeight: root.charSize + property bool selected: index >= root.selectionStart && index < root.selectionEnd + + color: ColorUtils.transparentize(root.selectionColor, selected ? 0 : 1) + MaterialShape { id: materialShape anchors.centerIn: parent @@ -46,7 +79,7 @@ StyledFlickable { ] shape: charShapes[charItem.index % charShapes.length] // Animate on appearance - color: Appearance.colors.colPrimary + color: charItem.selected ? root.selectedTextColor : root.color implicitSize: 0 opacity: 0 scale: 0.5 diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml new file mode 100644 index 000000000..0cbec1297 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml @@ -0,0 +1,112 @@ +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import QtQuick + +Item { + id: root + property var action + property var selectionMode + + property string description: switch (root.action) { + case RegionSelection.SnipAction.Copy: + case RegionSelection.SnipAction.Edit: + return Translation.tr("Copy region (LMB) or annotate (RMB)"); + case RegionSelection.SnipAction.Search: + return Translation.tr("Search with Google Lens"); + case RegionSelection.SnipAction.CharRecognition: + return Translation.tr("Recognize text"); + case RegionSelection.SnipAction.Record: + case RegionSelection.SnipAction.RecordWithSound: + return Translation.tr("Record region"); + } + property string materialSymbol: switch (root.action) { + case RegionSelection.SnipAction.Copy: + case RegionSelection.SnipAction.Edit: + return "content_cut"; + case RegionSelection.SnipAction.Search: + return "image_search"; + case RegionSelection.SnipAction.CharRecognition: + return "document_scanner"; + case RegionSelection.SnipAction.Record: + case RegionSelection.SnipAction.RecordWithSound: + return "videocam"; + default: + return ""; + } + + property bool showDescription: true + function hideDescription() { + root.showDescription = false + } + Timer { + id: descTimeout + interval: 1000 + running: true + onTriggered: { + root.hideDescription() + } + } + onActionChanged: { + root.showDescription = true + descTimeout.restart() + } + + property int margins: 8 + implicitWidth: content.implicitWidth + margins * 2 + implicitHeight: content.implicitHeight + margins * 2 + + Rectangle { + id: content + anchors.centerIn: parent + + property real padding: 8 + implicitHeight: 38 + implicitWidth: root.showDescription ? contentRow.implicitWidth + padding * 2 : implicitHeight + clip: true + + topLeftRadius: 6 + bottomLeftRadius: implicitHeight - topLeftRadius + bottomRightRadius: bottomLeftRadius + topRightRadius: bottomLeftRadius + + color: Appearance.colors.colPrimary + + Behavior on topLeftRadius { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on implicitWidth { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + + Row { + id: contentRow + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: content.padding + } + spacing: 12 + + MaterialSymbol { + anchors.verticalCenter: parent.verticalCenter + iconSize: 22 + color: Appearance.colors.colOnPrimary + animateChange: true + text: root.materialSymbol + } + + FadeLoader { + id: descriptionLoader + anchors.verticalCenter: parent.verticalCenter + shown: root.showDescription + sourceComponent: StyledText { + color: Appearance.colors.colOnPrimary + text: root.description + anchors.right: parent.right + anchors.rightMargin: 6 + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml index 63a3e8c7e..1c68886c8 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml @@ -23,48 +23,6 @@ Toolbar { // Signals signal dismiss() - MaterialShape { - Layout.fillHeight: true - Layout.leftMargin: 2 - Layout.rightMargin: 2 - implicitSize: 36 // Intentionally smaller because this one is brighter than others - shape: switch (root.action) { - case RegionSelection.SnipAction.Copy: - case RegionSelection.SnipAction.Edit: - return MaterialShape.Shape.Cookie4Sided; - case RegionSelection.SnipAction.Search: - return MaterialShape.Shape.Pentagon; - case RegionSelection.SnipAction.CharRecognition: - return MaterialShape.Shape.Sunny; - case RegionSelection.SnipAction.Record: - case RegionSelection.SnipAction.RecordWithSound: - return MaterialShape.Shape.Gem; - default: - return MaterialShape.Shape.Cookie12Sided; - } - color: Appearance.colors.colPrimary - MaterialSymbol { - anchors.centerIn: parent - iconSize: 22 - color: Appearance.colors.colOnPrimary - animateChange: true - text: switch (root.action) { - case RegionSelection.SnipAction.Copy: - case RegionSelection.SnipAction.Edit: - return "content_cut"; - case RegionSelection.SnipAction.Search: - return "image_search"; - case RegionSelection.SnipAction.CharRecognition: - return "document_scanner"; - case RegionSelection.SnipAction.Record: - case RegionSelection.SnipAction.RecordWithSound: - return "videocam"; - default: - return ""; - } - } - } - ToolbarTabBar { id: tabBar tabButtonList: [ @@ -76,5 +34,4 @@ Toolbar { root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle; } } - } diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml index 40069131d..3682e7d51 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml @@ -33,22 +33,40 @@ Item { } // Selection border - Rectangle { + // Rectangle { + // id: selectionBorder + // z: 1 + // anchors { + // left: parent.left + // top: parent.top + // leftMargin: root.regionX + // topMargin: root.regionY + // } + // width: root.regionWidth + // height: root.regionHeight + // color: "transparent" + // border.color: root.color + // border.width: 2 + // // radius: root.standardRounding + // radius: 0 // TODO: figure out how to make the overlay thing work with rounding + // } + + DashedBorder { id: selectionBorder - z: 1 + z: 9 anchors { left: parent.left top: parent.top - leftMargin: root.regionX - topMargin: root.regionY + leftMargin: Math.round(root.regionX) - borderWidth + topMargin: Math.round(root.regionY) - borderWidth } - width: root.regionWidth - height: root.regionHeight - color: "transparent" - border.color: root.color - border.width: 2 - // radius: root.standardRounding - radius: 0 // TODO: figure out how to make the overlay thing work with rounding + width: Math.round(root.regionWidth) + borderWidth * 2 + height: Math.round(root.regionHeight) + borderWidth * 2 + + color: root.color + dashLength: 6 + gapLength: 3 + borderWidth: 1 } StyledText { diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml index 920e69b56..5f5ed79ab 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml @@ -35,7 +35,7 @@ PanelWindow { signal dismiss() property string screenshotDir: Directories.screenshotTemp - property color overlayColor: "#88111111" + property color overlayColor: ColorUtils.transparentize("#000000", 0.4) property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0 property color brightSecondary: Appearance.m3colors.darkmode ? Appearance.colors.colSecondary : Appearance.colors.colOnSecondary property color brightTertiary: Appearance.m3colors.darkmode ? Appearance.colors.colTertiary : Qt.lighter(Appearance.colors.colPrimary) @@ -375,6 +375,14 @@ PanelWindow { } } + CursorGuide { + z: 9999 + x: root.dragging ? root.regionX + root.regionWidth : mouseArea.mouseX + y: root.dragging ? root.regionY + root.regionHeight : mouseArea.mouseY + action: root.action + selectionMode: root.selectionMode + } + // Window regions Repeater { model: ScriptModel { @@ -447,7 +455,7 @@ PanelWindow { // Controls Row { id: regionSelectionControls - z: 9999 + z: 10 anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml index f3ebfd356..654b46dfc 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml @@ -48,6 +48,7 @@ Item { spacing: sidebarPadding Toolbar { + visible: tabButtonList.length > 0 Layout.alignment: Qt.AlignHCenter enableShadow: false ToolbarTabBar { @@ -83,9 +84,10 @@ Item { } contentChildren: [ - ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []), + ...(root.aiChatEnabled ? [aiChat.createObject()] : []), ...(root.translatorEnabled ? [translator.createObject()] : []), - ...(root.animeEnabled ? [anime.createObject()] : []) + ...((root.tabButtonList.length === 0 || (!root.aiChatEnabled && !root.translatorEnabled && root.animeCloset)) ? [placeholder.createObject()] : []), + ...(root.animeEnabled ? [anime.createObject()] : []), ] } } @@ -102,6 +104,15 @@ Item { id: anime Anime {} } - + Component { + id: placeholder + Item { + StyledText { + anchors.centerIn: parent + text: root.animeCloset ? Translation.tr("Nothing") : Translation.tr("Enjoy your empty sidebar...") + color: Appearance.colors.colSubtext + } + } + } } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml index 4c6ea6dd9..e9c49dd4b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml @@ -138,7 +138,7 @@ Rectangle { anchors.fill: parent // implicitHeight: tabStack.implicitHeight - spacing: 10 + spacing: 20 // Navigation rail Item { @@ -146,7 +146,7 @@ Rectangle { Layout.fillWidth: false Layout.leftMargin: 10 Layout.topMargin: 10 - width: tabBar.width + implicitWidth: tabBar.implicitWidth // Navigation rail buttons NavigationRailTabArray { id: tabBar diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml index 62946cb61..69bc07c03 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml @@ -54,6 +54,7 @@ Scope { margins: Appearance.sizes.hyprlandGapsOut leftMargin: Appearance.sizes.elevationMargin } + asynchronous: true width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml index 8c713d3ca..e9d817563 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml @@ -47,6 +47,7 @@ DialogListItem { color: Appearance.colors.colOnSurfaceVariant elide: Text.ElideRight text: root.device?.name || Translation.tr("Unknown device") + textFormat: Text.PlainText } StyledText { visible: (root.device?.connected || root.device?.paired) ?? false diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml index 9f75224dd..9d411c4d9 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml @@ -40,6 +40,7 @@ DialogListItem { color: Appearance.colors.colOnSurfaceVariant elide: Text.ElideRight text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown") + textFormat: Text.PlainText } MaterialSymbol { visible: (root.wifiNetwork?.isSecure || root.wifiNetwork?.active) ?? false diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml index 73d7bee83..1d9af6c63 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml @@ -41,6 +41,7 @@ ExpandableChoiceButton { elide: Text.ElideRight font.pixelSize: Looks.font.pixelSize.large text: root.device?.name || Translation.tr("Unknown device") + textFormat: Text.PlainText } WText { // Status id: statusText diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml index 9274b2e86..ff3d09eb6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml @@ -61,6 +61,7 @@ ExpandableChoiceButton { elide: Text.ElideRight font.pixelSize: Looks.font.pixelSize.large text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown") + textFormat: Text.PlainText } WText { // Status id: statusText diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index f2f4f0a7e..b0c7f303e 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -148,14 +148,12 @@ Singleton { const brightnessValue = Math.max(monitor.multipliedBrightness, 0); if (isDdc) { const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1); - setProc.command = ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded]; - setProc.startDetached(); + setProc.exec(["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded]); } else { const valuePercentNumber = Math.floor(brightnessValue * 100); let valuePercent = `${valuePercentNumber}%`; if (valuePercentNumber == 0) valuePercent = "1"; // Prevent fully black - setProc.command = ["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"]; - setProc.startDetached(); + setProc.exec(["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"]) } } diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index 52e783e28..787da3090 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -43,9 +43,9 @@ Singleton { const actionName = fileName.replace(/\.[^/.]+$/, ""); // strip extension actions.push({ action: actionName, - execute: ((path) => (args) => { - Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]); - })(FileUtils.trimFileProtocol(filePath.toString())) + execute: (path => args => { + Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]); + })(FileUtils.trimFileProtocol(filePath.toString())) }); } } @@ -250,8 +250,8 @@ Singleton { if (!entry.runInTerminal) entry.execute(); else { - // Probably needs more proper escaping, but this will do for now - Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(entry.command.join(' '))}'`]); + print([...Config.options.apps.terminal, "-e", ...entry.command]) + Quickshell.execDetached([...Config.options.apps.terminal, "-e", ...entry.command]); } }, comment: entry.comment, @@ -267,7 +267,7 @@ Singleton { if (!action.runInTerminal) action.execute(); else { - Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(action.command.join(' '))}'`]); + Quickshell.execDetached([...Config.options.apps.terminal, "-e", ...action.command]); } } }); diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 96d469c03..689d78b6d 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -3,8 +3,8 @@ //@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic //@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000 -// Adjust this to make the shell smaller or larger -//@ pragma Env QT_SCALE_FACTOR=1 +// Remove two slashes below and adjust the value to change the UI scale +////@ pragma Env QT_SCALE_FACTOR=1 import "modules/common" import "services" From 58184f5be889d142bd8f9b42307a79ad78fae998 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:17:02 +0100 Subject: [PATCH 006/104] bar: component loader --- .../quickshell/ii/modules/common/Config.qml | 91 ++++++++++++------- .../ii/modules/common/Directories.qml | 1 + .../ii/modules/common/config/HeftyConfig.qml | 1 + .../common/widgets/StyledRectangle.qml | 21 +++++ .../common/widgets/UserFallbackLoader.qml | 17 ++++ .../hefty/topLayer/bar/HBarContent.qml | 7 +- .../hefty/topLayer/bar/HBarGroupContainer.qml | 57 ++++++++++++ .../bar/HBarUserFallbackComponentRepeater.qml | 61 +++++++++++++ .../hefty/topLayer/bar/widgets/Workspaces.qml | 11 +++ 9 files changed, 228 insertions(+), 39 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/UserFallbackLoader.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index fc418675d..54a95c8f5 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -50,7 +50,7 @@ Singleton { interval: root.readWriteDelay repeat: false onTriggered: { - configFileView.reload() + configFileView.reload(); } } @@ -59,7 +59,7 @@ Singleton { interval: root.readWriteDelay repeat: false onTriggered: { - configFileView.writeAdapter() + configFileView.writeAdapter(); } } @@ -92,12 +92,16 @@ Singleton { property string tool: "functions" // search, functions, or none property list extraModels: [ { - "api_format": "openai", // Most of the time you want "openai". Use "gemini" for Google's models + "api_format": "openai" // Most of the time you want "openai". Use "gemini" for Google's models + , "description": "This is a custom model. Edit the config to add more! | Anyway, this is DeepSeek R1 Distill LLaMA 70B", "endpoint": "https://openrouter.ai/api/v1/chat/completions", - "homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free", // Not mandatory - "icon": "spark-symbolic", // Not mandatory - "key_get_link": "https://openrouter.ai/settings/keys", // Not mandatory + "homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free" // Not mandatory + , + "icon": "spark-symbolic" // Not mandatory + , + "key_get_link": "https://openrouter.ai/settings/keys" // Not mandatory + , "key_id": "openrouter", "model": "deepseek/deepseek-r1-distill-llama-70b:free", "name": "Custom: DS R1 Dstl. LLaMA 70B", @@ -240,9 +244,15 @@ Singleton { property bool floatStyleShadow: true // Show shadow behind bar when cornerStyle == 1 (Float) property bool borderless: false // true for no grouping of items property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons + property list screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command property bool showBackground: true property bool verbose: true property bool vertical: false + property JsonObject indicators: JsonObject { + property JsonObject notifications: JsonObject { + property bool showUnreadCount: false + } + } property JsonObject resources: JsonObject { property bool alwaysShowSwap: true property bool alwaysShowCpu: true @@ -250,7 +260,9 @@ Singleton { property int swapWarningThreshold: 85 property int cpuWarningThreshold: 90 } - property list screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command + property JsonObject tooltips: JsonObject { + property bool clickToShow: false + } property JsonObject utilButtons: JsonObject { property bool showScreenSnip: true property bool showColorPicker: false @@ -260,6 +272,13 @@ Singleton { property bool showPerformanceProfileToggle: false property bool showScreenRecord: false } + property JsonObject weather: JsonObject { + property bool enable: false + property bool enableGPS: true // gps based location + property string city: "" // When 'enableGPS' is false + property bool useUSCS: false // Instead of metric (SI) units + property int fetchInterval: 10 // minutes + } property JsonObject workspaces: JsonObject { property bool monochromeIcons: true property int shown: 10 @@ -269,21 +288,6 @@ Singleton { property list numberMap: ["1", "2"] // Characters to show instead of numbers on workspace indicator property bool useNerdFont: false } - property JsonObject weather: JsonObject { - property bool enable: false - property bool enableGPS: true // gps based location - property string city: "" // When 'enableGPS' is false - property bool useUSCS: false // Instead of metric (SI) units - property int fetchInterval: 10 // minutes - } - property JsonObject indicators: JsonObject { - property JsonObject notifications: JsonObject { - property bool showUnreadCount: false - } - } - property JsonObject tooltips: JsonObject { - property bool clickToShow: false - } } property JsonObject battery: JsonObject { @@ -343,7 +347,8 @@ Singleton { property int mouseScrollFactor: 120 property int touchpadScrollFactor: 450 } - property JsonObject deadPixelWorkaround: JsonObject { // Hyprland leaves out 1 pixel on the right for interactions + property JsonObject deadPixelWorkaround: JsonObject { + // Hyprland leaves out 1 pixel on the right for interactions property bool enable: false } } @@ -358,7 +363,7 @@ Singleton { } property JsonObject launcher: JsonObject { - property list pinnedApps: [ "org.kde.dolphin", "kitty", "cmake-gui"] + property list pinnedApps: ["org.kde.dolphin", "kitty", "cmake-gui"] } property JsonObject light: JsonObject { @@ -463,7 +468,7 @@ Singleton { property bool monochromeIcons: true property bool showItemId: false property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area - property list pinnedItems: [ "Fcitx" ] + property list pinnedItems: ["Fcitx"] property bool filterPassive: true } @@ -527,12 +532,30 @@ Singleton { property JsonObject android: JsonObject { property int columns: 5 property list toggles: [ - { "size": 2, "type": "network" }, - { "size": 2, "type": "bluetooth" }, - { "size": 1, "type": "idleInhibitor" }, - { "size": 1, "type": "mic" }, - { "size": 2, "type": "audio" }, - { "size": 2, "type": "nightLight" } + { + "size": 2, + "type": "network" + }, + { + "size": 2, + "type": "bluetooth" + }, + { + "size": 1, + "type": "idleInhibitor" + }, + { + "size": 1, + "type": "mic" + }, + { + "size": 2, + "type": "audio" + }, + { + "size": 2, + "type": "nightLight" + } ] } } @@ -546,7 +569,7 @@ Singleton { } property JsonObject screenRecord: JsonObject { - property string savePath: Directories.videos.replace("file://","") // strip "file://" + property string savePath: Directories.videos.replace("file://", "") // strip "file://" } property JsonObject screenSnip: JsonObject { @@ -579,11 +602,11 @@ Singleton { property int adviseUpdateThreshold: 75 // packages property int stronglyAdviseUpdateThreshold: 200 // packages } - + property JsonObject wallpaperSelector: JsonObject { property bool useSystemFileDialog: false } - + property JsonObject windows: JsonObject { property bool showTitlebar: true // Client-side decoration for shell apps property bool centerTitle: true diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index f0dcc8a30..814d5d973 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -59,6 +59,7 @@ Singleton { // Cleanup on init Component.onCompleted: { Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) + Quickshell.execDetached(["mkdir", "-p", `${userComponents}`]) Quickshell.execDetached(["mkdir", "-p", `${favicons}`]) Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`]) Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`]) diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 57a18fe86..98091e50f 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -7,5 +7,6 @@ JsonObject { property list leftWidgets: [] property list centerWidgets: [] property list rightWidgets: [] + property bool m3ExpressiveGrouping: true } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml new file mode 100644 index 000000000..1997dafcb --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml @@ -0,0 +1,21 @@ +import QtQuick +import qs.modules.common as C + +// This is to enable future fancy styles for rectangles. Some ideas: +// - normal rounded rect +// - osk.sh +// - 3d +// i hope i actually get to this and not shrimply forget +Rectangle { + enum ContentLayer { Background, Pane, Group, Subgroup, Control } + property var contentLayer: StyledRectangle.ContentLayer.Pane // To appropriately add effects like shadows/3d-ization + + color: switch(contentLayer) { + case StyledRectangle.ContentLayer.Background: C.Appearance.colors.colLayer0; + case StyledRectangle.ContentLayer.Pane: C.Appearance.colors.colLayer1; + case StyledRectangle.ContentLayer.Group: C.Appearance.colors.colLayer2; + case StyledRectangle.ContentLayer.Subgroup: C.Appearance.colors.colLayer3; + case StyledRectangle.ContentLayer.Control: C.Appearance.colors.colLayer4; + default: C.Appearance.colors.colLayer1; + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/UserFallbackLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/UserFallbackLoader.qml new file mode 100644 index 000000000..6e9f89c1e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/UserFallbackLoader.qml @@ -0,0 +1,17 @@ +import QtQuick +import qs.modules.common as C + +FallbackLoader { + id: root + + required property string componentName + property string context // Path for the builtin component + + readonly property string componentNameWithExt: componentName.endsWith(".qml") ? componentName : `${componentName}.qml` + + source: `${C.Directories.userComponents}/${componentNameWithExt}` + fallbacks: [ + ...(context ? [ `${context}/${componentNameWithExt}` ] : []), + componentNameWithExt + ] +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index 6db44b69e..76bd27696 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -16,11 +16,8 @@ Item { Side { id: centerSide anchors.horizontalCenter: parent.horizontalCenter - - FallbackLoader { - asynchronous: true - source: "/home/end/.config/quickshell/ii/modules/ii/bar/WrongModuleName" - fallbacks: ["/home/end/.config/quickshell/ii/modules/ii/bar/Workspaces.qml"] + HBarUserFallbackComponentRepeater { + componentNames: [["Workspaces"]] } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml new file mode 100644 index 000000000..58a3aee0f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -0,0 +1,57 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common as C +import qs.modules.common.widgets as W + +Item { + id: root + + property bool startSide: false + property bool endSide: false + + property alias color: bg.color + property real margins: 4 + property real padding: 4 + default property alias data: layout.data + + readonly property bool vertical: C.Config.options.bar.vertical + readonly property bool m3eRadius: C.Config.options.hefty.bar.m3ExpressiveGrouping + readonly property real barUndirectionalWidth: C.Config.options.bar.vertical ? C.Appearance.sizes.baseVerticalBarWidth : C.Appearance.sizes.baseBarHeight + + implicitWidth: vertical ? barUndirectionalWidth : layout.implicitWidth + (padding + margins) * 2 + implicitHeight: vertical ? layout.implicitHeight + (padding + margins) * 2 : barUndirectionalWidth + + W.StyledRectangle { + id: bg + anchors.centerIn: parent + contentLayer: W.StyledRectangle.ContentLayer.Pane + + width: (root.vertical ? root.barUndirectionalWidth : root.width) - root.margins * 2 + height: (root.vertical ? root.height : root.barUndirectionalWidth) - root.margins * 2 + + property real fullRadius: Math.min(width, height) / 2 + function getRadius(atSide) { + if (root.m3eRadius) { + if (atSide) return fullRadius; + else return C.Appearance.rounding.unsharpenmore; + } else { + return 12; + } + } + property real startRadius: getRadius(root.startSide) + property real endRadius: getRadius(root.endSide) + topLeftRadius: startRadius + topRightRadius: root.vertical ? startRadius : endRadius + bottomLeftRadius: root.vertical ? endRadius : startRadius + bottomRightRadius: endRadius + } + + GridLayout { + id: layout + columns: C.Config.options.bar.vertical ? 1 : -1 + anchors.centerIn: parent + property real spacing: 0 + columnSpacing: spacing + rowSpacing: spacing + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml new file mode 100644 index 000000000..0961058f3 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml @@ -0,0 +1,61 @@ +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell +import qs.modules.common as C +import qs.modules.common.widgets as W + +Repeater { + id: root + + required property list componentNames + property string context: Quickshell.shellPath("modules/hefty/topLayer/bar/widgets") + + model: { + const m = componentNames.map(item => { + if (item instanceof Array) + return ({"type": "container", "value": item}); + else + return ({"type": "component", "value": item}); + }); + for (var i = 0;i < m.length; i++) { + const item = m[i]; + if (item.type === "container") { + item.startSide = (i === 0) || (m[i - 1].type !== "container"); + item.endSide = (i + 1 >= m.length) || (m[i + 1].type !== "container"); + } + } + // print(JSON.stringify(m, null, 2)); + return m; + } + delegate: DelegateChooser { + role: "type" + + DelegateChoice { + roleValue: "component" + delegate: W.UserFallbackLoader { + required property var modelData + componentName: modelData.value + context: root.context + } + } + + DelegateChoice { + roleValue: "container" + delegate: HBarGroupContainer { + id: group + required property var modelData + startSide: modelData.startSide + endSide: modelData.endSide + + Repeater { + model: group.modelData.value + delegate: W.UserFallbackLoader { + required property var modelData + componentName: modelData + context: root.context + } + } + } + } + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml new file mode 100644 index 000000000..7b0d7d026 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.ii.bar as IIBar +import qs.modules.common as C + +IIBar.Workspaces { + id: root + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + implicitWidth: root.vertical ? C.Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown - 2) + implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown - 2) : C.Appearance.sizes.barHeight +} From 5bb1aa06af1003de777b8cf37a21b04619aa88c6 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:43:09 +0100 Subject: [PATCH 007/104] hefty: bar: allow vertical --- .../quickshell/ii/modules/common/Config.qml | 19 +- .../ii/modules/common/config/HeftyConfig.qml | 2 +- .../modules/hefty/topLayer/HTopLayerPanel.qml | 2 +- .../ii/modules/hefty/topLayer/bar/HBar.qml | 211 +++++++++++++----- .../hefty/topLayer/bar/HBarContent.qml | 12 +- .../hefty/topLayer/bar/widgets/Workspaces.qml | 7 +- .../ii/modules/ii/bar/Workspaces.qml | 8 +- 7 files changed, 177 insertions(+), 84 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 54a95c8f5..64fcc7a85 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -15,6 +15,8 @@ Singleton { property int readWriteDelay: 50 // milliseconds property bool blockWrites: false + signal reloaded() + function setNestedValue(nestedKey, value) { let keys = nestedKey.split("."); let obj = root.options; @@ -70,7 +72,10 @@ Singleton { blockWrites: root.blockWrites onFileChanged: fileReloadTimer.restart() onAdapterUpdated: fileWriteTimer.restart() - onLoaded: root.ready = true + onLoaded: { + if (!root.ready) root.reloaded() + root.ready = true + } onLoadFailed: error => { if (error == FileViewError.FileNotFound) { writeAdapter(); @@ -92,16 +97,12 @@ Singleton { property string tool: "functions" // search, functions, or none property list extraModels: [ { - "api_format": "openai" // Most of the time you want "openai". Use "gemini" for Google's models - , + "api_format": "openai", // Most of the time you want "openai". Use "gemini" for Google's models "description": "This is a custom model. Edit the config to add more! | Anyway, this is DeepSeek R1 Distill LLaMA 70B", "endpoint": "https://openrouter.ai/api/v1/chat/completions", - "homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free" // Not mandatory - , - "icon": "spark-symbolic" // Not mandatory - , - "key_get_link": "https://openrouter.ai/settings/keys" // Not mandatory - , + "homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free", // Not mandatory + "icon": "spark-symbolic", // Not mandatory + "key_get_link": "https://openrouter.ai/settings/keys", // Not mandatory "key_id": "openrouter", "model": "deepseek/deepseek-r1-distill-llama-70b:free", "name": "Custom: DS R1 Dstl. LLaMA 70B", diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 98091e50f..7d765c1b2 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -5,7 +5,7 @@ import Quickshell.Io JsonObject { property JsonObject bar: JsonObject { property list leftWidgets: [] - property list centerWidgets: [] + property list centerWidgets: [["Workspaces"]] property list rightWidgets: [] property bool m3ExpressiveGrouping: true } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index a96129f7f..0a6e829f8 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -108,7 +108,7 @@ PanelWindow { HBar { id: bar - load: root.currentPanel === this + load: root.currentPanel === this && root.finishedMorphing // the extra condition is to prevent workspace widget from acting up when switching horizontal/vertical... should be fixed later shown: root.finishedMorphing } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml index 8da64c902..95eade0fe 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml @@ -10,8 +10,14 @@ import ".." HAbstractMorphedPanel { id: root + // Config + property bool vertical: Config.options.bar.vertical + property bool atBottom: Config.options.bar.bottom + property int cornerStyle: Config.options.bar.cornerStyle + // Own props property int barHeight: Appearance.sizes.baseBarHeight + property int barVerticalWidth: Appearance.sizes.baseVerticalBarWidth function getRounding(cornerStyle) { switch(cornerStyle) { case 0: return Appearance.rounding.screenRounding; @@ -39,94 +45,174 @@ HAbstractMorphedPanel { function getHug(cornerStyle) { return cornerStyle === 0; } - property int reservedArea: barHeight + getEdgeGap(Config.options.bar.cornerStyle) + property int reservedArea: (vertical ? barVerticalWidth : barHeight) + getEdgeGap(cornerStyle) // Some info - reservedTop: Config.options.bar.bottom ? 0 : reservedArea - reservedBottom: Config.options.bar.bottom ? reservedArea : 0 + reservedTop: (!atBottom && !vertical) ? reservedArea : 0 + reservedBottom: (atBottom && !vertical) ? reservedArea : 0 + reservedLeft: (!atBottom && vertical) ? reservedArea : 0 + reservedRight: (atBottom && vertical) ? reservedArea : 0 // Background - backgroundPolygon: { + function getBackgroundPolygon() { + print("Generating background polygon for HBar") // It's certainly cleaner to have the below props declared outside, but we do this // to make sure a config change only makes this re-evaluate exactly once - const bottom = Config.options.bar.bottom - const cornerStyle = Config.options.bar.cornerStyle - const rounding = root.getRounding(cornerStyle) + const bottom = root.atBottom + const vertical = root.vertical + const cornerStyle = root.cornerStyle + const hug = root.getHug(cornerStyle) const edgeGap = root.getEdgeGap(cornerStyle) const edgeRounding = root.getEdgeRounding(cornerStyle) - const hug = root.getHug(cornerStyle) - const xLeft = edgeGap - const xRight = root.screenWidth - edgeGap - const yTop = bottom ? (root.screenHeight - edgeGap - barHeight) : edgeGap - const yBottom = bottom ? (root.screenHeight - edgeGap) : (edgeGap + barHeight) - const topRounding = bottom ? rounding : edgeRounding - const bottomRounding = bottom ? edgeRounding : rounding - var topCornerDirection, bottomCornerDirection; - if (cornerStyle === 2) { // Rect - topCornerDirection = 0; - bottomCornerDirection = 0; - } else if (cornerStyle === 1) { // Rect - topCornerDirection = 1; - bottomCornerDirection = -1; + const rounding = root.getRounding(cornerStyle) + + const areaHeight = vertical ? root.screenHeight : (root.barHeight + edgeGap * 2) + const areaWidth = vertical ? (root.barVerticalWidth + edgeGap * 2) : root.screenWidth + const height = vertical ? (root.screenHeight - edgeGap * 2) : root.barHeight + const width = vertical ? root.barVerticalWidth : (root.screenWidth - edgeGap * 2) + + const xLeft = (vertical && bottom) ? (root.screenWidth - edgeGap - width) : edgeGap + const xRight = (vertical && !bottom) ? (areaWidth - edgeGap) : (root.screenWidth - edgeGap) + const yTop = (!vertical && bottom) ? (root.screenHeight - edgeGap - height) : edgeGap + const yBottom = (!vertical && !bottom) ? (areaHeight - edgeGap) : (root.screenHeight - edgeGap) + + const topLeftRounding = !bottom ? edgeRounding : rounding + const topRightRounding = !(bottom^vertical) ? edgeRounding : rounding + const bottomLeftRounding = !!(bottom^vertical) ? edgeRounding : rounding + const bottomRightRounding = bottom ? edgeRounding : rounding + + var topCornerYDirection = 0, bottomCornerYDirection = 0, leftCornerXDirection = 0, rightCornerXDirection = 0; + if (vertical) { + topCornerYDirection = 1; + bottomCornerYDirection = -1; + } else if (cornerStyle === 2) { // Rect + topCornerYDirection = 0; + bottomCornerYDirection = 0; + } else if (cornerStyle === 1) { // Rounded + topCornerYDirection = 1; + bottomCornerYDirection = -1; } else { // Hug - topCornerDirection = bottom ? -1 : 1; - bottomCornerDirection = bottom ? -1 : 1; + topCornerYDirection = bottom ? -1 : 1; + bottomCornerYDirection = bottom ? -1 : 1; } - - const points = [ + if (!vertical) { + leftCornerXDirection = 1; + rightCornerXDirection = -1 + } else if (cornerStyle === 2) { // Rect + leftCornerXDirection = 0; + rightCornerXDirection = 0; + } else if (cornerStyle === 1) { // Rounded + leftCornerXDirection = 1; + rightCornerXDirection = -1; + } else { // Hug + leftCornerXDirection = bottom ? -1 : 1; + rightCornerXDirection = bottom ? -1 : 1; + } + var points = [ // bottom-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 1/2, yBottom), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.1, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 1/2, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.1, yBottom), new CornerRounding.CornerRounding(0)), - // bottom-left /|| - new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding, yBottom), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom), new CornerRounding.CornerRounding(bottomRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom + rounding * bottomCornerDirection), new CornerRounding.CornerRounding(edgeRounding)), - // top-left |/- - new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + rounding * topCornerDirection), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop), new CornerRounding.CornerRounding(topRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding, yTop), new CornerRounding.CornerRounding(0)), + // bottom-left + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding * leftCornerXDirection, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom), new CornerRounding.CornerRounding(bottomLeftRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom + rounding * bottomCornerYDirection), new CornerRounding.CornerRounding(edgeRounding)), + + // middle-left + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 0.9), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 1/2), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 0.1), new CornerRounding.CornerRounding(0)), + + // top-left + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + rounding * topCornerYDirection), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop), new CornerRounding.CornerRounding(topLeftRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding * leftCornerXDirection, yTop), new CornerRounding.CornerRounding(0)), // top-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.1, yTop), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 1/2, yTop), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.9, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.1, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 1/2, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.9, yTop), new CornerRounding.CornerRounding(0)), - // top-right -\| - new MaterialShapes.PointNRound(new Offset.Offset(xRight - rounding, yTop), new CornerRounding.CornerRounding(0)), - new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop), new CornerRounding.CornerRounding(topRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + rounding * topCornerDirection), new CornerRounding.CornerRounding(0)), + // top-right + new MaterialShapes.PointNRound(new Offset.Offset(xRight + rounding * rightCornerXDirection, yTop), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop), new CornerRounding.CornerRounding(topRightRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + rounding * topCornerYDirection), new CornerRounding.CornerRounding(0)), - // bottom-right ||\ - new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom + rounding * bottomCornerDirection), new CornerRounding.CornerRounding(edgeRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom), new CornerRounding.CornerRounding(bottomRounding)), - new MaterialShapes.PointNRound(new Offset.Offset(xRight - rounding, yBottom), new CornerRounding.CornerRounding(0)), + // middle-right + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 0.1), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 1/2), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 0.9), new CornerRounding.CornerRounding(0)), + + // bottom-right + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom + rounding * bottomCornerYDirection), new CornerRounding.CornerRounding(edgeRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom), new CornerRounding.CornerRounding(bottomRightRounding)), + new MaterialShapes.PointNRound(new Offset.Offset(xRight + rounding * rightCornerXDirection, yBottom), new CornerRounding.CornerRounding(0)), // bottom-middle - new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth * 0.9, yBottom), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.9, yBottom), new CornerRounding.CornerRounding(0)), ] return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + barHeight / 2)) } + backgroundPolygon: getBackgroundPolygon() + Connections { + target: Config + function onReadyChanged() { + if (Config.ready) + root.backgroundPolygon = root.getBackgroundPolygon() + } + function onReloaded() { + root.extraLoadCondition = false + root.backgroundPolygon = root.getBackgroundPolygon() + root.extraLoadCondition = true + } + } // Content - implicitHeight: barHeight + getEdgeGap(Config.options.bar.cornerStyle) * 2 + implicitHeight: vertical ? screenHeight : (barHeight + getEdgeGap(cornerStyle) * 2) + implicitWidth: vertical ? (barVerticalWidth + getEdgeGap(cornerStyle) * 2) : screenWidth + width: implicitWidth + height: implicitHeight anchors { top: parent.top bottom: undefined - left: parent.left - right: parent.right + left: undefined + right: undefined } - states: State { - name: "bottom" - when: Config.options.bar.bottom - AnchorChanges { - target: root - anchors.top: undefined - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right + states: [ + State { + name: "bottom" + when: root.atBottom && !root.vertical + AnchorChanges { + target: root + anchors.top: undefined + anchors.bottom: parent.bottom + anchors.left: undefined + anchors.right: undefined + } + }, + State { + name: "left" + when: !root.atBottom && root.vertical + AnchorChanges { + target: root + anchors.top: undefined + anchors.bottom: undefined + anchors.left: parent.left + anchors.right: undefined + } + }, + State { + name: "right" + when: root.atBottom && root.vertical + AnchorChanges { + target: root + anchors.top: undefined + anchors.bottom: undefined + anchors.left: undefined + anchors.right: parent.right + } } - } + ] transitions: Transition { AnchorAnimation { duration: 500 @@ -135,10 +221,11 @@ HAbstractMorphedPanel { } } + property bool extraLoadCondition: true FadeLazyLoader { id: contentLoader - load: root.load - shown: root.shown + load: root.load && root.extraLoadCondition + shown: root.shown && root.extraLoadCondition anchors.fill: parent component: HBarContent { parent: contentLoader diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index 76bd27696..d090a9221 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -1,8 +1,6 @@ import QtQuick import QtQuick.Layouts -import Quickshell -import qs.services -import qs.modules.common.widgets +import qs.modules.common as C Item { id: root @@ -11,13 +9,16 @@ Item { id: leftSide anchors.left: parent.left width: (parent.width - centerSide.width) / 2 + HBarUserFallbackComponentRepeater { + componentNames: C.Config.options.hefty.bar.leftWidgets + } } Side { id: centerSide anchors.horizontalCenter: parent.horizontalCenter HBarUserFallbackComponentRepeater { - componentNames: [["Workspaces"]] + componentNames: C.Config.options.hefty.bar.centerWidgets } } @@ -25,6 +26,9 @@ Item { id: rightSide anchors.right: parent.right width: (parent.width - centerSide.width) / 2 + HBarUserFallbackComponentRepeater { + componentNames: C.Config.options.hefty.bar.rightWidgets + } } component Side: RowLayout { diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml index 7b0d7d026..c7141bbec 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml @@ -5,7 +5,8 @@ import qs.modules.common as C IIBar.Workspaces { id: root - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - implicitWidth: root.vertical ? C.Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown - 2) - implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown - 2) : C.Appearance.sizes.barHeight + vertical: C.Config.options.bar.vertical + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index 0ac6e3ea7..b8952b917 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -111,8 +111,8 @@ Item { rowSpacing: 0 columnSpacing: 0 - columns: root.vertical ? 1 : root.workspacesShown - rows: root.vertical ? root.workspacesShown : 1 + columns: root.vertical ? 1 : -1 + rows: root.vertical ? -1 : 1 Repeater { model: root.workspacesShown @@ -184,8 +184,8 @@ Item { id: wsNumbers z: 3 - columns: root.vertical ? 1 : root.workspacesShown - rows: root.vertical ? root.workspacesShown : 1 + columns: root.vertical ? 1 : -1 + rows: root.vertical ? -1 : 1 columnSpacing: 0 rowSpacing: 0 From ead98b98b8123e05556c1786998375738284a806 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:49:36 +0100 Subject: [PATCH 008/104] hefty: bar: workspace widget --- .../modules/common/functions/ColorUtils.qml | 5 +- .../modules/common/functions/NumberUtils.qml | 16 + .../modules/common/models/NestableObject.qml | 6 + .../modules/common/models/WorkspaceModel.qml | 59 +++ .../ii/modules/common/widgets/AppIcon.qml | 15 + .../ii/modules/common/widgets/Circle.qml | 2 +- .../common/widgets/MaskMultiEffect.qml | 9 + .../ii/modules/common/widgets/StateLayer.qml | 19 + .../modules/common/widgets/StateOverlay.qml | 53 +++ .../modules/hefty/topLayer/HTopLayerPanel.qml | 2 +- .../hefty/topLayer/bar/HBarGroupContainer.qml | 2 +- .../topLayer/bar/widgets/HWorkspaces.qml | 363 ++++++++++++++++++ .../{Workspaces.qml => IIWorkspaces.qml} | 0 .../ii/modules/ii/bar/Workspaces.qml | 99 ++--- .../ii/modules/waffle/looks/WAppIcon.qml | 9 +- 15 files changed, 587 insertions(+), 72 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/functions/NumberUtils.qml create mode 100644 dots/.config/quickshell/ii/modules/common/models/NestableObject.qml create mode 100644 dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/AppIcon.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/MaskMultiEffect.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StateLayer.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml rename dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/{Workspaces.qml => IIWorkspaces.qml} (100%) diff --git a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml index 74305c8fa..6a123d9b2 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml @@ -109,7 +109,8 @@ Singleton { */ function transparentize(color, percentage = 1) { var c = Qt.color(color); - return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage)); + var a = c.a * (1 - clamp01(percentage)); + return Qt.rgba(c.r, c.g, c.b, a); } /** @@ -121,7 +122,7 @@ Singleton { */ function applyAlpha(color, alpha) { var c = Qt.color(color); - var a = Math.max(0, Math.min(1, alpha)); + var a = clamp01(alpha); return Qt.rgba(c.r, c.g, c.b, a); } diff --git a/dots/.config/quickshell/ii/modules/common/functions/NumberUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/NumberUtils.qml new file mode 100644 index 000000000..94da6d371 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/functions/NumberUtils.qml @@ -0,0 +1,16 @@ +pragma Singleton +import Quickshell + +Singleton { + id: root + + /** + * Rounds the given number to the nearest even integer. + * + * @param {number} num - The number to round. + * @returns {number} The nearest even integer. + */ + function roundToEven(num) { + return Math.round(num / 2) * 2; + } +} diff --git a/dots/.config/quickshell/ii/modules/common/models/NestableObject.qml b/dots/.config/quickshell/ii/modules/common/models/NestableObject.qml new file mode 100644 index 000000000..50af20cb0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/NestableObject.qml @@ -0,0 +1,6 @@ +import QtQuick + +// QtObject that allows stuff to be freely declared inside +QtObject { + default property list data +} diff --git a/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml b/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml new file mode 100644 index 000000000..17b27c9f8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml @@ -0,0 +1,59 @@ +import QtQuick +import Quickshell.Wayland +import Quickshell.Hyprland +import qs.services +import qs.modules.common as C + +NestableObject { + id: root + + required property HyprlandMonitor monitor + readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + readonly property int activeWorkspace: monitor?.activeWorkspace?.id + readonly property bool currentWorkspaceNotFake: activeWindow?.activated ?? false // Active empty workspace = fake. At least, that's how I like to call it. + readonly property int fakeWorkspace: currentWorkspaceNotFake ? -9999 : activeWorkspace + readonly property int shownCount: C.Config.options.bar.workspaces.shown + readonly property int group: Math.floor((activeWorkspace - 1) / shownCount) + + property list occupied: [] + property list biggestWindow: occupied.map((_, index) => { + const wsId = getWorkspaceIdAt(index); + var biggestWindow = HyprlandData.biggestWindowForWorkspace(wsId); + return biggestWindow; + }) + + function getWorkspaceId(group, index) { + return group * root.shownCount + index + 1; + } + function getWorkspaceIdAt(index) { + return root.getWorkspaceId(root.group, index); + } + + // Function to update workspaceOccupied + function updateWorkspaceOccupied() { + root.occupied = Array.from({ + length: root.shownCount + }, (_, i) => { + const thisWorkspaceId = getWorkspaceId(root.group, i); + return Hyprland.workspaces.values.some(ws => ws.id === thisWorkspaceId); + }); + } + + // Occupied workspace updates + Component.onCompleted: updateWorkspaceOccupied() + Connections { + target: Hyprland.workspaces + function onValuesChanged() { + root.updateWorkspaceOccupied(); + } + } + Connections { + target: Hyprland + function onFocusedWorkspaceChanged() { + root.updateWorkspaceOccupied(); + } + } + onGroupChanged: { + updateWorkspaceOccupied(); + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AppIcon.qml b/dots/.config/quickshell/ii/modules/common/widgets/AppIcon.qml new file mode 100644 index 000000000..6e011793c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/AppIcon.qml @@ -0,0 +1,15 @@ +import QtQuick +import org.kde.kirigami as Kirigami +import qs.services +import qs.modules.common + +Kirigami.Icon { + id: root + + property real implicitSize: 26 + implicitWidth: implicitSize + implicitHeight: implicitSize + + roundToIconSize: false + animated: true // It's just fading from one icon to another +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Circle.qml b/dots/.config/quickshell/ii/modules/common/widgets/Circle.qml index ed137a94d..d3b727a97 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/Circle.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/Circle.qml @@ -1,7 +1,7 @@ import QtQuick Rectangle { - property double diameter + property real diameter implicitWidth: diameter implicitHeight: diameter diff --git a/dots/.config/quickshell/ii/modules/common/widgets/MaskMultiEffect.qml b/dots/.config/quickshell/ii/modules/common/widgets/MaskMultiEffect.qml new file mode 100644 index 000000000..18e2177d4 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/MaskMultiEffect.qml @@ -0,0 +1,9 @@ +import QtQuick +import QtQuick.Effects + +// Note: You still have to set sizes yourself +MultiEffect { + maskEnabled: true + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StateLayer.qml b/dots/.config/quickshell/ii/modules/common/widgets/StateLayer.qml new file mode 100644 index 000000000..19c825982 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StateLayer.qml @@ -0,0 +1,19 @@ +import QtQuick + +Rectangle { + id: root + + // https://m3.material.io/foundations/interaction/states/state-layers + enum State { + Hover, Focus, Press, Drag + } + + property var state: StateLayer.State.Hover + opacity: switch(state) { + case StateLayer.State.Hover: return 0.08; + case StateLayer.State.Focus: return 0.1; + case StateLayer.State.Press: return 0.1; + case StateLayer.State.Drag: return 0.16; + default: return 0; + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml b/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml new file mode 100644 index 000000000..65cabc218 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml @@ -0,0 +1,53 @@ +pragma ComponentBehavior: Bound +import QtQuick + +Rectangle { + id: root + + property bool hover: false + property bool press: false + property bool drag: false + property color contentColor: Appearance.m3colors.m3onBackground + color: "transparent" + + FadeLoader { + id: hoverLoader + anchors.fill: parent + shown: root.hover + sourceComponent: StateLayer { + radius: root.radius + state: StateLayer.State.Hover + color: root.contentColor + } + } + FadeLoader { + id: focusLoader + anchors.fill: parent + shown: root.focus + sourceComponent: StateLayer { + radius: root.radius + state: StateLayer.State.Focus + color: root.contentColor + } + } + FadeLoader { + id: pressLoader + anchors.fill: parent + shown: root.press + sourceComponent: StateLayer { + radius: root.radius + state: StateLayer.State.Press + color: root.contentColor + } + } + FadeLoader { + id: dragLoader + anchors.fill: parent + shown: root.drag + sourceComponent: StateLayer { + radius: root.radius + state: StateLayer.State.Drag + color: root.contentColor + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index 0a6e829f8..ff1600c9d 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -67,7 +67,7 @@ PanelWindow { borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1 borderColor: Appearance.colors.colLayer0Border visible: false // cuz there's already the shadow - debug: true + // debug: true } DropShadow { id: shadow diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml index 58a3aee0f..54bb0ef43 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -50,7 +50,7 @@ Item { id: layout columns: C.Config.options.bar.vertical ? 1 : -1 anchors.centerIn: parent - property real spacing: 0 + property real spacing: 4 columnSpacing: spacing rowSpacing: spacing } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml new file mode 100644 index 000000000..e7895fd2e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -0,0 +1,363 @@ +pragma ComponentBehavior: Bound +import qs +import qs.modules.common +import qs.modules.common.models +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.services +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland + +Item { + id: root + + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) + WorkspaceModel { + id: wsModel + monitor: root.monitor + } + + property bool vertical: Config.options.bar.vertical + property bool superPressAndHeld: false // Relevant modifications at bottom of file + + property real workspaceButtonWidth: 26 + property real activeWorkspaceMargin: 2 + property real activeWorkspaceSize: workspaceButtonWidth - activeWorkspaceMargin * 2 + property real workspaceIconSize: workspaceButtonWidth * 0.69 + property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 + property real workspaceIconOpacityShrinked: 1 + property real workspaceIconMarginShrinked: -4 + property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount + + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : occupiedIndicators.implicitWidth + implicitHeight: vertical ? occupiedIndicators.implicitHeight : Appearance.sizes.barHeight + + /////////////////// Occupied indicators /////////////////// + StyledRectangle { + id: occupiedIndicatorsBg + anchors.fill: parent + contentLayer: StyledRectangle.ContentLayer.Group + color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) + visible: false + } + + WorkspaceLayout { + id: occupiedIndicators + anchors.centerIn: parent + + // rowSpacing: 0 + // columnSpacing: 0 + // columns: root.vertical ? 1 : -1 + // rows: root.vertical ? -1 : 1 + + layer.enabled: true + visible: false + + Repeater { + model: wsModel.shownCount + delegate: Item { + id: wsBg + required property int index + readonly property int wsId: wsModel.getWorkspaceIdAt(index) + property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace + property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace + property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace + implicitWidth: root.workspaceButtonWidth + implicitHeight: root.workspaceButtonWidth + + // The idea: over-stretch to occupied sides, animate this for a smooth transition. + // masking already prevents weird overlaps + Circle { + property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied + property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied + property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth + radius: undirectionalWidth / 2 + anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter + anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined + x: root.vertical ? 0 : undirectionalOffset + y: root.vertical ? undirectionalOffset : 0 + implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength + implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth + + Behavior on undirectionalWidth { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on undirectionalLength { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on undirectionalOffset { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + } + } + } + + MaskMultiEffect { + id: occupiedIndicatorsMultiEffect + z: 1 + anchors.centerIn: parent + implicitWidth: occupiedIndicators.implicitWidth + implicitHeight: occupiedIndicators.implicitHeight + source: occupiedIndicatorsBg + maskSource: occupiedIndicators + } + + /////////////////// Active indicator /////////////////// + TrailingIndicator { + id: activeIndicator + anchors.fill: parent + z: 2 + + index: root.workspaceIndexInGroup + layer.enabled: true // For the masking + } + + /////////////////// Hover /////////////////// + MouseArea { + id: interactionMouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + property int hoverIndex: { + const position = root.vertical ? mouseY : mouseX; + return Math.floor(position / root.workspaceButtonWidth); + } + + onPressed: Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) + + TrailingIndicator { + id: interactionIndicator + index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup + color: "transparent" + StateOverlay { + id: hoverOverlay + anchors.fill: interactionIndicator.indicatorRectangle + radius: root.activeWorkspaceSize / 2 + hover: interactionMouseArea.containsMouse + press: interactionMouseArea.containsPress + contentColor: Appearance.colors.colPrimary + } + } + } + + /////////////////// Numbers /////////////////// + WorkspaceLayout { + id: numbersGrid + z: 4 + layer.enabled: true // For the masking + + Repeater { + model: wsModel.shownCount + delegate: WorkspaceItem { + id: wsNum + property bool hasBiggestWindow: !!wsModel.biggestWindow[index] + property color contentColor: wsModel.occupied[wsNum.index] ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive + + FadeLoader { + shown: !(Config.options?.bar.workspaces.alwaysShowNumbers + || root.superPressAndHeld + || (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow) + ) + anchors.centerIn: parent + Circle { + anchors.centerIn: parent + diameter: root.workspaceButtonWidth * 0.18 + color: wsNum.contentColor + } + } + FadeLoader { + shown: root.superPressAndHeld + || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers)) + || (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons) + ) + anchors.centerIn: parent + StyledText { + anchors.centerIn: parent + font { + pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) + family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont + } + color: wsNum.contentColor + text: wsNum.wsId + } + } + } + } + } + Colorizer { + z: 5 + anchors.fill: numbersGrid + colorizationColor: Appearance.colors.colOnPrimary + sourceColor: Appearance.colors.colOnSecondaryContainer + + source: activeIndicator + maskEnabled: true + maskSource: numbersGrid + + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + + /////////////////// App icons /////////////////// + WorkspaceLayout { + id: appsGrid + z: 6 + + Repeater { + model: wsModel.shownCount + delegate: WorkspaceItem { + id: wsApp + property var biggestWindow: wsModel.biggestWindow[index] + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + + AppIcon { + id: appIcon + property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? + (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin + rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin + } + + animated: !wsApp.biggestWindow // Prevent the "image-missing" icon + visible: false // Prevent dupe: the colorizer already copies the icon + + source: wsApp.mainAppIconSource + implicitSize: NumberUtils.roundToEven((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on cornerMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitSize { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + + Circle { + id: iconMask + visible: false + layer.enabled: true + diameter: appIcon.implicitSize + } + + Colorizer { + anchors.fill: appIcon + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + colorizationColor: Appearance.colors.colOnSecondaryContainer + colorization: Config.options.bar.workspaces.monochromeIcons * 0.7 + brightness: 0 + source: appIcon + + opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : + (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? + 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 + visible: opacity > 0 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + maskEnabled: true + maskSource: iconMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + } + } + } + + /////////////////// Components /////////////////// + component WorkspaceLayout: Grid { + anchors { + top: !vertical ? parent.top : undefined + bottom: !vertical ? parent.bottom : undefined + left: vertical ? parent.left : undefined + right: vertical ? parent.right : undefined + } + + rowSpacing: 0 + columnSpacing: 0 + columns: root.vertical ? 1 : -1 + rows: root.vertical ? -1 : 1 + } + + component WorkspaceItem: Item { + required property int index + readonly property int wsId: wsModel.getWorkspaceIdAt(index) + implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : root.workspaceButtonWidth + implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight + } + + component TrailingIndicator: Item { + id: trailingIndicator + anchors.fill: parent + required property int index + property alias indicatorRectangle: indicatorRect + property alias color: indicatorRect.color + + StyledRectangle { + id: indicatorRect + anchors { + verticalCenter: vertical ? undefined : parent.verticalCenter + horizontalCenter: vertical ? parent.horizontalCenter : undefined + } + + AnimatedTabIndexPair { + id: idxPair + index: trailingIndicator.index + } + + property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin + property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceSize + property real indicatorThickness: root.activeWorkspaceSize + + contentLayer: StyledRectangle.ContentLayer.Group + radius: indicatorThickness / 2 + color: Appearance.colors.colPrimary + + x: root.vertical ? null : indicatorPosition + y: root.vertical ? indicatorPosition : null + implicitWidth: root.vertical ? indicatorThickness : indicatorLength + implicitHeight: root.vertical ? indicatorLength : indicatorThickness + } + } + + /////////////////// Super key press handling /////////////////// + Timer { + id: superPressAndHeldTimer + interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100) + repeat: false + onTriggered: { + root.superPressAndHeld = true; + } + } + Connections { + target: GlobalStates + function onSuperDownChanged() { + if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) + return; + if (GlobalStates.superDown) + superPressAndHeldTimer.restart(); + else { + superPressAndHeldTimer.stop(); + root.superPressAndHeld = false; + } + } + function onSuperReleaseMightTriggerChanged() { + superPressAndHeldTimer.stop(); + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/IIWorkspaces.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Workspaces.qml rename to dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/IIWorkspaces.qml diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index b8952b917..05c7399b9 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import qs import qs.services import qs.modules.common @@ -15,21 +16,22 @@ import Qt5Compat.GraphicalEffects Item { id: root property bool vertical: false - property bool borderless: Config.options.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - - readonly property int workspacesShown: Config.options.bar.workspaces.shown - readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown) - property list workspaceOccupied: [] - property int widgetPadding: 4 + readonly property bool activeActuallyFocused: activeWindow?.activated ?? false + + WorkspaceModel { + id: wsModel + monitor: root.monitor + } + property int workspaceButtonWidth: 26 property real activeWorkspaceMargin: 2 property real workspaceIconSize: workspaceButtonWidth * 0.69 property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown + property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount property bool showNumbers: false Timer { @@ -55,33 +57,8 @@ Item { } } - // Function to update workspaceOccupied - function updateWorkspaceOccupied() { - workspaceOccupied = Array.from({ length: root.workspacesShown }, (_, i) => { - return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1); - }) - } - - // Occupied workspace updates - Component.onCompleted: updateWorkspaceOccupied() - Connections { - target: Hyprland.workspaces - function onValuesChanged() { - updateWorkspaceOccupied(); - } - } - Connections { - target: Hyprland - function onFocusedWorkspaceChanged() { - updateWorkspaceOccupied(); - } - } - onWorkspaceGroupChanged: { - updateWorkspaceOccupied(); - } - - implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown) - implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : Appearance.sizes.barHeight + implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * wsModel.shownCount) + implicitHeight: root.vertical ? (root.workspaceButtonWidth * wsModel.shownCount) : Appearance.sizes.barHeight // Scroll to switch workspaces WheelHandler { @@ -115,15 +92,18 @@ Item { rows: root.vertical ? -1 : 1 Repeater { - model: root.workspacesShown + model: wsModel.shownCount + + delegate: Rectangle { + required property int index - Rectangle { z: 1 - implicitWidth: workspaceButtonWidth - implicitHeight: workspaceButtonWidth + implicitWidth: root.workspaceButtonWidth + implicitHeight: root.workspaceButtonWidth radius: (width / 2) - property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index)) - property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2)) + property bool thisOccupied: (wsModel.occupied[index] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+1)) + property var previousOccupied: (wsModel.occupied[index-1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index)) + property var rightOccupied: (wsModel.occupied[index+1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+2)) property var radiusPrev: previousOccupied ? 0 : (width / 2) property var radiusNext: rightOccupied ? 0 : (width / 2) @@ -133,7 +113,7 @@ Item { bottomRightRadius: radiusNext color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0 + opacity: thisOccupied ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -168,9 +148,9 @@ Item { id: idxPair index: root.workspaceIndexInGroup } - property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin - property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2 - property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2 + property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin + property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.workspaceButtonWidth - root.activeWorkspaceMargin * 2 + property real indicatorThickness: root.workspaceButtonWidth - root.activeWorkspaceMargin * 2 x: root.vertical ? null : indicatorPosition implicitWidth: root.vertical ? indicatorThickness : indicatorLength @@ -183,36 +163,35 @@ Item { Grid { id: wsNumbers z: 3 + anchors.fill: parent columns: root.vertical ? 1 : -1 rows: root.vertical ? -1 : 1 columnSpacing: 0 rowSpacing: 0 - anchors.fill: parent - Repeater { - model: root.workspacesShown - - Button { + model: wsModel.shownCount + delegate: Button { id: button - property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1 + required property int index + property int workspaceValue: wsModel.getWorkspaceIdAt(index) implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`) - width: vertical ? undefined : workspaceButtonWidth - height: vertical ? workspaceButtonWidth : undefined + width: vertical ? undefined : root.workspaceButtonWidth + height: vertical ? root.workspaceButtonWidth : undefined background: Item { id: workspaceButtonBackground - implicitWidth: workspaceButtonWidth - implicitHeight: workspaceButtonWidth + implicitWidth: root.workspaceButtonWidth + implicitHeight: root.workspaceButtonWidth property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue) property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") property color numberColor: (monitor?.activeWorkspace?.id == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : - (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : + (wsModel.occupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) StyledText { // Workspace number text @@ -245,7 +224,7 @@ Item { ) ? 0 : 1 visible: opacity > 0 anchors.centerIn: parent - width: workspaceButtonWidth * 0.18 + width: root.workspaceButtonWidth * 0.18 height: width radius: width / 2 color: workspaceButtonBackground.numberColor @@ -256,8 +235,8 @@ Item { } Item { // Main app icon anchors.centerIn: parent - width: workspaceButtonWidth - height: workspaceButtonWidth + width: root.workspaceButtonWidth + height: root.workspaceButtonWidth opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? 1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0 @@ -267,9 +246,9 @@ Item { anchors.bottom: parent.bottom anchors.right: parent.right anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? - (workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked + (root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? - (workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked + (root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked source: workspaceButtonBackground.mainAppIconSource implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml index bd0f2fce4..4d9e491b2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml @@ -1,19 +1,14 @@ import QtQuick -import org.kde.kirigami as Kirigami import qs.services import qs.modules.common +import qs.modules.common.widgets as W -Kirigami.Icon { +W.AppIcon { id: root required property string iconName property bool separateLightDark: false property bool tryCustomIcon: true - property real implicitSize: 26 - implicitWidth: implicitSize - implicitHeight: implicitSize - - animated: true roundToIconSize: false fallback: root.iconName source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback From ed8c8ae8d7904b2bbc1660a913010dfba3bf9147 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:49:03 +0100 Subject: [PATCH 009/104] hefty: bar: special ws indication --- .../ii/modules/common/Appearance.qml | 14 + .../modules/common/models/WorkspaceModel.qml | 4 + .../ii/modules/common/widgets/Pill.qml | 5 + .../topLayer/bar/widgets/HWorkspaces.qml | 501 ++++++++++-------- 4 files changed, 302 insertions(+), 222 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/Pill.qml diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index d64253476..34663f066 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -282,6 +282,20 @@ Singleton { } } + property QtObject elementMoveSmall: QtObject { + property int duration: animationCurves.expressiveFastSpatialDuration + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.expressiveFastSpatial + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMoveSmall.duration + easing.type: root.animation.elementMoveSmall.type + easing.bezierCurve: root.animation.elementMoveSmall.bezierCurve + } + } + } + property QtObject elementMoveEnter: QtObject { property int duration: 400 property int type: Easing.BezierSpline diff --git a/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml b/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml index 17b27c9f8..4490c8b00 100644 --- a/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml +++ b/dots/.config/quickshell/ii/modules/common/models/WorkspaceModel.qml @@ -8,12 +8,16 @@ NestableObject { id: root required property HyprlandMonitor monitor + readonly property var liveMonitorData: HyprlandData.monitors.find(m => m.id === monitor.id) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel readonly property int activeWorkspace: monitor?.activeWorkspace?.id readonly property bool currentWorkspaceNotFake: activeWindow?.activated ?? false // Active empty workspace = fake. At least, that's how I like to call it. readonly property int fakeWorkspace: currentWorkspaceNotFake ? -9999 : activeWorkspace readonly property int shownCount: C.Config.options.bar.workspaces.shown readonly property int group: Math.floor((activeWorkspace - 1) / shownCount) + readonly property var specialWorkspace: liveMonitorData?.specialWorkspace + readonly property string specialWorkspaceName: specialWorkspace.name.replace("special:", "") + readonly property bool specialWorkspaceActive: specialWorkspaceName !== "" property list occupied: [] property list biggestWindow: occupied.map((_, index) => { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Pill.qml b/dots/.config/quickshell/ii/modules/common/widgets/Pill.qml new file mode 100644 index 000000000..7e9ae5d54 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/Pill.qml @@ -0,0 +1,5 @@ +import QtQuick + +Rectangle { + radius: Math.min(width, height) / 2 +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index e7895fd2e..059e365c4 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -31,6 +31,7 @@ Item { property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount + property real specialTextSize: workspaceButtonWidth * 0.5 Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter Layout.fillWidth: vertical @@ -38,243 +39,264 @@ Item { implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : occupiedIndicators.implicitWidth implicitHeight: vertical ? occupiedIndicators.implicitHeight : Appearance.sizes.barHeight - /////////////////// Occupied indicators /////////////////// - StyledRectangle { - id: occupiedIndicatorsBg + property real specialBlur: wsModel.specialWorkspaceActive ? 1 : 0 + Behavior on specialBlur { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + Item { + id: regularWorkspaces anchors.fill: parent - contentLayer: StyledRectangle.ContentLayer.Group - color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - visible: false - } - WorkspaceLayout { - id: occupiedIndicators - anchors.centerIn: parent - - // rowSpacing: 0 - // columnSpacing: 0 - // columns: root.vertical ? 1 : -1 - // rows: root.vertical ? -1 : 1 - - layer.enabled: true - visible: false - - Repeater { - model: wsModel.shownCount - delegate: Item { - id: wsBg - required property int index - readonly property int wsId: wsModel.getWorkspaceIdAt(index) - property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace - property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace - property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace - implicitWidth: root.workspaceButtonWidth - implicitHeight: root.workspaceButtonWidth - - // The idea: over-stretch to occupied sides, animate this for a smooth transition. - // masking already prevents weird overlaps - Circle { - property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied - property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied - property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth - radius: undirectionalWidth / 2 - anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter - anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined - x: root.vertical ? 0 : undirectionalOffset - y: root.vertical ? undirectionalOffset : 0 - implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength - implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth - - Behavior on undirectionalWidth { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on undirectionalLength { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on undirectionalOffset { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - } - } - } - } - - MaskMultiEffect { - id: occupiedIndicatorsMultiEffect - z: 1 - anchors.centerIn: parent - implicitWidth: occupiedIndicators.implicitWidth - implicitHeight: occupiedIndicators.implicitHeight - source: occupiedIndicatorsBg - maskSource: occupiedIndicators - } - - /////////////////// Active indicator /////////////////// - TrailingIndicator { - id: activeIndicator - anchors.fill: parent - z: 2 - - index: root.workspaceIndexInGroup - layer.enabled: true // For the masking - } - - /////////////////// Hover /////////////////// - MouseArea { - id: interactionMouseArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - property int hoverIndex: { - const position = root.vertical ? mouseY : mouseX; - return Math.floor(position / root.workspaceButtonWidth); + scale: 1 - 0.08 * root.specialBlur + layer.smooth: true + layer.enabled: root.specialBlur > 0 + layer.effect: MultiEffect { + brightness: -0.1 * root.specialBlur + blurEnabled: true + blur: root.specialBlur + blurMax: 32 } - onPressed: Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) - - TrailingIndicator { - id: interactionIndicator - index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup - color: "transparent" - StateOverlay { - id: hoverOverlay - anchors.fill: interactionIndicator.indicatorRectangle - radius: root.activeWorkspaceSize / 2 - hover: interactionMouseArea.containsMouse - press: interactionMouseArea.containsPress - contentColor: Appearance.colors.colPrimary - } + /////////////////// Occupied indicators /////////////////// + StyledRectangle { + id: occupiedIndicatorsBg + anchors.fill: parent + contentLayer: StyledRectangle.ContentLayer.Group + color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) + visible: false } - } - /////////////////// Numbers /////////////////// - WorkspaceLayout { - id: numbersGrid - z: 4 - layer.enabled: true // For the masking + WorkspaceLayout { + id: occupiedIndicators + anchors.centerIn: parent - Repeater { - model: wsModel.shownCount - delegate: WorkspaceItem { - id: wsNum - property bool hasBiggestWindow: !!wsModel.biggestWindow[index] - property color contentColor: wsModel.occupied[wsNum.index] ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive + layer.enabled: true + visible: false - FadeLoader { - shown: !(Config.options?.bar.workspaces.alwaysShowNumbers - || root.superPressAndHeld - || (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow) - ) - anchors.centerIn: parent - Circle { - anchors.centerIn: parent - diameter: root.workspaceButtonWidth * 0.18 - color: wsNum.contentColor - } - } - FadeLoader { - shown: root.superPressAndHeld - || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers)) - || (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons) - ) - anchors.centerIn: parent - StyledText { - anchors.centerIn: parent - font { - pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) - family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont + Repeater { + model: wsModel.shownCount + delegate: Item { + id: wsBg + required property int index + readonly property int wsId: wsModel.getWorkspaceIdAt(index) + property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace + property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace + property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace + implicitWidth: root.workspaceButtonWidth + implicitHeight: root.workspaceButtonWidth + + // The idea: over-stretch to occupied sides, animate this for a smooth transition. + // masking already prevents weird overlaps + Pill { + property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied + property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied + property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth + anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter + anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined + x: root.vertical ? 0 : undirectionalOffset + y: root.vertical ? undirectionalOffset : 0 + implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength + implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth + + Behavior on undirectionalWidth { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) } - color: wsNum.contentColor - text: wsNum.wsId + Behavior on undirectionalLength { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + Behavior on undirectionalOffset { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + } + } + } + } + + MaskMultiEffect { + id: occupiedIndicatorsMultiEffect + z: 1 + anchors.centerIn: parent + implicitWidth: occupiedIndicators.implicitWidth + implicitHeight: occupiedIndicators.implicitHeight + source: occupiedIndicatorsBg + maskSource: occupiedIndicators + } + + /////////////////// Active indicator /////////////////// + TrailingIndicator { + id: activeIndicator + anchors.fill: parent + z: 2 + + index: root.workspaceIndexInGroup + } + + /////////////////// Hover /////////////////// + MouseArea { + id: interactionMouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + property int hoverIndex: { + const position = root.vertical ? mouseY : mouseX; + return Math.floor(position / root.workspaceButtonWidth); + } + + onPressed: Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) + onWheel: (event) => { + if (event.angleDelta.y < 0) + Hyprland.dispatch(`workspace r+1`); + else if (event.angleDelta.y > 0) + Hyprland.dispatch(`workspace r-1`); + } + + TrailingIndicator { + id: interactionIndicator + index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup + color: "transparent" + StateOverlay { + id: hoverOverlay + anchors.fill: interactionIndicator.indicatorRectangle + radius: root.activeWorkspaceSize / 2 + hover: interactionMouseArea.containsMouse + press: interactionMouseArea.containsPress + contentColor: Appearance.colors.colPrimary + } + } + } + + /////////////////// Numbers /////////////////// + WorkspaceLayout { + id: numbersGrid + z: 4 + layer.enabled: true // For the masking + + Repeater { + model: wsModel.shownCount + delegate: NumberWorkspaceItem {} + } + } + Colorizer { + z: 5 + anchors.fill: numbersGrid + colorizationColor: Appearance.colors.colOnPrimary + sourceColor: Appearance.colors.colOnSecondaryContainer + + source: activeIndicator + maskEnabled: true + maskSource: numbersGrid + + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + + /////////////////// App icons /////////////////// + WorkspaceLayout { + id: appsGrid + z: 6 + + Repeater { + model: wsModel.shownCount + delegate: WorkspaceItem { + id: wsApp + property var biggestWindow: wsModel.biggestWindow[index] + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + + AppIcon { + id: appIcon + property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? + (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin + rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin + } + + animated: !wsApp.biggestWindow // Prevent the "image-missing" icon + visible: false // Prevent dupe: the colorizer already copies the icon + + source: wsApp.mainAppIconSource + implicitSize: NumberUtils.roundToEven((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on cornerMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitSize { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + + Circle { + id: iconMask + visible: false + layer.enabled: true + diameter: appIcon.implicitSize + } + + Colorizer { + anchors.fill: appIcon + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary + colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 + brightness: 0 + source: appIcon + + opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : + (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? + 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 + visible: opacity > 0 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + maskEnabled: true + maskSource: iconMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 } } } } } - Colorizer { - z: 5 - anchors.fill: numbersGrid - colorizationColor: Appearance.colors.colOnPrimary - sourceColor: Appearance.colors.colOnSecondaryContainer - source: activeIndicator - maskEnabled: true - maskSource: numbersGrid + FadeLoader { + anchors.centerIn: parent + shown: wsModel.specialWorkspaceActive - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } + scale: 0.8 + 0.2 * root.specialBlur + // layer.enabled: true + // layer.smooth: true - /////////////////// App icons /////////////////// - WorkspaceLayout { - id: appsGrid - z: 6 + Pill { + anchors.centerIn: parent + property real undirectionalWidth: root.activeWorkspaceSize + property real undirectionalLength: { + const base = root.workspaceButtonWidth * Math.min(1.35, wsModel.shownCount) // Who tf only configures only 2 workspaces shown anyway? + if (root.vertical) return base; + return specialWsText.implicitWidth + undirectionalWidth + } + color: Appearance.colors.colPrimary - Repeater { - model: wsModel.shownCount - delegate: WorkspaceItem { - id: wsApp - property var biggestWindow: wsModel.biggestWindow[index] - property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength + implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth - AppIcon { - id: appIcon - property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? - (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked - anchors { - bottom: parent.bottom - right: parent.right - bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin - rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin - } + StyledText { + id: specialWsText + anchors.centerIn: parent + text: (!root.vertical ? wsModel.specialWorkspaceName : "S") + color: Appearance.colors.colOnPrimary + font.pixelSize: root.specialTextSize + } - animated: !wsApp.biggestWindow // Prevent the "image-missing" icon - visible: false // Prevent dupe: the colorizer already copies the icon - - source: wsApp.mainAppIconSource - implicitSize: NumberUtils.roundToEven((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) - - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on cornerMargin { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on implicitSize { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - } - - Circle { - id: iconMask - visible: false - layer.enabled: true - diameter: appIcon.implicitSize - } - - Colorizer { - anchors.fill: appIcon - implicitWidth: appIcon.implicitWidth - implicitHeight: appIcon.implicitHeight - colorizationColor: Appearance.colors.colOnSecondaryContainer - colorization: Config.options.bar.workspaces.monochromeIcons * 0.7 - brightness: 0 - source: appIcon - - opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : - (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? - 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 - visible: opacity > 0 - - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - - maskEnabled: true - maskSource: iconMask - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } + Behavior on undirectionalLength { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } } } @@ -301,6 +323,41 @@ Item { implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight } + component NumberWorkspaceItem: WorkspaceItem { + id: wsNum + property bool hasBiggestWindow: !!wsModel.biggestWindow[index] + property color contentColor: wsModel.occupied[wsNum.index] ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive + + FadeLoader { + shown: !(Config.options?.bar.workspaces.alwaysShowNumbers + || root.superPressAndHeld + || (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow) + ) + anchors.centerIn: parent + Circle { + anchors.centerIn: parent + diameter: root.workspaceButtonWidth * 0.18 + color: wsNum.contentColor + } + } + FadeLoader { + shown: root.superPressAndHeld + || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers)) + || (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons) + ) + anchors.centerIn: parent + StyledText { + anchors.centerIn: parent + font { + pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) + family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont + } + color: wsNum.contentColor + text: wsNum.wsId + } + } + } + component TrailingIndicator: Item { id: trailingIndicator anchors.fill: parent @@ -308,6 +365,11 @@ Item { property alias indicatorRectangle: indicatorRect property alias color: indicatorRect.color + property var indexPair: AnimatedTabIndexPair { + id: idxPair + index: trailingIndicator.index + } + StyledRectangle { id: indicatorRect anchors { @@ -315,11 +377,6 @@ Item { horizontalCenter: vertical ? parent.horizontalCenter : undefined } - AnimatedTabIndexPair { - id: idxPair - index: trailingIndicator.index - } - property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceSize property real indicatorThickness: root.activeWorkspaceSize From 879111fe01911177a55324f893e2982d0cb029c3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:10:36 +0100 Subject: [PATCH 010/104] ai: remove openrouter deepseek --- dots/.config/quickshell/ii/services/Ai.qml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index 580b3cfdb..a273415f5 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -294,18 +294,6 @@ Singleton { "key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"), "api_format": "mistral", }), - "openrouter-deepseek-r1": aiModelComponent.createObject(this, { - "name": "DeepSeek R1", - "icon": "deepseek-symbolic", - "description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("DeepSeek"), - "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", - "endpoint": "https://openrouter.ai/api/v1/chat/completions", - "model": "deepseek/deepseek-r1:free", - "requires_key": true, - "key_id": "openrouter", - "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), - }), } property var modelList: Object.keys(root.models) property var currentModelId: Persistent.states?.ai?.model || modelList[0] From 4a44994168afe378d770354b55e60e96e6856af4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:37:27 +0100 Subject: [PATCH 011/104] revert weird launcher entries with terminal apps --- dots/.config/quickshell/ii/services/LauncherSearch.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index 787da3090..12fc67de8 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -250,8 +250,7 @@ Singleton { if (!entry.runInTerminal) entry.execute(); else { - print([...Config.options.apps.terminal, "-e", ...entry.command]) - Quickshell.execDetached([...Config.options.apps.terminal, "-e", ...entry.command]); + Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(entry.command.join(' '))}'`]); } }, comment: entry.comment, @@ -267,7 +266,7 @@ Singleton { if (!action.runInTerminal) action.execute(); else { - Quickshell.execDetached([...Config.options.apps.terminal, "-e", ...action.command]); + Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(action.command.join(' '))}'`]); } } }); From 5f9fe2d250febd9434cefca9012730af82fec880 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:38:00 +0100 Subject: [PATCH 012/104] hefty: bar: ws: make number map work --- .../ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 059e365c4..ff8965df2 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -353,7 +353,7 @@ Item { family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont } color: wsNum.contentColor - text: wsNum.wsId + text: Config.options?.bar.workspaces.numberMap[wsNum.wsId - 1] || wsNum.wsId } } } From d8eb55fea9df7f39c84c84a3ce3bdc5196c1c155 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:49:53 +0100 Subject: [PATCH 013/104] fish: more typo aliases --- dots/.config/fish/config.fish | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dots/.config/fish/config.fish b/dots/.config/fish/config.fish index 72476660f..0d26fe1dc 100755 --- a/dots/.config/fish/config.fish +++ b/dots/.config/fish/config.fish @@ -18,9 +18,11 @@ if status is-interactive # Commands to run in interactive sessions can go here end # Aliases - alias pamcan pacman + alias clear "printf '\033[2J\033[3J\033[1;1H'" # fix: kitty doesn't clear properly + alias celar "printf '\033[2J\033[3J\033[1;1H'" + alias claer "printf '\033[2J\033[3J\033[1;1H'" alias ls 'eza --icons' - alias clear "printf '\033[2J\033[3J\033[1;1H'" + alias pamcan pacman alias q 'qs -c ii' end From 4718922c55754faabae13e7355782f71b1624c1b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:38:45 +0100 Subject: [PATCH 014/104] hefty: bar: proper vertical layouting --- .../ii/modules/common/config/HeftyConfig.qml | 6 ++- .../modules/common/widgets/AxisRectangle.qml | 15 ++++++ .../hefty/topLayer/bar/HBarContent.qml | 47 ++++++++++++++++--- .../hefty/topLayer/bar/HBarGroupContainer.qml | 20 ++++---- 4 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 7d765c1b2..509d4dccf 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -4,8 +4,10 @@ import Quickshell.Io JsonObject { property JsonObject bar: JsonObject { - property list leftWidgets: [] - property list centerWidgets: [["Workspaces"]] + property list leftWidgets: [["HWindowInfo"]] + property list centerLeftWidgets: [["HTime"]] + property list centerWidgets: [["HWorkspaces"]] + property list centerRightWidgets: [["HBattery"]] property list rightWidgets: [] property bool m3ExpressiveGrouping: true } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml new file mode 100644 index 000000000..d54594ecd --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml @@ -0,0 +1,15 @@ +pragma ComponentBehavior: Bound +import QtQuick + +StyledRectangle { + id: root + + property bool vertical: false + property real startRadius + property real endRadius + + topLeftRadius: startRadius + topRightRadius: vertical ? startRadius : endRadius + bottomLeftRadius: vertical ? endRadius : startRadius + bottomRightRadius: endRadius +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index d090a9221..a26e5b232 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import qs.modules.common as C @@ -5,36 +6,70 @@ import qs.modules.common as C Item { id: root + property bool vertical: C.Config.options.bar.vertical + property real spacing: 4 + Side { id: leftSide anchors.left: parent.left - width: (parent.width - centerSide.width) / 2 + anchors.top: parent.top + HBarUserFallbackComponentRepeater { componentNames: C.Config.options.hefty.bar.leftWidgets } } + Side { + id: centerLeftSide + anchors.right: !root.vertical ? centerSide.left : parent.right + anchors.bottom: root.vertical ? parent.bottom : undefined + HBarUserFallbackComponentRepeater { + componentNames: C.Config.options.hefty.bar.centerLeftWidgets + } + } + Side { id: centerSide - anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined + anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined HBarUserFallbackComponentRepeater { componentNames: C.Config.options.hefty.bar.centerWidgets } } + Side { + id: centerRightSide + anchors.left: !root.vertical ? centerSide.right : parent.left + anchors.top: root.vertical ? parent.top : undefined + HBarUserFallbackComponentRepeater { + componentNames: C.Config.options.hefty.bar.centerRightWidgets + } + } + Side { id: rightSide anchors.right: parent.right - width: (parent.width - centerSide.width) / 2 + anchors.bottom: parent.bottom HBarUserFallbackComponentRepeater { componentNames: C.Config.options.hefty.bar.rightWidgets } } - component Side: RowLayout { + component Side: GridLayout { anchors { - top: parent.top - bottom: parent.bottom + top: !root.vertical ? parent.top : undefined + bottom: !root.vertical ? parent.bottom : undefined + topMargin: root.spacing * root.vertical + bottomMargin: root.spacing * root.vertical + left: root.vertical ? parent.left : undefined + right: root.vertical ? parent.right : undefined + leftMargin: root.spacing * !root.vertical + rightMargin: root.spacing * !root.vertical } + + columns: C.Config.options.bar.vertical ? 1 : -1 + property real spacing: root.spacing + columnSpacing: spacing + rowSpacing: spacing } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml index 54bb0ef43..fc8f2b863 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -17,17 +17,18 @@ Item { readonly property bool vertical: C.Config.options.bar.vertical readonly property bool m3eRadius: C.Config.options.hefty.bar.m3ExpressiveGrouping readonly property real barUndirectionalWidth: C.Config.options.bar.vertical ? C.Appearance.sizes.baseVerticalBarWidth : C.Appearance.sizes.baseBarHeight + readonly property real backgroundUndirectionalWidth: barUndirectionalWidth - margins * 2 - implicitWidth: vertical ? barUndirectionalWidth : layout.implicitWidth + (padding + margins) * 2 - implicitHeight: vertical ? layout.implicitHeight + (padding + margins) * 2 : barUndirectionalWidth + implicitWidth: vertical ? barUndirectionalWidth : layout.implicitWidth + padding * 2 + implicitHeight: vertical ? layout.implicitHeight + padding * 2 : barUndirectionalWidth - W.StyledRectangle { + W.AxisRectangle { id: bg anchors.centerIn: parent contentLayer: W.StyledRectangle.ContentLayer.Pane - width: (root.vertical ? root.barUndirectionalWidth : root.width) - root.margins * 2 - height: (root.vertical ? root.height : root.barUndirectionalWidth) - root.margins * 2 + width: root.vertical ? root.backgroundUndirectionalWidth : root.width + height: root.vertical ? root.height : root.backgroundUndirectionalWidth property real fullRadius: Math.min(width, height) / 2 function getRadius(atSide) { @@ -38,12 +39,9 @@ Item { return 12; } } - property real startRadius: getRadius(root.startSide) - property real endRadius: getRadius(root.endSide) - topLeftRadius: startRadius - topRightRadius: root.vertical ? startRadius : endRadius - bottomLeftRadius: root.vertical ? endRadius : startRadius - bottomRightRadius: endRadius + vertical: root.vertical + startRadius: getRadius(root.startSide) + endRadius: getRadius(root.endSide) } GridLayout { From 74368ad25a644fa085810d54d617bd22f59b6e74 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:38:58 +0100 Subject: [PATCH 015/104] hefty: bar: ws: bouncy special ws --- .../ii/modules/common/widgets/ButtonMouseArea.qml | 9 +++++++++ .../hefty/topLayer/bar/widgets/HWorkspaces.qml | 13 +++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/ButtonMouseArea.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ButtonMouseArea.qml b/dots/.config/quickshell/ii/modules/common/widgets/ButtonMouseArea.qml new file mode 100644 index 000000000..d7b42cb91 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ButtonMouseArea.qml @@ -0,0 +1,9 @@ +pragma ComponentBehavior: Bound +import QtQuick + +// MouseArea that contains good defaults for buttons +MouseArea { + id: root + hoverEnabled: true + cursorShape: Qt.PointingHandCursor +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index ff8965df2..469de3155 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -39,9 +39,9 @@ Item { implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : occupiedIndicators.implicitWidth implicitHeight: vertical ? occupiedIndicators.implicitHeight : Appearance.sizes.barHeight - property real specialBlur: wsModel.specialWorkspaceActive ? 1 : 0 + property real specialBlur: (wsModel.specialWorkspaceActive && !interactionMouseArea.containsMouse) ? 1 : 0 Behavior on specialBlur { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) } Item { @@ -133,7 +133,7 @@ Item { } /////////////////// Hover /////////////////// - MouseArea { + ButtonMouseArea { id: interactionMouseArea anchors.fill: parent cursorShape: Qt.PointingHandCursor @@ -268,13 +268,10 @@ Item { FadeLoader { anchors.centerIn: parent - shown: wsModel.specialWorkspaceActive - + shown: wsModel.specialWorkspaceActive && root.specialBlur > 0 scale: 0.8 + 0.2 * root.specialBlur - // layer.enabled: true - // layer.smooth: true - Pill { + sourceComponent: Pill { anchors.centerIn: parent property real undirectionalWidth: root.activeWorkspaceSize property real undirectionalLength: { From 74c012c930f71bd2469305ac9c2891c22a86019f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 23:38:05 +0100 Subject: [PATCH 016/104] hefty: bar: time widget --- .../modules/common/functions/ObjectUtils.qml | 11 +++ .../modules/common/widgets/StateOverlay.qml | 23 ++++- .../hefty/topLayer/bar/HBarContent.qml | 32 ++++++- .../hefty/topLayer/bar/HBarGroupContainer.qml | 9 +- .../bar/HBarUserFallbackComponentRepeater.qml | 13 ++- .../hefty/topLayer/bar/widgets/HTime.qml | 91 +++++++++++++++++++ .../topLayer/bar/widgets/HWorkspaces.qml | 9 +- .../hefty/topLayer/bar/widgets/Invisible.qml | 6 ++ .../ii/modules/ii/bar/ClockWidget.qml | 1 - 9 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml diff --git a/dots/.config/quickshell/ii/modules/common/functions/ObjectUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/ObjectUtils.qml index d1204cddc..178d07645 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/ObjectUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/ObjectUtils.qml @@ -95,4 +95,15 @@ Singleton { } } } + + function findParentWithProperty(obj, propertyName) { + let current = obj; + while (current) { + if (current.hasOwnProperty(propertyName)) { + return current; + } + current = current.parent; + } + return null; + } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml b/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml index 65cabc218..44fc64494 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StateOverlay.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick +import qs.modules.common as C Rectangle { id: root @@ -7,7 +8,7 @@ Rectangle { property bool hover: false property bool press: false property bool drag: false - property color contentColor: Appearance.m3colors.m3onBackground + property color contentColor: C.Appearance.m3colors.m3onBackground color: "transparent" FadeLoader { @@ -15,9 +16,12 @@ Rectangle { anchors.fill: parent shown: root.hover sourceComponent: StateLayer { - radius: root.radius state: StateLayer.State.Hover color: root.contentColor + topLeftRadius: root.topLeftRadius + topRightRadius: root.topRightRadius + bottomLeftRadius: root.bottomLeftRadius + bottomRightRadius: root.bottomRightRadius } } FadeLoader { @@ -25,9 +29,12 @@ Rectangle { anchors.fill: parent shown: root.focus sourceComponent: StateLayer { - radius: root.radius state: StateLayer.State.Focus color: root.contentColor + topLeftRadius: root.topLeftRadius + topRightRadius: root.topRightRadius + bottomLeftRadius: root.bottomLeftRadius + bottomRightRadius: root.bottomRightRadius } } FadeLoader { @@ -35,9 +42,12 @@ Rectangle { anchors.fill: parent shown: root.press sourceComponent: StateLayer { - radius: root.radius state: StateLayer.State.Press color: root.contentColor + topLeftRadius: root.topLeftRadius + topRightRadius: root.topRightRadius + bottomLeftRadius: root.bottomLeftRadius + bottomRightRadius: root.bottomRightRadius } } FadeLoader { @@ -45,9 +55,12 @@ Rectangle { anchors.fill: parent shown: root.drag sourceComponent: StateLayer { - radius: root.radius state: StateLayer.State.Drag color: root.contentColor + topLeftRadius: root.topLeftRadius + topRightRadius: root.topRightRadius + bottomLeftRadius: root.bottomLeftRadius + bottomRightRadius: root.bottomRightRadius } } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index a26e5b232..cd0fe80ea 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -9,13 +9,19 @@ Item { property bool vertical: C.Config.options.bar.vertical property real spacing: 4 + property list leftWidgets: C.Config.options.hefty.bar.leftWidgets + property list centerLeftWidgets: C.Config.options.hefty.bar.centerLeftWidgets + property list centerWidgets: C.Config.options.hefty.bar.centerWidgets + property list centerRightWidgets: C.Config.options.hefty.bar.centerRightWidgets + property list rightWidgets: C.Config.options.hefty.bar.rightWidgets + Side { id: leftSide anchors.left: parent.left anchors.top: parent.top HBarUserFallbackComponentRepeater { - componentNames: C.Config.options.hefty.bar.leftWidgets + componentNames: root.leftWidgets } } @@ -24,7 +30,16 @@ Item { anchors.right: !root.vertical ? centerSide.left : parent.right anchors.bottom: root.vertical ? parent.bottom : undefined HBarUserFallbackComponentRepeater { - componentNames: C.Config.options.hefty.bar.centerLeftWidgets + componentNames: { + print(JSON.stringify([ + ...root.centerLeftWidgets, + ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), + ], null, 2)); + return [ + ...root.centerLeftWidgets, + ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), + ]; + } } } @@ -33,7 +48,11 @@ Item { anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined HBarUserFallbackComponentRepeater { - componentNames: C.Config.options.hefty.bar.centerWidgets + componentNames: [ + ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), + ...root.centerWidgets, + ...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []), + ] } } @@ -42,7 +61,10 @@ Item { anchors.left: !root.vertical ? centerSide.right : parent.left anchors.top: root.vertical ? parent.top : undefined HBarUserFallbackComponentRepeater { - componentNames: C.Config.options.hefty.bar.centerRightWidgets + componentNames: [ + ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), + ...root.centerRightWidgets, + ] } } @@ -51,7 +73,7 @@ Item { anchors.right: parent.right anchors.bottom: parent.bottom HBarUserFallbackComponentRepeater { - componentNames: C.Config.options.hefty.bar.rightWidgets + componentNames: root.rightWidgets } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml index fc8f2b863..a96eeb32b 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -22,10 +22,17 @@ Item { implicitWidth: vertical ? barUndirectionalWidth : layout.implicitWidth + padding * 2 implicitHeight: vertical ? layout.implicitHeight + padding * 2 : barUndirectionalWidth + property alias startRadius: bg.startRadius + property alias endRadius: bg.endRadius + property alias topLeftRadius: bg.topLeftRadius + property alias topRightRadius: bg.topRightRadius + property alias bottomLeftRadius: bg.bottomLeftRadius + property alias bottomRightRadius: bg.bottomRightRadius + W.AxisRectangle { id: bg anchors.centerIn: parent - contentLayer: W.StyledRectangle.ContentLayer.Pane + contentLayer: W.StyledRectangle.ContentLayer.Group width: root.vertical ? root.backgroundUndirectionalWidth : root.width height: root.vertical ? root.height : root.backgroundUndirectionalWidth diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml index 0961058f3..9d265c81b 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml @@ -7,21 +7,25 @@ import qs.modules.common.widgets as W Repeater { id: root + readonly property string invisibleItem: "_invisible" required property list componentNames property string context: Quickshell.shellPath("modules/hefty/topLayer/bar/widgets") + model: { const m = componentNames.map(item => { if (item instanceof Array) return ({"type": "container", "value": item}); + else if (item === root.invisibleItem) + return ({"type": "invisible", "value": item}); else return ({"type": "component", "value": item}); }); for (var i = 0;i < m.length; i++) { const item = m[i]; if (item.type === "container") { - item.startSide = (i === 0) || (m[i - 1].type !== "container"); - item.endSide = (i + 1 >= m.length) || (m[i + 1].type !== "container"); + item.startSide = (i === 0) || (m[i - 1].type === "component"); + item.endSide = (i + 1 >= m.length) || (m[i + 1].type === "component"); } } // print(JSON.stringify(m, null, 2)); @@ -29,6 +33,11 @@ Repeater { } delegate: DelegateChooser { role: "type" + + DelegateChoice { + roleValue: root.invisibleItem + delegate: Item { visible: false } + } DelegateChoice { roleValue: "component" diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml new file mode 100644 index 000000000..de2d1f8a0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -0,0 +1,91 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.services as S +import qs.modules.common.widgets as W + +W.ButtonMouseArea { + id: root + + property bool vertical: C.Config.options.bar.vertical + property bool showPopup: false + + property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide") + property real layoutParentTopLeftRadius: layoutParent.topLeftRadius + property real layoutParentTopRightRadius: layoutParent.topRightRadius + property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius + property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius + + readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight + property var activeContent: vertical ? verticalContent : horizontalContent + property real parentRadiusToPaddingRatio: 0.3 + implicitWidth: vertical ? barThickness : (activeContent.implicitWidth + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + implicitHeight: !vertical ? barThickness : (activeContent.implicitHeight + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + + onClicked: showPopup = !showPopup + + W.StateOverlay { + id: hoverOverlay + anchors.fill: parent + property real parentMargins: 4 + property real ownMargins: 2 + property real edgeMargins: parentMargins + ownMargins + property real sideMargins: -2 + anchors { + leftMargin: root.vertical ? edgeMargins : sideMargins + rightMargin: root.vertical ? edgeMargins : sideMargins + topMargin: root.vertical ? sideMargins : edgeMargins + bottomMargin: root.vertical ? sideMargins : edgeMargins + } + topLeftRadius: root.layoutParentTopLeftRadius - ownMargins + topRightRadius: root.layoutParentTopRightRadius - ownMargins + bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins + bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins + + hover: root.containsMouse + press: root.containsPress + } + + W.FadeLoader { + id: horizontalContent + anchors.fill: parent + shown: !root.vertical + sourceComponent: RowLayout { + anchors.fill: parent + + W.StyledText { + Layout.leftMargin: root.layoutParentTopLeftRadius * root.parentRadiusToPaddingRatio + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.time + } + + W.StyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: "•" + } + + W.StyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.longDate + } + } + } + + W.FadeLoader { + id: verticalContent + anchors.fill: parent + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 469de3155..4625ed9a6 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -36,8 +36,9 @@ Item { Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter Layout.fillWidth: vertical Layout.fillHeight: !vertical - implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : occupiedIndicators.implicitWidth - implicitHeight: vertical ? occupiedIndicators.implicitHeight : Appearance.sizes.barHeight + readonly property real barThickness: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight + implicitWidth: vertical ? barThickness : occupiedIndicators.implicitWidth + implicitHeight: vertical ? occupiedIndicators.implicitHeight : barThickness property real specialBlur: (wsModel.specialWorkspaceActive && !interactionMouseArea.containsMouse) ? 1 : 0 Behavior on specialBlur { @@ -316,8 +317,8 @@ Item { component WorkspaceItem: Item { required property int index readonly property int wsId: wsModel.getWorkspaceIdAt(index) - implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : root.workspaceButtonWidth - implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight + implicitWidth: root.vertical ? root.barThickness : root.workspaceButtonWidth + implicitHeight: root.vertical ? root.workspaceButtonWidth : root.barThickness } component NumberWorkspaceItem: WorkspaceItem { diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml new file mode 100644 index 000000000..08b2d0fb5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml @@ -0,0 +1,6 @@ +pragma ComponentBehavior: Bound +import QtQuick + +Item { + visible: false +} diff --git a/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml index 1d4b9f865..eb63fc4c0 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml @@ -6,7 +6,6 @@ import QtQuick.Layouts Item { id: root - property bool borderless: Config.options.bar.borderless property bool showDate: Config.options.bar.verbose implicitWidth: rowLayout.implicitWidth implicitHeight: Appearance.sizes.barHeight From e1913d0e95dbd63af7152e4967e19151cd93e7d2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 23:52:31 +0100 Subject: [PATCH 017/104] hefty: bar: ws: improve hover indicator readability --- .../ii/modules/hefty/topLayer/bar/HBarContent.qml | 10 ++-------- .../modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 4 +++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index cd0fe80ea..ebd79a96a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -30,16 +30,10 @@ Item { anchors.right: !root.vertical ? centerSide.left : parent.right anchors.bottom: root.vertical ? parent.bottom : undefined HBarUserFallbackComponentRepeater { - componentNames: { - print(JSON.stringify([ + componentNames: [ ...root.centerLeftWidgets, ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), - ], null, 2)); - return [ - ...root.centerLeftWidgets, - ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), - ]; - } + ] } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 4625ed9a6..69514ebea 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -136,6 +136,7 @@ Item { /////////////////// Hover /////////////////// ButtonMouseArea { id: interactionMouseArea + z: 3 anchors.fill: parent cursorShape: Qt.PointingHandCursor hoverEnabled: true @@ -162,7 +163,8 @@ Item { radius: root.activeWorkspaceSize / 2 hover: interactionMouseArea.containsMouse press: interactionMouseArea.containsPress - contentColor: Appearance.colors.colPrimary + drag: true // There are too many layers so we need to force this to be a lil more opaque + contentColor: Appearance.colors.colTertiary } } } From 6bb0c251a999cf316a3b807e010f005d8a762ef0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Feb 2026 09:45:54 +0100 Subject: [PATCH 018/104] thicker text weight --- dots/.config/quickshell/ii/modules/common/Appearance.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index 34663f066..fbe362075 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -224,7 +224,7 @@ Singleton { } property QtObject variableAxes: QtObject { property var main: ({ - "wght": 450, + "wght": 500, "wdth": 100, }) property var numbers: ({ From c2f65e79bc39d574fe59087e4f0387af3f2fb36c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Feb 2026 09:46:21 +0100 Subject: [PATCH 019/104] hefty: bar: ws: consistent icons & bouncy icon dodge anim --- .../topLayer/bar/widgets/HWorkspaces.qml | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 69514ebea..c5915fb7b 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -164,7 +164,7 @@ Item { hover: interactionMouseArea.containsMouse press: interactionMouseArea.containsPress drag: true // There are too many layers so we need to force this to be a lil more opaque - contentColor: Appearance.colors.colTertiary + contentColor: Appearance.colors.colPrimary } } } @@ -221,16 +221,13 @@ Item { visible: false // Prevent dupe: the colorizer already copies the icon source: wsApp.mainAppIconSource - implicitSize: NumberUtils.roundToEven((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) + implicitSize: NumberUtils.roundToEven(root.workspaceIconSize) Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } Behavior on cornerMargin { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on implicitSize { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) } } @@ -254,10 +251,14 @@ Item { (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 visible: opacity > 0 + scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } + Behavior on scale { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } maskEnabled: true maskSource: iconMask @@ -304,10 +305,10 @@ Item { /////////////////// Components /////////////////// component WorkspaceLayout: Grid { anchors { - top: !vertical ? parent.top : undefined - bottom: !vertical ? parent.bottom : undefined - left: vertical ? parent.left : undefined - right: vertical ? parent.right : undefined + top: !root.vertical ? parent.top : undefined + bottom: !root.vertical ? parent.bottom : undefined + left: root.vertical ? parent.left : undefined + right: root.vertical ? parent.right : undefined } rowSpacing: 0 @@ -372,9 +373,10 @@ Item { StyledRectangle { id: indicatorRect + anchors { - verticalCenter: vertical ? undefined : parent.verticalCenter - horizontalCenter: vertical ? parent.horizontalCenter : undefined + verticalCenter: root.vertical ? undefined : parent.verticalCenter + horizontalCenter: root.vertical ? parent.horizontalCenter : undefined } property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin From 10491f23627a597ffa3dbabbd99b120064f5ebcc Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Feb 2026 09:46:31 +0100 Subject: [PATCH 020/104] kb focus fix from main --- .../quickshell/ii/modules/ii/overview/Overview.qml | 2 +- .../ii/modules/ii/sidebarRight/SidebarRight.qml | 3 +-- .../modules/ii/wallpaperSelector/WallpaperSelector.qml | 1 + .../.config/quickshell/ii/services/GlobalFocusGrab.qml | 10 +++++++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index d94404721..fe3df64a3 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -24,7 +24,7 @@ Scope { WlrLayershell.namespace: "quickshell:overview" WlrLayershell.layer: WlrLayer.Top - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" mask: Region { diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml index 69bc07c03..73620241b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml @@ -22,8 +22,7 @@ Scope { exclusiveZone: 0 implicitWidth: sidebarWidth WlrLayershell.namespace: "quickshell:sidebarRight" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.keyboardFocus: GlobalStates.sidebarRightOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" anchors { diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml index 5bd79b56b..9d9ba9008 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml @@ -25,6 +25,7 @@ Scope { exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "quickshell:wallpaperSelector" WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand color: "transparent" anchors.top: true diff --git a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml index 547d79e17..6385d3df7 100644 --- a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml +++ b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml @@ -52,9 +52,17 @@ Singleton { } } + function hasActive(element) { + return element?.activeFocus || Array.from( + element?.children + ).some( + (child) => hasActive(child) + ); + } + HyprlandFocusGrab { id: grab - windows: [...root.persistent, ...root.dismissable] + windows: root.dismissable.every(w => !w?.focusable) || root.dismissable.some(w => hasActive(w?.contentItem)) ? [...root.dismissable, ...root.persistent] : [...root.dismissable] active: root.dismissable.length > 0 onCleared: () => { root.dismiss(); From 314b0ab3d02ee2038455582bdec55af847d66d3c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:00:35 +0100 Subject: [PATCH 021/104] hefty: bar: center time --- .../widgets/VisuallyCenteredStyledText.qml | 38 +++++++++++++++++++ .../hefty/topLayer/bar/widgets/HTime.qml | 4 +- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml new file mode 100644 index 000000000..77e353ce6 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml @@ -0,0 +1,38 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + property alias text: textWidget.text + property alias horizontalAlignment: textWidget.horizontalAlignment + property alias verticalAlignment: textWidget.verticalAlignment + property alias font: textWidget.font + property alias color: textWidget.color + + // In many cases the baseline is a bit high to accomodate the dangling parts of "g" and "y", + // making most text (especiall number-only text) not well-balanced. + // This adjusts the rounding to make sure the text gets lowered if not internally pixel-aligned + property bool lowerBias: true + + implicitWidth: textMetrics.width + implicitHeight: textMetrics.height + + TextMetrics { + id: textMetrics + font: root.font + text: root.text + } + + StyledText { + id: textWidget + anchors.horizontalCenter: parent.horizontalCenter + y: { + const value = (parent.height - textMetrics.height) / 2; + return root.lowerBias ? Math.ceil(value) : Math.round(value); + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index de2d1f8a0..a0cb3a6b3 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -59,10 +59,10 @@ W.ButtonMouseArea { sourceComponent: RowLayout { anchors.fill: parent - W.StyledText { + W.VisuallyCenteredStyledText { Layout.leftMargin: root.layoutParentTopLeftRadius * root.parentRadiusToPaddingRatio Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter + Layout.fillHeight: true font.pixelSize: C.Appearance.font.pixelSize.large color: C.Appearance.colors.colOnLayer1 text: S.DateTime.time From be4eb16b604091ed7542e845351dfdd21bcc016d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:29:49 +0100 Subject: [PATCH 022/104] clearer session screen download warning, update check config option --- .../quickshell/ii/modules/common/Config.qml | 1 + .../ii/sessionScreen/SessionScreen.qml | 13 ++++++---- .../ii/modules/settings/ServicesConfig.qml | 26 +++++++++++++++++++ .../quickshell/ii/services/Updates.qml | 4 +-- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 64fcc7a85..659c3eb22 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -599,6 +599,7 @@ Singleton { } property JsonObject updates: JsonObject { + property bool enableCheck: true property int checkInterval: 120 // minutes property int adviseUpdateThreshold: 75 // packages property int stronglyAdviseUpdateThreshold: 200 // packages diff --git a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml index ee8714793..78b93c4d0 100644 --- a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml +++ b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml @@ -238,7 +238,7 @@ Scope { } } - RowLayout { + ColumnLayout { anchors { top: contentColumn.bottom topMargin: 10 @@ -247,19 +247,22 @@ Scope { spacing: 10 Loader { - active: SessionWarnings.packageManagerRunning + Layout.alignment: Qt.AlignHCenter + active: SessionWarnings.downloadRunning visible: active sourceComponent: DescriptionLabel { - text: Translation.tr("Your package manager is running") + text: Translation.tr("There might be a download in progress. Check your Downloads folder.") textColor: Appearance.m3colors.m3onErrorContainer color: Appearance.m3colors.m3errorContainer } } + Loader { - active: SessionWarnings.downloadRunning + Layout.alignment: Qt.AlignHCenter + active: SessionWarnings.packageManagerRunning visible: active sourceComponent: DescriptionLabel { - text: Translation.tr("There might be a download in progress") + text: Translation.tr("Your package manager is running") textColor: Appearance.m3colors.m3onErrorContainer color: Appearance.m3colors.m3errorContainer } diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index f5931bb1b..aec8dc98a 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -203,6 +203,32 @@ ContentPage { } } + // There's no update indicator in ii for now so we shouldn't show this yet + // ContentSection { + // icon: "deployed_code_update" + // title: Translation.tr("System updates (Arch only)") + + // ConfigSwitch { + // text: Translation.tr("Enable update checks") + // checked: Config.options.updates.enableCheck + // onCheckedChanged: { + // Config.options.updates.enableCheck = checked; + // } + // } + + // ConfigSpinBox { + // icon: "av_timer" + // text: Translation.tr("Check interval (mins)") + // value: Config.options.updates.checkInterval + // from: 60 + // to: 1440 + // stepSize: 60 + // onValueChanged: { + // Config.options.updates.checkInterval = value; + // } + // } + // } + ContentSection { icon: "weather_mix" title: Translation.tr("Weather") diff --git a/dots/.config/quickshell/ii/services/Updates.qml b/dots/.config/quickshell/ii/services/Updates.qml index 48549ac3c..ee9093143 100644 --- a/dots/.config/quickshell/ii/services/Updates.qml +++ b/dots/.config/quickshell/ii/services/Updates.qml @@ -29,7 +29,7 @@ Singleton { Timer { interval: Config.options.updates.checkInterval * 60 * 1000 repeat: true - running: Config.ready + running: Config.ready && Config.options.updates.enableCheck onTriggered: { print("[Updates] Periodic update check due") root.refresh(); @@ -38,7 +38,7 @@ Singleton { Process { id: checkAvailabilityProc - running: true + running: Config.ready && Config.options.updates.enableCheck command: ["which", "checkupdates"] onExited: (exitCode, exitStatus) => { root.available = (exitCode === 0); From aec849c820fab6346fd2c8204044aabeb03f3a7e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:31:14 +0100 Subject: [PATCH 023/104] hefty: bar: cleaner hover anim when in special ws --- .../ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index c5915fb7b..6a05a822d 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -272,9 +272,12 @@ Item { FadeLoader { anchors.centerIn: parent - shown: wsModel.specialWorkspaceActive && root.specialBlur > 0 + shown: wsModel.specialWorkspaceActive scale: 0.8 + 0.2 * root.specialBlur + opacity: root.specialBlur + Behavior on opacity {} // Don't animate, as specialBlur is already animated + sourceComponent: Pill { anchors.centerIn: parent property real undirectionalWidth: root.activeWorkspaceSize From 6fd2bd51adcadec305d1337c3b650fc093077781 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Feb 2026 23:54:04 +0100 Subject: [PATCH 024/104] hefty: bar: ws: proper occupied ws detection for numbers/dots --- .../ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 6a05a822d..bfe947553 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -330,7 +330,8 @@ Item { component NumberWorkspaceItem: WorkspaceItem { id: wsNum property bool hasBiggestWindow: !!wsModel.biggestWindow[index] - property color contentColor: wsModel.occupied[wsNum.index] ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive + property int wsId: wsModel.getWorkspaceIdAt(index) + property color contentColor: (wsModel.occupied[wsNum.index] && wsId !== wsModel.fakeWorkspace) ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive FadeLoader { shown: !(Config.options?.bar.workspaces.alwaysShowNumbers From 7ba30cee4a3fa056c5c1744ebfd095b1c01f0f77 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:41:33 +0100 Subject: [PATCH 025/104] hefty: bar: ws: nicer ws icon anim on populate --- .../ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index bfe947553..30878bf44 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -208,7 +208,7 @@ Item { AppIcon { id: appIcon - property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? + property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons && wsApp.biggestWindow) ? (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked anchors { bottom: parent.bottom From 1c295ddaac5d976543a9fdff61c42346d1b2922e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:42:03 +0100 Subject: [PATCH 026/104] accent color preventing wall switch fix from main --- dots/.config/quickshell/ii/scripts/colors/switchwall.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 430114d5b..1ae164d0c 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -406,6 +406,12 @@ main() { imgpath="$(kdialog --getopenfilename . --title 'Choose wallpaper')" fi + if [[ -n "$imgpath" && -z "$noswitch_flag" ]]; then + set_accent_color "" + color_flag="" + color="" + fi + # If type_flag is 'auto', detect scheme type from image (after imgpath is set) if [[ "$type_flag" == "auto" ]]; then if [[ -n "$imgpath" && -f "$imgpath" ]]; then From 3447198e1300f6dcb77df7e1db5807033bed8b0d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:39:48 +0100 Subject: [PATCH 027/104] Update shapes --- dots/.config/quickshell/ii/modules/common/widgets/shapes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/shapes b/dots/.config/quickshell/ii/modules/common/widgets/shapes index b62e4482a..e31ec4cb4 160000 --- a/dots/.config/quickshell/ii/modules/common/widgets/shapes +++ b/dots/.config/quickshell/ii/modules/common/widgets/shapes @@ -1 +1 @@ -Subproject commit b62e4482af56722840dd4b86c4dab41d5c9f2dda +Subproject commit e31ec4cb4ebf6a46b267f5c42eabf6874916fa16 From 6f5ab232a60eb9a0f45c92f3f8dac44893cecf6c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:40:23 +0100 Subject: [PATCH 028/104] hefty: bar: time: popout --- .../ii/modules/common/config/HeftyConfig.qml | 8 +- .../common/models/AnimatedTabIndexPair.qml | 2 +- .../modules/common/widgets/AxisRectangle.qml | 2 - .../widgets/AxisRectangularContainerShape.qml | 12 + .../widgets/RectangularContainerShape.qml | 91 +++ .../common/widgets/StyledRectangle.qml | 1 + .../hefty/topLayer/HAbstractMorphedPanel.qml | 37 +- .../modules/hefty/topLayer/HTopLayerPanel.qml | 4 +- .../hefty/topLayer/bar/HBarGroupContainer.qml | 37 +- .../modules/hefty/topLayer/bar/HBarPopout.qml | 42 ++ .../bar/HBarUserFallbackComponentRepeater.qml | 13 +- .../topLayer/bar/HBarWidgetContainer.qml | 9 + .../hefty/topLayer/bar/widgets/HTime.qml | 267 ++++++-- .../topLayer/bar/widgets/HWorkspaces.qml | 620 +++++++++--------- 14 files changed, 739 insertions(+), 406 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/AxisRectangularContainerShape.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/RectangularContainerShape.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarPopout.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContainer.qml diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 509d4dccf..131e04e41 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -4,10 +4,10 @@ import Quickshell.Io JsonObject { property JsonObject bar: JsonObject { - property list leftWidgets: [["HWindowInfo"]] - property list centerLeftWidgets: [["HTime"]] - property list centerWidgets: [["HWorkspaces"]] - property list centerRightWidgets: [["HBattery"]] + property list leftWidgets: ["HWindowInfo"] + property list centerLeftWidgets: ["HTime"] + property list centerWidgets: ["HWorkspaces"] + property list centerRightWidgets: ["HBattery"] property list rightWidgets: [] property bool m3ExpressiveGrouping: true } diff --git a/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml b/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml index c18e9ccf2..edb86c406 100644 --- a/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml +++ b/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml @@ -4,7 +4,7 @@ import QtQuick // The former animates faster than the latter, see the NumberAnimations below QtObject { id: root - required property int index + property int index property real idx1: index property real idx2: index diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml index d54594ecd..73899ac0c 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangle.qml @@ -2,8 +2,6 @@ pragma ComponentBehavior: Bound import QtQuick StyledRectangle { - id: root - property bool vertical: false property real startRadius property real endRadius diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangularContainerShape.qml b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangularContainerShape.qml new file mode 100644 index 000000000..d218be004 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/AxisRectangularContainerShape.qml @@ -0,0 +1,12 @@ +import QtQuick + +RectangularContainerShape { + property bool vertical: false + property real startRadius + property real endRadius + + topLeftRadius: startRadius + topRightRadius: vertical ? startRadius : endRadius + bottomLeftRadius: vertical ? endRadius : startRadius + bottomRightRadius: endRadius +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/RectangularContainerShape.qml b/dots/.config/quickshell/ii/modules/common/widgets/RectangularContainerShape.qml new file mode 100644 index 000000000..d07578d6f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/RectangularContainerShape.qml @@ -0,0 +1,91 @@ +import QtQuick +import qs.modules.common.models as M +import "shapes/material-shapes.js" as MaterialShapes +import "shapes/shapes/corner-rounding.js" as CornerRounding +import "shapes/geometry/offset.js" as Offset + +// For returning the points +M.NestableObject { + id: root + + required property real width + required property real height + property real radius: 0 + property real topLeftRadius: radius + property real topRightRadius: radius + property real bottomLeftRadius: radius + property real bottomRightRadius: radius + property real xOffset: 0 + property real yOffset: 0 + + readonly property real radiusLimit: Math.min(width, height) / 2 + readonly property real effectiveTopLeftRadius: Math.min(topLeftRadius, radiusLimit) + readonly property real effectiveTopRightRadius: Math.min(topRightRadius, radiusLimit) + readonly property real effectiveBottomLeftRadius: Math.min(bottomLeftRadius, radiusLimit) + readonly property real effectiveBottomRightRadius: Math.min(bottomRightRadius, radiusLimit) + + // Clockwise starting from bottom + property list bottomPoints: [ + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width - effectiveBottomRightRadius, yOffset + height), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width / 2, yOffset + height), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + effectiveBottomLeftRadius, yOffset + height), new CornerRounding.CornerRounding(0)), + ] + property list leftPoints: [ + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + height - effectiveBottomLeftRadius), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + height / 2), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + effectiveTopLeftRadius), new CornerRounding.CornerRounding(0)), + ] + property list topPoints: [ + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + effectiveTopLeftRadius, yOffset + 0), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width / 2, yOffset + 0), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width - effectiveTopRightRadius, yOffset + 0), new CornerRounding.CornerRounding(0)), + ] + property list rightPoints: [ + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + effectiveTopRightRadius), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + height / 2), new CornerRounding.CornerRounding(0)), + new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + height - effectiveBottomRightRadius), new CornerRounding.CornerRounding(0)), + ] + + function getFirstBottomPoints() { + return bottomPoints.slice(Math.floor(bottomPoints.length / 2)) + } + + function getLastBottomPoints() { + return bottomPoints.slice(0, Math.floor(bottomPoints.length / 2)) + } + + function getBottomLeftPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) { + if (radius === undefined) radius = effectiveBottomLeftRadius; + return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + 0, yOffset + extraYOffset + height), new CornerRounding.CornerRounding(radius)) + } + + function getTopLeftPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) { + if (radius === undefined) radius = effectiveTopLeftRadius; + return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + 0, yOffset + extraYOffset + 0), new CornerRounding.CornerRounding(radius)) + } + + function getTopRightPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) { + if (radius === undefined) radius = effectiveTopRightRadius; + return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + width, yOffset + extraYOffset + 0), new CornerRounding.CornerRounding(radius)) + } + + function getBottomRightPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) { + if (radius === undefined) radius = effectiveBottomRightRadius; + return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + width, yOffset + extraYOffset + height), new CornerRounding.CornerRounding(radius)) + } + + function getFullShape() { + const points = [ + ...getFirstBottomPoints(), + getBottomLeftPoint(), + ...leftPoints, + getTopLeftPoint(), + ...topPoints, + getTopRightPoint(), + ...rightPoints, + getBottomRightPoint(), + ...getLastBottomPoints(), + ] + return MaterialShapes.customPolygon(points); + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml index 1997dafcb..da5d3048a 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledRectangle.qml @@ -6,6 +6,7 @@ import qs.modules.common as C // - osk.sh // - 3d // i hope i actually get to this and not shrimply forget +// aaaaa i realized for this to work i would have to make this for shapes in general not just rects Rectangle { enum ContentLayer { Background, Pane, Group, Subgroup, Control } property var contentLayer: StyledRectangle.ContentLayer.Pane // To appropriately add effects like shadows/3d-ization diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml index 5eb6e21aa..fa3486951 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HAbstractMorphedPanel.qml @@ -1,5 +1,6 @@ import QtQuick import Quickshell +import qs.services as S /** * Abstract morphed panel to be used in TopLayerPanel. @@ -21,6 +22,7 @@ Item { // Signals & loading signal requestFocus() signal dismissed() + signal focusGrabDismissed() property bool load: true property bool shown: true @@ -32,7 +34,40 @@ Item { // Main stuff property var backgroundPolygon + property list baseMaskItems: [root] + property list attachedMaskItems: [] + property list maskItems: [...baseMaskItems, ...attachedMaskItems] property Region maskRegion: Region { - item: root + regions: root.maskItems.map(item => regionComp.createObject(this, { "item": item })) + } + + function addAttachedMaskItem(item) { + if (root.attachedMaskItems.includes(item)) return; + root.attachedMaskItems.push(item); + } + + function removeAttachedMaskItem(item) { + root.attachedMaskItems = root.attachedMaskItems.filter(i => i !== item); + } + + onAttachedMaskItemsChanged: { + if (attachedMaskItems.length > 0) { + S.GlobalFocusGrab.addDismissable(root.QsWindow.window); + } else { + S.GlobalFocusGrab.removeDismissable(root.QsWindow.window); + } + } + + Connections { + target: S.GlobalFocusGrab + function onDismissed() { + root.attachedMaskItems = []; + root.focusGrabDismissed(); + } + } + + Component { + id: regionComp + Region {} } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index ff1600c9d..ab2981707 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -29,9 +29,7 @@ PanelWindow { bottom: true } - mask: Region { - item: root.currentPanel - } + mask: root.currentPanel.maskRegion // HyprlandWindow.visibleMask: mask // TODO: use this later to optimize hyprland's rendering ///////////////// Content ////////////////// diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml index a96eeb32b..91202f218 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -28,30 +28,32 @@ Item { property alias topRightRadius: bg.topRightRadius property alias bottomLeftRadius: bg.bottomLeftRadius property alias bottomRightRadius: bg.bottomRightRadius + property real backgroundWidth: root.vertical ? root.backgroundUndirectionalWidth : root.width + property real backgroundHeight: root.vertical ? root.height : root.backgroundUndirectionalWidth + property real fullBackgroundRadius: Math.min(backgroundWidth, backgroundHeight) / 2 + function getBackgroundRadius(atSide) { + if (root.m3eRadius) { + if (atSide) return fullBackgroundRadius; + else return C.Appearance.rounding.unsharpenmore; + } else { + return 12; + } + } - W.AxisRectangle { + property Item background: W.AxisRectangle { id: bg anchors.centerIn: parent contentLayer: W.StyledRectangle.ContentLayer.Group - width: root.vertical ? root.backgroundUndirectionalWidth : root.width - height: root.vertical ? root.height : root.backgroundUndirectionalWidth + width: root.backgroundWidth + height: root.backgroundHeight - property real fullRadius: Math.min(width, height) / 2 - function getRadius(atSide) { - if (root.m3eRadius) { - if (atSide) return fullRadius; - else return C.Appearance.rounding.unsharpenmore; - } else { - return 12; - } - } vertical: root.vertical - startRadius: getRadius(root.startSide) - endRadius: getRadius(root.endSide) + startRadius: root.getBackgroundRadius(root.startSide) + endRadius: root.getBackgroundRadius(root.endSide) } - GridLayout { + property Item contentItem: GridLayout { id: layout columns: C.Config.options.bar.vertical ? 1 : -1 anchors.centerIn: parent @@ -59,4 +61,9 @@ Item { columnSpacing: spacing rowSpacing: spacing } + + children: [ + background, + contentItem + ] } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarPopout.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarPopout.qml new file mode 100644 index 000000000..1f568dd51 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarPopout.qml @@ -0,0 +1,42 @@ +pragma ComponentBehavior: Bound +import QtQuick +import qs.modules.common as C +import qs.modules.common.widgets as W + +W.StyledRectangle { + id: root + contentLayer: W.StyledRectangle.ContentLayer.Pane + + color: C.Appearance.colors.colLayer2Base + + transitions: Transition { + AnchorAnimation { + duration: C.Appearance.animation.elementMove.duration + easing.type: C.Appearance.animation.elementMove.type + easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve + } + ColorAnimation { + duration: C.Appearance.animation.elementMoveFast.duration + easing.type: C.Appearance.animation.elementMoveFast.type + easing.bezierCurve: C.Appearance.animation.elementMoveFast.bezierCurve + } + PropertyAnimation { + properties: "topLeftRadius,topRightRadius,bottomLeftRadius,bottomRightRadius,intendedWidth,intendedHeight" + duration: C.Appearance.animation.elementMove.duration + easing.type: C.Appearance.animation.elementMove.type + easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve + } + PropertyAnimation { + properties: "opacity" + duration: C.Appearance.animation.elementMoveFast.duration + easing.type: C.Appearance.animation.elementMoveFast.type + easing.bezierCurve: C.Appearance.animation.elementMoveFast.bezierCurve + } + } + + W.StyledRectangularShadow { + target: root + z: -1 + radius: root.topLeftRadius + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml index 9d265c81b..7cc44ba89 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml @@ -23,9 +23,9 @@ Repeater { }); for (var i = 0;i < m.length; i++) { const item = m[i]; - if (item.type === "container") { - item.startSide = (i === 0) || (m[i - 1].type === "component"); - item.endSide = (i + 1 >= m.length) || (m[i + 1].type === "component"); + if (item.type === "container" || item.type === "component") { + item.startSide = (i === 0) || (m[i - 1].type !== m[i].type); + item.endSide = (i + 1 >= m.length) || (m[i + 1].type !== m[i].type); } } // print(JSON.stringify(m, null, 2)); @@ -43,8 +43,11 @@ Repeater { roleValue: "component" delegate: W.UserFallbackLoader { required property var modelData + required property int index componentName: modelData.value context: root.context + property bool startSide: index === 0 + property bool endSide: index === root.model.length - 1 } } @@ -57,11 +60,15 @@ Repeater { endSide: modelData.endSide Repeater { + id: containerRepeater model: group.modelData.value delegate: W.UserFallbackLoader { required property var modelData + required property int index componentName: modelData context: root.context + property bool startSide: index === 0 + property bool endSide: index === group.modelData.value.length - 1 } } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContainer.qml new file mode 100644 index 000000000..666ed98d7 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContainer.qml @@ -0,0 +1,9 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common as C +import qs.modules.common.widgets as W + +HBarGroupContainer { + startSide: parent.startSide ?? true + endSide: parent.endSide ?? true +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index a0cb3a6b3..cfbb1fc86 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -1,91 +1,226 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts +import Quickshell import qs.modules.common as C import qs.modules.common.functions as F import qs.services as S import qs.modules.common.widgets as W +import qs.modules.common.widgets.shapes as Shapes +import ".." +import "../../../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../../../common/widgets/shapes/geometry/offset.js" as Offset -W.ButtonMouseArea { - id: root +HBarWidgetContainer { + id: containerRoot - property bool vertical: C.Config.options.bar.vertical - property bool showPopup: false + // Interactions + property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") + Connections { + target: root + function onShowPopupChanged() { + if (root.showPopup) { + morphedPanelParent.addAttachedMaskItem(bgShape); + } else { + morphedPanelParent.removeAttachedMaskItem(bgShape); + } + } + } + Connections { + target: morphedPanelParent + function onFocusGrabDismissed() { + root.showPopup = false; + } + } - property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide") - property real layoutParentTopLeftRadius: layoutParent.topLeftRadius - property real layoutParentTopRightRadius: layoutParent.topRightRadius - property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius - property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius - - readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight - property var activeContent: vertical ? verticalContent : horizontalContent - property real parentRadiusToPaddingRatio: 0.3 - implicitWidth: vertical ? barThickness : (activeContent.implicitWidth + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) - implicitHeight: !vertical ? barThickness : (activeContent.implicitHeight + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) - Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter - Layout.fillWidth: vertical - Layout.fillHeight: !vertical - - onClicked: showPopup = !showPopup - - W.StateOverlay { - id: hoverOverlay - anchors.fill: parent - property real parentMargins: 4 - property real ownMargins: 2 - property real edgeMargins: parentMargins + ownMargins - property real sideMargins: -2 + // Background container shape + background: Shapes.ShapeCanvas { + id: bgShape + property real baseTopMargin: (parent.height - containerShape.height) / 2 anchors { - leftMargin: root.vertical ? edgeMargins : sideMargins - rightMargin: root.vertical ? edgeMargins : sideMargins - topMargin: root.vertical ? sideMargins : edgeMargins - bottomMargin: root.vertical ? sideMargins : edgeMargins + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: { + if (!root.atBottom || !root.showPopup) return baseTopMargin; + else return baseTopMargin - popupShape.height - bgShape.spacing; + } } - topLeftRadius: root.layoutParentTopLeftRadius - ownMargins - topRightRadius: root.layoutParentTopRightRadius - ownMargins - bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins - bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins + width: root.showPopup ? Math.max(containerShape.width, popupShape.width) : containerShape.width + height: root.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height + color: root.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 + // color: "green" + // debug: true + xOffset: (width - containerShape.width) / 2 + yOffset: root.atBottom ? (height - containerShape.height) : 0 + animation: Anim {} - hover: root.containsMouse - press: root.containsPress + Behavior on width { + Anim {} + } + Behavior on height { + Anim {} + } + Behavior on anchors.topMargin { + Anim {} + } + + // Rectangle { + // anchors.fill: parent + // } + + polygonIsNormalized: false + property real spacing: baseTopMargin * 2 + W.AxisRectangularContainerShape { + id: containerShape + width: containerRoot.backgroundWidth + height: containerRoot.backgroundHeight + startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) + endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) + } + W.RectangularContainerShape { + id: popupShape + width: 400 // TODO + height: 500 // TODO + radius: C.Appearance.rounding.large + xOffset: -(width - containerShape.width) / 2 + yOffset: root.atBottom ? -(popupShape.height + bgShape.spacing) : (containerShape.height + bgShape.spacing) + } + + + roundedPolygon: { + if (!root.showPopup) return containerShape.getFullShape() + // return popupShape.getFullShape(); // debug + const points = [ + ...(root.atBottom ? containerShape.getFirstBottomPoints() : [ + ...popupShape.getFirstBottomPoints(), + popupShape.getBottomLeftPoint(), + ...popupShape.leftPoints, + popupShape.getTopLeftPoint(), + ]), + containerShape.getBottomLeftPoint(0, bgShape.spacing * (!root.atBottom ? 1 : 0), containerShape.radiusLimit), + // ...containerShape.leftPoints, + containerShape.getTopLeftPoint(0, bgShape.spacing * (root.atBottom ? -1 : 0), containerShape.radiusLimit), + ...(!root.atBottom ? containerShape.topPoints : [ + popupShape.getBottomLeftPoint(), + ...popupShape.leftPoints, + popupShape.getTopLeftPoint(), + ...popupShape.topPoints, + popupShape.getTopRightPoint(), + ...popupShape.rightPoints, + popupShape.getBottomRightPoint(), + ]), + containerShape.getTopRightPoint(0, bgShape.spacing * (root.atBottom ? -1 : 0), containerShape.radiusLimit), + // ...containerShape.rightPoints, + containerShape.getBottomRightPoint(0, bgShape.spacing * (!root.atBottom ? 1 : 0), containerShape.radiusLimit), + ...(root.atBottom ? containerShape.getLastBottomPoints() : [ + popupShape.getTopRightPoint(), + ...popupShape.rightPoints, + popupShape.getBottomRightPoint(), + ...popupShape.getLastBottomPoints(), + ]), + ]; + return MaterialShapes.customPolygon(points); + } + + component Anim: SpringAnimation { + spring: 3.5 + damping: 0.35 + } } - W.FadeLoader { - id: horizontalContent - anchors.fill: parent - shown: !root.vertical - sourceComponent: RowLayout { + // The button on the bar + W.ButtonMouseArea { + id: root + + property bool vertical: C.Config.options.bar.vertical + property bool atBottom: C.Config.options.bar.bottom + property bool showPopup: false + + property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide") + property real layoutParentTopLeftRadius: layoutParent.topLeftRadius + property real layoutParentTopRightRadius: layoutParent.topRightRadius + property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius + property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius + + readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight + property var activeContent: vertical ? verticalContent : horizontalContent + property real parentRadiusToPaddingRatio: 0.3 + implicitWidth: vertical ? barThickness : (activeContent.implicitWidth + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + implicitHeight: !vertical ? barThickness : (activeContent.implicitHeight + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + + onClicked: showPopup = !showPopup + + W.StateOverlay { + id: hoverOverlay anchors.fill: parent - - W.VisuallyCenteredStyledText { - Layout.leftMargin: root.layoutParentTopLeftRadius * root.parentRadiusToPaddingRatio - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter - Layout.fillHeight: true - font.pixelSize: C.Appearance.font.pixelSize.large - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.time + property real parentMargins: 4 + property real ownMargins: 2 + property real edgeMargins: parentMargins + ownMargins + property real sideMargins: -2 + anchors { + leftMargin: root.vertical ? edgeMargins : sideMargins + rightMargin: root.vertical ? edgeMargins : sideMargins + topMargin: root.vertical ? sideMargins : edgeMargins + bottomMargin: root.vertical ? sideMargins : edgeMargins } + topLeftRadius: root.layoutParentTopLeftRadius - ownMargins + topRightRadius: root.layoutParentTopRightRadius - ownMargins + bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins + bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins - W.StyledText { - Layout.alignment: Qt.AlignVCenter - font.pixelSize: C.Appearance.font.pixelSize.small - color: C.Appearance.colors.colOnLayer1 - text: "•" - } + hover: root.containsMouse + press: root.containsPress + } + + W.FadeLoader { + id: horizontalContent + anchors.fill: parent + shown: !root.vertical + + sourceComponent: Item { + anchors.fill: parent + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + + RowLayout { + id: contentLayout + anchors.fill: parent + + W.VisuallyCenteredStyledText { + Layout.leftMargin: root.layoutParentTopLeftRadius * root.parentRadiusToPaddingRatio + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.fillHeight: true + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.time + } + + W.StyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: "•" + } + + W.StyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.longDate + } + } - W.StyledText { - Layout.alignment: Qt.AlignVCenter - font.pixelSize: C.Appearance.font.pixelSize.small - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.longDate } } - } - W.FadeLoader { - id: verticalContent - anchors.fill: parent + W.FadeLoader { + id: verticalContent + anchors.fill: parent + } } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index 30878bf44..cf28e9232 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -10,297 +10,327 @@ import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Hyprland +import ".." -Item { - id: root - - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) - WorkspaceModel { - id: wsModel - monitor: root.monitor - } - - property bool vertical: Config.options.bar.vertical - property bool superPressAndHeld: false // Relevant modifications at bottom of file - - property real workspaceButtonWidth: 26 - property real activeWorkspaceMargin: 2 - property real activeWorkspaceSize: workspaceButtonWidth - activeWorkspaceMargin * 2 - property real workspaceIconSize: workspaceButtonWidth * 0.69 - property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 - property real workspaceIconOpacityShrinked: 1 - property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount - property real specialTextSize: workspaceButtonWidth * 0.5 - - Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter - Layout.fillWidth: vertical - Layout.fillHeight: !vertical - readonly property real barThickness: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight - implicitWidth: vertical ? barThickness : occupiedIndicators.implicitWidth - implicitHeight: vertical ? occupiedIndicators.implicitHeight : barThickness - - property real specialBlur: (wsModel.specialWorkspaceActive && !interactionMouseArea.containsMouse) ? 1 : 0 - Behavior on specialBlur { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - +HBarWidgetContainer { Item { - id: regularWorkspaces - anchors.fill: parent + id: root - scale: 1 - 0.08 * root.specialBlur - layer.smooth: true - layer.enabled: root.specialBlur > 0 - layer.effect: MultiEffect { - brightness: -0.1 * root.specialBlur - blurEnabled: true - blur: root.specialBlur - blurMax: 32 + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) + WorkspaceModel { + id: wsModel + monitor: root.monitor } - /////////////////// Occupied indicators /////////////////// - StyledRectangle { - id: occupiedIndicatorsBg + property bool vertical: Config.options.bar.vertical + property bool superPressAndHeld: false // Relevant modifications at bottom of file + + property real workspaceButtonWidth: 26 + property real activeWorkspaceMargin: 2 + property real activeWorkspaceSize: workspaceButtonWidth - activeWorkspaceMargin * 2 + property real workspaceIconSize: workspaceButtonWidth * 0.69 + property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 + property real workspaceIconOpacityShrinked: 1 + property real workspaceIconMarginShrinked: -4 + property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount + property real specialTextSize: workspaceButtonWidth * 0.5 + + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + readonly property real barThickness: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight + implicitWidth: vertical ? barThickness : occupiedIndicators.implicitWidth + implicitHeight: vertical ? occupiedIndicators.implicitHeight : barThickness + + property real specialBlur: (wsModel.specialWorkspaceActive && !interactionMouseArea.containsMouse) ? 1 : 0 + Behavior on specialBlur { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + + Item { + id: regularWorkspaces anchors.fill: parent - contentLayer: StyledRectangle.ContentLayer.Group - color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - visible: false - } - WorkspaceLayout { - id: occupiedIndicators - anchors.centerIn: parent - - layer.enabled: true - visible: false - - Repeater { - model: wsModel.shownCount - delegate: Item { - id: wsBg - required property int index - readonly property int wsId: wsModel.getWorkspaceIdAt(index) - property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace - property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace - property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace - implicitWidth: root.workspaceButtonWidth - implicitHeight: root.workspaceButtonWidth - - // The idea: over-stretch to occupied sides, animate this for a smooth transition. - // masking already prevents weird overlaps - Pill { - property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied - property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied - property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth - anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter - anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined - x: root.vertical ? 0 : undirectionalOffset - y: root.vertical ? undirectionalOffset : 0 - implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength - implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth - - Behavior on undirectionalWidth { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - Behavior on undirectionalLength { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - Behavior on undirectionalOffset { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - } - } - } - } - - MaskMultiEffect { - id: occupiedIndicatorsMultiEffect - z: 1 - anchors.centerIn: parent - implicitWidth: occupiedIndicators.implicitWidth - implicitHeight: occupiedIndicators.implicitHeight - source: occupiedIndicatorsBg - maskSource: occupiedIndicators - } - - /////////////////// Active indicator /////////////////// - TrailingIndicator { - id: activeIndicator - anchors.fill: parent - z: 2 - - index: root.workspaceIndexInGroup - } - - /////////////////// Hover /////////////////// - ButtonMouseArea { - id: interactionMouseArea - z: 3 - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - property int hoverIndex: { - const position = root.vertical ? mouseY : mouseX; - return Math.floor(position / root.workspaceButtonWidth); + scale: 1 - 0.08 * root.specialBlur + layer.smooth: true + layer.enabled: root.specialBlur > 0 + layer.effect: MultiEffect { + brightness: -0.1 * root.specialBlur + blurEnabled: true + blur: root.specialBlur + blurMax: 32 } - onPressed: Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) - onWheel: (event) => { - if (event.angleDelta.y < 0) - Hyprland.dispatch(`workspace r+1`); - else if (event.angleDelta.y > 0) - Hyprland.dispatch(`workspace r-1`); + /////////////////// Occupied indicators /////////////////// + StyledRectangle { + id: occupiedIndicatorsBg + anchors.fill: parent + contentLayer: StyledRectangle.ContentLayer.Group + color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) + visible: false } - TrailingIndicator { - id: interactionIndicator - index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup - color: "transparent" - StateOverlay { - id: hoverOverlay - anchors.fill: interactionIndicator.indicatorRectangle - radius: root.activeWorkspaceSize / 2 - hover: interactionMouseArea.containsMouse - press: interactionMouseArea.containsPress - drag: true // There are too many layers so we need to force this to be a lil more opaque - contentColor: Appearance.colors.colPrimary - } - } - } - - /////////////////// Numbers /////////////////// - WorkspaceLayout { - id: numbersGrid - z: 4 - layer.enabled: true // For the masking - - Repeater { - model: wsModel.shownCount - delegate: NumberWorkspaceItem {} - } - } - Colorizer { - z: 5 - anchors.fill: numbersGrid - colorizationColor: Appearance.colors.colOnPrimary - sourceColor: Appearance.colors.colOnSecondaryContainer - - source: activeIndicator - maskEnabled: true - maskSource: numbersGrid - - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - - /////////////////// App icons /////////////////// - WorkspaceLayout { - id: appsGrid - z: 6 - - Repeater { - model: wsModel.shownCount - delegate: WorkspaceItem { - id: wsApp - property var biggestWindow: wsModel.biggestWindow[index] - property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") - - AppIcon { - id: appIcon - property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons && wsApp.biggestWindow) ? - (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked - anchors { - bottom: parent.bottom - right: parent.right - bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin - rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin - } - - animated: !wsApp.biggestWindow // Prevent the "image-missing" icon - visible: false // Prevent dupe: the colorizer already copies the icon - - source: wsApp.mainAppIconSource - implicitSize: NumberUtils.roundToEven(root.workspaceIconSize) - - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on cornerMargin { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - } - - Circle { - id: iconMask - visible: false - layer.enabled: true - diameter: appIcon.implicitSize - } - - Colorizer { - anchors.fill: appIcon - implicitWidth: appIcon.implicitWidth - implicitHeight: appIcon.implicitHeight - colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary - colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 - brightness: 0 - source: appIcon - - opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : - (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? - 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 - visible: opacity > 0 - scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize - - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on scale { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - - maskEnabled: true - maskSource: iconMask - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - } - } - } - } - - FadeLoader { - anchors.centerIn: parent - shown: wsModel.specialWorkspaceActive - scale: 0.8 + 0.2 * root.specialBlur - - opacity: root.specialBlur - Behavior on opacity {} // Don't animate, as specialBlur is already animated - - sourceComponent: Pill { - anchors.centerIn: parent - property real undirectionalWidth: root.activeWorkspaceSize - property real undirectionalLength: { - const base = root.workspaceButtonWidth * Math.min(1.35, wsModel.shownCount) // Who tf only configures only 2 workspaces shown anyway? - if (root.vertical) return base; - return specialWsText.implicitWidth + undirectionalWidth - } - color: Appearance.colors.colPrimary - - implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength - implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth - - StyledText { - id: specialWsText + WorkspaceLayout { + id: occupiedIndicators anchors.centerIn: parent - text: (!root.vertical ? wsModel.specialWorkspaceName : "S") - color: Appearance.colors.colOnPrimary - font.pixelSize: root.specialTextSize + + layer.enabled: true + visible: false + + Repeater { + model: wsModel.shownCount + delegate: Item { + id: wsBg + required property int index + readonly property int wsId: wsModel.getWorkspaceIdAt(index) + property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace + property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace + property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace + implicitWidth: root.workspaceButtonWidth + implicitHeight: root.workspaceButtonWidth + + // The idea: over-stretch to occupied sides, animate this for a smooth transition. + // masking already prevents weird overlaps + Pill { + property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied + property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied + property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth + anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter + anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined + x: root.vertical ? 0 : undirectionalOffset + y: root.vertical ? undirectionalOffset : 0 + implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength + implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth + + Behavior on undirectionalWidth { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + Behavior on undirectionalLength { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + Behavior on undirectionalOffset { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + } + } + } } - Behavior on undirectionalLength { - animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + MaskMultiEffect { + id: occupiedIndicatorsMultiEffect + z: 1 + anchors.centerIn: parent + implicitWidth: occupiedIndicators.implicitWidth + implicitHeight: occupiedIndicators.implicitHeight + source: occupiedIndicatorsBg + maskSource: occupiedIndicators + } + + /////////////////// Active indicator /////////////////// + TrailingIndicator { + id: activeIndicator + anchors.fill: parent + z: 2 + + index: root.workspaceIndexInGroup + } + + /////////////////// Hover /////////////////// + ButtonMouseArea { + id: interactionMouseArea + z: 3 + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + property int hoverIndex: { + const position = root.vertical ? mouseY : mouseX; + return Math.floor(position / root.workspaceButtonWidth); + } + + function switchWorkspaceToHovered() { + Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) + } + onPressed: switchWorkspaceToHovered() + onWheel: event => { + if (event.angleDelta.y < 0) + Hyprland.dispatch(`workspace r+1`); + else if (event.angleDelta.y > 0) + Hyprland.dispatch(`workspace r-1`); + } + + TrailingIndicator { + id: interactionIndicator + index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup + color: "transparent" + StateOverlay { + id: hoverOverlay + anchors.fill: interactionIndicator.indicatorRectangle + radius: root.activeWorkspaceSize / 2 + hover: interactionMouseArea.containsMouse + press: interactionMouseArea.containsPress + drag: true // There are too many layers so we need to force this to be a lil more opaque + contentColor: Appearance.colors.colPrimary + } + } + } + + /////////////////// Numbers /////////////////// + WorkspaceLayout { + id: numbersGrid + z: 4 + layer.enabled: true // For the masking + + Repeater { + model: wsModel.shownCount + delegate: NumberWorkspaceItem {} + } + } + Colorizer { + z: 5 + anchors.fill: numbersGrid + colorizationColor: Appearance.colors.colOnPrimary + sourceColor: Appearance.colors.colOnSecondaryContainer + + source: activeIndicator + maskEnabled: true + maskSource: numbersGrid + + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + + /////////////////// App icons /////////////////// + WorkspaceLayout { + id: appsGrid + z: 6 + + Repeater { + model: wsModel.shownCount + delegate: WorkspaceItem { + id: wsApp + property var biggestWindow: wsModel.biggestWindow[index] + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + + AppIcon { + id: appIcon + property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons && wsApp.biggestWindow) ? (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin + rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin + } + + animated: !wsApp.biggestWindow // Prevent the "image-missing" icon + visible: false // Prevent dupe: the colorizer already copies the icon + + source: wsApp.mainAppIconSource + implicitSize: NumberUtils.roundToEven(root.workspaceIconSize) + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on cornerMargin { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + } + + Circle { + id: iconMask + visible: false + layer.enabled: true + diameter: appIcon.implicitSize + } + + Colorizer { + anchors.fill: appIcon + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary + colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 + brightness: 0 + source: appIcon + + opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 + visible: opacity > 0 + scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on scale { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + + maskEnabled: true + maskSource: iconMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + } + } + } + } + + FadeLoader { + anchors.centerIn: parent + shown: wsModel.specialWorkspaceActive + scale: 0.8 + 0.2 * root.specialBlur + + opacity: root.specialBlur + Behavior on opacity {} // Don't animate, as specialBlur is already animated + + sourceComponent: Pill { + anchors.centerIn: parent + property real undirectionalWidth: root.activeWorkspaceSize + property real undirectionalLength: { + const base = root.workspaceButtonWidth * Math.min(1.35, wsModel.shownCount); // Who tf only configures only 2 workspaces shown anyway? + if (root.vertical) + return base; + return specialWsText.implicitWidth + undirectionalWidth; + } + color: Appearance.colors.colPrimary + + implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength + implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth + + StyledText { + id: specialWsText + anchors.centerIn: parent + text: (!root.vertical ? wsModel.specialWorkspaceName : "S") + color: Appearance.colors.colOnPrimary + font.pixelSize: root.specialTextSize + } + + Behavior on undirectionalLength { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + } + } + + /////////////////// Super key press handling /////////////////// + Timer { + id: superPressAndHeldTimer + interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100) + repeat: false + onTriggered: { + root.superPressAndHeld = true; + } + } + Connections { + target: GlobalStates + function onSuperDownChanged() { + if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) + return; + if (GlobalStates.superDown) + superPressAndHeldTimer.restart(); + else { + superPressAndHeldTimer.stop(); + root.superPressAndHeld = false; + } + } + function onSuperReleaseMightTriggerChanged() { + superPressAndHeldTimer.stop(); } } } @@ -334,10 +364,7 @@ Item { property color contentColor: (wsModel.occupied[wsNum.index] && wsId !== wsModel.fakeWorkspace) ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive FadeLoader { - shown: !(Config.options?.bar.workspaces.alwaysShowNumbers - || root.superPressAndHeld - || (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow) - ) + shown: !(Config.options?.bar.workspaces.alwaysShowNumbers || root.superPressAndHeld || (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow)) anchors.centerIn: parent Circle { anchors.centerIn: parent @@ -346,10 +373,7 @@ Item { } } FadeLoader { - shown: root.superPressAndHeld - || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers)) - || (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons) - ) + shown: root.superPressAndHeld || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers)) || (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons)) anchors.centerIn: parent StyledText { anchors.centerIn: parent @@ -397,30 +421,4 @@ Item { implicitHeight: root.vertical ? indicatorLength : indicatorThickness } } - - /////////////////// Super key press handling /////////////////// - Timer { - id: superPressAndHeldTimer - interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100) - repeat: false - onTriggered: { - root.superPressAndHeld = true; - } - } - Connections { - target: GlobalStates - function onSuperDownChanged() { - if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) - return; - if (GlobalStates.superDown) - superPressAndHeldTimer.restart(); - else { - superPressAndHeldTimer.stop(); - root.superPressAndHeld = false; - } - } - function onSuperReleaseMightTriggerChanged() { - superPressAndHeldTimer.stop(); - } - } } From 368df5b717fae39b831ef43229f428895a8adba0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:56:59 +0100 Subject: [PATCH 029/104] changes from main --- .../background/widgets/clock/CookieClock.qml | 15 ++++----------- .../ii/modules/ii/overview/SearchWidget.qml | 19 +++++++++++++++++++ .../ii/modules/settings/BackgroundConfig.qml | 4 ++-- .../scripts/ai/gemini-categorize-wallpaper.sh | 2 +- .../ii/scripts/colors/switchwall.sh | 10 ++++++++-- .../ii/services/MprisController.qml | 16 +++++----------- 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml index 7eaa04b98..f6ab64d81 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml @@ -66,16 +66,9 @@ Item { } } - Connections { - target: Config - function onReadyChanged() { - categoryFileView.path = Directories.generatedWallpaperCategoryPath - } - } - FileView { id: categoryFileView - path: "" + path: Config.ready ? Directories.generatedWallpaperCategoryPath : "" watchChanges: true onFileChanged: reload() onLoaded: { @@ -85,7 +78,7 @@ Item { property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie StyledDropShadow { - target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader + target: root.useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader RotationAnimation on rotation { running: Config.options.background.widgets.clock.cookie.constantlyRotate @@ -100,7 +93,7 @@ Item { id: sineCookieLoader z: 0 visible: false // The DropShadow already draws it - active: useSineCookie + active: root.useSineCookie sourceComponent: SineCookie { implicitSize: root.implicitSize sides: Config.options.background.widgets.clock.cookie.sides @@ -111,7 +104,7 @@ Item { id: roundedPolygonCookieLoader z: 0 visible: false // The DropShadow already draws it - active: !useSineCookie + active: !root.useSineCookie sourceComponent: MaterialCookie { implicitSize: root.implicitSize sides: Config.options.background.widgets.clock.cookie.sides diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index c99b930c1..c6292c926 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -95,6 +95,25 @@ Item { // Wrapper root.focusFirstItem(); } } + + // Ctrl+N / Ctrl+P navigation + if (event.modifiers & Qt.ControlModifier) { + if (event.key === Qt.Key_N) { + if (appResults.visible && appResults.count > 0) { + // Wrap around the list rather than the default arrow key behaviour + appResults.currentIndex = (appResults.currentIndex + 1) % appResults.count; + event.accepted = true; + return; + } + } else if (event.key === Qt.Key_P) { + if (appResults.visible && appResults.count > 0) { + // Wrap around too + appResults.currentIndex = (appResults.count + appResults.currentIndex - 1) % appResults.count; + event.accepted = true; + return; + } + } + } } StyledRectangularShadow { diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index b67e10409..80814146f 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -246,8 +246,8 @@ ContentPage { value: Config.options.background.widgets.clock.digital.font.size usePercentTooltip: false buttonIcon: "format_size" - from: 70 - to: 150 + from: 50 + to: 700 stopIndicatorValues: [90] onValueChanged: { Config.options.background.widgets.clock.digital.font.size = value; diff --git a/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh b/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh index 41963d161..ead46561d 100755 --- a/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh +++ b/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh @@ -8,7 +8,7 @@ fi # Variables SOURCE_IMG_PATH="$1" -MODEL="${2:-${GEMINI_WALLPAPER_MODEL:-gemini-2.0-flash}}" # We use the flash variant so it's fast +MODEL="${2:-${GEMINI_WALLPAPER_MODEL:-gemini-2.5-flash}}" # We use the flash variant so it's fast WALLPAPER_NAME="$(basename "$SOURCE_IMG_PATH")" PROMPT="${3:-${GEMINI_WALLPAPER_PROMPT:-Categorize the wallpaper. Its file name is $WALLPAPER_NAME}}" RESIZED_IMG_PATH="/tmp/quickshell/ai/wallpaper.jpg" diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 1ae164d0c..6352e3f6d 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -155,6 +155,12 @@ set_thumbnail_path() { fi } +categorize_wallpaper() { + img_cat=$("$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$1") + # notify-send "Wallpaper category" "$img_cat" + echo "$img_cat" > "$STATE_DIR/user/generated/wallpaper/category.txt" +} + switch() { imgpath="$1" mode_flag="$2" @@ -163,9 +169,9 @@ switch() { color="$5" # Start Gemini auto-categorization if enabled - aiStylingEnabled=$(jq -r '.background.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE") + aiStylingEnabled=$(jq -r '.background.widgets.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE") if [[ "$aiStylingEnabled" == "true" ]]; then - "$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$imgpath" > "$STATE_DIR/user/generated/wallpaper/category.txt" & + categorize_wallpaper "$imgpath" & fi read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs) diff --git a/dots/.config/quickshell/ii/services/MprisController.qml b/dots/.config/quickshell/ii/services/MprisController.qml index 02151f1c1..7faed2274 100644 --- a/dots/.config/quickshell/ii/services/MprisController.qml +++ b/dots/.config/quickshell/ii/services/MprisController.qml @@ -25,22 +25,16 @@ Singleton { property var activeTrack; - property bool hasPlasmaIntegration: false - Process { - id: plasmaIntegrationAvailabilityCheckProc - running: true - command: ["bash", "-c", "command -v plasma-browser-integration-host"] - onExited: (exitCode, exitStatus) => { - root.hasPlasmaIntegration = (exitCode === 0); - } - } + readonly property bool hasActivePlasmaIntegration: Mpris.players.values.some( + p => p.dbusName?.startsWith('org.mpris.MediaPlayer2.plasma-browser-integration') + ) function isRealPlayer(player) { if (!Config.options.media.filterDuplicatePlayers) { return true; } return ( - // Remove unecessary native buses from browsers if there's plasma integration - !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && + // Remove native browser buses only if plasma-browser-integration is actually active on D-Bus + !(hasActivePlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasActivePlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && // playerctld just copies other buses and we don't need duplicates !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && // Non-instance mpd bus From bb1000297647b9aad0573a9421ee47bdd8166f87 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:58:52 +0100 Subject: [PATCH 030/104] refactor bar widget shape background and make it work at the right edge --- .../ii/modules/hefty/topLayer/bar/HBar.qml | 1 - .../bar/HBarWidgetShapeBackground.qml | 112 ++++++++++++++++++ .../hefty/topLayer/bar/widgets/HTime.qml | 104 ++-------------- .../hefty/topLayer/bar/widgets/Invisible.qml | 3 + .../topLayer/bar/widgets/InvisibleFill.qml | 9 ++ 5 files changed, 135 insertions(+), 94 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/InvisibleFill.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml index 95eade0fe..223f7ad5a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBar.qml @@ -55,7 +55,6 @@ HAbstractMorphedPanel { // Background function getBackgroundPolygon() { - print("Generating background polygon for HBar") // It's certainly cleaner to have the below props declared outside, but we do this // to make sure a config change only makes this re-evaluate exactly once const bottom = root.atBottom diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml new file mode 100644 index 000000000..0756efb03 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -0,0 +1,112 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell + +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.services as S +import qs.modules.common.widgets as W +import qs.modules.common.widgets.shapes as Shapes +import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes +import "../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding +import "../../../common/widgets/shapes/geometry/offset.js" as Offset + +// TODO: generalize this shi for vertical +Shapes.ShapeCanvas { + id: bgShape + + property bool atBottom: root.atBottom + property bool showPopup: root.showPopup + property real backgroundWidth: containerRoot.backgroundWidth + property real backgroundHeight: containerRoot.backgroundHeight + property real popupWidth: 400 + property real popupHeight: 500 + property real startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) + property real endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) + property real baseMargin: (parent.height - containerShape.height) / 2 + + property alias containerShape: containerShape + property alias popupShape: popupShape + + // mapToGlobal is not reactive so we gotta hook manually + property real xInGlobal + function updateXInGlobal() { + xInGlobal = mapToGlobal(0, 0).x + xOffset; + } + Component.onCompleted: updateXInGlobal(); + onXChanged: updateXInGlobal(); + readonly property real minPopupXOffset: -xInGlobal + baseMargin + readonly property real maxPopupXOffset: { + const maxPopupX = QsWindow.window.screen.width - popupWidth - baseMargin; + const maxOffset = maxPopupX - xInGlobal; + return maxOffset + } + readonly property real popupXOffset: Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset) + + anchors { + left: parent.left + leftMargin: -xOffset + top: parent.top + topMargin: { + if (!bgShape.atBottom || !bgShape.showPopup) + return baseMargin; + else + return baseMargin - popupShape.height - bgShape.spacing; + } + } + width: bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth + height: bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height + color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 + xOffset: showPopup ? -popupXOffset : 0 + yOffset: bgShape.atBottom ? (height - containerShape.height) : 0 + animation: Anim {} + + Behavior on width { + Anim {} + } + Behavior on height { + Anim {} + } + Behavior on anchors.topMargin { + Anim {} + } + Behavior on xOffset { + Anim {} + } + + polygonIsNormalized: false + property real spacing: baseMargin * 2 + W.AxisRectangularContainerShape { + id: containerShape + width: bgShape.backgroundWidth + height: bgShape.backgroundHeight + startRadius: bgShape.startRadius + endRadius: bgShape.endRadius + } + W.RectangularContainerShape { + id: popupShape + width: bgShape.popupWidth + height: bgShape.popupHeight + radius: C.Appearance.rounding.large + xOffset: bgShape.popupXOffset + yOffset: bgShape.atBottom ? -(popupShape.height + bgShape.spacing) : (containerShape.height + bgShape.spacing) + } + + roundedPolygon: { + if (!bgShape.showPopup) + return containerShape.getFullShape(); + // return popupShape.getFullShape(); // debug + const points = [...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(),]), containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), + // ...containerShape.leftPoints, + containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), ...(!bgShape.atBottom ? containerShape.topPoints : [popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(), ...popupShape.topPoints, popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(),]), containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), + // ...containerShape.rightPoints, + containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), ...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(), ...popupShape.getLastBottomPoints(),]),]; + return MaterialShapes.customPolygon(points); + } + + component Anim: SpringAnimation { + spring: 3.5 + damping: 0.35 + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index cfbb1fc86..dc244c5a2 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -22,112 +22,30 @@ HBarWidgetContainer { target: root function onShowPopupChanged() { if (root.showPopup) { - morphedPanelParent.addAttachedMaskItem(bgShape); + containerRoot.morphedPanelParent.addAttachedMaskItem(bgShape); } else { - morphedPanelParent.removeAttachedMaskItem(bgShape); + containerRoot.morphedPanelParent.removeAttachedMaskItem(bgShape); } } } Connections { - target: morphedPanelParent + target: containerRoot.morphedPanelParent function onFocusGrabDismissed() { root.showPopup = false; } } // Background container shape - background: Shapes.ShapeCanvas { + background: HBarWidgetShapeBackground { id: bgShape - property real baseTopMargin: (parent.height - containerShape.height) / 2 - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - topMargin: { - if (!root.atBottom || !root.showPopup) return baseTopMargin; - else return baseTopMargin - popupShape.height - bgShape.spacing; - } - } - width: root.showPopup ? Math.max(containerShape.width, popupShape.width) : containerShape.width - height: root.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height - color: root.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 - // color: "green" - // debug: true - xOffset: (width - containerShape.width) / 2 - yOffset: root.atBottom ? (height - containerShape.height) : 0 - animation: Anim {} - Behavior on width { - Anim {} - } - Behavior on height { - Anim {} - } - Behavior on anchors.topMargin { - Anim {} - } - - // Rectangle { - // anchors.fill: parent - // } - - polygonIsNormalized: false - property real spacing: baseTopMargin * 2 - W.AxisRectangularContainerShape { - id: containerShape - width: containerRoot.backgroundWidth - height: containerRoot.backgroundHeight - startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) - endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) - } - W.RectangularContainerShape { - id: popupShape - width: 400 // TODO - height: 500 // TODO - radius: C.Appearance.rounding.large - xOffset: -(width - containerShape.width) / 2 - yOffset: root.atBottom ? -(popupShape.height + bgShape.spacing) : (containerShape.height + bgShape.spacing) - } - - - roundedPolygon: { - if (!root.showPopup) return containerShape.getFullShape() - // return popupShape.getFullShape(); // debug - const points = [ - ...(root.atBottom ? containerShape.getFirstBottomPoints() : [ - ...popupShape.getFirstBottomPoints(), - popupShape.getBottomLeftPoint(), - ...popupShape.leftPoints, - popupShape.getTopLeftPoint(), - ]), - containerShape.getBottomLeftPoint(0, bgShape.spacing * (!root.atBottom ? 1 : 0), containerShape.radiusLimit), - // ...containerShape.leftPoints, - containerShape.getTopLeftPoint(0, bgShape.spacing * (root.atBottom ? -1 : 0), containerShape.radiusLimit), - ...(!root.atBottom ? containerShape.topPoints : [ - popupShape.getBottomLeftPoint(), - ...popupShape.leftPoints, - popupShape.getTopLeftPoint(), - ...popupShape.topPoints, - popupShape.getTopRightPoint(), - ...popupShape.rightPoints, - popupShape.getBottomRightPoint(), - ]), - containerShape.getTopRightPoint(0, bgShape.spacing * (root.atBottom ? -1 : 0), containerShape.radiusLimit), - // ...containerShape.rightPoints, - containerShape.getBottomRightPoint(0, bgShape.spacing * (!root.atBottom ? 1 : 0), containerShape.radiusLimit), - ...(root.atBottom ? containerShape.getLastBottomPoints() : [ - popupShape.getTopRightPoint(), - ...popupShape.rightPoints, - popupShape.getBottomRightPoint(), - ...popupShape.getLastBottomPoints(), - ]), - ]; - return MaterialShapes.customPolygon(points); - } - - component Anim: SpringAnimation { - spring: 3.5 - damping: 0.35 - } + atBottom: root.atBottom + showPopup: root.showPopup + backgroundWidth: containerRoot.backgroundWidth + backgroundHeight: containerRoot.backgroundHeight + startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) + endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) + baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical } // The button on the bar diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml index 08b2d0fb5..d32a22e33 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/Invisible.qml @@ -1,6 +1,9 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Layouts Item { visible: false + Layout.fillWidth: false + Layout.fillHeight: false } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/InvisibleFill.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/InvisibleFill.qml new file mode 100644 index 000000000..bc8a87b43 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/InvisibleFill.qml @@ -0,0 +1,9 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +Item { + visible: false + Layout.fillHeight: true + Layout.fillWidth: true +} From 15afa07b14cf09cfa2a4d81ba2730ac0b905038b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 21 Feb 2026 21:56:41 +0100 Subject: [PATCH 031/104] gtk theme changes from main --- .../.config/matugen/templates/gtk-3.0/gtk.css | 40 ++++++--- .../.config/matugen/templates/gtk-4.0/gtk.css | 83 ++++++++++++++----- 2 files changed, 89 insertions(+), 34 deletions(-) diff --git a/dots/.config/matugen/templates/gtk-3.0/gtk.css b/dots/.config/matugen/templates/gtk-3.0/gtk.css index c0054f864..0d8cb9cbe 100644 --- a/dots/.config/matugen/templates/gtk-3.0/gtk.css +++ b/dots/.config/matugen/templates/gtk-3.0/gtk.css @@ -1,22 +1,38 @@ /* -* GTK Colors -* Generated with Matugen +* GTK colors generated with Matugen +* The source template is here: ~/.config/matugen/templates/gtk-3.0/gtk.css */ +/* Accents */ @define-color accent_color {{colors.primary.default.hex}}; @define-color accent_fg_color {{colors.on_primary.default.hex}}; @define-color accent_bg_color {{colors.primary.default.hex}}; +@define-color destructive_bg_color {{colors.error_container.default.hex}}; +@define-color destructive_fg_color {{colors.on_error_container.default.hex}}; +@define-color destructive_color {{colors.error.default.hex}}; +@define-color success_bg_color #374B3E; +@define-color success_fg_color #D1E9D6; +@define-color success_color #B5CCBA; +/* Base surfaces */ @define-color window_bg_color {{colors.background.default.hex}}; @define-color window_fg_color {{colors.on_background.default.hex}}; -@define-color headerbar_bg_color {{colors.surface_dim.default.hex}}; +@define-color headerbar_bg_color {{colors.surface_container.default.hex}}; +@define-color headerbar_backdrop_color {{colors.surface_container.default.hex}}; @define-color headerbar_fg_color {{colors.on_surface.default.hex}}; -@define-color popover_bg_color {{colors.surface_dim.default.hex}}; -@define-color popover_fg_color {{colors.on_surface.default.hex}}; -@define-color view_bg_color {{colors.surface.default.hex}}; -@define-color view_fg_color {{colors.on_surface.default.hex}}; -@define-color card_bg_color {{colors.surface.default.hex}}; +@define-color card_bg_color {{colors.surface_container.default.hex}}; @define-color card_fg_color {{colors.on_surface.default.hex}}; -@define-color sidebar_bg_color @window_bg_color; -@define-color sidebar_fg_color @window_fg_color; -@define-color sidebar_border_color @window_bg_color; -@define-color sidebar_backdrop_color @window_bg_color; +@define-color sidebar_bg_color {{colors.surface_container.default.hex}}; +@define-color sidebar_fg_color {{colors.on_surface.default.hex}}; +@define-color secondary_sidebar_bg_color {{colors.surface_container_low.default.hex}}; +@define-color secondary_sidebar_fg_color {{colors.on_surface.default.hex}}; +@define-color sidebar_border_color @sidebar_bg_color; +@define-color sidebar_backdrop_color @sidebar_bg_color; +@define-color view_bg_color {{colors.surface_container_lowest.default.hex}}; +@define-color view_fg_color {{colors.on_surface.default.hex}}; +@define-color overview_bg_color {{colors.surface_container_lowest.default.hex}}; +@define-color overview_fg_color {{colors.on_surface.default.hex}}; +/* Popups */ +@define-color popover_bg_color {{colors.surface_container_highest.default.hex}}; +@define-color popover_fg_color {{colors.on_surface.default.hex}}; +@define-color dialog_bg_color {{colors.surface_container_high.default.hex}}; +@define-color dialog_fg_color {{colors.on_surface.default.hex}}; diff --git a/dots/.config/matugen/templates/gtk-4.0/gtk.css b/dots/.config/matugen/templates/gtk-4.0/gtk.css index 80b1d6975..07c8099f5 100644 --- a/dots/.config/matugen/templates/gtk-4.0/gtk.css +++ b/dots/.config/matugen/templates/gtk-4.0/gtk.css @@ -1,44 +1,83 @@ /* -* GTK Colors -* Generated with Matugen +* GTK colors generated with Matugen +* The source template is here: ~/.config/matugen/templates/gtk-4.0/gtk.css */ @media (prefers-color-scheme: light) { + /* Accents */ @define-color accent_color {{colors.primary.light.hex}}; @define-color accent_fg_color {{colors.on_primary.light.hex}}; @define-color accent_bg_color {{colors.primary.light.hex}}; + @define-color destructive_bg_color {{colors.error_container.light.hex}}; + @define-color destructive_fg_color {{colors.on_error_container.light.hex}}; + @define-color destructive_color {{colors.error.light.hex}}; + @define-color success_bg_color #B5CCBA; + @define-color success_fg_color #213528; + @define-color success_color #374B3E; + /* Base surfaces */ @define-color window_bg_color {{colors.background.light.hex}}; @define-color window_fg_color {{colors.on_background.light.hex}}; - @define-color headerbar_bg_color {{colors.surface_dim.light.hex}}; + @define-color headerbar_bg_color {{colors.surface_container.light.hex}}; + @define-color headerbar_backdrop_color {{colors.surface_container.light.hex}}; @define-color headerbar_fg_color {{colors.on_surface.light.hex}}; - @define-color popover_bg_color {{colors.surface_dim.light.hex}}; - @define-color popover_fg_color {{colors.on_surface.light.hex}}; - @define-color view_bg_color {{colors.surface.light.hex}}; - @define-color view_fg_color {{colors.on_surface.light.hex}}; - @define-color card_bg_color {{colors.surface.light.hex}}; + @define-color card_bg_color {{colors.surface_container.light.hex}}; @define-color card_fg_color {{colors.on_surface.light.hex}}; - @define-color sidebar_bg_color @window_bg_color; - @define-color sidebar_fg_color @window_fg_color; - @define-color sidebar_border_color @window_bg_color; - @define-color sidebar_backdrop_color @window_bg_color; + @define-color sidebar_bg_color {{colors.surface_container.light.hex}}; + @define-color sidebar_fg_color {{colors.on_surface.light.hex}}; + @define-color secondary_sidebar_bg_color {{colors.surface_container_low.light.hex}}; + @define-color secondary_sidebar_backdrop_color {{colors.surface_container_low.light.hex}}; + @define-color secondary_sidebar_fg_color {{colors.on_surface.light.hex}}; + @define-color sidebar_border_color @sidebar_bg_color; + @define-color sidebar_backdrop_color @sidebar_bg_color; + @define-color view_bg_color {{colors.surface_container_lowest.light.hex}}; + @define-color view_fg_color {{colors.on_surface.light.hex}}; + @define-color overview_bg_color {{colors.surface_container_lowest.light.hex}}; + @define-color overview_fg_color {{colors.on_surface.light.hex}}; + /* Popups */ + @define-color popover_bg_color {{colors.surface_container_highest.light.hex}}; + @define-color popover_fg_color {{colors.on_surface.light.hex}}; + @define-color dialog_bg_color {{colors.surface_container_high.light.hex}}; + @define-color dialog_fg_color {{colors.on_surface.light.hex}}; + @define-color thumbnail_bg_color {{colors.surface_container_high.light.hex}}; + @define-color thumbnail_fg_color {{colors.on_surface.light.hex}}; } @media (prefers-color-scheme: dark) { + + /* Accents */ @define-color accent_color {{colors.primary.dark.hex}}; @define-color accent_fg_color {{colors.on_primary.dark.hex}}; @define-color accent_bg_color {{colors.primary.dark.hex}}; + @define-color destructive_bg_color {{colors.error_container.dark.hex}}; + @define-color destructive_fg_color {{colors.on_error_container.dark.hex}}; + @define-color destructive_color {{colors.error.dark.hex}}; + @define-color success_bg_color #374B3E; + @define-color success_fg_color #D1E9D6; + @define-color success_color #B5CCBA; + /* Base surfaces */ @define-color window_bg_color {{colors.background.dark.hex}}; @define-color window_fg_color {{colors.on_background.dark.hex}}; - @define-color headerbar_bg_color {{colors.surface_dim.dark.hex}}; + @define-color headerbar_bg_color {{colors.surface_container.dark.hex}}; + @define-color headerbar_backdrop_color {{colors.surface_container.dark.hex}}; @define-color headerbar_fg_color {{colors.on_surface.dark.hex}}; - @define-color popover_bg_color {{colors.surface_dim.dark.hex}}; - @define-color popover_fg_color {{colors.on_surface.dark.hex}}; - @define-color view_bg_color {{colors.surface.dark.hex}}; - @define-color view_fg_color {{colors.on_surface.dark.hex}}; - @define-color card_bg_color {{colors.surface.dark.hex}}; + @define-color card_bg_color {{colors.surface_container.dark.hex}}; @define-color card_fg_color {{colors.on_surface.dark.hex}}; - @define-color sidebar_bg_color @window_bg_color; - @define-color sidebar_fg_color @window_fg_color; - @define-color sidebar_border_color @window_bg_color; - @define-color sidebar_backdrop_color @window_bg_color; + @define-color sidebar_bg_color {{colors.surface_container.dark.hex}}; + @define-color sidebar_fg_color {{colors.on_surface.dark.hex}}; + @define-color secondary_sidebar_bg_color {{colors.surface_container_low.dark.hex}}; + @define-color secondary_sidebar_backdrop_color {{colors.surface_container_low.dark.hex}}; + @define-color secondary_sidebar_fg_color {{colors.on_surface.dark.hex}}; + @define-color sidebar_border_color @sidebar_bg_color; + @define-color sidebar_backdrop_color @sidebar_bg_color; + @define-color view_bg_color {{colors.surface_container_lowest.dark.hex}}; + @define-color view_fg_color {{colors.on_surface.dark.hex}}; + @define-color overview_bg_color {{colors.surface_container_lowest.dark.hex}}; + @define-color overview_fg_color {{colors.on_surface.dark.hex}}; + /* Popups */ + @define-color popover_bg_color {{colors.surface_container_highest.dark.hex}}; + @define-color popover_fg_color {{colors.on_surface.dark.hex}}; + @define-color dialog_bg_color {{colors.surface_container_high.dark.hex}}; + @define-color dialog_fg_color {{colors.on_surface.dark.hex}}; + @define-color thumbnail_bg_color {{colors.surface_container_high.dark.hex}}; + @define-color thumbnail_fg_color {{colors.on_surface.dark.hex}}; } From 2bb001a62bfb6d050b04ed8646f32677caaf6888 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:10:57 +0100 Subject: [PATCH 032/104] hefty: bar: click to close popups --- .../modules/hefty/topLayer/HTopLayerPanel.qml | 44 ++++++++++++++----- .../ii/services/GlobalFocusGrab.qml | 1 + 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index ab2981707..bd7da8d83 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -4,6 +4,7 @@ import Quickshell import Quickshell.Wayland import Quickshell.Hyprland import qs +import qs.services import "../../common" import "../../common/widgets/shapes" as S import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes @@ -43,7 +44,7 @@ PanelWindow { // While it overshoots because of the spring animation, waiting for the bounce to finish entirely would be too slow // ^ (totally not an excuse for my laziness) if (backgroundShape.progress >= 1.0) { - root.finishedMorphing = true + root.finishedMorphing = true; } } } @@ -104,18 +105,37 @@ PanelWindow { root.currentPanel = bar; } - HBar { - id: bar - load: root.currentPanel === this && root.finishedMorphing // the extra condition is to prevent workspace widget from acting up when switching horizontal/vertical... should be fixed later - shown: root.finishedMorphing - } + Item { + id: panelRootItem + anchors.fill: parent - HOverview { - id: overview - load: root.currentPanel === this - shown: root.finishedMorphing - onRequestFocus: root.currentPanel = overview; - onDismissed: root.dismiss(); + Connections { + target: GlobalFocusGrab + function onActiveChanged() { + if (GlobalFocusGrab.active) { + panelRootItem.focus = true + } + } + } + Keys.onPressed: (event) => { // Esc to close + if (event.key === Qt.Key_Escape) { + GlobalFocusGrab.dismiss(); + } + } + + HBar { + id: bar + load: root.currentPanel === this && root.finishedMorphing // the extra condition is to prevent workspace widget from acting up when switching horizontal/vertical... should be fixed later + shown: root.finishedMorphing + } + + HOverview { + id: overview + load: root.currentPanel === this + shown: root.finishedMorphing + onRequestFocus: root.currentPanel = overview + onDismissed: root.dismiss() + } } //////////////// Components ///////////////// diff --git a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml index 6385d3df7..4867a5bc0 100644 --- a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml +++ b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml @@ -12,6 +12,7 @@ import Quickshell.Hyprland Singleton { id: root + property alias active: grab.active signal dismissed() property list persistent: [] From 6b29e73ab703a89c888e3e809a0cdf4a7094799a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 22 Feb 2026 00:12:26 +0100 Subject: [PATCH 033/104] hefty: bar: refactor content hover effect --- .../hefty/topLayer/bar/HBarWidgetContent.qml | 54 +++++++++++ .../bar/HBarWidgetShapeBackground.qml | 15 +-- .../hefty/topLayer/bar/widgets/HTime.qml | 93 ++++++------------- 3 files changed, 89 insertions(+), 73 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContent.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContent.qml new file mode 100644 index 000000000..6c191fd35 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetContent.qml @@ -0,0 +1,54 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.modules.common.widgets as W +import ".." + +W.ButtonMouseArea { + id: root + + required property bool vertical + required property bool atBottom + required property bool showPopup + + property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide") + property real layoutParentTopLeftRadius: layoutParent.topLeftRadius + property real layoutParentTopRightRadius: layoutParent.topRightRadius + property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius + property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius + + readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight + required property real contentImplicitWidth + required property real contentImplicitHeight + property real parentRadiusToPaddingRatio: 0.3 + implicitWidth: vertical ? barThickness : (contentImplicitWidth + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + implicitHeight: !vertical ? barThickness : (contentImplicitHeight + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) + Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + + W.StateOverlay { + id: hoverOverlay + anchors.fill: parent + property real parentMargins: 4 + property real ownMargins: 2 + property real edgeMargins: parentMargins + ownMargins + property real sideMargins: -2 + anchors { + leftMargin: root.vertical ? edgeMargins : sideMargins + rightMargin: root.vertical ? edgeMargins : sideMargins + topMargin: root.vertical ? sideMargins : edgeMargins + bottomMargin: root.vertical ? sideMargins : edgeMargins + } + topLeftRadius: root.layoutParentTopLeftRadius - ownMargins + topRightRadius: root.layoutParentTopRightRadius - ownMargins + bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins + bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins + + hover: root.containsMouse + press: root.containsPress + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 0756efb03..0ad37ae91 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -16,15 +16,16 @@ import "../../../common/widgets/shapes/geometry/offset.js" as Offset Shapes.ShapeCanvas { id: bgShape - property bool atBottom: root.atBottom - property bool showPopup: root.showPopup - property real backgroundWidth: containerRoot.backgroundWidth - property real backgroundHeight: containerRoot.backgroundHeight + required property bool vertical + required property bool atBottom + required property bool showPopup + required property real backgroundWidth + required property real backgroundHeight property real popupWidth: 400 property real popupHeight: 500 - property real startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) - property real endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) - property real baseMargin: (parent.height - containerShape.height) / 2 + required property real startRadius + required property real endRadius + property real baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical property alias containerShape: containerShape property alias popupShape: popupShape diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index dc244c5a2..6fe1d0a3e 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -1,35 +1,31 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts -import Quickshell import qs.modules.common as C import qs.modules.common.functions as F import qs.services as S import qs.modules.common.widgets as W -import qs.modules.common.widgets.shapes as Shapes import ".." -import "../../../../common/widgets/shapes/material-shapes.js" as MaterialShapes -import "../../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding -import "../../../../common/widgets/shapes/geometry/offset.js" as Offset HBarWidgetContainer { - id: containerRoot + id: root + + property bool showPopup: false + readonly property bool vertical: C.Config.options.bar.vertical + readonly property bool atBottom: C.Config.options.bar.bottom // Interactions property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") - Connections { - target: root - function onShowPopupChanged() { - if (root.showPopup) { - containerRoot.morphedPanelParent.addAttachedMaskItem(bgShape); - } else { - containerRoot.morphedPanelParent.removeAttachedMaskItem(bgShape); - } + onShowPopupChanged: { + if (root.showPopup) { + root.morphedPanelParent.addAttachedMaskItem(bgShape); + } else { + root.morphedPanelParent.removeAttachedMaskItem(bgShape); } } Connections { - target: containerRoot.morphedPanelParent + target: root.morphedPanelParent function onFocusGrabDismissed() { root.showPopup = false; } @@ -39,66 +35,32 @@ HBarWidgetContainer { background: HBarWidgetShapeBackground { id: bgShape + vertical: root.vertical atBottom: root.atBottom showPopup: root.showPopup - backgroundWidth: containerRoot.backgroundWidth - backgroundHeight: containerRoot.backgroundHeight - startRadius: containerRoot.getBackgroundRadius(containerRoot.startSide) - endRadius: containerRoot.getBackgroundRadius(containerRoot.endSide) - baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical + + backgroundWidth: root.backgroundWidth + backgroundHeight: root.backgroundHeight + startRadius: root.getBackgroundRadius(root.startSide) + endRadius: root.getBackgroundRadius(root.endSide) } // The button on the bar - W.ButtonMouseArea { - id: root + HBarWidgetContent { + id: contentRoot - property bool vertical: C.Config.options.bar.vertical - property bool atBottom: C.Config.options.bar.bottom - property bool showPopup: false + vertical: root.vertical + atBottom: root.atBottom + showPopup: root.showPopup - property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide") - property real layoutParentTopLeftRadius: layoutParent.topLeftRadius - property real layoutParentTopRightRadius: layoutParent.topRightRadius - property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius - property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius - - readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight - property var activeContent: vertical ? verticalContent : horizontalContent - property real parentRadiusToPaddingRatio: 0.3 - implicitWidth: vertical ? barThickness : (activeContent.implicitWidth + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) - implicitHeight: !vertical ? barThickness : (activeContent.implicitHeight + (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio) - Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter - Layout.fillWidth: vertical - Layout.fillHeight: !vertical - - onClicked: showPopup = !showPopup - - W.StateOverlay { - id: hoverOverlay - anchors.fill: parent - property real parentMargins: 4 - property real ownMargins: 2 - property real edgeMargins: parentMargins + ownMargins - property real sideMargins: -2 - anchors { - leftMargin: root.vertical ? edgeMargins : sideMargins - rightMargin: root.vertical ? edgeMargins : sideMargins - topMargin: root.vertical ? sideMargins : edgeMargins - bottomMargin: root.vertical ? sideMargins : edgeMargins - } - topLeftRadius: root.layoutParentTopLeftRadius - ownMargins - topRightRadius: root.layoutParentTopRightRadius - ownMargins - bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins - bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins - - hover: root.containsMouse - press: root.containsPress - } + onClicked: root.showPopup = !showPopup + contentImplicitWidth: vertical ? verticalContent.implicitWidth : horizontalContent.implicitWidth + contentImplicitHeight: vertical ? verticalContent.implicitHeight : horizontalContent.implicitHeight W.FadeLoader { id: horizontalContent anchors.fill: parent - shown: !root.vertical + shown: !contentRoot.vertical sourceComponent: Item { anchors.fill: parent @@ -110,7 +72,7 @@ HBarWidgetContainer { anchors.fill: parent W.VisuallyCenteredStyledText { - Layout.leftMargin: root.layoutParentTopLeftRadius * root.parentRadiusToPaddingRatio + Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true font.pixelSize: C.Appearance.font.pixelSize.large @@ -132,7 +94,6 @@ HBarWidgetContainer { text: S.DateTime.longDate } } - } } From 0235e56af32a9b865879f184135226c0495ba15e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 27 Feb 2026 22:50:19 +0100 Subject: [PATCH 034/104] update hyprland config --- dots/.config/hypr/hyprland/general.conf | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index aedba4578..071515e4c 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -48,7 +48,7 @@ dwindle { decoration { # 2 = circle, higher = squircle, 4 = very obvious squircle # Clear squircles look really off; we use only extra .4 here to make the rounding feel more continuous - rounding_power = 2.4 + rounding_power = 2 rounding = 18 blur { @@ -81,7 +81,7 @@ decoration { # Dim dim_inactive = true dim_strength = 0.05 - dim_special = 0.07 + dim_special = 0.2 } animations { @@ -115,6 +115,8 @@ animations { ## specialWorkspace animation = specialWorkspaceIn, 1, 2.8, emphasizedDecel, slidevert animation = specialWorkspaceOut, 1, 1.2, emphasizedAccel, slidevert + # zoom + animation = zoomFactor, 1, 3, standardDecel } input { @@ -160,19 +162,7 @@ binds { cursor { zoom_factor = 1 zoom_rigid = false + zoom_disable_aa = true hotspot_padding = 1 } -# Overview -plugin { - hyprexpo { - columns = 3 - gap_size = 5 - bg_col = rgb(000000) - workspace_method = first 1 # [center/first] [workspace] e.g. first 1 or center m+1 - - enable_gesture = false # laptop touchpad, 4 fingers - gesture_distance = 300 # how far is the "max" - gesture_positive = false - } -} From aa4c18b86a37b55ef9ba6e556155d70c4eac3239 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:54:06 +0100 Subject: [PATCH 035/104] waffles: more readable months when scrolling --- .../ii/modules/waffle/notificationCenter/CalendarWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 7c01a0b01..16dc831e1 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -79,7 +79,7 @@ BodyRectangle { id: dayButton required property var model checked: model.today - enabled: hovered || calendarView.scrolling || checked || model.month === calendarView.focusedMonth + enabled: hovered || checked || model.month === calendarView.focusedMonth implicitWidth: calendarView.buttonSize implicitHeight: calendarView.buttonSize radius: height / 2 From 28936dd226756b34c635b7ac7433500285b64c89 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:55:18 +0100 Subject: [PATCH 036/104] hefty: bar: popup choreography --- .../widgets/AbstractChoreographable.qml | 19 +++++++++ .../common/widgets/ChoreographerGrid.qml | 41 +++++++++++++++++++ .../widgets/FlyFadeEnterChoreographable.qml | 26 ++++++++++++ .../bar/HBarWidgetShapeBackground.qml | 23 ++++++----- .../hefty/topLayer/bar/widgets/HTime.qml | 34 +++++++++++++++ 5 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/AbstractChoreographable.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/FlyFadeEnterChoreographable.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AbstractChoreographable.qml b/dots/.config/quickshell/ii/modules/common/widgets/AbstractChoreographable.qml new file mode 100644 index 000000000..f6a973a18 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/AbstractChoreographable.qml @@ -0,0 +1,19 @@ +pragma ComponentBehavior: Bound +import QtQuick +import ".." + +Item { + id: root + + property real progress: 0 + default property Item child + implicitWidth: child.implicitWidth + implicitHeight: child.implicitHeight + + children: [child] + + property var animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + Behavior on progress { + animation: root.animation + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml new file mode 100644 index 000000000..7223458d1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml @@ -0,0 +1,41 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +GridLayout { + id: root + + property bool vertical: true + columns: vertical ? 1 : -1 + property real totalDuration: 250 + property real interval: totalDuration / count + + default property list choreographableChildren + readonly property int count: choreographableChildren.length + children: choreographableChildren + + property bool shown: true + onShownChanged: { + // When hiding, hide all at once + if (!shown) { + for (var i = 0; i < count; i++) { + choreographableChildren[i].progress = 0; + } + } + // When showing, choreograph + root.choreographIndex = 0; + } + property int choreographIndex: count + Timer { + id: choreographTimer + interval: root.interval + property bool step: root.shown && root.choreographIndex < root.count + running: step + repeat: step + onTriggered: { + const index = root.choreographIndex; + root.choreographableChildren[index].progress = 1; + root.choreographIndex++; + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FlyFadeEnterChoreographable.qml b/dots/.config/quickshell/ii/modules/common/widgets/FlyFadeEnterChoreographable.qml new file mode 100644 index 000000000..657968d27 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/FlyFadeEnterChoreographable.qml @@ -0,0 +1,26 @@ +pragma ComponentBehavior: Bound +import QtQuick + +AbstractChoreographable { + id: root + + progress: 0 + property bool vertical: true + property bool reverseDirection: false + property real distance: 15 + + readonly property real directionMultiplier: reverseDirection ? -1 : 1 + + Component.onCompleted: syncProgress() + onProgressChanged: syncProgress() + + function syncProgress() { + const progressDistance = distance * (1 - progress) * directionMultiplier; + root.child.opacity = progress + if (vertical) { + root.child.y = progressDistance + } else { + root.child.x = progressDistance + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 0ad37ae91..464dc683a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -1,16 +1,11 @@ pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Layouts import Quickshell import qs.modules.common as C -import qs.modules.common.functions as F -import qs.services as S import qs.modules.common.widgets as W import qs.modules.common.widgets.shapes as Shapes import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes -import "../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding -import "../../../common/widgets/shapes/geometry/offset.js" as Offset // TODO: generalize this shi for vertical Shapes.ShapeCanvas { @@ -21,11 +16,17 @@ Shapes.ShapeCanvas { required property bool showPopup required property real backgroundWidth required property real backgroundHeight - property real popupWidth: 400 - property real popupHeight: 500 + property real popupContentWidth: 400 + property real popupContentHeight: 500 + property real popupPadding: 10 + property real popupWidth: popupContentWidth + popupPadding * 2 + property real popupHeight: popupContentHeight + popupPadding * 2 required property real startRadius required property real endRadius property real baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical + readonly property real popupContentOffsetBase: -baseMargin + popupPadding + readonly property real popupContentOffsetY: spacing + popupContentOffsetBase + (atBottom ? -(popupHeight + backgroundHeight + spacing * 2) : 0) + readonly property real popupContentOffsetX: popupXOffset + popupContentOffsetBase property alias containerShape: containerShape property alias popupShape: popupShape @@ -35,13 +36,13 @@ Shapes.ShapeCanvas { function updateXInGlobal() { xInGlobal = mapToGlobal(0, 0).x + xOffset; } - Component.onCompleted: updateXInGlobal(); - onXChanged: updateXInGlobal(); + Component.onCompleted: updateXInGlobal() + onXChanged: updateXInGlobal() readonly property real minPopupXOffset: -xInGlobal + baseMargin readonly property real maxPopupXOffset: { const maxPopupX = QsWindow.window.screen.width - popupWidth - baseMargin; const maxOffset = maxPopupX - xInGlobal; - return maxOffset + return maxOffset; } readonly property real popupXOffset: Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset) @@ -108,6 +109,6 @@ Shapes.ShapeCanvas { component Anim: SpringAnimation { spring: 3.5 - damping: 0.35 + damping: 0.3 } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index 6fe1d0a3e..a76832140 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -41,6 +41,8 @@ HBarWidgetContainer { backgroundWidth: root.backgroundWidth backgroundHeight: root.backgroundHeight + popupContentWidth: popupContent.implicitWidth + popupContentHeight: popupContent.implicitHeight startRadius: root.getBackgroundRadius(root.startSide) endRadius: root.getBackgroundRadius(root.endSide) } @@ -57,6 +59,7 @@ HBarWidgetContainer { contentImplicitWidth: vertical ? verticalContent.implicitWidth : horizontalContent.implicitWidth contentImplicitHeight: vertical ? verticalContent.implicitHeight : horizontalContent.implicitHeight + // When horizontal W.FadeLoader { id: horizontalContent anchors.fill: parent @@ -97,9 +100,40 @@ HBarWidgetContainer { } } + // When vertical W.FadeLoader { id: verticalContent anchors.fill: parent } + + // Popup content + W.ChoreographerGrid { + id: popupContent + anchors { + top: horizontalContent.bottom + topMargin: bgShape.popupContentOffsetY + left: horizontalContent.left + leftMargin: bgShape.popupContentOffsetX + } + + shown: root.showPopup + + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "Kalender" + font.pixelSize: 25 + } + } + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "Lorem ipsum okakumalum tung\ntung tung tung" + } + } + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "BAJLANDO\nUUOOOUUUOOOUUOOUOU" + } + } + } } } From 7a74897b4774dc01131e27e6fd445191977320f2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:35:24 +0100 Subject: [PATCH 037/104] update quickshell --- sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD index 690b73123..fe910ba95 100644 --- a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD @@ -1,4 +1,4 @@ -_commit='db1777c20b936a86528c1095cbcb1ebd92801402' +_commit='6e17efab83d3a5ad5d6e59bc08d26095c6660502' # Useful links: # https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master # https://aur.archlinux.org/packages/quickshell-git From 6cd2d31c991f424178030f8786f80688d1e5112d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 5 Mar 2026 16:20:06 +0100 Subject: [PATCH 038/104] hefty: bar: make popups work for vertical --- .../hefty/topLayer/bar/HBarContent.qml | 20 +- .../bar/HBarWidgetShapeBackground.qml | 179 +++++++++++++++--- .../hefty/topLayer/bar/widgets/HTime.qml | 50 ++++- 3 files changed, 202 insertions(+), 47 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index ebd79a96a..a388cdb62 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -28,12 +28,9 @@ Item { Side { id: centerLeftSide anchors.right: !root.vertical ? centerSide.left : parent.right - anchors.bottom: root.vertical ? parent.bottom : undefined + anchors.bottom: !root.vertical ? parent.bottom : centerSide.top HBarUserFallbackComponentRepeater { - componentNames: [ - ...root.centerLeftWidgets, - ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), - ] + componentNames: [...root.centerLeftWidgets, ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),] } } @@ -42,23 +39,16 @@ Item { anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined HBarUserFallbackComponentRepeater { - componentNames: [ - ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), - ...root.centerWidgets, - ...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []), - ] + componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerWidgets, ...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []),] } } Side { id: centerRightSide anchors.left: !root.vertical ? centerSide.right : parent.left - anchors.top: root.vertical ? parent.top : undefined + anchors.top: root.vertical ? parent.top : centerSide.bottom HBarUserFallbackComponentRepeater { - componentNames: [ - ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), - ...root.centerRightWidgets, - ] + componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerRightWidgets,] } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 464dc683a..aca8d7da7 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -7,10 +7,13 @@ import qs.modules.common.widgets as W import qs.modules.common.widgets.shapes as Shapes import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes -// TODO: generalize this shi for vertical +// Notes +// vertical + atBottom = right side +// start radius = top or left, end radius = bottom or right Shapes.ShapeCanvas { id: bgShape + // Stuff fed from outside required property bool vertical required property bool atBottom required property bool showPopup @@ -23,45 +26,98 @@ Shapes.ShapeCanvas { property real popupHeight: popupContentHeight + popupPadding * 2 required property real startRadius required property real endRadius - property real baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical - readonly property real popupContentOffsetBase: -baseMargin + popupPadding - readonly property real popupContentOffsetY: spacing + popupContentOffsetBase + (atBottom ? -(popupHeight + backgroundHeight + spacing * 2) : 0) - readonly property real popupContentOffsetX: popupXOffset + popupContentOffsetBase + property real baseMargin: { + if (!vertical) + return (parent.height - containerShape.height) / 2; + else + return (parent.width - containerShape.width) / 2; + } property alias containerShape: containerShape property alias popupShape: popupShape + // Popup constraints // mapToGlobal is not reactive so we gotta hook manually property real xInGlobal + property real yInGlobal function updateXInGlobal() { xInGlobal = mapToGlobal(0, 0).x + xOffset; } - Component.onCompleted: updateXInGlobal() - onXChanged: updateXInGlobal() + function updateYInGlobal() { + yInGlobal = mapToGlobal(0, 0).y + yOffset; + } + function updatePosInGlobal() { + updateXInGlobal() + updateYInGlobal() + } + Component.onCompleted: updatePosInGlobal() + onXChanged: updatePosInGlobal() + onYChanged: updatePosInGlobal() readonly property real minPopupXOffset: -xInGlobal + baseMargin + readonly property real minPopupYOffset: -yInGlobal + baseMargin readonly property real maxPopupXOffset: { const maxPopupX = QsWindow.window.screen.width - popupWidth - baseMargin; const maxOffset = maxPopupX - xInGlobal; return maxOffset; } - readonly property real popupXOffset: Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset) + readonly property real maxPopupYOffset: { + const maxPopupY = QsWindow.window.screen.height - popupHeight - baseMargin; + const maxOffset = maxPopupY - yInGlobal; + return maxOffset; + } + readonly property real popupXOffset: { + if (!vertical) return Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset); + else return atBottom ? -(popupShape.width + spacing) : (containerShape.width + spacing); + } + readonly property real popupYOffset: { + if (!vertical) return atBottom ? -(popupShape.height + spacing) : (containerShape.height + spacing); + else return Math.min(Math.max(-(popupHeight - containerShape.height) / 2, minPopupYOffset), maxPopupYOffset) + } + + // Positioning + readonly property real popupContentOffsetBase: -baseMargin + popupPadding + readonly property real paddedContainerHeight: containerShape.height + baseMargin * 2 + readonly property real paddedContainerWidth: containerShape.width + baseMargin * 2 + readonly property real popupContentOffsetY: { + if (!vertical) return paddedContainerHeight + spacing + popupContentOffsetBase + (atBottom ? -(popupHeight + backgroundHeight + spacing * 2) : 0) + else return popupYOffset + popupContentOffsetBase; + } + readonly property real popupContentOffsetX: { + if (!vertical) return popupXOffset + popupContentOffsetBase; + else return paddedContainerWidth + spacing + popupContentOffsetBase + (atBottom ? -(popupWidth + backgroundWidth + spacing * 2) : 0); + } anchors { left: parent.left - leftMargin: -xOffset + leftMargin: { + if (!vertical) return -xOffset; + if (!bgShape.atBottom || !bgShape.showPopup) return baseMargin; + return baseMargin - popupShape.width - bgShape.spacing; + } top: parent.top topMargin: { - if (!bgShape.atBottom || !bgShape.showPopup) - return baseMargin; - else - return baseMargin - popupShape.height - bgShape.spacing; + if (vertical) return -yOffset; + if (!bgShape.atBottom || !bgShape.showPopup) return baseMargin; + return baseMargin - popupShape.height - bgShape.spacing; } } - width: bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth - height: bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height + width: { + if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth; + else return bgShape.showPopup ? (containerShape.width + popupShape.width + bgShape.spacing) : containerShape.width; + } + height: { + if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height; + else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight; + } color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 - xOffset: showPopup ? -popupXOffset : 0 - yOffset: bgShape.atBottom ? (height - containerShape.height) : 0 + xOffset: { + if (!vertical) return showPopup ? -popupXOffset : 0; + else return bgShape.atBottom ? (width - containerShape.width) : 0; + } + yOffset: { + if (!vertical) return bgShape.atBottom ? (height - containerShape.height) : 0; + else return showPopup ? -popupYOffset : 0; + } animation: Anim {} Behavior on width { @@ -71,10 +127,16 @@ Shapes.ShapeCanvas { Anim {} } Behavior on anchors.topMargin { - Anim {} + animation: !bgShape.vertical ? animComp.createObject(this) : undefined + } + Behavior on anchors.leftMargin { + animation: bgShape.vertical ? animComp.createObject(this) : undefined } Behavior on xOffset { - Anim {} + animation: !bgShape.vertical ? animComp.createObject(this) : undefined + } + Behavior on yOffset { + animation: bgShape.vertical ? animComp.createObject(this) : undefined } polygonIsNormalized: false @@ -85,6 +147,7 @@ Shapes.ShapeCanvas { height: bgShape.backgroundHeight startRadius: bgShape.startRadius endRadius: bgShape.endRadius + vertical: bgShape.vertical } W.RectangularContainerShape { id: popupShape @@ -92,18 +155,73 @@ Shapes.ShapeCanvas { height: bgShape.popupHeight radius: C.Appearance.rounding.large xOffset: bgShape.popupXOffset - yOffset: bgShape.atBottom ? -(popupShape.height + bgShape.spacing) : (containerShape.height + bgShape.spacing) + yOffset: bgShape.popupYOffset } roundedPolygon: { - if (!bgShape.showPopup) - return containerShape.getFullShape(); - // return popupShape.getFullShape(); // debug - const points = [...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(),]), containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), - // ...containerShape.leftPoints, - containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), ...(!bgShape.atBottom ? containerShape.topPoints : [popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(), ...popupShape.topPoints, popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(),]), containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), - // ...containerShape.rightPoints, - containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), ...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(), ...popupShape.getLastBottomPoints(),]),]; + var points = []; + if (!bgShape.showPopup) return containerShape.getFullShape(); + if (!bgShape.vertical) { + // Inline comment spam to mitigate qmlls' sabotaging of the (code) layout + points = [ + ...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [ // + ...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), // + ...popupShape.leftPoints, // + popupShape.getTopLeftPoint(), // + ]), // + containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), // + containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), // + ...(!bgShape.atBottom ? containerShape.topPoints : [ // + popupShape.getBottomLeftPoint(), // + ...popupShape.leftPoints, // + popupShape.getTopLeftPoint(), // + ...popupShape.topPoints, // + popupShape.getTopRightPoint(), // + ...popupShape.rightPoints, // + popupShape.getBottomRightPoint(), // + ]), // + containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), // + containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), // + ...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [ // + popupShape.getTopRightPoint(), // + ...popupShape.rightPoints, // + popupShape.getBottomRightPoint(), // + ...popupShape.getLastBottomPoints(), // + ]), + ]; + } else { + points = [ // + ...containerShape.getFirstBottomPoints(), // + containerShape.getBottomLeftPoint(), // + ...(!bgShape.atBottom ? containerShape.leftPoints : [ // + containerShape.getBottomLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), // + popupShape.getBottomRightPoint(), // + ...popupShape.bottomPoints, // + popupShape.getBottomLeftPoint(), // + ...popupShape.leftPoints, // + popupShape.getTopLeftPoint(), // + ...popupShape.topPoints, // + popupShape.getTopRightPoint(), // + containerShape.getTopLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), // + ]), // + containerShape.getTopLeftPoint(), // + ...containerShape.topPoints, // + containerShape.getTopRightPoint(), // + ...(bgShape.atBottom ? containerShape.rightPoints : [ // + containerShape.getTopRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), // + popupShape.getTopLeftPoint(), // + ...popupShape.topPoints, // + popupShape.getTopRightPoint(), // + ...popupShape.rightPoints, // + popupShape.getBottomRightPoint(), // + ...popupShape.bottomPoints, // + popupShape.getBottomLeftPoint(), // + containerShape.getBottomRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), // + ]), // + containerShape.getBottomRightPoint(), // + ...containerShape.getLastBottomPoints(), // + ]; + } return MaterialShapes.customPolygon(points); } @@ -111,4 +229,9 @@ Shapes.ShapeCanvas { spring: 3.5 damping: 0.3 } + + Component { + id: animComp + Anim {} + } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index a76832140..6e917652a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -15,6 +15,11 @@ HBarWidgetContainer { readonly property bool vertical: C.Config.options.bar.vertical readonly property bool atBottom: C.Config.options.bar.bottom + readonly property string timeFormatString: C.Config.options.time.format + readonly property bool is12h: timeFormatString.startsWith("h:") + readonly property bool hasAmPm: timeFormatString.toLowerCase().includes("ap") || timeFormatString.toLowerCase().endsWith("a") + readonly property bool capitalizedAmPm: timeFormatString.includes("AP") || timeFormatString.endsWith("A") + // Interactions property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") onShowPopupChanged: { @@ -83,14 +88,14 @@ HBarWidgetContainer { text: S.DateTime.time } - W.StyledText { + W.VisuallyCenteredStyledText { Layout.alignment: Qt.AlignVCenter font.pixelSize: C.Appearance.font.pixelSize.small color: C.Appearance.colors.colOnLayer1 text: "•" } - W.StyledText { + W.VisuallyCenteredStyledText { Layout.alignment: Qt.AlignVCenter font.pixelSize: C.Appearance.font.pixelSize.small color: C.Appearance.colors.colOnLayer1 @@ -104,15 +109,52 @@ HBarWidgetContainer { W.FadeLoader { id: verticalContent anchors.fill: parent + shown: contentRoot.vertical + + sourceComponent: Item { + anchors.fill: parent + implicitWidth: contentLayoutVertical.implicitWidth + implicitHeight: contentLayoutVertical.implicitHeight + + ColumnLayout { + id: contentLayoutVertical + anchors.fill: parent + spacing: -6 + + W.StyledText { + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: { + var hrs = S.DateTime.clock.hours; + if (root.is12h) hrs %= 12; + return hrs.toString().padStart(2, '0') + } + } + W.StyledText { + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.clock.minutes.toString().padStart(2, '0') + } + W.StyledText { + visible: root.hasAmPm + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.smaller + color: C.Appearance.colors.colOnLayer1 + text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap") + } + } + } } // Popup content W.ChoreographerGrid { id: popupContent anchors { - top: horizontalContent.bottom + top: root.vertical ? verticalContent.top : horizontalContent.top topMargin: bgShape.popupContentOffsetY - left: horizontalContent.left + left: root.vertical ? verticalContent.left : horizontalContent.left leftMargin: bgShape.popupContentOffsetX } From c155bde816cf6394bfe2e65a134960004b042431 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 5 Mar 2026 16:21:30 +0100 Subject: [PATCH 039/104] changes from main --- dots/.config/hypr/hyprland/keybinds.conf | 14 ++++++-------- .../scripts/colors/code/material-code-set-color.sh | 2 ++ dots/.config/quickshell/ii/services/Network.qml | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 0cca263f1..6085e7de4 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -109,8 +109,8 @@ bind = Super+Shift+Alt, Q, exec, hyprctl kill # Forcefully zap a window # Window split ratio #/# binde = Super, ;/',, # Adjust split ratio -binde = Super, Semicolon, splitratio, -0.1 # [hidden] -binde = Super, Apostrophe, splitratio, +0.1 # [hidden] +binde = Super, Semicolon, layoutmsg, splitratio -0.1 # [hidden] +binde = Super, Apostrophe, layoutmsg, splitratio +0.1 # [hidden] # Positioning mode bind = Super+Alt, Space, togglefloating, # Float/Tile bind = Super, D, fullscreen, 1 # Maximize @@ -230,10 +230,8 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi ##! Screen # Zoom -binde = Super, Minus, exec, qs -c $qsConfig ipc call zoom zoomOut # Zoom out -binde = Super, Equal, exec, qs -c $qsConfig ipc call zoom zoomIn # Zoom in -binde = Super, Minus, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # [hidden] Zoom out -binde = Super, Equal, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # [hidden] Zoom in +binde = Super, Minus, exec, ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.3 # Zoom out +binde = Super, Equal, exec, ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.3 # Zoom in # Zoom with keypad binde = Super, code:82, exec, qs -c $qsConfig ipc call zoom zoomOut # [hidden] Zoom out binde = Super, code:86, exec, qs -c $qsConfig ipc call zoom zoomIn # [hidden] Zoom in @@ -253,11 +251,11 @@ bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden] ##! Apps bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal -bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt) +bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt) bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (for Ubuntu people) bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "${TERMINAL}" "kitty -1 fish -c yazi" # File manager bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser -bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" "command -v micro && kitty -1 micro" # Code editor +bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "antigravity" "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" "command -v micro && kitty -1 micro" # Code editor bind = Ctrl+Super+Shift+Alt, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" "libreoffice" # Office software bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer diff --git a/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh b/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh index b29fa7775..2f68d2658 100755 --- a/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh +++ b/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh @@ -8,6 +8,8 @@ settings_paths=( "${XDG_CONFIG_HOME:-$HOME/.config}/Code - OSS/User/settings.json" "${XDG_CONFIG_HOME:-$HOME/.config}/Code - Insiders/User/settings.json" "${XDG_CONFIG_HOME:-$HOME/.config}/Cursor/User/settings.json" + "${XDG_CONFIG_HOME:-$HOME/.config}/Antigravity/User/settings.json" + # Add more paths as needed for other forks ) diff --git a/dots/.config/quickshell/ii/services/Network.qml b/dots/.config/quickshell/ii/services/Network.qml index 8c3e5a9d1..418258636 100644 --- a/dots/.config/quickshell/ii/services/Network.qml +++ b/dots/.config/quickshell/ii/services/Network.qml @@ -36,13 +36,13 @@ Singleton { property int networkStrength property string materialSymbol: root.ethernet ? "lan" - : root.wifiEnabled + : (root.wifiEnabled && root.wifiStatus === "connected") ? ( - Network.networkStrength > 83 ? "signal_wifi_4_bar" : - Network.networkStrength > 67 ? "network_wifi" : - Network.networkStrength > 50 ? "network_wifi_3_bar" : - Network.networkStrength > 33 ? "network_wifi_2_bar" : - Network.networkStrength > 17 ? "network_wifi_1_bar" : + (root.active?.strength ?? 0) > 83 ? "signal_wifi_4_bar" : + (root.active?.strength ?? 0) > 67 ? "network_wifi" : + (root.active?.strength ?? 0) > 50 ? "network_wifi_3_bar" : + (root.active?.strength ?? 0) > 33 ? "network_wifi_2_bar" : + (root.active?.strength ?? 0) > 17 ? "network_wifi_1_bar" : "signal_wifi_0_bar" ) : (root.wifiStatus === "connecting") From 9ffe4dfb1147971438f0e0951c188aba768d37cd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:47:00 +0100 Subject: [PATCH 040/104] hefty: bar: time: calendar --- .../ii/modules/common/Appearance.qml | 2 +- .../quickshell/ii/modules/common/Config.qml | 3 + .../ii/modules/common/config/WaffleConfig.qml | 3 - .../modules/common/widgets/CalendarView.qml | 37 ++- ...erGrid.qml => ChoreographerGridLayout.qml} | 2 +- .../modules/common/widgets/StyledButton.qml | 72 +++++ .../common/widgets/StyledIconButton.qml | 19 ++ .../hefty/topLayer/bar/widgets/HTime.qml | 275 +++++++++++++----- .../notificationCenter/CalendarWidget.qml | 3 +- 9 files changed, 326 insertions(+), 90 deletions(-) rename dots/.config/quickshell/ii/modules/common/widgets/{ChoreographerGrid.qml => ChoreographerGridLayout.qml} (93%) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StyledIconButton.qml diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index fbe362075..1d3890487 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -375,7 +375,7 @@ Singleton { property QtObject scroll: QtObject { property int duration: 200 property int type: Easing.BezierSpline - property list bezierCurve: animationCurves.standardDecel + property list bezierCurve: root.animationCurves.standardDecel } property QtObject menuDecel: QtObject { diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 659c3eb22..fb0817ed4 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -301,6 +301,9 @@ Singleton { property JsonObject calendar: JsonObject { property string locale: "en-GB" + property bool force2CharDayOfWeek: true + property bool animate: false // Disabled by default cuz laggy + property bool weekScrollPrecision: false // One scroll advances 1 week instead of 1 month } property JsonObject cheatsheet: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml b/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml index 9509ff166..d7936bb56 100644 --- a/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/WaffleConfig.qml @@ -18,7 +18,4 @@ JsonObject { property JsonObject actionCenter: JsonObject { property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ] } - property JsonObject calendar: JsonObject { - property bool force2CharDayOfWeek: true - } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml index 0e1bd13fc..25b70818c 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml @@ -1,15 +1,10 @@ pragma ComponentBehavior: Bound import QtQml import QtQuick -import QtQuick.Controls import QtQuick.Layouts -import Quickshell -import qs -import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.common.functions -import qs.modules.waffle.looks Item { id: root @@ -36,15 +31,23 @@ Item { const diffWeeks = Math.round(diffMillis / root.millisPerWeek); root.targetWeekDiff += diffWeeks; } + function scrollToToday() { + root.targetWeekDiff = 0; + } property int weeksPerScroll: 1 property real targetWeekDiff: 0 property real weekDiff: targetWeekDiff property int contentWeekDiff: weekDiff // whole part of weekDiff property bool scrolling: false + property Animation scrollAnimation: NumberAnimation { + duration: Config.options.calendar.animate ? Appearance.animation.scroll.duration : 0 + easing.type: Appearance.animation.scroll.type + easing.bezierCurve: Appearance.animation.scroll.bezierCurve + } Behavior on weekDiff { id: weekScrollBehavior - animation: Looks.transition.scroll.createObject(this) + animation: root.scrollAnimation } Timer { id: scrollAnimationCheckTimer @@ -56,12 +59,20 @@ Item { scrollAnimationCheckTimer.restart(); } - MouseArea { - anchors.fill: parent - onWheel: wheel => { - root.targetWeekDiff += wheel.angleDelta.y / 120 * -root.weeksPerScroll; // Reverse cuz scrolling down should advance + property var wheelAction: (wheel) => { + // Reverse cuz scrolling down should advance + const sign = wheel.angleDelta.y / 120 * -1; + if (Config.options.calendar.weekScrollPrecision) { + root.targetWeekDiff += sign * root.weeksPerScroll; + } else { + scrollMonthsAndSnap(sign); } } + MouseArea { + id: mouseArea + anchors.fill: parent + onWheel: (wheel) => root.wheelAction(wheel) + } // Date calculations readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000 @@ -82,14 +93,16 @@ Item { return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday } property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed + property string title: locale.toString(focusedDate, "MMMM yyyy") // Sizes property real verticalPadding: 0 + property real horizontalPadding: 0 property real buttonSize: 40 property real buttonSpacing: 2 property real buttonVerticalSpacing: buttonSpacing implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding) - implicitWidth: weeksColumn.implicitWidth + implicitWidth: weeksColumn.implicitWidth + (2 * horizontalPadding) clip: true ColumnLayout { @@ -97,6 +110,8 @@ Item { anchors { left: parent.left right: parent.right + leftMargin: root.horizontalPadding + rightMargin: root.horizontalPadding } y: { const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing; diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml similarity index 93% rename from dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml rename to dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml index 7223458d1..97223d904 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGrid.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml @@ -10,7 +10,7 @@ GridLayout { property real totalDuration: 250 property real interval: totalDuration / count - default property list choreographableChildren + default property list choreographableChildren readonly property int count: choreographableChildren.length children: choreographableChildren diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml new file mode 100644 index 000000000..970e212bf --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml @@ -0,0 +1,72 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import ".." +import "../functions" + +Button { + id: root + + property alias radius: bg.radius + property alias contentLayer: bg.contentLayer + + property color colFocusRing: Appearance.colors.colOnSecondaryContainer + property color colBackground: checked ? colBackgroundChecked : colBackgroundUnchecked + property color colForeground: checked ? colForegroundChecked : colForegroundUnchecked + + property color colBackgroundUnchecked: ColorUtils.transparentize(Appearance.colors.colLayer4Base, 1) + property color colBackgroundChecked: Appearance.colors.colPrimary + property color colForegroundUnchecked: Appearance.colors.colOnLayer4 + property color colForegroundChecked: Appearance.colors.colOnPrimary + + hoverEnabled: true + opacity: root.enabled ? 1 : 0.5 + + HoverHandler { + cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor + } + + background: StyledRectangle { + id: bg + implicitHeight: root.contentItem.implicitHeight + implicitWidth: root.contentItem.implicitWidth + + radius: Math.min(width, height) / 2 + color: root.colBackground + + StateOverlay { + anchors.fill: parent + hover: root.hovered && root.enabled + press: root.pressed && root.enabled + focus: false // We use a ring instead + radius: bg.radius + } + + Rectangle { + id: focusRing + radius: bg.radius - anchors.margins + visible: root.visualFocus + color: "transparent" + anchors { + fill: parent + margins: -4 + } + border { + color: root.colFocusRing + width: 2 + } + } + } + + contentItem: Item { + implicitWidth: buttonText.implicitWidth + implicitHeight: buttonText.implicitHeight + VisuallyCenteredStyledText { + id: buttonText + anchors.centerIn: parent + text: root.text + color: root.colForeground + } + } + +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledIconButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledIconButton.qml new file mode 100644 index 000000000..66387c410 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledIconButton.qml @@ -0,0 +1,19 @@ +pragma ComponentBehavior: Bound +import QtQuick + +StyledButton { + id: root + + property alias implicitSize: root.implicitHeight + implicitWidth: implicitHeight + property alias iconSize: icon.iconSize + + contentItem: Item { + MaterialSymbol { + id: icon + anchors.centerIn: parent + color: root.colForeground + text: root.text + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index 6e917652a..36bbd1d11 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Controls import QtQuick.Layouts import qs.modules.common as C @@ -70,38 +71,8 @@ HBarWidgetContainer { anchors.fill: parent shown: !contentRoot.vertical - sourceComponent: Item { + sourceComponent: HorizontalClock { anchors.fill: parent - implicitWidth: contentLayout.implicitWidth - implicitHeight: contentLayout.implicitHeight - - RowLayout { - id: contentLayout - anchors.fill: parent - - W.VisuallyCenteredStyledText { - Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter - Layout.fillHeight: true - font.pixelSize: C.Appearance.font.pixelSize.large - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.time - } - - W.VisuallyCenteredStyledText { - Layout.alignment: Qt.AlignVCenter - font.pixelSize: C.Appearance.font.pixelSize.small - color: C.Appearance.colors.colOnLayer1 - text: "•" - } - - W.VisuallyCenteredStyledText { - Layout.alignment: Qt.AlignVCenter - font.pixelSize: C.Appearance.font.pixelSize.small - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.longDate - } - } } } @@ -111,45 +82,13 @@ HBarWidgetContainer { anchors.fill: parent shown: contentRoot.vertical - sourceComponent: Item { + sourceComponent: VerticalClock { anchors.fill: parent - implicitWidth: contentLayoutVertical.implicitWidth - implicitHeight: contentLayoutVertical.implicitHeight - - ColumnLayout { - id: contentLayoutVertical - anchors.fill: parent - spacing: -6 - - W.StyledText { - Layout.alignment: Qt.AlignHCenter - font.pixelSize: C.Appearance.font.pixelSize.large - color: C.Appearance.colors.colOnLayer1 - text: { - var hrs = S.DateTime.clock.hours; - if (root.is12h) hrs %= 12; - return hrs.toString().padStart(2, '0') - } - } - W.StyledText { - Layout.alignment: Qt.AlignHCenter - font.pixelSize: C.Appearance.font.pixelSize.large - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.clock.minutes.toString().padStart(2, '0') - } - W.StyledText { - visible: root.hasAmPm - Layout.alignment: Qt.AlignHCenter - font.pixelSize: C.Appearance.font.pixelSize.smaller - color: C.Appearance.colors.colOnLayer1 - text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap") - } - } } } // Popup content - W.ChoreographerGrid { + PopupContent { id: popupContent anchors { top: root.vertical ? verticalContent.top : horizontalContent.top @@ -159,21 +98,211 @@ HBarWidgetContainer { } shown: root.showPopup + } + } + + component HorizontalClock: Item { + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + + RowLayout { + id: contentLayout + anchors.fill: parent + + W.VisuallyCenteredStyledText { + Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.fillHeight: true + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.time + } + + W.VisuallyCenteredStyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: "•" + } + + W.VisuallyCenteredStyledText { + Layout.alignment: Qt.AlignVCenter + font.pixelSize: C.Appearance.font.pixelSize.small + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.longDate + } + } + } + + component VerticalClock: Item { + implicitWidth: contentLayoutVertical.implicitWidth + implicitHeight: contentLayoutVertical.implicitHeight + + ColumnLayout { + id: contentLayoutVertical + anchors.fill: parent + spacing: amPmText.visible ? -2 : -4 + + ColumnLayout { + id: verticalTime + Layout.alignment: Qt.AlignHCenter + spacing: -4 - W.FlyFadeEnterChoreographable { W.StyledText { - text: "Kalender" - font.pixelSize: 25 + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: { + var hrs = S.DateTime.clock.hours; + if (root.is12h && hrs != 12) + hrs %= 12; + return hrs.toString().padStart(2, '0'); + } + } + W.StyledText { + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.clock.minutes.toString().padStart(2, '0') + } + W.StyledText { + id: amPmText + visible: root.hasAmPm + Layout.topMargin: -2 + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.smaller + color: C.Appearance.colors.colOnLayer1 + text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap") } } - W.FlyFadeEnterChoreographable { + + W.StyledText { + Layout.alignment: Qt.AlignHCenter + font.pixelSize: C.Appearance.font.pixelSize.smallest + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.shortDate + } + } + } + + component PopupContent: W.ChoreographerGridLayout { + id: popupRoot + + property real buttonSize: C.Appearance.rounding.normal * 2 + property real buttonSpacing: 4 + + rowSpacing: 0 + + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + + RowLayout { + width: parent.width + spacing: 0 + W.StyledText { - text: "Lorem ipsum okakumalum tung\ntung tung tung" + Layout.leftMargin: 6 + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + text: calendarView.title + font.pixelSize: C.Appearance.font.pixelSize.larger + elide: Text.ElideRight + color: C.Appearance.colors.colSecondary + } + W.StyledIconButton { + implicitSize: 30 + text: "chevron_left" + iconSize: 20 + onClicked: calendarView.scrollMonthsAndSnap(-1) + colForeground: C.Appearance.colors.colPrimary + } + W.StyledIconButton { + implicitSize: 30 + text: "chevron_right" + iconSize: 20 + onClicked: calendarView.scrollMonthsAndSnap(1) + colForeground: C.Appearance.colors.colPrimary + } + W.StyledIconButton { + implicitSize: 30 + text: "rotate_left" + iconSize: 20 + onClicked: calendarView.scrollToToday() + colForeground: C.Appearance.colors.colPrimary + enabled: calendarView.targetWeekDiff != 0 } } - W.FlyFadeEnterChoreographable { - W.StyledText { - text: "BAJLANDO\nUUOOOUUUOOOUUOOUOU" + } + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + + DayOfWeekRow { + id: dayOfWeekRow + width: parent.width + locale: calendarView.locale + spacing: popupRoot.buttonSpacing + delegate: Item { + id: dayOfWeekItem + required property var model + implicitHeight: popupRoot.buttonSize + implicitWidth: popupRoot.buttonSize + + W.VisuallyCenteredStyledText { + anchors.centerIn: parent + text: { + var result = dayOfWeekItem.model.shortName; + if (C.Config.options.calendar.force2CharDayOfWeek) + result = result.substring(0, 2); + return result; + } + } + } + } + } + W.FlyFadeEnterChoreographable { + Item { + implicitWidth: calendarView.implicitWidth - calendarView.horizontalPadding * 2 + implicitHeight: calendarView.implicitHeight - calendarView.verticalPadding * 2 + W.CalendarView { + id: calendarView + anchors.centerIn: parent + locale: Qt.locale(C.Config.options.calendar.locale) + verticalPadding: 4 + horizontalPadding: 4 + buttonSize: popupRoot.buttonSize + buttonSpacing: popupRoot.buttonSpacing + buttonVerticalSpacing: popupRoot.buttonSpacing + Layout.fillWidth: true + + delegate: W.StyledButton { + id: dayButton + required property var model + + focus: model.today + checked: model.today + enabled: model.month === calendarView.focusedMonth + implicitWidth: popupRoot.buttonSize + implicitHeight: popupRoot.buttonSize + width: implicitWidth + height: implicitHeight + text: model.day + + Connections { + target: popupRoot + enabled: dayButton.model.today + function onShownChanged() { + if (popupRoot.shown) dayButton.forceActiveFocus(); + } + } + + contentItem: Item { + W.VisuallyCenteredStyledText { + anchors.centerIn: parent + text: dayButton.text + color: dayButton.colForeground + } + } + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 16dc831e1..8255dba4e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -54,7 +54,7 @@ BodyRectangle { anchors.centerIn: parent text: { var result = dayOfWeekItem.model.shortName; - if (Config.options.waffles.calendar.force2CharDayOfWeek) result = result.substring(0,2); + if (Config.options.calendar.force2CharDayOfWeek) result = result.substring(0,2); return result; } color: Looks.colors.fg @@ -70,6 +70,7 @@ BodyRectangle { buttonSpacing: 6 buttonVerticalSpacing: 1 Layout.fillWidth: true + scrollAnimation: Looks.transition.scroll.createObject(this) delegate: DayButton {} } } From 635d49cad0f6b64c22243c04db4f8d5833197dc6 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:47:12 +0100 Subject: [PATCH 041/104] notifications: dont print on discard --- dots/.config/quickshell/ii/services/Notifications.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/services/Notifications.qml b/dots/.config/quickshell/ii/services/Notifications.qml index a45b86007..d93bc4479 100644 --- a/dots/.config/quickshell/ii/services/Notifications.qml +++ b/dots/.config/quickshell/ii/services/Notifications.qml @@ -190,7 +190,7 @@ Singleton { } function discardNotification(id) { - console.log("[Notifications] Discarding notification with ID: " + id); + // console.log("[Notifications] Discarding notification with ID: " + id); const index = root.list.findIndex((notif) => notif.notificationId === id); const notifServerIndex = notifServer.trackedNotifications.values.findIndex((notif) => notif.id + root.idOffset === id); if (index !== -1) { From 3da23ce176140b9a93207222f795076735eff4be Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:49:38 +0100 Subject: [PATCH 042/104] change spring anims to expressive default spatial --- .../ii/modules/hefty/topLayer/HTopLayerPanel.qml | 12 ++++-------- .../hefty/topLayer/bar/HBarWidgetShapeBackground.qml | 7 ++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index bd7da8d83..e0241722e 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -53,14 +53,10 @@ PanelWindow { anchors.fill: parent polygonIsNormalized: false roundedPolygon: MaterialShapes.customPolygon([new MaterialShapes.PointNRound(new Offset.Offset(root.screen.width, 0), new CornerRounding.CornerRounding(9999)),]) - // animation: NumberAnimation { - // duration: 500 - // easing.type: Easing.BezierSpline - // easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial - // } - animation: SpringAnimation { - spring: 3.5 - damping: 0.35 + animation: NumberAnimation { + duration: 500 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial } color: Appearance.colors.colLayer0 borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1 diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index aca8d7da7..6c32d72ba 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -225,9 +225,10 @@ Shapes.ShapeCanvas { return MaterialShapes.customPolygon(points); } - component Anim: SpringAnimation { - spring: 3.5 - damping: 0.3 + component Anim: NumberAnimation { + duration: C.Appearance.animation.elementMove.duration + easing.type: C.Appearance.animation.elementMove.type + easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve } Component { From 83baff78946fc7d14998d242eb921d3663ebcfa1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:09:34 +0100 Subject: [PATCH 043/104] refactor time duration formatting --- .../ii/modules/common/functions/DateUtils.qml | 15 +++++++++++++ .../ii/modules/ii/bar/BatteryPopup.qml | 21 ++++--------------- .../quickshell/ii/services/DateTime.qml | 18 +++------------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml index 3cbae5bee..cb50243e6 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml @@ -24,4 +24,19 @@ Singleton { targetDate.setDate(firstDayDate.getDate() + i); return targetDate; } + + function formatDuration(seconds) { + const d = Math.floor(seconds / 86400); + const h = Math.floor((seconds % 86400) / 3600); + const m = Math.floor((seconds % 3600) / 60); + + let str = ""; + if (d > 0) + str += `${d}d`; + if (h > 0) + str += `${str ? ", " : ""}${h}h`; + if (m > 0 || !str) + str += `${str ? ", " : ""}${m}m`; + return str; + } } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml index 26eda569e..84b25cbc1 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.functions import qs.modules.common.widgets import qs.services import QtQuick @@ -27,18 +28,10 @@ StyledPopup { icon: "schedule" label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") value: { - function formatTime(seconds) { - var h = Math.floor(seconds / 3600); - var m = Math.floor((seconds % 3600) / 60); - if (h > 0) - return `${h}h, ${m}m`; - else - return `${m}m`; - } if (Battery.isCharging) - return formatTime(Battery.timeToFull); + return DateUtils.formatDuration(Battery.timeToFull); else - return formatTime(Battery.timeToEmpty); + return DateUtils.formatDuration(Battery.timeToEmpty); } } @@ -54,13 +47,7 @@ StyledPopup { return Translation.tr("Discharging:"); } } - value: { - if (Battery.chargeState == 4) { - return ""; - } else { - return `${Battery.energyRate.toFixed(2)}W`; - } - } + value: `${Battery.energyRate.toFixed(2)}W` } StyledPopupValueRow { diff --git a/dots/.config/quickshell/ii/services/DateTime.qml b/dots/.config/quickshell/ii/services/DateTime.qml index e1a8300d4..462243e6f 100644 --- a/dots/.config/quickshell/ii/services/DateTime.qml +++ b/dots/.config/quickshell/ii/services/DateTime.qml @@ -2,6 +2,7 @@ pragma Singleton pragma ComponentBehavior: Bound import qs import qs.modules.common +import qs.modules.common.functions import QtQuick import Quickshell import Quickshell.Io @@ -10,6 +11,7 @@ import Quickshell.Io * A nice wrapper for date and time strings. */ Singleton { + id: root property var clock: SystemClock { id: clock precision: { @@ -33,21 +35,7 @@ Singleton { fileUptime.reload(); const textUptime = fileUptime.text(); const uptimeSeconds = Number(textUptime.split(" ")[0] ?? 0); - - // Convert seconds to days, hours, and minutes - const days = Math.floor(uptimeSeconds / 86400); - const hours = Math.floor((uptimeSeconds % 86400) / 3600); - const minutes = Math.floor((uptimeSeconds % 3600) / 60); - - // Build the formatted uptime string - let formatted = ""; - if (days > 0) - formatted += `${days}d`; - if (hours > 0) - formatted += `${formatted ? ", " : ""}${hours}h`; - if (minutes > 0 || !formatted) - formatted += `${formatted ? ", " : ""}${minutes}m`; - uptime = formatted; + root.uptime = DateUtils.formatDuration(uptimeSeconds) interval = Config.options?.resources?.updateInterval ?? 3000; } } From e480477db22b118bbae193a7f92476b93d2c849a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:14:38 +0100 Subject: [PATCH 044/104] hefty: bar: fix ws icons sometimes not showing --- .../topLayer/bar/widgets/HWorkspaces.qml | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index cf28e9232..da2b6a8bc 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -242,30 +242,33 @@ HBarWidgetContainer { diameter: appIcon.implicitSize } - Colorizer { + Loader { // Somehow putting this multieffect in a loader prevents it from not showing up + id: colorizer anchors.fill: appIcon - implicitWidth: appIcon.implicitWidth - implicitHeight: appIcon.implicitHeight - colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary - colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 - brightness: 0 - source: appIcon + sourceComponent: Colorizer { + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary + colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 + brightness: 0 + source: appIcon - opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 - visible: opacity > 0 - scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize + opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0 + visible: opacity > 0 + scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on scale { + animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) + } + + maskEnabled: true + maskSource: iconMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 } - Behavior on scale { - animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this) - } - - maskEnabled: true - maskSource: iconMask - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 } } } From 2a714378c9f379361561e766f3f36032d87fa783 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:15:00 +0100 Subject: [PATCH 045/104] refactor fixed width text --- .../widgets/FixedWidthTextContainer.qml | 16 ++++++++++++++ .../waffle/looks/WTextWithFixedWidth.qml | 22 ++++++------------- 2 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/FixedWidthTextContainer.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FixedWidthTextContainer.qml b/dots/.config/quickshell/ii/modules/common/widgets/FixedWidthTextContainer.qml new file mode 100644 index 000000000..4033bc092 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/FixedWidthTextContainer.qml @@ -0,0 +1,16 @@ +import QtQuick + +Item { + id: root + + property alias longestText: longestTextMetrics.text + property alias font: longestTextMetrics.font + + implicitWidth: longestTextMetrics.width + implicitHeight: longestTextMetrics.height + + TextMetrics { + id: longestTextMetrics + text: root.longestText + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextWithFixedWidth.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextWithFixedWidth.qml index 935878dc4..e0727eb0e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WTextWithFixedWidth.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextWithFixedWidth.qml @@ -1,31 +1,23 @@ import QtQuick +import qs.modules.common.widgets as W -Item { +W.FixedWidthTextContainer { id: root - property string longestText property alias text: textItem.text - property alias font: textItem.font property alias horizontalAlignment: textItem.horizontalAlignment property alias verticalAlignment: textItem.verticalAlignment property alias color: textItem.color - implicitWidth: longestTextMetrics.width - implicitHeight: longestTextMetrics.height - - TextMetrics { - id: longestTextMetrics - text: root.longestText - font { - family: Looks.font.family.ui - pixelSize: Looks.font.pixelSize.large - weight: Looks.font.weight.regular - } + font { + family: Looks.font.family.ui + pixelSize: Looks.font.pixelSize.large + weight: Looks.font.weight.regular } WText { id: textItem anchors.fill: parent - font.pixelSize: Looks.font.pixelSize.large + font: root.font } } From 233a06a4c0e04573f133c9ef48a808424585d15f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:15:25 +0100 Subject: [PATCH 046/104] hefty: bar: fix wrong placement of center-right group --- .../quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index a388cdb62..7b1e5d6d3 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -46,7 +46,7 @@ Item { Side { id: centerRightSide anchors.left: !root.vertical ? centerSide.right : parent.left - anchors.top: root.vertical ? parent.top : centerSide.bottom + anchors.top: !root.vertical ? parent.top : centerSide.bottom HBarUserFallbackComponentRepeater { componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerRightWidgets,] } From 6b08e16222be9ecaba0ab1722ba96fd251bb53d9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:15:50 +0100 Subject: [PATCH 047/104] MaterialSymbol: allow customization of fill anim --- .../ii/modules/common/widgets/MaterialSymbol.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml b/dots/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml index d117bb60b..101c0d424 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml @@ -20,11 +20,12 @@ StyledText { } } + property Animation fillAnimation: NumberAnimation { + duration: Appearance?.animation.elementMoveFast.duration ?? 200 + easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline + easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1] + } Behavior on fill { // Leaky leaky, no good - NumberAnimation { - duration: Appearance?.animation.elementMoveFast.duration ?? 200 - easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline - easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1] - } + animation: root.fillAnimation } } From dd0b8a511496f9015608b260eaa5965075396022 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:16:31 +0100 Subject: [PATCH 048/104] battery service: add energy rate known-or-not status --- dots/.config/quickshell/ii/services/Battery.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index e2a285f22..ef9502a15 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -11,6 +11,7 @@ Singleton { id: root property bool available: UPower.displayDevice.isLaptopBattery property var chargeState: UPower.displayDevice.state + property bool knownEnergyRate: energyRate != 0 && (chargeState == UPowerDeviceState.Charging || chargeState == UPowerDeviceState.Discharging) property bool isCharging: chargeState == UPowerDeviceState.Charging property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge property real percentage: UPower.displayDevice?.percentage ?? 1 From 3a4804653dcaa650083b389335488a2349a34d3c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:17:01 +0100 Subject: [PATCH 049/104] ClippedProgressBar: switch from opacitymask to multieffect --- .../common/widgets/ClippedProgressBar.qml | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml index b47b49ba7..af5407223 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml @@ -3,7 +3,7 @@ import qs.modules.common.functions import qs.modules.common.widgets import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects /** * A progress bar with both ends rounded and text acts as clipping like OneUI 7's battery indicator. @@ -16,15 +16,17 @@ ProgressBar { property color highlightColor: Appearance?.colors.colOnSecondaryContainer ?? "#685496" property color trackColor: ColorUtils.transparentize(highlightColor, 0.5) ?? "#F1D3F9" property alias radius: contentItem.radius + property alias progressRadius: progressFill.radius property string text default property Item textMask: Item { - width: valueBarWidth - height: valueBarHeight - StyledText { - anchors.centerIn: parent + width: root.valueBarWidth + height: root.valueBarHeight + VisuallyCenteredStyledText { + anchors.fill: parent font: root.font text: root.text } + layer.enabled: true } text: Math.round(value * 100) @@ -80,22 +82,40 @@ ProgressBar { } } - OpacityMask { - id: roundingMask + Rectangle { + id: contentMaskRect + anchors.fill: contentItem + width: contentItem.width + height: contentItem.height + radius: contentItem.radius + layer.enabled: true visible: false - anchors.fill: parent - source: contentItem - maskSource: Rectangle { - width: contentItem.width - height: contentItem.height - radius: contentItem.radius - } } - OpacityMask { - anchors.fill: parent - source: roundingMask - invert: true - maskSource: root.textMask + Item { + // textMask has to be rendered somewhere so we put it in a practically invisible item + anchors.centerIn: parent + opacity: 0 + Component.onCompleted: root.textMask.layer.enabled = true // for multieffect masking + children: [root.textMask] } + + MaskMultiEffect { + id: boxClip + anchors.fill: parent + source: contentItem + maskSource: contentMaskRect + visible: false + } + + MaskMultiEffect { + id: textClip + anchors.fill: parent + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + source: boxClip + maskSource: root.textMask + maskInverted: true + } + } From f8840e8389dfa0b02a80abb2a7cfbcf28694920d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:17:44 +0100 Subject: [PATCH 050/104] hefty: bar: add system info indicator (only battery for now) --- .../topLayer/bar/widgets/HSystemInfo.qml | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml new file mode 100644 index 000000000..04970fc64 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml @@ -0,0 +1,224 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Services.UPower + +import qs.services as S +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.modules.common.widgets as W +import ".." + +HBarWidgetWithPopout { + id: root + + property bool chargingAndNotFull: S.Battery.isCharging && S.Battery.percentage < 1 + property bool powerSaving: PowerProfiles.profile == PowerProfile.PowerSaver + + popupContentWidth: popupContent.implicitWidth + popupContentHeight: popupContent.implicitHeight + + HBarWidgetContent { + id: contentRoot + vertical: root.vertical + atBottom: root.atBottom + showPopup: root.showPopup + contentImplicitWidth: activeItem.implicitWidth + contentImplicitHeight: activeItem.implicitHeight + onClicked: root.showPopup = !root.showPopup + property var activeItem: vertical ? verticalContent : horizontalContent + + W.FadeLoader { + id: horizontalContent + anchors.fill: parent + shown: !contentRoot.vertical + + sourceComponent: HorizontalSysInfo {} + } + + W.FadeLoader { + id: verticalContent + anchors.fill: parent + shown: contentRoot.vertical + + sourceComponent: HorizontalSysInfo {} + } + + SysInfoPopupContent { + id: popupContent + anchors { + top: root.vertical ? contentRoot.activeItem.top : contentRoot.activeItem.top + topMargin: root.popupContentOffsetY + left: root.vertical ? contentRoot.activeItem.left : contentRoot.activeItem.left + leftMargin: root.popupContentOffsetX + } + + shown: root.showPopup + } + } + + component HorizontalSysInfo: Item { + implicitWidth: row.implicitWidth + row.anchors.leftMargin + row.anchors.rightMargin + implicitHeight: row.implicitHeight + row.anchors.topMargin + row.anchors.bottomMargin + + RowLayout { + id: row + anchors.fill: parent + anchors.leftMargin: root.startSide ? 8 : 6 + anchors.rightMargin: root.endSide ? 0 : -3 + + Battery {} + } + } + + component Battery: Row { + spacing: 1.5 + Layout.fillHeight: true + + W.ClippedProgressBar { + id: batteryProgress + anchors.verticalCenter: parent.verticalCenter + valueBarWidth: 28 + valueBarHeight: 16 + radius: 4 + progressRadius: 0 + value: S.Battery.percentage + highlightColor: (S.Battery.isLow && !S.Battery.isCharging) ? C.Appearance.m3colors.m3error : C.Appearance.colors.colOnSecondaryContainer + font.pixelSize: boltIcon.visible ? 13 : 14 + + Item { + layer.enabled: true + width: batteryProgress.valueBarWidth + height: batteryProgress.valueBarHeight + RowLayout { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: (parent.height - height) / 2 + } + spacing: 0 + + W.MaterialSymbol { + id: boltIcon + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: -2 + Layout.rightMargin: -2 + fill: 1 * (text == "bolt") + fillAnimation: null + text: { + if (root.chargingAndNotFull) + return "bolt"; + if (root.powerSaving) + return "nest_eco_leaf"; + return "circle"; + } + iconSize: C.Appearance.font.pixelSize.small + font.weight: Font.DemiBold + visible: root.chargingAndNotFull || root.powerSaving + } + W.VisuallyCenteredStyledText { + Layout.fillHeight: true + font: batteryProgress.font + text: batteryProgress.text + } + } + } + } + Rectangle { + anchors.verticalCenter: parent.verticalCenter + color: batteryProgress.trackColor + topRightRadius: width + bottomRightRadius: width + implicitWidth: 2.5 + implicitHeight: 8 + } + } + + component SysInfoPopupContent: W.ChoreographerGridLayout { + + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + + RowLayout { + anchors.fill: parent + spacing: 10 + + W.CircularProgress { + implicitSize: 46 + lineWidth: 3 + value: S.Battery.health / 100 + W.MaterialSymbol { + anchors.centerIn: parent + iconSize: 22 + text: { + if (root.chargingAndNotFull) + return "battery_android_plus"; + if (root.powerSaving) + return "energy_savings_leaf"; + return "battery_android_full"; + } + } + } + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + RowLayout { + visible: S.Battery.knownEnergyRate + Layout.fillWidth: true + spacing: 4 + W.StyledText { + Layout.alignment: Qt.AlignBaseline + text: F.DateUtils.formatDuration(S.Battery.isCharging ? S.Battery.timeToFull : S.Battery.timeToEmpty) + font.pixelSize: C.Appearance.font.pixelSize.title + } + W.StyledText { + Layout.alignment: Qt.AlignBaseline + text: S.Battery.isCharging ? S.Translation.tr("to full") : S.Translation.tr("remaining") + } + } + RowLayout { + Layout.fillWidth: true + StatWithIcon { + visible: S.Battery.knownEnergyRate + Layout.fillWidth: true + icon: "bolt" + value: `${S.Battery.energyRate.toFixed(2)}W` + } + StatWithIcon { + Layout.fillWidth: true + icon: "heart_check" + value: `${(S.Battery.health).toFixed(1)}%` + } + } + } + } + } + } + + component StatWithIcon: Item { + id: statItem + required property string icon + required property string value + implicitWidth: statRow.implicitWidth + implicitHeight: statRow.implicitHeight + RowLayout { + id: statRow + anchors.fill: parent + spacing: 4 + W.MaterialSymbol { + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + text: statItem.icon + } + W.VisuallyCenteredStyledText { + Layout.fillWidth: false + Layout.fillHeight: true + text: statItem.value + } + Item { + Layout.fillWidth: true + } + } + } +} From 3b13d0c9386ad68588a01ed5616fcf344a5a89e9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:41:41 +0100 Subject: [PATCH 051/104] hefty: bar: make day of week row reliable --- .../common/widgets/CalendarDaysOfWeek.qml | 36 +++++++ .../topLayer/bar/HBarWidgetWithPopout.qml | 53 +++++++++++ .../hefty/topLayer/bar/widgets/HTime.qml | 95 +++++++------------ 3 files changed, 123 insertions(+), 61 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/CalendarDaysOfWeek.qml create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetWithPopout.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarDaysOfWeek.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarDaysOfWeek.qml new file mode 100644 index 000000000..bb0e11674 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarDaysOfWeek.qml @@ -0,0 +1,36 @@ +pragma ComponentBehavior: Bound +import QtQuick + +/** + * Replacement for QtQuick Controls DayOfWeek row. + * I have to do this because that one is somehow really unreliable in my dynamically loaded widget + */ +Row { + id: root + property Component delegate + property alias model: repeater.model + + property var locale: Qt.locale() + readonly property var firstDayOfWeek: locale.firstDayOfWeek + + Repeater { + id: repeater + model: Array.from({ + length: 7 + }, (_, i) => { + const day = (root.firstDayOfWeek + i + 7 - 1) % 7 + 1 + return ({ + // Convert Locale day of week enum values to that of Qt enum values for + // consistency with DayOfWeekRow. Note that Locale day of week enum values are 0-indexed, + // while Qt day of week enum values are 1-indexed. + // Refererences: + // Locale enum values: https://doc.qt.io/qt-6/qml-qtqml-locale.html#firstDayOfWeek-prop + // DayOfWeek model values: https://doc.qt.io/qt-6/qml-qtquick-controls-dayofweekrow.html#delegate-prop + // which mentions the enum values in the Qt namespace at: https://doc.qt.io/qt-6/qt.html#DayOfWeek-enum + day: day, + shortName: root.locale.toString(new Date(2024, 0, day), "ddd") + }) + }) + delegate: root.delegate + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetWithPopout.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetWithPopout.qml new file mode 100644 index 000000000..f84f3a50b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetWithPopout.qml @@ -0,0 +1,53 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.services as S +import qs.modules.common.widgets as W +import ".." + +HBarWidgetContainer { + id: root + + property bool showPopup: false + readonly property bool vertical: C.Config.options.bar.vertical + readonly property bool atBottom: C.Config.options.bar.bottom + + // Interactions + property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") + onShowPopupChanged: { + if (root.showPopup) { + root.morphedPanelParent.addAttachedMaskItem(bgShape); + } else { + root.morphedPanelParent.removeAttachedMaskItem(bgShape); + } + } + Connections { + target: root.morphedPanelParent + function onFocusGrabDismissed() { + root.showPopup = false; + } + } + + // Background container shape + property alias backgroundShape: bgShape + property alias popupContentWidth: bgShape.popupContentWidth + property alias popupContentHeight: bgShape.popupContentHeight + property alias popupContentOffsetX: bgShape.popupContentOffsetX + property alias popupContentOffsetY: bgShape.popupContentOffsetY + background: HBarWidgetShapeBackground { + id: bgShape + + vertical: root.vertical + atBottom: root.atBottom + showPopup: root.showPopup + + backgroundWidth: root.backgroundWidth + backgroundHeight: root.backgroundHeight + startRadius: root.getBackgroundRadius(root.startSide) + endRadius: root.getBackgroundRadius(root.endSide) + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index 36bbd1d11..aa46e6877 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -4,54 +4,20 @@ import QtQuick.Controls import QtQuick.Layouts import qs.modules.common as C -import qs.modules.common.functions as F import qs.services as S import qs.modules.common.widgets as W import ".." -HBarWidgetContainer { +HBarWidgetWithPopout { id: root - property bool showPopup: false - readonly property bool vertical: C.Config.options.bar.vertical - readonly property bool atBottom: C.Config.options.bar.bottom - readonly property string timeFormatString: C.Config.options.time.format readonly property bool is12h: timeFormatString.startsWith("h:") readonly property bool hasAmPm: timeFormatString.toLowerCase().includes("ap") || timeFormatString.toLowerCase().endsWith("a") readonly property bool capitalizedAmPm: timeFormatString.includes("AP") || timeFormatString.endsWith("A") - // Interactions - property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") - onShowPopupChanged: { - if (root.showPopup) { - root.morphedPanelParent.addAttachedMaskItem(bgShape); - } else { - root.morphedPanelParent.removeAttachedMaskItem(bgShape); - } - } - Connections { - target: root.morphedPanelParent - function onFocusGrabDismissed() { - root.showPopup = false; - } - } - - // Background container shape - background: HBarWidgetShapeBackground { - id: bgShape - - vertical: root.vertical - atBottom: root.atBottom - showPopup: root.showPopup - - backgroundWidth: root.backgroundWidth - backgroundHeight: root.backgroundHeight - popupContentWidth: popupContent.implicitWidth - popupContentHeight: popupContent.implicitHeight - startRadius: root.getBackgroundRadius(root.startSide) - endRadius: root.getBackgroundRadius(root.endSide) - } + popupContentWidth: popupContent.implicitWidth + popupContentHeight: popupContent.implicitHeight // The button on the bar HBarWidgetContent { @@ -62,8 +28,10 @@ HBarWidgetContainer { showPopup: root.showPopup onClicked: root.showPopup = !showPopup - contentImplicitWidth: vertical ? verticalContent.implicitWidth : horizontalContent.implicitWidth - contentImplicitHeight: vertical ? verticalContent.implicitHeight : horizontalContent.implicitHeight + contentImplicitWidth: activeItem.implicitWidth + contentImplicitHeight: activeItem.implicitHeight + + property Item activeItem: vertical ? verticalContent : horizontalContent // When horizontal W.FadeLoader { @@ -71,9 +39,7 @@ HBarWidgetContainer { anchors.fill: parent shown: !contentRoot.vertical - sourceComponent: HorizontalClock { - anchors.fill: parent - } + sourceComponent: HorizontalClock {} } // When vertical @@ -82,9 +48,7 @@ HBarWidgetContainer { anchors.fill: parent shown: contentRoot.vertical - sourceComponent: VerticalClock { - anchors.fill: parent - } + sourceComponent: VerticalClock {} } // Popup content @@ -92,9 +56,9 @@ HBarWidgetContainer { id: popupContent anchors { top: root.vertical ? verticalContent.top : horizontalContent.top - topMargin: bgShape.popupContentOffsetY + topMargin: root.popupContentOffsetY left: root.vertical ? verticalContent.left : horizontalContent.left - leftMargin: bgShape.popupContentOffsetX + leftMargin: root.popupContentOffsetX } shown: root.showPopup @@ -109,13 +73,19 @@ HBarWidgetContainer { id: contentLayout anchors.fill: parent - W.VisuallyCenteredStyledText { + W.FixedWidthTextContainer { Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true - font.pixelSize: C.Appearance.font.pixelSize.large - color: C.Appearance.colors.colOnLayer1 - text: S.DateTime.time + longestText: Qt.locale().toString(new Date(1984, 6, 9, 00, 00, 00), root.timeFormatString) + font: clockText.font + W.VisuallyCenteredStyledText { + id: clockText + anchors.fill: parent + font.pixelSize: C.Appearance.font.pixelSize.large + color: C.Appearance.colors.colOnLayer1 + text: S.DateTime.time + } } W.VisuallyCenteredStyledText { @@ -191,10 +161,11 @@ HBarWidgetContainer { property real buttonSize: C.Appearance.rounding.normal * 2 property real buttonSpacing: 4 - rowSpacing: 0 + rowSpacing: 4 W.FlyFadeEnterChoreographable { Layout.fillWidth: true + Layout.bottomMargin: 4 RowLayout { width: parent.width @@ -234,23 +205,24 @@ HBarWidgetContainer { } } W.FlyFadeEnterChoreographable { - Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter - DayOfWeekRow { - id: dayOfWeekRow - width: parent.width + W.CalendarDaysOfWeek { locale: calendarView.locale spacing: popupRoot.buttonSpacing delegate: Item { - id: dayOfWeekItem + id: dowItem required property var model - implicitHeight: popupRoot.buttonSize implicitWidth: popupRoot.buttonSize + implicitHeight: dowText.implicitHeight W.VisuallyCenteredStyledText { + id: dowText anchors.centerIn: parent + font.pixelSize: C.Appearance.font.pixelSize.smaller + color: C.Appearance.colors.colOutline text: { - var result = dayOfWeekItem.model.shortName; + var result = dowItem.model.shortName; if (C.Config.options.calendar.force2CharDayOfWeek) result = result.substring(0, 2); return result; @@ -273,7 +245,7 @@ HBarWidgetContainer { buttonSpacing: popupRoot.buttonSpacing buttonVerticalSpacing: popupRoot.buttonSpacing Layout.fillWidth: true - + delegate: W.StyledButton { id: dayButton required property var model @@ -291,7 +263,8 @@ HBarWidgetContainer { target: popupRoot enabled: dayButton.model.today function onShownChanged() { - if (popupRoot.shown) dayButton.forceActiveFocus(); + if (popupRoot.shown) + dayButton.forceActiveFocus(); } } From 8e21d8c8e35cc588cc459b93dc3f7737268691d2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:45:35 +0100 Subject: [PATCH 052/104] hefty: bar: fix popup displaying wrong value --- .../modules/hefty/topLayer/bar/widgets/HSystemInfo.qml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml index 04970fc64..a3e81902d 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml @@ -147,7 +147,7 @@ HBarWidgetWithPopout { W.CircularProgress { implicitSize: 46 lineWidth: 3 - value: S.Battery.health / 100 + value: S.Battery.percentage W.MaterialSymbol { anchors.centerIn: parent iconSize: 22 @@ -164,17 +164,21 @@ HBarWidgetWithPopout { Layout.fillWidth: true spacing: 0 RowLayout { - visible: S.Battery.knownEnergyRate Layout.fillWidth: true spacing: 4 W.StyledText { Layout.alignment: Qt.AlignBaseline + visible: S.Battery.knownEnergyRate text: F.DateUtils.formatDuration(S.Battery.isCharging ? S.Battery.timeToFull : S.Battery.timeToEmpty) font.pixelSize: C.Appearance.font.pixelSize.title } W.StyledText { Layout.alignment: Qt.AlignBaseline - text: S.Battery.isCharging ? S.Translation.tr("to full") : S.Translation.tr("remaining") + text: { + if (!S.Battery.knownEnergyRate) + return S.Battery.isCharging ? S.Translation.tr("Charging") : S.Translation.tr("Discharging"); + return S.Battery.isCharging ? S.Translation.tr("to full") : S.Translation.tr("remaining"); + } } } RowLayout { From 422db1ee91ca6e09db467bb4d90dcfc308b8be0f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:19:10 +0100 Subject: [PATCH 053/104] add focus ring to group and ripple buttons --- .../ii/modules/common/widgets/GroupButton.qml | 21 ++++++++++++++++--- .../modules/common/widgets/RippleButton.qml | 16 ++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml index 080439613..031dfe778 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml @@ -42,6 +42,7 @@ Button { property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F" property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C" property color colBackgroundToggledActive: Appearance?.colors.colPrimaryActive ?? "#D6CEE2" + property color colFocusRing: Appearance.colors.colOnSecondaryContainer property real radius: root.down ? root.buttonRadiusPressed : root.buttonRadius property real leftRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius @@ -117,7 +118,6 @@ Button { }; } - property bool tabbedTo: root.focus && (focusReason === Qt.TabFocusReason || focusReason === Qt.BacktabFocusReason) background: Rectangle { id: buttonBackground topLeftRadius: root.leftRadius @@ -130,9 +130,24 @@ Button { Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } + } - border.width: root.tabbedTo ? 2 : 0 - border.color: Appearance.colors.colSecondary + Rectangle { + id: focusRing + topLeftRadius: root.leftRadius - anchors.margins + topRightRadius: root.rightRadius - anchors.margins + bottomLeftRadius: root.leftRadius - anchors.margins + bottomRightRadius: root.rightRadius - anchors.margins + visible: root.visualFocus + color: "transparent" + anchors { + fill: parent + margins: -4 + } + border { + color: root.colFocusRing + width: 2 + } } contentItem: StyledText { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 498bfc1cb..408515f7c 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -29,6 +29,7 @@ Button { property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C" property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2" property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2" + property color colFocusRing: Appearance.colors.colOnSecondaryContainer opacity: root.enabled ? 1 : 0.4 property color buttonColor: ColorUtils.transparentize(root.toggled ? @@ -180,6 +181,21 @@ Button { } } + Rectangle { + id: focusRing + radius: buttonBackground.radius - anchors.margins + visible: root.visualFocus + color: "transparent" + anchors { + fill: parent + margins: -4 + } + border { + color: root.colFocusRing + width: 2 + } + } + contentItem: StyledText { text: root.buttonText } From 96b8af05ccc4a7fcb0861024fa59268e66a54951 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:19:28 +0100 Subject: [PATCH 054/104] hefty: bar: adjust calendar spacing --- .../ii/modules/hefty/topLayer/bar/widgets/HTime.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index aa46e6877..1de60905a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -161,11 +161,11 @@ HBarWidgetWithPopout { property real buttonSize: C.Appearance.rounding.normal * 2 property real buttonSpacing: 4 - rowSpacing: 4 + rowSpacing: 2 W.FlyFadeEnterChoreographable { Layout.fillWidth: true - Layout.bottomMargin: 4 + Layout.bottomMargin: 6 RowLayout { width: parent.width From 3d5c43135ae2a3a475758796ce478e0a45bfb2d1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:31:35 +0100 Subject: [PATCH 055/104] set default calendar locale to C instead of bri'ish --- dots/.config/quickshell/ii/modules/common/Config.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index fb0817ed4..f658fa395 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -300,7 +300,7 @@ Singleton { } property JsonObject calendar: JsonObject { - property string locale: "en-GB" + property string locale: "C" property bool force2CharDayOfWeek: true property bool animate: false // Disabled by default cuz laggy property bool weekScrollPrecision: false // One scroll advances 1 week instead of 1 month From 245aae965f048d46ebda20db16e50de9cc7f9d26 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:57:57 +0100 Subject: [PATCH 056/104] make focus rings not go behind --- .../quickshell/ii/modules/common/widgets/GroupButton.qml | 1 + .../quickshell/ii/modules/common/widgets/RippleButton.qml | 1 + .../quickshell/ii/modules/common/widgets/StyledButton.qml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml index 031dfe778..2e1440f0f 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml @@ -132,6 +132,7 @@ Button { } } + z: visualFocus ? 1 : 0 Rectangle { id: focusRing topLeftRadius: root.leftRadius - anchors.margins diff --git a/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 408515f7c..f6c2a2ecd 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -181,6 +181,7 @@ Button { } } + z: visualFocus ? 1 : 0 Rectangle { id: focusRing radius: buttonBackground.radius - anchors.margins diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml index 970e212bf..ebd176a5a 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml @@ -58,6 +58,8 @@ Button { } } + z: visualFocus ? 1 : 0 + contentItem: Item { implicitWidth: buttonText.implicitWidth implicitHeight: buttonText.implicitHeight From a174ed1a84bf591410231f292f15459487add655 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:58:37 +0100 Subject: [PATCH 057/104] hefty: bar: systeminfo: add to default bar config, add ppd btns --- .../ii/modules/common/config/HeftyConfig.qml | 2 +- .../common/widgets/ConfigSelectionArray.qml | 5 +++ .../topLayer/bar/widgets/HSystemInfo.qml | 37 ++++++++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 131e04e41..efe35cf33 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -7,7 +7,7 @@ JsonObject { property list leftWidgets: ["HWindowInfo"] property list centerLeftWidgets: ["HTime"] property list centerWidgets: ["HWorkspaces"] - property list centerRightWidgets: ["HBattery"] + property list centerRightWidgets: ["HSystemInfo"] property list rightWidgets: [] property bool m3ExpressiveGrouping: true } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSelectionArray.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSelectionArray.qml index dd102070c..95f22a9e0 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSelectionArray.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSelectionArray.qml @@ -23,6 +23,10 @@ Flow { ] property var currentValue: null + function focusSelectedChild() { + children.find(c => c.value == currentValue).forceActiveFocus() + } + signal selected(var newValue) Repeater { @@ -31,6 +35,7 @@ Flow { id: paletteButton required property var modelData required property int index + readonly property var value: modelData.value onYChanged: { if (index === 0) { paletteButton.leftmost = true diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml index a3e81902d..62fa558c1 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml @@ -136,12 +136,19 @@ HBarWidgetWithPopout { } component SysInfoPopupContent: W.ChoreographerGridLayout { + id: popupRoot + rowSpacing: 8 + + onShownChanged: { + if (shown) { + powerProfileSelection.focusSelectedChild() + } + } W.FlyFadeEnterChoreographable { Layout.fillWidth: true RowLayout { - anchors.fill: parent spacing: 10 W.CircularProgress { @@ -198,6 +205,34 @@ HBarWidgetWithPopout { } } } + + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + W.ConfigSelectionArray { + id: powerProfileSelection + currentValue: PowerProfiles.profile + onSelected: newValue => { + PowerProfiles.profile = newValue + } + options: [ + { + displayName: S.Translation.tr("Power saver"), + // icon: "line_curve", + value: PowerProfile.PowerSaver + }, + { + displayName: S.Translation.tr("Balanced"), + // icon: "page_header", + value: PowerProfile.Balanced + }, + { + displayName: S.Translation.tr("Performance"), + // icon: "toolbar", + value: PowerProfile.Performance + } + ] + } + } } component StatWithIcon: Item { From 7a9b08061604f43ec0b8f24e29f8e36a6a092abd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:01:21 +0100 Subject: [PATCH 058/104] faster text anim --- .../quickshell/ii/modules/common/widgets/StyledText.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml index 14f6a9c23..fa625c535 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml @@ -23,7 +23,7 @@ Text { component Anim: NumberAnimation { target: root - duration: 300 / 2 + duration: 130 easing.type: Easing.BezierSpline easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve } @@ -55,7 +55,8 @@ Text { Anim { property: "opacity" to: 0 - easing.type: Easing.InSine + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve } } PropertyAction {} // Tie the text update to this point (we don't want it to happen during the first slide+fade) @@ -83,7 +84,8 @@ Text { Anim { property: "opacity" to: 1 - easing.type: Easing.OutSine + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve } } } From 38dbc8769bd29f9808b345e95b4d5ebcb13a976c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:02:16 +0100 Subject: [PATCH 059/104] hefty: bar: put popups in loaders --- .../widgets/ChoreographerGridLayout.qml | 2 +- .../common/widgets/ChoreographerLoader.qml | 7 + .../widgets/VisuallyCenteredStyledText.qml | 6 +- .../topLayer/bar/widgets/HSystemInfo.qml | 153 +++++++------ .../hefty/topLayer/bar/widgets/HTime.qml | 212 +++++++++--------- 5 files changed, 201 insertions(+), 179 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml index 97223d904..b723cc27e 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml @@ -14,7 +14,7 @@ GridLayout { readonly property int count: choreographableChildren.length children: choreographableChildren - property bool shown: true + property bool shown: false onShownChanged: { // When hiding, hide all at once if (!shown) { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml new file mode 100644 index 000000000..c2950146f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml @@ -0,0 +1,7 @@ +pragma ComponentBehavior: Bound +import QtQuick + +FadeLoader { + id: root + onActiveChanged: item.shown = true +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml index 77e353ce6..1390d2caa 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts Item { id: root + property alias textWidget: textWidget property alias text: textWidget.text property alias horizontalAlignment: textWidget.horizontalAlignment property alias verticalAlignment: textWidget.verticalAlignment @@ -27,7 +28,10 @@ Item { StyledText { id: textWidget - anchors.horizontalCenter: parent.horizontalCenter + anchors { + left: parent.left + right: parent.right + } y: { const value = (parent.height - textMetrics.height) / 2; return root.lowerBias ? Math.ceil(value) : Math.round(value); diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml index 62fa558c1..64966abda 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml @@ -29,19 +29,17 @@ HBarWidgetWithPopout { onClicked: root.showPopup = !root.showPopup property var activeItem: vertical ? verticalContent : horizontalContent - W.FadeLoader { + Loader { id: horizontalContent anchors.fill: parent - shown: !contentRoot.vertical - + active: !contentRoot.vertical sourceComponent: HorizontalSysInfo {} } - W.FadeLoader { + Loader { id: verticalContent anchors.fill: parent - shown: contentRoot.vertical - + active: contentRoot.vertical sourceComponent: HorizontalSysInfo {} } @@ -135,43 +133,46 @@ HBarWidgetWithPopout { } } - component SysInfoPopupContent: W.ChoreographerGridLayout { - id: popupRoot - rowSpacing: 8 + component SysInfoPopupContent: W.ChoreographerLoader { + sourceComponent: W.ChoreographerGridLayout { + id: popupRoot + rowSpacing: 8 - onShownChanged: { - if (shown) { - powerProfileSelection.focusSelectedChild() + onShownChanged: { + if (shown) { + powerProfileSelection.focusSelectedChild(); + } } - } - W.FlyFadeEnterChoreographable { - Layout.fillWidth: true + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true - RowLayout { - spacing: 10 + RowLayout { + spacing: 10 + width: parent.width - W.CircularProgress { - implicitSize: 46 - lineWidth: 3 - value: S.Battery.percentage - W.MaterialSymbol { - anchors.centerIn: parent - iconSize: 22 - text: { - if (root.chargingAndNotFull) - return "battery_android_plus"; - if (root.powerSaving) - return "energy_savings_leaf"; - return "battery_android_full"; + W.CircularProgress { + implicitSize: notSoImportantBatteryStats.implicitHeight + lineWidth: 3 + value: S.Battery.percentage + W.MaterialSymbol { + anchors.centerIn: parent + iconSize: 22 + fill: 1 + animateChange: true + text: { + if (root.chargingAndNotFull) + return "bolt"; + if (root.powerSaving) + return "energy_savings_leaf"; + if (PowerProfiles.profile == PowerProfile.Performance) + return "local_fire_department"; + return "battery_android_full"; + } } } - } - ColumnLayout { - Layout.fillWidth: true - spacing: 0 RowLayout { - Layout.fillWidth: true + Layout.fillWidth: false spacing: 4 W.StyledText { Layout.alignment: Qt.AlignBaseline @@ -182,55 +183,59 @@ HBarWidgetWithPopout { W.StyledText { Layout.alignment: Qt.AlignBaseline text: { - if (!S.Battery.knownEnergyRate) + if (!S.Battery.knownEnergyRate) return S.Battery.isCharging ? S.Translation.tr("Charging") : S.Translation.tr("Discharging"); return S.Battery.isCharging ? S.Translation.tr("to full") : S.Translation.tr("remaining"); } } } - RowLayout { + Item { Layout.fillWidth: true + } + ColumnLayout { + id: notSoImportantBatteryStats + Layout.fillWidth: false + spacing: 4 StatWithIcon { visible: S.Battery.knownEnergyRate - Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft icon: "bolt" - value: `${S.Battery.energyRate.toFixed(2)}W` + value: `${S.Battery.energyRate.toFixed(1)}W` + longestValueString: "69.0W" } StatWithIcon { - Layout.fillWidth: true - icon: "heart_check" - value: `${(S.Battery.health).toFixed(1)}%` + Layout.alignment: Qt.AlignLeft + icon: "favorite" + value: `${(S.Battery.health).toFixed(1 * (S.Battery.health < 100))}%` + longestValueString: "69.0%" } } } } - } - W.FlyFadeEnterChoreographable { - Layout.fillWidth: true - W.ConfigSelectionArray { - id: powerProfileSelection - currentValue: PowerProfiles.profile - onSelected: newValue => { - PowerProfiles.profile = newValue - } - options: [ - { - displayName: S.Translation.tr("Power saver"), - // icon: "line_curve", - value: PowerProfile.PowerSaver - }, - { - displayName: S.Translation.tr("Balanced"), - // icon: "page_header", - value: PowerProfile.Balanced - }, - { - displayName: S.Translation.tr("Performance"), - // icon: "toolbar", - value: PowerProfile.Performance + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + W.ConfigSelectionArray { + id: powerProfileSelection + currentValue: PowerProfiles.profile + onSelected: newValue => { + PowerProfiles.profile = newValue; } - ] + options: [ + { + displayName: S.Translation.tr("Power saver"), + value: PowerProfile.PowerSaver + }, + { + displayName: S.Translation.tr("Balanced"), + value: PowerProfile.Balanced + }, + { + displayName: S.Translation.tr("Performance"), + value: PowerProfile.Performance + } + ] + } } } } @@ -239,6 +244,7 @@ HBarWidgetWithPopout { id: statItem required property string icon required property string value + property string longestValueString implicitWidth: statRow.implicitWidth implicitHeight: statRow.implicitHeight RowLayout { @@ -249,11 +255,16 @@ HBarWidgetWithPopout { Layout.fillWidth: false Layout.alignment: Qt.AlignVCenter text: statItem.icon + fill: 1 + iconSize: 16 } - W.VisuallyCenteredStyledText { - Layout.fillWidth: false - Layout.fillHeight: true - text: statItem.value + W.FixedWidthTextContainer { + longestText: statItem.longestValueString + W.VisuallyCenteredStyledText { + anchors.fill: parent + horizontalAlignment: Text.AlignLeft + text: statItem.value + } } Item { Layout.fillWidth: true diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml index 1de60905a..7f754722b 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HTime.qml @@ -34,20 +34,18 @@ HBarWidgetWithPopout { property Item activeItem: vertical ? verticalContent : horizontalContent // When horizontal - W.FadeLoader { + Loader { id: horizontalContent anchors.fill: parent - shown: !contentRoot.vertical - + active: !contentRoot.vertical sourceComponent: HorizontalClock {} } // When vertical - W.FadeLoader { + Loader { id: verticalContent anchors.fill: parent - shown: contentRoot.vertical - + active: contentRoot.vertical sourceComponent: VerticalClock {} } @@ -155,124 +153,126 @@ HBarWidgetWithPopout { } } - component PopupContent: W.ChoreographerGridLayout { - id: popupRoot + component PopupContent: W.ChoreographerLoader { + sourceComponent: W.ChoreographerGridLayout { + id: popupRoot - property real buttonSize: C.Appearance.rounding.normal * 2 - property real buttonSpacing: 4 + property real buttonSize: C.Appearance.rounding.normal * 2 + property real buttonSpacing: 4 - rowSpacing: 2 + rowSpacing: 2 - W.FlyFadeEnterChoreographable { - Layout.fillWidth: true - Layout.bottomMargin: 6 + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + Layout.bottomMargin: 6 - RowLayout { - width: parent.width - spacing: 0 + RowLayout { + width: parent.width + spacing: 0 - W.StyledText { - Layout.leftMargin: 6 - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - text: calendarView.title - font.pixelSize: C.Appearance.font.pixelSize.larger - elide: Text.ElideRight - color: C.Appearance.colors.colSecondary - } - W.StyledIconButton { - implicitSize: 30 - text: "chevron_left" - iconSize: 20 - onClicked: calendarView.scrollMonthsAndSnap(-1) - colForeground: C.Appearance.colors.colPrimary - } - W.StyledIconButton { - implicitSize: 30 - text: "chevron_right" - iconSize: 20 - onClicked: calendarView.scrollMonthsAndSnap(1) - colForeground: C.Appearance.colors.colPrimary - } - W.StyledIconButton { - implicitSize: 30 - text: "rotate_left" - iconSize: 20 - onClicked: calendarView.scrollToToday() - colForeground: C.Appearance.colors.colPrimary - enabled: calendarView.targetWeekDiff != 0 + W.StyledText { + Layout.leftMargin: 6 + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + text: calendarView.title + font.pixelSize: C.Appearance.font.pixelSize.larger + elide: Text.ElideRight + color: C.Appearance.colors.colSecondary + } + W.StyledIconButton { + implicitSize: 30 + text: "chevron_left" + iconSize: 20 + onClicked: calendarView.scrollMonthsAndSnap(-1) + colForeground: C.Appearance.colors.colPrimary + } + W.StyledIconButton { + implicitSize: 30 + text: "chevron_right" + iconSize: 20 + onClicked: calendarView.scrollMonthsAndSnap(1) + colForeground: C.Appearance.colors.colPrimary + } + W.StyledIconButton { + implicitSize: 30 + text: "rotate_left" + iconSize: 20 + onClicked: calendarView.scrollToToday() + colForeground: C.Appearance.colors.colPrimary + enabled: calendarView.targetWeekDiff != 0 + } } } - } - W.FlyFadeEnterChoreographable { - Layout.alignment: Qt.AlignHCenter + W.FlyFadeEnterChoreographable { + Layout.alignment: Qt.AlignHCenter - W.CalendarDaysOfWeek { - locale: calendarView.locale - spacing: popupRoot.buttonSpacing - delegate: Item { - id: dowItem - required property var model - implicitWidth: popupRoot.buttonSize - implicitHeight: dowText.implicitHeight + W.CalendarDaysOfWeek { + locale: calendarView.locale + spacing: popupRoot.buttonSpacing + delegate: Item { + id: dowItem + required property var model + implicitWidth: popupRoot.buttonSize + implicitHeight: dowText.implicitHeight - W.VisuallyCenteredStyledText { - id: dowText - anchors.centerIn: parent - font.pixelSize: C.Appearance.font.pixelSize.smaller - color: C.Appearance.colors.colOutline - text: { - var result = dowItem.model.shortName; - if (C.Config.options.calendar.force2CharDayOfWeek) - result = result.substring(0, 2); - return result; + W.VisuallyCenteredStyledText { + id: dowText + anchors.centerIn: parent + font.pixelSize: C.Appearance.font.pixelSize.smaller + color: C.Appearance.colors.colOutline + text: { + var result = dowItem.model.shortName; + if (C.Config.options.calendar.force2CharDayOfWeek) + result = result.substring(0, 2); + return result; + } } } } } - } - W.FlyFadeEnterChoreographable { - Item { - implicitWidth: calendarView.implicitWidth - calendarView.horizontalPadding * 2 - implicitHeight: calendarView.implicitHeight - calendarView.verticalPadding * 2 - W.CalendarView { - id: calendarView - anchors.centerIn: parent - locale: Qt.locale(C.Config.options.calendar.locale) - verticalPadding: 4 - horizontalPadding: 4 - buttonSize: popupRoot.buttonSize - buttonSpacing: popupRoot.buttonSpacing - buttonVerticalSpacing: popupRoot.buttonSpacing - Layout.fillWidth: true + W.FlyFadeEnterChoreographable { + Item { + implicitWidth: calendarView.implicitWidth - calendarView.horizontalPadding * 2 + implicitHeight: calendarView.implicitHeight - calendarView.verticalPadding * 2 + W.CalendarView { + id: calendarView + anchors.centerIn: parent + locale: Qt.locale(C.Config.options.calendar.locale) + verticalPadding: 4 + horizontalPadding: 4 + buttonSize: popupRoot.buttonSize + buttonSpacing: popupRoot.buttonSpacing + buttonVerticalSpacing: popupRoot.buttonSpacing + Layout.fillWidth: true - delegate: W.StyledButton { - id: dayButton - required property var model + delegate: W.StyledButton { + id: dayButton + required property var model - focus: model.today - checked: model.today - enabled: model.month === calendarView.focusedMonth - implicitWidth: popupRoot.buttonSize - implicitHeight: popupRoot.buttonSize - width: implicitWidth - height: implicitHeight - text: model.day + focus: model.today + checked: model.today + enabled: model.month === calendarView.focusedMonth + implicitWidth: popupRoot.buttonSize + implicitHeight: popupRoot.buttonSize + width: implicitWidth + height: implicitHeight + text: model.day - Connections { - target: popupRoot - enabled: dayButton.model.today - function onShownChanged() { - if (popupRoot.shown) - dayButton.forceActiveFocus(); + Connections { + target: popupRoot + enabled: dayButton.model.today + function onShownChanged() { + if (popupRoot.shown) + dayButton.forceActiveFocus(); + } } - } - contentItem: Item { - W.VisuallyCenteredStyledText { - anchors.centerIn: parent - text: dayButton.text - color: dayButton.colForeground + contentItem: Item { + W.VisuallyCenteredStyledText { + anchors.centerIn: parent + text: dayButton.text + color: dayButton.colForeground + } } } } From d41cda858ce73ddea0952ed8adf83dd08d57842a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:02:47 +0100 Subject: [PATCH 060/104] hefty: use ii overview for now; add global focus scrim --- .../ii/modules/hefty/topLayer/HOverview.qml | 78 +++++++++---------- .../modules/hefty/topLayer/HTopLayerPanel.qml | 31 +++++--- .../ii/panelFamilies/HeftyHypeFamily.qml | 2 +- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml index 1ebea2d10..7844b394e 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HOverview.qml @@ -47,48 +47,48 @@ HAbstractMorphedPanel { return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2)) } - // Keybinds - GlobalShortcut { - name: "searchToggle" - description: "Toggles search on press" + // // Keybinds + // GlobalShortcut { + // name: "searchToggle" + // description: "Toggles search on press" - onPressed: { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; - } - } - GlobalShortcut { - name: "searchToggleRelease" - description: "Toggles search on release" + // onPressed: { + // GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + // } + // } + // GlobalShortcut { + // name: "searchToggleRelease" + // description: "Toggles search on release" - onPressed: { - GlobalStates.superReleaseMightTrigger = true; - } + // onPressed: { + // GlobalStates.superReleaseMightTrigger = true; + // } - onReleased: { - if (!GlobalStates.superReleaseMightTrigger) { - GlobalStates.superReleaseMightTrigger = true; - return; - } - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; - } - } - GlobalShortcut { - name: "searchToggleReleaseInterrupt" - description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + // onReleased: { + // if (!GlobalStates.superReleaseMightTrigger) { + // GlobalStates.superReleaseMightTrigger = true; + // return; + // } + // GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + // } + // } + // GlobalShortcut { + // name: "searchToggleReleaseInterrupt" + // description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." - onPressed: { - GlobalStates.superReleaseMightTrigger = false; - } - } + // onPressed: { + // GlobalStates.superReleaseMightTrigger = false; + // } + // } - Connections { - target: GlobalStates - function onOverviewOpenChanged() { - if (GlobalStates.overviewOpen) { - root.requestFocus(); - } else { - root.dismissed(); - } - } - } + // Connections { + // target: GlobalStates + // function onOverviewOpenChanged() { + // if (GlobalStates.overviewOpen) { + // root.requestFocus(); + // } else { + // root.dismissed(); + // } + // } + // } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index e0241722e..b403f8001 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -2,8 +2,8 @@ import QtQuick import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Wayland -import Quickshell.Hyprland -import qs +import qs.modules.common +import qs.modules.common.widgets import qs.services import "../../common" import "../../common/widgets/shapes" as S @@ -33,6 +33,18 @@ PanelWindow { mask: root.currentPanel.maskRegion // HyprlandWindow.visibleMask: mask // TODO: use this later to optimize hyprland's rendering + ///////////////// Focus dim ////////////////// + + FadeLoader { + z: 0 + anchors.fill: parent + shown: GlobalFocusGrab.active + sourceComponent: Rectangle { + opacity: 0.4 + color: Appearance.m3colors.m3scrim + } + } + ///////////////// Content ////////////////// property alias roundedPolygon: backgroundShape.roundedPolygon @@ -50,6 +62,7 @@ PanelWindow { } S.ShapeCanvas { id: backgroundShape + z: 1 anchors.fill: parent polygonIsNormalized: false roundedPolygon: MaterialShapes.customPolygon([new MaterialShapes.PointNRound(new Offset.Offset(root.screen.width, 0), new CornerRounding.CornerRounding(9999)),]) @@ -125,13 +138,13 @@ PanelWindow { shown: root.finishedMorphing } - HOverview { - id: overview - load: root.currentPanel === this - shown: root.finishedMorphing - onRequestFocus: root.currentPanel = overview - onDismissed: root.dismiss() - } + // HOverview { + // id: overview + // load: root.currentPanel === this + // shown: root.finishedMorphing + // onRequestFocus: root.currentPanel = overview + // onDismissed: root.dismiss() + // } } //////////////// Components ///////////////// diff --git a/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml b/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml index 62e969f2f..f4fc13cb1 100644 --- a/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml +++ b/dots/.config/quickshell/ii/panelFamilies/HeftyHypeFamily.qml @@ -37,7 +37,7 @@ Scope { PanelLoader { component: OnScreenDisplay {} } PanelLoader { component: OnScreenKeyboard {} } PanelLoader { component: Overlay {} } - // PanelLoader { component: Overview {} } + PanelLoader { component: Overview {} } PanelLoader { component: Polkit {} } PanelLoader { component: RegionSelector {} } PanelLoader { component: ScreenCorners {} } From 1113b6162c91b760eab677176fe7dd136fe2ef7d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:50:31 +0100 Subject: [PATCH 061/104] fix sidebar detach crash (from main) --- .../.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml index 1952557bc..d58deb3f1 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml @@ -67,6 +67,7 @@ Scope { // Scope onDetachChanged: { if (root.detach) { + GlobalFocusGrab.removeDismissable(sidebarLoader.item) // Remove sidebar from the focus grab system sidebarContent.parent = null; // Detach content from sidebar sidebarLoader.active = false; // Unload sidebar detachedSidebarLoader.active = true; // Load detached window From d1dc89b9f275ad102a09f209304dce7afab5eed1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:51:54 +0100 Subject: [PATCH 062/104] hefty: bar: make battery vertical-compatible --- .../common/widgets/ChoreographerLoader.qml | 2 +- .../topLayer/bar/widgets/HSystemInfo.qml | 58 +++--- .../quickshell/ii/translations/en_US.json | 183 +++++++++++++++++- 3 files changed, 218 insertions(+), 25 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml index c2950146f..198fd7138 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerLoader.qml @@ -3,5 +3,5 @@ import QtQuick FadeLoader { id: root - onActiveChanged: item.shown = true + onActiveChanged: if (active) item.shown = true } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml index 64966abda..a07026371 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml @@ -27,20 +27,11 @@ HBarWidgetWithPopout { contentImplicitWidth: activeItem.implicitWidth contentImplicitHeight: activeItem.implicitHeight onClicked: root.showPopup = !root.showPopup - property var activeItem: vertical ? verticalContent : horizontalContent + property var activeItem: sysInfoContent - Loader { - id: horizontalContent + SysInfoContent { + id: sysInfoContent anchors.fill: parent - active: !contentRoot.vertical - sourceComponent: HorizontalSysInfo {} - } - - Loader { - id: verticalContent - anchors.fill: parent - active: contentRoot.vertical - sourceComponent: HorizontalSysInfo {} } SysInfoPopupContent { @@ -56,23 +47,40 @@ HBarWidgetWithPopout { } } - component HorizontalSysInfo: Item { - implicitWidth: row.implicitWidth + row.anchors.leftMargin + row.anchors.rightMargin - implicitHeight: row.implicitHeight + row.anchors.topMargin + row.anchors.bottomMargin + component SysInfoContent: Item { + implicitWidth: contentGrid.implicitWidth + contentGrid.anchors.leftMargin + contentGrid.anchors.rightMargin + implicitHeight: contentGrid.implicitHeight + contentGrid.anchors.topMargin + contentGrid.anchors.bottomMargin - RowLayout { - id: row + GridLayout { + id: contentGrid + columns: root.vertical ? 1 : -1 anchors.fill: parent - anchors.leftMargin: root.startSide ? 8 : 6 - anchors.rightMargin: root.endSide ? 0 : -3 - Battery {} + Battery { + Layout.leftMargin: !root.vertical ? (root.startSide ? 8 : 6) : 0 + Layout.rightMargin: !root.vertical ? (root.endSide ? 0 : -3) : 0 + Layout.bottomMargin: root.vertical ? (root.endSide ? 4 : 2) : 0 + Layout.topMargin: root.vertical ? 2 : 0 + Layout.fillWidth: root.vertical + Layout.fillHeight: !root.vertical + } } } - component Battery: Row { - spacing: 1.5 + component Battery: Item { + implicitWidth: !root.vertical ? battShape.implicitWidth : battShape.implicitHeight + implicitHeight: !root.vertical ? battShape.implicitHeight : battShape.implicitWidth + + BatteryShape { + id: battShape + anchors.centerIn: parent + } + } + + component BatteryShape: Row { Layout.fillHeight: true + spacing: 1.5 + rotation: -90 * root.vertical W.ClippedProgressBar { id: batteryProgress @@ -89,6 +97,7 @@ HBarWidgetWithPopout { layer.enabled: true width: batteryProgress.valueBarWidth height: batteryProgress.valueBarHeight + RowLayout { anchors { horizontalCenter: parent.horizontalCenter @@ -102,6 +111,7 @@ HBarWidgetWithPopout { Layout.alignment: Qt.AlignVCenter Layout.leftMargin: -2 Layout.rightMargin: -2 + rotation: 90 * root.vertical fill: 1 * (text == "bolt") fillAnimation: null text: { @@ -116,7 +126,9 @@ HBarWidgetWithPopout { visible: root.chargingAndNotFull || root.powerSaving } W.VisuallyCenteredStyledText { + visible: batteryProgress.value < 1 Layout.fillHeight: true + rotation: 90 * root.vertical font: batteryProgress.font text: batteryProgress.text } @@ -152,7 +164,7 @@ HBarWidgetWithPopout { width: parent.width W.CircularProgress { - implicitSize: notSoImportantBatteryStats.implicitHeight + implicitSize: Math.max(notSoImportantBatteryStats.implicitHeight, 44) lineWidth: 3 value: S.Battery.percentage W.MaterialSymbol { diff --git a/dots/.config/quickshell/ii/translations/en_US.json b/dots/.config/quickshell/ii/translations/en_US.json index 2fbdc8e6d..67cc2faf4 100644 --- a/dots/.config/quickshell/ii/translations/en_US.json +++ b/dots/.config/quickshell/ii/translations/en_US.json @@ -604,5 +604,186 @@ "Recognize music": "Recognize music", "Stroke width": "Stroke width", "Use varying shapes for password characters": "Use varying shapes for password characters", - "Battery full": "Battery full" + "Battery full": "Battery full", + "Fonts": "Fonts", + "Desktop %1": "Desktop %1", + "Balance brightness based on content": "Balance brightness based on content", + "Polkit": "Polkit", + "Online | Google's model\nPro-level intelligence at the speed and pricing of Flash.": "Online | Google's model\nPro-level intelligence at the speed and pricing of Flash.", + "Widget: Weather": "Widget: Weather", + "Used for headings and titles": "Used for headings and titles", + "Shut down": "Shut down", + "Wi-Fi": "Wi-Fi", + "Used for reading large blocks of text": "Used for reading large blocks of text", + "Off": "Off", + "Enable update checks": "Enable update checks", + "Font weight": "Font weight", + "Show only when locked": "Show only when locked", + "Command": "Command", + "Numbers font": "Numbers font", + "Use adaptive alignment": "Use adaptive alignment", + "Task View": "Task View", + "%1 mins": "%1 mins", + "Copy region (LMB) or annotate (RMB)": "Copy region (LMB) or annotate (RMB)", + "Media": "Media", + "No new notifications": "No new notifications", + "Enable opening zoom animation": "Enable opening zoom animation", + "Font family": "Font family", + "Window": "Window", + "Move to front": "Move to front", + "Health:": "Health:", + "Manage accounts": "Manage accounts", + "Sound effects": "Sound effects", + "Video Recording Path": "Video Recording Path", + "Last refresh: %1": "Last refresh: %1", + "Move left": "Move left", + "Check interval (mins)": "Check interval (mins)", + "Apps": "Apps", + "Open": "Open", + "Main font": "Main font", + "Show hidden icons": "Show hidden icons", + "Pin to Start": "Pin to Start", + "Snip": "Snip", + "Digital": "Digital", + "Monospace font": "Monospace font", + "Aligns the date and quote to left, center or right depending on its position on the screen.": "Aligns the date and quote to left, center or right depending on its position on the screen.", + "Adjust the color temperature": "Adjust the color temperature", + "Quick markup (Ctrl+E)": "Quick markup (Ctrl+E)", + "Least busy": "Least busy", + "Right to left": "Right to left", + "More Internet settings": "More Internet settings", + "Reading font": "Reading font", + "Text extractor": "Text extractor", + "Used for code and terminal": "Used for code and terminal", + "Restart": "Restart", + "Font family name (e.g., Google Sans Flex)": "Font family name (e.g., Google Sans Flex)", + "Recognize text": "Recognize text", + "Not secured": "Not secured", + "Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages": "Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages", + "Image source": "Image source", + "Close (Esc)": "Close (Esc)", + "Cookie": "Cookie", + "Do you want to allow this app to make changes to your device?": "Do you want to allow this app to make changes to your device?", + "Discharging": "Discharging", + "+%1 notifications": "+%1 notifications", + "Command-line-invoked Action": "Command-line-invoked Action", + "Saving...": "Saving...", + "Font family name (e.g., Readex Pro)": "Font family name (e.g., Readex Pro)", + "Productivity": "Productivity", + "Font width and roundness settings are only available for some fonts like Google Sans Flex": "Font width and roundness settings are only available for some fonts like Google Sans Flex", + "Anti-flashbang": "Anti-flashbang", + "Overlay: General": "Overlay: General", + "%1\nInternet access": "%1\nInternet access", + "Font family name": "Font family name", + "Font roundness": "Font roundness", + "On": "On", + "Clear all": "Clear all", + "More Bluetooth settings": "More Bluetooth settings", + "Widgets": "Widgets", + "New desktop": "New desktop", + "Clock style (locked)": "Clock style (locked)", + "Swap": "Swap", + "Focusing": "Focusing", + "There might be a download in progress. Check your Downloads folder.": "There might be a download in progress. Check your Downloads folder.", + "Speakers (%1): %2": "Speakers (%1): %2", + "Creativity": "Creativity", + "remaining": "remaining", + "Show this window on all desktops": "Show this window on all desktops", + "Actions": "Actions", + "Enjoy your empty sidebar...": "Enjoy your empty sidebar...", + "Utilities & Tools": "Utilities & Tools", + "Super key symbol": "Super key symbol", + "Snipping area": "Snipping area", + "Battery: %1%2": "Battery: %1%2", + "All": "All", + "Expressive font": "Expressive font", + "System updates (Arch only)": "System updates (Arch only)", + "Darken screen": "Darken screen", + "Font family name (e.g., JetBrains Mono NF)": "Font family name (e.g., JetBrains Mono NF)", + "Description font size": "Description font size", + "Web": "Web", + "Show date": "Show date", + "Search for apps": "Search for apps", + "e.g. 󱊫 for F1, 󱊶 for F12": "e.g. 󱊫 for F1, 󱊶 for F12", + "Save paths": "Save paths", + "Used for general UI text": "Used for general UI text", + "Notes": "Notes", + "CPU": "CPU", + "Not connected": "Not connected", + "Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep": "Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep", + "Close all windows": "Close all windows", + "Search with Google Lens": "Search with Google Lens", + "Draggable": "Draggable", + "Screenshot Path (leave empty to just copy)": "Screenshot Path (leave empty to just copy)", + "Move right": "Move right", + "Local account": "Local account", + "Record region": "Record region", + "Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \"󰘴 + A\" instead of \"󰘴 A\")": "Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \"󰘴 + A\" instead of \"󰘴 A\")", + "Use symbols for mouse": "Use symbols for mouse", + "See fewer": "See fewer", + "More comfortable viewing at night": "More comfortable viewing at night", + "Best match": "Best match", + "Parallax": "Parallax", + "Enter a valid number": "Enter a valid number", + "Other": "Other", + "No applications": "No applications", + "Overlay: Crosshair": "Overlay: Crosshair", + "Center icons": "Center icons", + "Pinned": "Pinned", + "Use symbols for function keys": "Use symbols for function keys", + "Pin to taskbar": "Pin to taskbar", + "Click to show": "Click to show", + "Widget: Clock": "Widget: Clock", + "File Explorer": "File Explorer", + "Sound output": "Sound output", + "to full": "to full", + "Font width": "Font width", + "Commands": "Commands", + "Emoji": "Emoji", + "Use macOS-like symbols for mods keys": "Use macOS-like symbols for mods keys", + "Nerd font icons": "Nerd font icons", + "Charging": "Charging", + "Tooltips": "Tooltips", + "Most busy": "Most busy", + "Manage my account": "Manage my account", + "Sign out": "Sign out", + "Change password": "Change password", + "Image search": "Image search", + "Replace 󱕐 for \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"LMB\", R󰍽 \"RMB\", 󱕒 \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"": "Replace 󱕐 for \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"LMB\", R󰍽 \"RMB\", 󱕒 \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"", + "Sound input": "Sound input", + "Open recordings folder": "Open recordings folder", + "Secured": "Secured", + "Bottom-up": "Bottom-up", + "Top-down": "Top-down", + "Type /key to get started with online models\nCtrl+O to expand sidebar\nCtrl+P to pin sidebar\nCtrl+D to detach sidebar": "Type /key to get started with online models\nCtrl+O to expand sidebar\nCtrl+P to pin sidebar\nCtrl+D to detach sidebar", + "Left to right": "Left to right", + "Title font": "Title font", + "Font used for Nerd Font icons": "Font used for Nerd Font icons", + "e.g. 󰘴 for Ctrl, 󰘵 for Alt, 󰘶 for Shift, etc": "e.g. 󰘴 for Ctrl, 󰘵 for Alt, 󰘶 for Shift, etc", + "Font family name (e.g., Space Grotesk)": "Font family name (e.g., Space Grotesk)", + "Press Super+G to open the overlay and pin the crosshair": "Press Super+G to open the overlay and pin the crosshair", + "Rectangle": "Rectangle", + "RAM": "RAM", + "Font size": "Font size", + "Split buttons": "Split buttons", + "Unknown Application": "Unknown Application", + "Unpin from taskbar": "Unpin from taskbar", + "Close window": "Close window", + "Set FPS limit": "Set FPS limit", + "Keybind font size": "Keybind font size", + "Han chars": "Han chars", + "You can also manually edit cheatsheet.superKey": "You can also manually edit cheatsheet.superKey", + "Unpin from Start": "Unpin from Start", + "Overlay: Floating Image": "Overlay: Floating Image", + "Turn on from sunset to sunrise": "Turn on from sunset to sunrise", + "Input device": "Input device", + "End session": "End session", + "Used for displaying numbers": "Used for displaying numbers", + "Saved": "Saved", + "More volume settings": "More volume settings", + "Network": "Network", + "(Plugged in)": "(Plugged in)", + "Output device": "Output device", + "of %1": "of %1", + "Used for decorative/expressive text": "Used for decorative/expressive text" } \ No newline at end of file From 6952d89a7f163278553e23c585c0cb959eaf2ebd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:03:56 +0100 Subject: [PATCH 063/104] refactor flexible grids into Box --- .../quickshell/ii/modules/common/widgets/Box.qml | 11 +++++++++++ .../ii/modules/common/widgets/BoxLayout.qml | 13 +++++++++++++ .../common/widgets/ChoreographerGridLayout.qml | 5 ++--- .../hefty/topLayer/bar/widgets/HWorkspaces.qml | 5 ++--- .../quickshell/ii/modules/ii/bar/BarGroup.qml | 5 +++-- .../quickshell/ii/modules/ii/bar/SysTray.qml | 4 ++-- .../quickshell/ii/modules/ii/bar/Workspaces.qml | 10 ++++------ 7 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/Box.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Box.qml b/dots/.config/quickshell/ii/modules/common/widgets/Box.qml new file mode 100644 index 000000000..a043e4bcb --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/Box.qml @@ -0,0 +1,11 @@ +pragma ComponentBehavior: Bound +import QtQuick + +// A type that's both capable of being rows and columns +// Qt Row is just a locked down Grid smh +// Calling it a Box because that's how row-or-column widget is called in Gtk +Grid { + property bool vertical: false + columns: vertical ? 1 : -1 + rows: vertical ? -1 : 1 +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml b/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml new file mode 100644 index 000000000..47747a730 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml @@ -0,0 +1,13 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts + +// Box, Layout version +// A type that's both capable of being rows and columns +// Qt Row is just a locked down Grid smh +// Calling it a Box because that's how row-or-column widget is called in Gtk +GridLayout { + property bool vertical: false + columns: vertical ? 1 : -1 + rows: vertical ? -1 : 1 +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml index b723cc27e..1581f0601 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml @@ -2,11 +2,10 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts -GridLayout { +BoxLayout { id: root - property bool vertical: true - columns: vertical ? 1 : -1 + vertical: true property real totalDuration: 250 property real interval: totalDuration / count diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index da2b6a8bc..db4700967 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -339,7 +339,7 @@ HBarWidgetContainer { } /////////////////// Components /////////////////// - component WorkspaceLayout: Grid { + component WorkspaceLayout: Box { anchors { top: !root.vertical ? parent.top : undefined bottom: !root.vertical ? parent.bottom : undefined @@ -349,8 +349,7 @@ HBarWidgetContainer { rowSpacing: 0 columnSpacing: 0 - columns: root.vertical ? 1 : -1 - rows: root.vertical ? -1 : 1 + vertical: root.vertical } component WorkspaceItem: Item { diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BarGroup.qml b/dots/.config/quickshell/ii/modules/ii/bar/BarGroup.qml index 35769e405..fc8708d6e 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BarGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BarGroup.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.widgets import QtQuick import QtQuick.Layouts @@ -23,9 +24,9 @@ Item { radius: Appearance.rounding.small } - GridLayout { + BoxLayout { id: gridLayout - columns: root.vertical ? 1 : -1 + vertical: root.vertical anchors { verticalCenter: root.vertical ? undefined : parent.verticalCenter horizontalCenter: root.vertical ? parent.horizontalCenter : undefined diff --git a/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml b/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml index 56d33e0d4..98b9c7fa6 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml @@ -60,9 +60,9 @@ Item { } } - GridLayout { + BoxLayout { id: gridLayout - columns: root.vertical ? 1 : -1 + vertical: root.vertical anchors.fill: parent rowSpacing: 8 columnSpacing: 15 diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index 05c7399b9..fe755d4ed 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -82,14 +82,13 @@ Item { } // Workspaces - background - Grid { + Box { z: 1 anchors.centerIn: parent rowSpacing: 0 columnSpacing: 0 - columns: root.vertical ? 1 : -1 - rows: root.vertical ? -1 : 1 + vertical: root.vertical Repeater { model: wsModel.shownCount @@ -160,13 +159,12 @@ Item { } // Workspaces - numbers - Grid { + Box { id: wsNumbers z: 3 anchors.fill: parent - columns: root.vertical ? 1 : -1 - rows: root.vertical ? -1 : 1 + vertical: root.vertical columnSpacing: 0 rowSpacing: 0 From 6bd6f30a5ef918f155443f2f3a96e6dff2acbd8f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:27:11 +0100 Subject: [PATCH 064/104] boxes: add spacing prop --- dots/.config/quickshell/ii/modules/common/widgets/Box.qml | 5 +++++ .../quickshell/ii/modules/common/widgets/BoxLayout.qml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Box.qml b/dots/.config/quickshell/ii/modules/common/widgets/Box.qml index a043e4bcb..b37019003 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/Box.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/Box.qml @@ -5,7 +5,12 @@ import QtQuick // Qt Row is just a locked down Grid smh // Calling it a Box because that's how row-or-column widget is called in Gtk Grid { + id: root + property bool vertical: false columns: vertical ? 1 : -1 rows: vertical ? -1 : 1 + + property alias spacing: root.rowSpacing + columnSpacing: rowSpacing } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml b/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml index 47747a730..3ab312c23 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/BoxLayout.qml @@ -7,7 +7,12 @@ import QtQuick.Layouts // Qt Row is just a locked down Grid smh // Calling it a Box because that's how row-or-column widget is called in Gtk GridLayout { + id: root + property bool vertical: false columns: vertical ? 1 : -1 rows: vertical ? -1 : 1 + + property alias spacing: root.rowSpacing + columnSpacing: rowSpacing } From 4e162bd8a6a269b01d53343abe3576e10073e42f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:27:52 +0100 Subject: [PATCH 065/104] hefty: bar: fix weird popup shape when smaller than container --- .../bar/HBarWidgetShapeBackground.qml | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 6c32d72ba..c540d9163 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -111,12 +111,12 @@ Shapes.ShapeCanvas { } color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 xOffset: { - if (!vertical) return showPopup ? -popupXOffset : 0; + if (!vertical) return showPopup ? Math.max(-popupXOffset, 0) : 0; else return bgShape.atBottom ? (width - containerShape.width) : 0; } yOffset: { if (!vertical) return bgShape.atBottom ? (height - containerShape.height) : 0; - else return showPopup ? -popupYOffset : 0; + else return showPopup ? Math.max(-popupYOffset, 0) : 0; } animation: Anim {} @@ -161,64 +161,64 @@ Shapes.ShapeCanvas { roundedPolygon: { var points = []; if (!bgShape.showPopup) return containerShape.getFullShape(); - if (!bgShape.vertical) { + + const joinRadiusOverride = containerShape.radiusLimit; + if (!bgShape.vertical) { + const popupBigger = bgShape.popupWidth > bgShape.backgroundWidth; // Inline comment spam to mitigate qmlls' sabotaging of the (code) layout points = [ ...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [ // ...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), // ...popupShape.leftPoints, // - popupShape.getTopLeftPoint(), // + popupShape.getTopLeftPoint(0, -bgShape.spacing * (!popupBigger), joinRadiusOverride), // ]), // - containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), // - containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), // + containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom && popupBigger), joinRadiusOverride), // + containerShape.getTopLeftPoint(0, -bgShape.spacing * (bgShape.atBottom && popupBigger), joinRadiusOverride), // ...(!bgShape.atBottom ? containerShape.topPoints : [ // - popupShape.getBottomLeftPoint(), // + popupShape.getBottomLeftPoint(0, bgShape.spacing * (!popupBigger), joinRadiusOverride), // ...popupShape.leftPoints, // popupShape.getTopLeftPoint(), // ...popupShape.topPoints, // popupShape.getTopRightPoint(), // ...popupShape.rightPoints, // - popupShape.getBottomRightPoint(), // + popupShape.getBottomRightPoint(0, bgShape.spacing * (!popupBigger), joinRadiusOverride), // ]), // - containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), // - containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), // + containerShape.getTopRightPoint(0, -bgShape.spacing * (bgShape.atBottom && popupBigger), joinRadiusOverride), // + containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom && popupBigger), joinRadiusOverride), // ...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [ // - popupShape.getTopRightPoint(), // + popupShape.getTopRightPoint(0, -bgShape.spacing * (!popupBigger), joinRadiusOverride), // ...popupShape.rightPoints, // popupShape.getBottomRightPoint(), // ...popupShape.getLastBottomPoints(), // ]), ]; } else { + const popupBigger = bgShape.popupHeight > bgShape.backgroundHeight; points = [ // ...containerShape.getFirstBottomPoints(), // - containerShape.getBottomLeftPoint(), // + containerShape.getBottomLeftPoint(-bgShape.spacing * (popupBigger && bgShape.atBottom), 0, joinRadiusOverride), // ...(!bgShape.atBottom ? containerShape.leftPoints : [ // - containerShape.getBottomLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), // - popupShape.getBottomRightPoint(), // + popupShape.getBottomRightPoint(bgShape.spacing * (!popupBigger)), // ...popupShape.bottomPoints, // popupShape.getBottomLeftPoint(), // ...popupShape.leftPoints, // popupShape.getTopLeftPoint(), // ...popupShape.topPoints, // - popupShape.getTopRightPoint(), // - containerShape.getTopLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), // + popupShape.getTopRightPoint(bgShape.spacing * (!popupBigger)), // ]), // - containerShape.getTopLeftPoint(), // + containerShape.getTopLeftPoint(-bgShape.spacing * (popupBigger && bgShape.atBottom), 0, joinRadiusOverride), // ...containerShape.topPoints, // - containerShape.getTopRightPoint(), // + containerShape.getTopRightPoint(bgShape.spacing * (popupBigger && !bgShape.atBottom), 0, joinRadiusOverride), // ...(bgShape.atBottom ? containerShape.rightPoints : [ // - containerShape.getTopRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), // - popupShape.getTopLeftPoint(), // + popupShape.getTopLeftPoint(-bgShape.spacing * (!popupBigger), 0, joinRadiusOverride), // ...popupShape.topPoints, // popupShape.getTopRightPoint(), // ...popupShape.rightPoints, // popupShape.getBottomRightPoint(), // ...popupShape.bottomPoints, // - popupShape.getBottomLeftPoint(), // - containerShape.getBottomRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), // + popupShape.getBottomLeftPoint(-bgShape.spacing * (!popupBigger), 0, joinRadiusOverride), // ]), // - containerShape.getBottomRightPoint(), // + containerShape.getBottomRightPoint(bgShape.spacing * (popupBigger && !bgShape.atBottom), 0, joinRadiusOverride), // ...containerShape.getLastBottomPoints(), // ]; } From ff1dfedc72d340550f0b568fe6c1807b54ecfe3a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:28:14 +0100 Subject: [PATCH 066/104] rename HSystemInfo to HResources --- .../quickshell/ii/modules/common/config/HeftyConfig.qml | 2 +- .../bar/widgets/{HSystemInfo.qml => HResources.qml} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/{HSystemInfo.qml => HResources.qml} (98%) diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index efe35cf33..6284263d0 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -7,7 +7,7 @@ JsonObject { property list leftWidgets: ["HWindowInfo"] property list centerLeftWidgets: ["HTime"] property list centerWidgets: ["HWorkspaces"] - property list centerRightWidgets: ["HSystemInfo"] + property list centerRightWidgets: ["HResources"] property list rightWidgets: [] property bool m3ExpressiveGrouping: true } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml similarity index 98% rename from dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml rename to dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index a07026371..0399c530d 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemInfo.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -51,9 +51,9 @@ HBarWidgetWithPopout { implicitWidth: contentGrid.implicitWidth + contentGrid.anchors.leftMargin + contentGrid.anchors.rightMargin implicitHeight: contentGrid.implicitHeight + contentGrid.anchors.topMargin + contentGrid.anchors.bottomMargin - GridLayout { + W.BoxLayout { id: contentGrid - columns: root.vertical ? 1 : -1 + vertical: root.vertical anchors.fill: parent Battery { @@ -164,7 +164,7 @@ HBarWidgetWithPopout { width: parent.width W.CircularProgress { - implicitSize: Math.max(notSoImportantBatteryStats.implicitHeight, 44) + implicitSize: 44 lineWidth: 3 value: S.Battery.percentage W.MaterialSymbol { From 6111956c4aff6221df91a1e6c9cb7e25feb156d2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:13:21 +0100 Subject: [PATCH 067/104] hefty: bar: fix widget backgrounds flash when changing size --- .../modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index c540d9163..52ef16406 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -109,7 +109,10 @@ Shapes.ShapeCanvas { if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height; else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight; } - color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 + property bool popupHiding: false + onShowPopupChanged: popupHiding = true + onProgressChanged: if (progress == 1) popupHiding = false + color: showPopup || popupHiding ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 xOffset: { if (!vertical) return showPopup ? Math.max(-popupXOffset, 0) : 0; else return bgShape.atBottom ? (width - containerShape.width) : 0; From e2805ef1a14437cfe90133d9e451320224e1657f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:13:31 +0100 Subject: [PATCH 068/104] hefty: bar: add power button widget --- .../topLayer/bar/widgets/HPowerButton.qml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HPowerButton.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HPowerButton.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HPowerButton.qml new file mode 100644 index 000000000..0859cab2d --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HPowerButton.qml @@ -0,0 +1,27 @@ +pragma ComponentBehavior: Bound +import QtQuick + +import qs +import qs.modules.common.widgets +import ".." + +HBarWidgetWithPopout { + id: root + + HBarWidgetContent { + id: contentRoot + vertical: root.vertical + atBottom: root.atBottom + contentImplicitWidth: symbol.implicitWidth + contentImplicitHeight: symbol.implicitHeight + showPopup: false + onClicked: GlobalStates.sessionOpen = true; + + MaterialSymbol { + id: symbol + anchors.centerIn: parent + iconSize: 20 + text: "power_settings_new" + } + } +} From 653dc9c95f2370ba2da7cf5b101c6b8478e59ee0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:22:46 +0100 Subject: [PATCH 069/104] directories: add user ai prompts dir creation --- .../ii/modules/common/Directories.qml | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 814d5d973..25a2e2ada 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -58,15 +58,22 @@ Singleton { // Cleanup on init Component.onCompleted: { - Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) - Quickshell.execDetached(["mkdir", "-p", `${userComponents}`]) - Quickshell.execDetached(["mkdir", "-p", `${favicons}`]) - Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`]) - Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`]) - Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`]) - Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`]) Quickshell.execDetached(["mkdir", "-p", `${aiChats}`]) - Quickshell.execDetached(["mkdir", "-p", `${userActions}`]) + Quickshell.execDetached(["mkdir", "-p", `${favicons}`]) + Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) Quickshell.execDetached(["rm", "-rf", `${tempImages}`]) + Quickshell.execDetached(["mkdir", "-p", `${userComponents}`]) + Quickshell.execDetached(["mkdir", "-p", `${userAiPrompts}`]) + Quickshell.execDetached(["mkdir", "-p", `${userActions}`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`]) + } + Component.onDestruction: { + Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`]) + Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`]) } } From be87a1ae790c6abe9b8cfa3aad54b50b17fdefbe Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:55:56 +0100 Subject: [PATCH 070/104] move material symbol in Network service to Icon --- .../quickshell/ii/modules/common/Icons.qml | 19 +++++++++++++++++++ .../models/quickToggles/NetworkToggle.qml | 2 +- .../ii/modules/ii/bar/BarContent.qml | 2 +- .../classicStyle/NetworkToggle.qml | 2 +- .../ii/verticalBar/VerticalBarContent.qml | 2 +- .../quickshell/ii/services/Network.qml | 18 ------------------ 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Icons.qml b/dots/.config/quickshell/ii/modules/common/Icons.qml index 9858d2963..96a745b9f 100644 --- a/dots/.config/quickshell/ii/modules/common/Icons.qml +++ b/dots/.config/quickshell/ii/modules/common/Icons.qml @@ -3,6 +3,7 @@ pragma Singleton // From https://github.com/caelestia-dots/shell (GPLv3) import Quickshell +import qs.services Singleton { id: root @@ -21,6 +22,24 @@ Singleton { return "bluetooth"; } + function getNetworkMaterialSymbol() { + if (Network.ethernet) return "lan"; + if (Network.wifiEnabled && Network.wifiStatus === "connected") { + const strength = Network.active?.strength ?? 0 + if (strength > 83) return "signal_wifi_4_bar"; + if (strength > 67) return "network_wifi"; + if (strength > 50) return "network_wifi_3_bar"; + if (strength > 33) return "network_wifi_2_bar"; + if (strength > 17) return "network_wifi_1_bar"; + return "signal_wifi_0_bar" + } else { + if (Network.wifiStatus === "connecting") return "signal_wifi_statusbar_not_connected"; + if (Network.wifiStatus === "disconnected") return "wifi_find"; + if (Network.wifiStatus === "disabled") return "signal_wifi_off"; + return "signal_wifi_bad"; + } + } + readonly property var weatherIconMap: ({ "113": "clear_day", "116": "partly_cloudy_day", diff --git a/dots/.config/quickshell/ii/modules/common/models/quickToggles/NetworkToggle.qml b/dots/.config/quickshell/ii/modules/common/models/quickToggles/NetworkToggle.qml index f6f412d01..70eff0e8f 100644 --- a/dots/.config/quickshell/ii/modules/common/models/quickToggles/NetworkToggle.qml +++ b/dots/.config/quickshell/ii/modules/common/models/quickToggles/NetworkToggle.qml @@ -8,7 +8,7 @@ QuickToggleModel { name: Translation.tr("Internet") statusText: Network.networkName tooltipText: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) - icon: Network.materialSymbol + icon: Icons.getNetworkMaterialSymbol() toggled: Network.wifiStatus !== "disabled" mainAction: () => Network.toggleWifi() diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml index 2a72e3382..c000ef8bf 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml @@ -303,7 +303,7 @@ Item { // Bar content region } } MaterialSymbol { - text: Network.materialSymbol + text: Icons.getNetworkMaterialSymbol() iconSize: Appearance.font.pixelSize.larger color: rightSidebarButton.colText } diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/classicStyle/NetworkToggle.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/classicStyle/NetworkToggle.qml index 63fdb85bc..8285cefed 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/classicStyle/NetworkToggle.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/classicStyle/NetworkToggle.qml @@ -11,7 +11,7 @@ import Quickshell.Hyprland QuickToggleButton { toggled: Network.wifiStatus !== "disabled" - buttonIcon: Network.materialSymbol + buttonIcon: Icons.getNetworkMaterialSymbol() onClicked: Network.toggleWifi() altAction: () => { Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? Config.options.apps.networkEthernet : Config.options.apps.network}`]) diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBarContent.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBarContent.qml index 69ffc4bbe..e022399f7 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBarContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBarContent.qml @@ -280,7 +280,7 @@ Item { // Bar content region } } MaterialSymbol { - text: Network.materialSymbol + text: Icons.getNetworkMaterialSymbol() iconSize: Appearance.font.pixelSize.larger color: rightSidebarButton.colText } diff --git a/dots/.config/quickshell/ii/services/Network.qml b/dots/.config/quickshell/ii/services/Network.qml index 418258636..5e464cf99 100644 --- a/dots/.config/quickshell/ii/services/Network.qml +++ b/dots/.config/quickshell/ii/services/Network.qml @@ -34,24 +34,6 @@ Singleton { property string networkName: "" property int networkStrength - property string materialSymbol: root.ethernet - ? "lan" - : (root.wifiEnabled && root.wifiStatus === "connected") - ? ( - (root.active?.strength ?? 0) > 83 ? "signal_wifi_4_bar" : - (root.active?.strength ?? 0) > 67 ? "network_wifi" : - (root.active?.strength ?? 0) > 50 ? "network_wifi_3_bar" : - (root.active?.strength ?? 0) > 33 ? "network_wifi_2_bar" : - (root.active?.strength ?? 0) > 17 ? "network_wifi_1_bar" : - "signal_wifi_0_bar" - ) - : (root.wifiStatus === "connecting") - ? "signal_wifi_statusbar_not_connected" - : (root.wifiStatus === "disconnected") - ? "wifi_find" - : (root.wifiStatus === "disabled") - ? "signal_wifi_off" - : "signal_wifi_bad" // Control function enableWifi(enabled = true): void { From 5e16f2bc100b42e012a17d3a5211b05cf1780965 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:09:54 +0100 Subject: [PATCH 071/104] tiny reload popup design tweaks --- dots/.config/quickshell/ii/ReloadPopup.qml | 292 +++++++++++---------- 1 file changed, 152 insertions(+), 140 deletions(-) diff --git a/dots/.config/quickshell/ii/ReloadPopup.qml b/dots/.config/quickshell/ii/ReloadPopup.qml index 7a4e7112e..a314b201e 100644 --- a/dots/.config/quickshell/ii/ReloadPopup.qml +++ b/dots/.config/quickshell/ii/ReloadPopup.qml @@ -1,160 +1,172 @@ +pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Effects import QtQuick.Layouts -import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Wayland Scope { - id: root - property bool failed; - property string errorString; + id: root + property bool failed + property string errorString + property real progressHeight: 3 - // Connect to the Quickshell global to listen for the reload signals. - Connections { - target: Quickshell + // Connect to the Quickshell global to listen for the reload signals. + Connections { + target: Quickshell - function onReloadCompleted() { - root.failed = false; - popupLoader.loading = true; - } + function onReloadCompleted() { + root.failed = false; + popupLoader.loading = true; + } - function onReloadFailed(error: string) { - // Close any existing popup before making a new one. - popupLoader.active = false; + function onReloadFailed(error: string) { + // Close any existing popup before making a new one. + popupLoader.active = false; - root.failed = true; - root.errorString = error; - popupLoader.loading = true; - } - } + root.failed = true; + root.errorString = error; + popupLoader.loading = true; + } + } - // Keep the popup in a loader because it isn't needed most of the time - LazyLoader { - id: popupLoader + // Keep the popup in a loader because it isn't needed most of the time + LazyLoader { + id: popupLoader - PanelWindow { - id: popup + PanelWindow { + id: popup - exclusiveZone: 0 - anchors.top: true - margins.top: 0 + exclusiveZone: 0 + anchors.top: true + margins.top: 0 - implicitWidth: rect.width + shadow.radius * 2 - implicitHeight: rect.height + shadow.radius * 2 + implicitWidth: rect.width + 8 * 2 + implicitHeight: rect.height + 8 * 2 - WlrLayershell.namespace: "quickshell:reloadPopup" + WlrLayershell.namespace: "quickshell:reloadPopup" - // color blending is a bit odd as detailed in the type reference. - color: "transparent" + // color blending is a bit odd as detailed in the type reference. + color: "transparent" - Rectangle { - id: rect - anchors.centerIn: parent - color: failed ? "#ffe99195" : "#ffD1E8D5" - - implicitHeight: layout.implicitHeight + 30 - implicitWidth: layout.implicitWidth + 30 - radius: 12 - - // Fills the whole area of the rectangle, making any clicks go to it, - // which dismiss the popup. - MouseArea { - id: mouseArea - anchors.fill: parent - onPressed: { - popupLoader.active = false - } - - // makes the mouse area track mouse hovering, so the hide animation - // can be paused when hovering. - hoverEnabled: true - } - - ColumnLayout { - id: layout - spacing: 10 - anchors { - top: parent.top - topMargin: 10 - horizontalCenter: parent.horizontalCenter - } - - Text { - renderType: Text.NativeRendering - font.family: "Google Sans Flex" - font.pointSize: 14 - text: root.failed ? "Quickshell: Reload failed" : "Quickshell reloaded" - color: failed ? "#ff93000A" : "#ff0C1F13" - } - - Text { - renderType: Text.NativeRendering - font.family: "JetBrains Mono NF" - font.pointSize: 11 - text: root.errorString - color: failed ? "#ff93000A" : "#ff0C1F13" - // When visible is false, it also takes up no space. - visible: root.errorString != "" - } - } - - // A progress bar on the bottom of the screen, showing how long until the - // popup is removed. - Rectangle { - z: 2 - id: bar - color: failed ? "#ff93000A" : "#ff0C1F13" - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.margins: 10 - height: 5 - radius: 9999 - - PropertyAnimation { - id: anim - target: bar - property: "width" - from: rect.width - bar.anchors.margins * 2 - to: 0 - duration: failed ? 10000 : 1000 - onFinished: popupLoader.active = false - - // Pause the animation when the mouse is hovering over the popup, - // so it stays onscreen while reading. This updates reactively - // when the mouse moves on and off the popup. - paused: mouseArea.containsMouse - } - } - // Its bg - Rectangle { - z: 1 - id: bar_bg - color: failed ? "#30af1b25" : "#4027643e" - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.margins: 10 - height: 5 - radius: 9999 - width: rect.width - bar.anchors.margins * 2 - } - - // We could set `running: true` inside the animation, but the width of the - // rectangle might not be calculated yet, due to the layout. - // In the `Component.onCompleted` event handler, all of the component's - // properties and children have been initialized. - Component.onCompleted: anim.start() - } - - DropShadow { - id: shadow + RectangularShadow { anchors.fill: rect - horizontalOffset: 0 - verticalOffset: 2 - radius: 6 - samples: radius * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: "#44000000" - source: rect + radius: rect.radius + blur: 6.3 + offset: Qt.vector2d(0.0, 1.0) + spread: 1 + color: "#55000000" } - } - } + + Rectangle { + id: rect + anchors.centerIn: parent + color: root.failed ? "#ffe99195" : "#ffD1E8D5" + + implicitHeight: layout.implicitHeight + 30 + implicitWidth: layout.implicitWidth + 30 + radius: 8 + + // Fills the whole area of the rectangle, making any clicks go to it, + // which dismiss the popup. + MouseArea { + id: mouseArea + anchors.fill: parent + onPressed: { + popupLoader.active = false; + } + + // makes the mouse area track mouse hovering, so the hide animation + // can be paused when hovering. + hoverEnabled: true + } + + ColumnLayout { + id: layout + spacing: 10 + anchors { + top: parent.top + topMargin: 10 + horizontalCenter: parent.horizontalCenter + } + + Text { + id: title + renderType: Text.NativeRendering + font.family: "Google Sans Flex" + font.pointSize: 14 + text: root.failed ? "Quickshell: Reload failed" : "Quickshell reloaded" + color: root.failed ? "#ff93000A" : "#ff0C1F13" + } + + Text { + id: info + renderType: Text.NativeRendering + font.family: "JetBrains Mono NF" + font.pointSize: 11 + text: root.errorString + color: root.failed ? "#ff93000A" : "#ff0C1F13" + // When visible is false, it also takes up no space. + visible: root.errorString != "" + } + } + + // A progress bar on the bottom of the screen, showing how long until the + // popup is removed. + Rectangle { + id: bar + z: 2 + color: root.failed ? "#ff93000A" : "#ff0C1F13" + property real maxWidth: Math.max(title.width, info.width) + anchors { + left: parent.left + leftMargin: (parent.width - maxWidth) / 2 + bottom: parent.bottom + bottomMargin: 10 + } + height: root.progressHeight + radius: 9999 + + PropertyAnimation { + id: anim + target: bar + property: "width" + from: Math.max(title.width, info.width) + to: 0 + duration: root.failed ? 10000 : 1000 + onFinished: popupLoader.active = false + + // Pause the animation when the mouse is hovering over the popup, + // so it stays onscreen while reading. This updates reactively + // when the mouse moves on and off the popup. + paused: mouseArea.containsMouse + } + } + // Its bg + Rectangle { + id: bar_bg + z: 1 + color: root.failed ? "#30af1b25" : "#4027643e" + property real maxWidth: Math.max(title.width, info.width) + anchors { + left: parent.left + right: parent.right + leftMargin: (parent.width - maxWidth) / 2 + rightMargin: anchors.leftMargin + bottom: parent.bottom + bottomMargin: 10 + } + height: root.progressHeight + radius: 9999 + width: bar.width + } + + // We could set `running: true` inside the animation, but the width of the + // rectangle might not be calculated yet, due to the layout. + // In the `Component.onCompleted` event handler, all of the component's + // properties and children have been initialized. + Component.onCompleted: anim.start() + } + } + } } From 227d822655f9441b25b3c6e54bc714b4e059d4b0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:10:23 +0100 Subject: [PATCH 072/104] replace 9999 radius with pill --- .../quickshell/ii/modules/common/widgets/CircularProgress.qml | 3 +-- .../ii/modules/common/widgets/ClippedProgressBar.qml | 3 +-- dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml b/dots/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml index e1b2effaf..7bf07e0fa 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml @@ -42,8 +42,7 @@ Item { active: root.fill anchors.fill: parent - sourceComponent: Rectangle { - radius: 9999 + sourceComponent: Circle { color: root.colSecondary } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml index af5407223..3a09dee7b 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml @@ -40,10 +40,9 @@ ProgressBar { implicitWidth: valueBarWidth } - contentItem: Rectangle { + contentItem: Pill { id: contentItem anchors.fill: parent - radius: 9999 color: root.trackColor visible: false diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml index bdc962806..65b4eae2d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml @@ -11,10 +11,9 @@ ScrollBar { active: hovered || pressed property color color: Looks.colors.controlBg - contentItem: Rectangle { + contentItem: Pill { implicitWidth: root.active ? 4 : 2 implicitHeight: root.visualSize - radius: 9999 color: root.color opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0 From 0b0693b1fdd128b5fbc57b434305a626c42355be Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:11:33 +0100 Subject: [PATCH 073/104] hefty: bar: battery: checkmark when full, more natural order for vertical --- .../hefty/topLayer/bar/widgets/HResources.qml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 0399c530d..29d6d978e 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -104,6 +104,7 @@ HBarWidgetWithPopout { bottom: parent.bottom bottomMargin: (parent.height - height) / 2 } + rotation: 180 * root.vertical spacing: 0 W.MaterialSymbol { @@ -111,24 +112,26 @@ HBarWidgetWithPopout { Layout.alignment: Qt.AlignVCenter Layout.leftMargin: -2 Layout.rightMargin: -2 - rotation: 90 * root.vertical + rotation: -90 * root.vertical fill: 1 * (text == "bolt") fillAnimation: null text: { + if (batteryProgress.value == 1) + return "check"; if (root.chargingAndNotFull) return "bolt"; if (root.powerSaving) return "nest_eco_leaf"; - return "circle"; + return ""; } iconSize: C.Appearance.font.pixelSize.small font.weight: Font.DemiBold - visible: root.chargingAndNotFull || root.powerSaving + visible: text != "" } W.VisuallyCenteredStyledText { visible: batteryProgress.value < 1 Layout.fillHeight: true - rotation: 90 * root.vertical + rotation: -90 * root.vertical font: batteryProgress.font text: batteryProgress.text } @@ -164,6 +167,7 @@ HBarWidgetWithPopout { width: parent.width W.CircularProgress { + id: battCircProg implicitSize: 44 lineWidth: 3 value: S.Battery.percentage @@ -173,6 +177,8 @@ HBarWidgetWithPopout { fill: 1 animateChange: true text: { + if (battCircProg.value == 1) + return "check"; if (root.chargingAndNotFull) return "bolt"; if (root.powerSaving) From e3eeff8d5d0abe93da89fadb2704b89d73c458de Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:33:45 +0100 Subject: [PATCH 074/104] make battery icons adapt to battery percentage --- dots/.config/quickshell/ii/modules/common/Icons.qml | 11 +++++++++++ .../modules/hefty/topLayer/bar/widgets/HResources.qml | 2 +- .../quickshell/ii/modules/ii/bar/BatteryPopup.qml | 2 +- .../quickshell/ii/modules/ii/lock/LockSurface.qml | 2 +- .../ii/modules/ii/overlay/OverlayTaskbar.qml | 2 +- .../ii/modules/ii/verticalBar/BatteryIndicator.qml | 2 +- 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Icons.qml b/dots/.config/quickshell/ii/modules/common/Icons.qml index 96a745b9f..30bd3bf14 100644 --- a/dots/.config/quickshell/ii/modules/common/Icons.qml +++ b/dots/.config/quickshell/ii/modules/common/Icons.qml @@ -8,6 +8,17 @@ import qs.services Singleton { id: root + function getBatteryIcon(percentage: int): string { + if (percentage >= 93) return "battery_android_full"; + if (percentage >= 78) return "battery_android_6"; + if (percentage >= 64) return "battery_android_5"; + if (percentage >= 50) return "battery_android_4"; + if (percentage >= 35) return "battery_android_3"; + if (percentage >= 21) return "battery_android_2"; + if (percentage >= 7) return "battery_android_1"; + return "battery_android_0"; + } + function getBluetoothDeviceMaterialSymbol(systemIconName: string): string { if (systemIconName.includes("headset") || systemIconName.includes("headphones")) return "headphones"; diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 29d6d978e..6d4f0e8a3 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -185,7 +185,7 @@ HBarWidgetWithPopout { return "energy_savings_leaf"; if (PowerProfiles.profile == PowerProfile.Performance) return "local_fire_department"; - return "battery_android_full"; + return C.Icons.getBatteryIcon(battCircProg.value * 100); } } } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml index 84b25cbc1..0ce472791 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BatteryPopup.qml @@ -15,7 +15,7 @@ StyledPopup { // Header StyledPopupHeaderRow { - icon: "battery_android_full" + icon: Icons.getBatteryIcon(Battery.percentage * 100) label: Translation.tr("Battery") } diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml index 82d10260a..0bee2ddf2 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml @@ -307,7 +307,7 @@ MouseArea { IconAndTextPair { visible: Battery.available - icon: Battery.isCharging ? "bolt" : "battery_android_full" + icon: Battery.isCharging ? "bolt" : Icons.getBatteryIcon(Battery.percentage * 100) text: Math.round(Battery.percentage * 100) color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant } diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayTaskbar.qml b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayTaskbar.qml index 232903474..fb595538c 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayTaskbar.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayTaskbar.qml @@ -92,7 +92,7 @@ Rectangle { id: boltIcon anchors.verticalCenter: parent.verticalCenter fill: 1 - text: Battery.isCharging ? "bolt" : "battery_android_full" + text: Battery.isCharging ? "bolt" : Icons.getBatteryIcon(Battery.percentage * 100) color: batteryWidget.colText iconSize: 24 animateChange: true diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml index 3f20cbd2c..0302486dd 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml @@ -44,7 +44,7 @@ MouseArea { id: boltIcon Layout.alignment: Qt.AlignHCenter fill: 1 - text: isCharging ? "bolt" : "battery_android_full" + text: isCharging ? "bolt" : Icons.getBatteryIcon(Battery.percentage * 100) iconSize: Appearance.font.pixelSize.normal animateChange: true } From f9bd67699c5b3ad2f2ee437cc71e5e0e5c3eb50a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:21:58 +0100 Subject: [PATCH 075/104] hefty: bar: add wifi bt sound indicator --- .../bar/widgets/HSystemIndicators.qml | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml new file mode 100644 index 000000000..45e834054 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml @@ -0,0 +1,133 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Services.UPower + +import qs.services as S +import qs.modules.common as C +import qs.modules.common.functions as F +import qs.modules.common.widgets as W +import ".." + +HBarWidgetWithPopout { + id: root + + property bool chargingAndNotFull: S.Battery.isCharging && S.Battery.percentage < 1 + property bool powerSaving: PowerProfiles.profile == PowerProfile.PowerSaver + + popupContentWidth: popupContent.implicitWidth + popupContentHeight: popupContent.implicitHeight + + HBarWidgetContent { + id: contentRoot + vertical: root.vertical + atBottom: root.atBottom + showPopup: root.showPopup + contentImplicitWidth: activeItem.implicitWidth + contentImplicitHeight: activeItem.implicitHeight + // onClicked: root.showPopup = !root.showPopup + property var activeItem: content + + SystemIndicatorsContent { + id: content + anchors { + top: !root.vertical ? parent.top : undefined + bottom: !root.vertical ? parent.bottom : undefined + left: root.vertical ? parent.left : undefined + right: root.vertical ? parent.right : undefined + horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined + verticalCenter: root.vertical ? parent.verticalCenter : undefined + } + } + + SystemPanel { + id: popupContent + anchors { + top: root.vertical ? contentRoot.activeItem.top : contentRoot.activeItem.top + topMargin: root.popupContentOffsetY + left: root.vertical ? contentRoot.activeItem.left : contentRoot.activeItem.left + leftMargin: root.popupContentOffsetX + } + + shown: root.showPopup + } + } + + component SystemIndicatorsContent: W.BoxLayout { + vertical: root.vertical + rowSpacing: 2 + columnSpacing: 8 + + VolMuteIcon {} + MicMuteIcon {} + WifiIcon {} + BluetoothIcon {} + } + + component VolMuteIcon: IconIndicator { + visible: S.Audio.sink?.audio?.muted ?? false + W.MaterialSymbol { + anchors.centerIn: parent + text: "volume_off" + iconSize: 20 + } + } + + component MicMuteIcon: IconIndicator { + visible: S.Audio.source?.audio?.muted ?? false + W.MaterialSymbol { + anchors.centerIn: parent + text: "mic_off" + iconSize: 20 + } + } + + component BluetoothIcon: IconIndicator { + visible: S.BluetoothStatus.available + child: W.MaterialSymbol { + anchors.centerIn: parent + iconSize: 20 + text: S.BluetoothStatus.connected ? "bluetooth_connected" : S.BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" + } + } + + component WifiIcon: IconIndicator { + child: W.MaterialSymbol { + anchors.centerIn: parent + text: C.Icons.getNetworkMaterialSymbol() + iconSize: 20 + } + } + + component IconIndicator: Item { + Layout.fillWidth: root.vertical + Layout.fillHeight: !root.vertical + default property Item child + implicitWidth: child.implicitWidth + implicitHeight: child.implicitHeight + children: [child] + } + + component SystemPanel: W.ChoreographerLoader { + sourceComponent: W.ChoreographerGridLayout { + id: panelRoot + + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "five little chuddies jumping on da bed" + } + } + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "one fell off and bumped his head" + } + } + W.FlyFadeEnterChoreographable { + W.StyledText { + text: "momma call the doctor and the doctor sez" + } + } + } + } +} From b59c3f5c30f1af119dc5d9e6aedb5e1555033f0f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:22:26 +0100 Subject: [PATCH 076/104] ii bar resources popup: use formatting func from ResourceUsage --- .../ii/modules/ii/bar/ResourcesPopup.qml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/ResourcesPopup.qml b/dots/.config/quickshell/ii/modules/ii/bar/ResourcesPopup.qml index 40ed72756..b926f131b 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/ResourcesPopup.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/ResourcesPopup.qml @@ -7,11 +7,6 @@ import QtQuick.Layouts StyledPopup { id: root - // Helper function to format KB to GB - function formatKB(kb) { - return (kb / (1024 * 1024)).toFixed(1) + " GB"; - } - Row { anchors.centerIn: parent spacing: 12 @@ -29,17 +24,17 @@ StyledPopup { StyledPopupValueRow { icon: "clock_loader_60" label: Translation.tr("Used:") - value: root.formatKB(ResourceUsage.memoryUsed) + value: ResourceUsage.kbToGbString(ResourceUsage.memoryUsed) } StyledPopupValueRow { icon: "check_circle" label: Translation.tr("Free:") - value: root.formatKB(ResourceUsage.memoryFree) + value: ResourceUsage.kbToGbString(ResourceUsage.memoryFree) } StyledPopupValueRow { icon: "empty_dashboard" label: Translation.tr("Total:") - value: root.formatKB(ResourceUsage.memoryTotal) + value: ResourceUsage.kbToGbString(ResourceUsage.memoryTotal) } } } @@ -58,17 +53,17 @@ StyledPopup { StyledPopupValueRow { icon: "clock_loader_60" label: Translation.tr("Used:") - value: root.formatKB(ResourceUsage.swapUsed) + value: ResourceUsage.kbToGbString(ResourceUsage.swapUsed) } StyledPopupValueRow { icon: "check_circle" label: Translation.tr("Free:") - value: root.formatKB(ResourceUsage.swapFree) + value: ResourceUsage.kbToGbString(ResourceUsage.swapFree) } StyledPopupValueRow { icon: "empty_dashboard" label: Translation.tr("Total:") - value: root.formatKB(ResourceUsage.swapTotal) + value: ResourceUsage.kbToGbString(ResourceUsage.swapTotal) } } } From 32e8f90b141f836a40a2bd750431737a3f8c1a12 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:23:48 +0100 Subject: [PATCH 077/104] hefty: bar: resources popup: show mem & cpu --- .../widgets/AbstractCombinedProgressBar.qml | 62 +++++++++ .../widgets/StyledCombinedProgressBar.qml | 72 ++++++++++ .../hefty/topLayer/bar/widgets/HResources.qml | 126 ++++++++++++++++++ .../quickshell/ii/services/ResourceUsage.qml | 58 +++++--- 4 files changed, 303 insertions(+), 15 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/AbstractCombinedProgressBar.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/AbstractCombinedProgressBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/AbstractCombinedProgressBar.qml new file mode 100644 index 000000000..9d6e0b59b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/AbstractCombinedProgressBar.qml @@ -0,0 +1,62 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls + +Control { + id: root + + property list valueWeights: [1] + property list values: [0.5] + property list valueHighlights: ["white"] + property list valueTroughs: [] + + readonly property list normalizedValueWeights: { + const totalWeight = valueWeights.reduce((sum, weight) => sum + weight, 0) + return valueWeights.map(weight => weight / totalWeight) + } + + readonly property list visualEnds: { + let cumsum = 0; + let positions = []; + for (let i = 0; i < normalizedValueWeights.length; i++) { + cumsum += normalizedValueWeights[i]; + positions.push(cumsum); + } + return positions; + } + + readonly property list visualPositions: { + let positions = []; + let lastEnd = 0; + for(let i = 0; i < visualEnds.length; i++) { + const thisEnd = visualEnds[i]; + const width = thisEnd - lastEnd; + const thisPos = lastEnd + width * values[i]; + positions.push(thisPos); + lastEnd = visualEnds[i]; + } + return positions; + } + + readonly property list visualSegments: { + let segs = []; + let lastEnd = 0; + for(let i = 0; i < visualEnds.length; i++) { + const thisEnd = visualEnds[i]; + const thisPos = visualPositions[i]; + segs.push([lastEnd, thisPos]); + segs.push([thisPos, thisEnd]); + lastEnd = visualEnds[i]; + } + return segs; + } + + readonly property list segmentColors: { + var cols = []; + for(let i = 0; i < valueHighlights.length; i++) { + cols.push(valueHighlights[i]); + cols.push(valueTroughs[i]); + } + return cols; + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml new file mode 100644 index 000000000..15478a145 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml @@ -0,0 +1,72 @@ +pragma ComponentBehavior: Bound +import QtQuick +import qs.modules.common + +AbstractCombinedProgressBar { + id: root + + property real valueBarWidth: 120 + property real valueBarHeight: 4 + property real valueBarGap: 4 + property real valueBarInnerRadius: Appearance.rounding.unsharpen + valueHighlights: [Appearance.colors.colPrimary, Appearance.colors.colTertiary] + valueTroughs: [Appearance.colors.colSecondaryContainer, Appearance.colors.colTertiaryContainer] + + background: Item { + implicitWidth: root.valueBarWidth + implicitHeight: root.valueBarHeight + } + + // "negligible" = too small that it'd look weird when shown + function isNegligibleSegment(seg: var): bool { + const wdth = seg[1] - seg[0]; + const visualWidth = availableWidth * wdth; + return (visualWidth <= valueBarGap + valueBarHeight) + } + + contentItem: Item { + Repeater { + model: root.visualSegments + + delegate: Rectangle { + required property int index + required property var modelData + + visible: !root.isNegligibleSegment(modelData) + anchors { + top: parent.top + bottom: parent.bottom + } + property bool atStart: index == 0 + property bool atEnd: index == root.visualSegments.length - 1 + property real displaySegStart: { // swallow previous segments if they're "negligible" + var i = index; + while ((i > 0 && root.isNegligibleSegment(root.visualSegments[i-1]))) + i--; + return root.visualSegments[i][0] + } + + x: { + var result = root.availableWidth * displaySegStart; + if (!atStart) result += root.valueBarGap / 2; + return result; + } + width: { + var result = root.availableWidth * (modelData[1] - displaySegStart) + if (atStart || atEnd) result -= root.valueBarGap / 2; + else result -= root.valueBarGap; + return result; + } + color: root.segmentColors[index % root.segmentColors.length] + + property real startRadius: atStart ? height / 2 : root.valueBarInnerRadius + property real endRadius: atEnd ? height / 2 : root.valueBarInnerRadius + + topLeftRadius: startRadius + bottomLeftRadius: startRadius + topRightRadius: endRadius + bottomRightRadius: endRadius + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 6d4f0e8a3..676aa6421 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -162,6 +162,102 @@ HBarWidgetWithPopout { W.FlyFadeEnterChoreographable { Layout.fillWidth: true + Column { + anchors { + left: parent.left + right: parent.right + } + + spacing: 2 + + Item { + anchors { + left: parent.left + right: parent.right + } + implicitHeight: memUsed.implicitHeight + + BigSmallTextPair { + id: memUsed + materialSymbol: "memory" + bigText: S.ResourceUsage.kbToGbString(S.ResourceUsage.memoryUsed, false) + smallText: { + const total = S.ResourceUsage.kbToGbString(S.ResourceUsage.memoryTotal, false); + return S.Translation.tr("%1").arg(`/ ${total}`) + } + W.StyledText { + Layout.alignment: Qt.AlignBaseline + text: S.Translation.tr("Memory") + color: C.Appearance.colors.colOutline + } + } + BigSmallTextPair { + id: swapUsed + TextMetrics { + id: plusTextMetric + font: swapUsed.bigFont + text: "+" + } + property real halfWidthOfAPlus: plusTextMetric.width / 2 + x: Math.min(memProg.availableWidth * memProg.visualEnds[0] - halfWidthOfAPlus, parent.width - width) + bigText: "+ " + S.ResourceUsage.kbToGbString(S.ResourceUsage.swapUsed, false) + smallText: { + const total = S.ResourceUsage.kbToGbString(S.ResourceUsage.swapTotal, false); + return `/ ${total} GB` + } + } + + } + W.StyledCombinedProgressBar { + id: memProg + anchors { + left: parent.left + right: parent.right + } + valueWeights: [S.ResourceUsage.memoryTotal, S.ResourceUsage.swapTotal] + values: [S.ResourceUsage.memoryUsedPercentage, S.ResourceUsage.swapUsedPercentage] + } + } + } + + W.FlyFadeEnterChoreographable { + Layout.fillWidth: true + + Column { + anchors { + left: parent.left + right: parent.right + } + + spacing: 2 + + BigSmallTextPair { + spacing: 0 + materialSymbol: "developer_board" + bigText: Math.round(S.ResourceUsage.cpuUsage * 100) + smallText: "%" + W.StyledText { + Layout.alignment: Qt.AlignBaseline + text: " " + S.Translation.tr("CPU") + color: C.Appearance.colors.colOutline + } + } + W.StyledCombinedProgressBar { + anchors { + left: parent.left + right: parent.right + } + property bool useSingleAggregate: S.ResourceUsage.cpuCoreUsages.length > 8 + valueWeights: useSingleAggregate ? [1] : S.ResourceUsage.cpuCoreFreqCaps + values: useSingleAggregate ? [S.ResourceUsage.cpuUsage] : S.ResourceUsage.cpuCoreUsages + } + } + } + + W.FlyFadeEnterChoreographable { + Layout.topMargin: 8 + Layout.fillWidth: true + RowLayout { spacing: 10 width: parent.width @@ -258,6 +354,36 @@ HBarWidgetWithPopout { } } + component BigSmallTextPair: RowLayout { + id: txtPair + property string materialSymbol: "" + property string bigText: "" + property string smallText: "" + property alias bigFont: bigTxt.font + property alias smallFont: smallTxt.font + spacing: 6 + + W.MaterialSymbol { + Layout.rightMargin: 6 - spacing + visible: text.length > 0 + Layout.alignment: Qt.AlignVCenter + text: txtPair.materialSymbol + fill: 1 + iconSize: 24 + } + W.StyledText { + id: bigTxt + Layout.alignment: Qt.AlignBaseline + font.pixelSize: C.Appearance.font.pixelSize.title + text: txtPair.bigText + } + W.StyledText { + id: smallTxt + Layout.alignment: Qt.AlignBaseline + text: txtPair.smallText + } + } + component StatWithIcon: Item { id: statItem required property string icon diff --git a/dots/.config/quickshell/ii/services/ResourceUsage.qml b/dots/.config/quickshell/ii/services/ResourceUsage.qml index c513b90ca..8c5570450 100644 --- a/dots/.config/quickshell/ii/services/ResourceUsage.qml +++ b/dots/.config/quickshell/ii/services/ResourceUsage.qml @@ -20,6 +20,8 @@ Singleton { property real swapUsed: swapTotal - swapFree property real swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0 property real cpuUsage: 0 + property list cpuCoreUsages: [] + property list cpuCoreFreqCaps: [] property var previousCpuStats property string maxAvailableMemoryString: kbToGbString(ResourceUsage.memoryTotal) @@ -31,10 +33,13 @@ Singleton { property list memoryUsageHistory: [] property list swapUsageHistory: [] - function kbToGbString(kb) { - return (kb / (1024 * 1024)).toFixed(1) + " GB"; + function kbToGbString(kb, attachUnit = true) { + return (kb / (1024 * 1024)).toFixed(1) + (attachUnit ? " GB" : ""); } + // onCpuCoreUsagesChanged: print(cpuCoreUsages) + // onCpuCoreFreqCapsChanged: print(cpuCoreFreqCaps) + function updateMemoryUsageHistory() { memoryUsageHistory = [...memoryUsageHistory, memoryUsedPercentage] if (memoryUsageHistory.length > historyLength) { @@ -77,20 +82,36 @@ Singleton { // Parse CPU usage const textStat = fileStat.text() - const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) - if (cpuLine) { - const stats = cpuLine.slice(1).map(Number) - const total = stats.reduce((a, b) => a + b, 0) - const idle = stats[3] + const lines = textStat.split("\n") + const currentStats = {} + const coreUsages = [] - if (previousCpuStats) { - const totalDiff = total - previousCpuStats.total - const idleDiff = idle - previousCpuStats.idle - cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0 + for (const line of lines) { + const match = line.match(/^(cpu\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) + if (match) { + const name = match[1] + const stats = match.slice(2).map(Number) + const total = stats.reduce((a, b) => a + b, 0) + const idle = stats[3] + + let usage = 0 + if (previousCpuStats && previousCpuStats[name]) { + const totalDiff = total - previousCpuStats[name].total + const idleDiff = idle - previousCpuStats[name].idle + usage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0 + } + + currentStats[name] = { total, idle } + + if (name === "cpu") { + cpuUsage = usage + } else { + coreUsages.push(usage) + } } - - previousCpuStats = { total, idle } } + previousCpuStats = currentStats + cpuCoreUsages = coreUsages root.updateHistories() interval = Config.options?.resources?.updateInterval ?? 3000 @@ -106,12 +127,19 @@ Singleton { LANG: "C", LC_ALL: "C" }) - command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"] + command: ["bash", "-c", "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq 2>/dev/null || lscpu | grep 'CPU max MHz' | awk '{print $4 * 1000}'"] running: true stdout: StdioCollector { id: outputCollector onStreamFinished: { - root.maxAvailableCpuString = (parseFloat(outputCollector.text) / 1000).toFixed(0) + " GHz" + const lines = outputCollector.text.trim().split("\n") + const caps = lines.map(line => parseFloat(line)).filter(val => !isNaN(val)) + + if (caps.length > 0) { + root.cpuCoreFreqCaps = caps + const maxFreq = Math.max(...caps) + root.maxAvailableCpuString = (maxFreq / 1000000).toFixed(1) + " GHz" + } } } } From d0e51ffe198f8bf061934ef2ac434a09810a10b5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:10:05 +0100 Subject: [PATCH 078/104] add fluent ethernet filled icon --- .../quickshell/ii/assets/icons/fluent/ethernet-filled.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/ethernet-filled.svg diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/ethernet-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/ethernet-filled.svg new file mode 100644 index 000000000..fca073c3f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/ethernet-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From cb5966de0f0c69de0b47a5002c61587e4f9b09c8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:11:04 +0100 Subject: [PATCH 079/104] hefty: bar: add "left sidebar" button for now... i'll probably replace the left sidebar later --- .../bar/widgets/HLeftSidebarButton.qml | 76 +++++++++++++++++++ .../bar/widgets/HSystemIndicators.qml | 3 +- .../ii/modules/ii/bar/LeftSidebarButton.qml | 4 +- 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml new file mode 100644 index 000000000..bc84e7837 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml @@ -0,0 +1,76 @@ +pragma ComponentBehavior: Bound +import QtQuick + +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import ".." + +HBarWidgetWithPopout { + id: root + + property bool showPing: false + + Connections { + target: Ai + function onResponseFinished() { + if (GlobalStates.sidebarLeftOpen) return; + root.showPing = true; + } + } + + Connections { + target: Booru + function onResponseFinished() { + if (GlobalStates.sidebarLeftOpen) return; + root.showPing = true; + } + } + + Connections { + target: GlobalStates + function onSidebarLeftOpenChanged() { + root.showPing = false; + } + } + + HBarWidgetContent { + id: contentRoot + vertical: root.vertical + atBottom: root.atBottom + contentImplicitWidth: 14 + contentImplicitHeight: 18 + showPopup: false + onClicked: GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + + CustomIcon { + id: distroIcon + anchors.centerIn: parent + width: 20 + height: 20 + source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : `${Config.options.bar.topLeftIcon}-symbolic` + colorize: true + color: Appearance.colors.colOnLayer0 + + Rectangle { + opacity: root.showPing ? 1 : 0 + visible: opacity > 0 + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: -2 + rightMargin: -2 + } + implicitWidth: 8 + implicitHeight: 8 + radius: Appearance.rounding.full + color: Appearance.colors.colTertiary + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml index 45e834054..eb25c2815 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HSystemIndicators.qml @@ -4,6 +4,7 @@ import QtQuick.Controls import QtQuick.Layouts import Quickshell.Services.UPower +import qs import qs.services as S import qs.modules.common as C import qs.modules.common.functions as F @@ -26,7 +27,7 @@ HBarWidgetWithPopout { showPopup: root.showPopup contentImplicitWidth: activeItem.implicitWidth contentImplicitHeight: activeItem.implicitHeight - // onClicked: root.showPopup = !root.showPopup + onClicked: GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; // TODO: use own popup property var activeItem: content SystemIndicatorsContent { diff --git a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml index 6c73ca72e..9274703ec 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml @@ -55,8 +55,8 @@ RippleButton { CustomIcon { id: distroIcon anchors.centerIn: parent - width: 19.5 - height: 19.5 + width: 20 + height: 20 source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : `${Config.options.bar.topLeftIcon}-symbolic` colorize: true color: Appearance.colors.colOnLayer0 From a66d0f314602edf321738cc9acf848cdb2bc44c7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:31:25 +0100 Subject: [PATCH 080/104] matugen 4.0 fix --- dots/.config/quickshell/ii/scripts/colors/switchwall.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 6352e3f6d..318e3411e 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -181,8 +181,10 @@ switch() { cursorposy=$(bc <<< "scale=0; ($cursorposy - $screeny) * $scale / 1") cursorposy_inverted=$((screensizey - cursorposy)) + matugen_args=(--source-color-index 0) + if [[ "$color_flag" == "1" ]]; then - matugen_args=(color hex "$color") + matugen_args+=(color hex "$color") generate_colors_material_args=(--color "$color") else if [[ -z "$imgpath" ]]; then @@ -240,7 +242,7 @@ switch() { set_thumbnail_path "$thumbnail" if [ -f "$thumbnail" ]; then - matugen_args=(image "$thumbnail") + matugen_args+=(image "$thumbnail") generate_colors_material_args=(--path "$thumbnail") create_restore_script "$video_path" else @@ -249,7 +251,7 @@ switch() { exit 1 fi else - matugen_args=(image "$imgpath") + matugen_args+=(image "$imgpath") generate_colors_material_args=(--path "$imgpath") # Update wallpaper path in config set_wallpaper_path "$imgpath" From da7bddf1d16b687351a38502877f7689a365b1a5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:56:40 +0100 Subject: [PATCH 081/104] network: fix strength prop --- dots/.config/quickshell/ii/services/Network.qml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Network.qml b/dots/.config/quickshell/ii/services/Network.qml index 5e464cf99..79be8f74a 100644 --- a/dots/.config/quickshell/ii/services/Network.qml +++ b/dots/.config/quickshell/ii/services/Network.qml @@ -33,7 +33,7 @@ Singleton { property string wifiStatus: "disconnected" property string networkName: "" - property int networkStrength + property int networkStrength: active?.strength ?? 0 // Control function enableWifi(enabled = true): void { @@ -139,7 +139,6 @@ Singleton { updateConnectionType.startCheck(); wifiStatusProcess.running = true updateNetworkName.running = true; - updateNetworkStrength.running = true; } Process { @@ -212,17 +211,6 @@ Singleton { } } - Process { - id: updateNetworkStrength - running: true - command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"] - stdout: SplitParser { - onRead: data => { - root.networkStrength = parseInt(data); - } - } - } - Process { id: wifiStatusProcess command: ["nmcli", "radio", "wifi"] From 79fe2651cc486fba6822914b4d7ccc8a6a841366 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:57:04 +0100 Subject: [PATCH 082/104] ai: extra user models fix from main --- dots/.config/quickshell/ii/services/Ai.qml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index a273415f5..d177cac80 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -305,14 +305,18 @@ Singleton { } property ApiStrategy currentApiStrategy: apiStrategies[models[currentModelId]?.api_format || "openai"] + function addUserModels() { + (Config?.options.ai?.extraModels ?? []).forEach(model => { + const safeModelName = root.safeModelName(model["model"]); + root.addModel(safeModelName, model) + }); + } + Connections { target: Config function onReadyChanged() { if (!Config.ready) return; - (Config?.options.ai?.extraModels ?? []).forEach(model => { - const safeModelName = root.safeModelName(model["model"]); - root.addModel(safeModelName, model) - }); + root.addUserModels() } } @@ -321,6 +325,7 @@ Singleton { Component.onCompleted: { setModel(currentModelId, false, false); // Do necessary setup for model + root.addUserModels() } function guessModelLogo(model) { @@ -345,7 +350,9 @@ Singleton { } function addModel(modelName, data) { - root.models[modelName] = aiModelComponent.createObject(this, data); + root.models = Object.assign({}, root.models, { + [modelName]: aiModelComponent.createObject(this, data) + }); } Process { From 675e14e338488593b939b9cff23d226f617fa826 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:01:59 +0100 Subject: [PATCH 083/104] wicons: wifi: also check for connected status --- dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml index b804aa3d9..99df3c472 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml @@ -24,9 +24,9 @@ Singleton { property string internetIcon: { if (Network.ethernet) return "ethernet"; - if (Network.wifiEnabled) { + if (Network.wifiEnabled && Network.wifiStatus === "connected") { const strength = Network.networkStrength; - return wifiIconForStrength(strength); + return root.wifiIconForStrength(strength); } if (Network.wifiStatus === "connecting") return "wifi-4"; From 83dcb349da54a7f7e08a6472aa8ccc5069b1193c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:02:20 +0100 Subject: [PATCH 084/104] ii: ActiveWindow: remove unused props --- dots/.config/quickshell/ii/modules/ii/bar/ActiveWindow.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/ActiveWindow.qml b/dots/.config/quickshell/ii/modules/ii/bar/ActiveWindow.qml index b4f1c69cf..5f30b655b 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/ActiveWindow.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/ActiveWindow.qml @@ -12,9 +12,8 @@ Item { readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - property string activeWindowAddress: `0x${activeWindow?.HyprlandToplevel?.address}` - property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name - property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id) + readonly property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name + readonly property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id) implicitWidth: colLayout.implicitWidth From e0f287514169c8cd001439a895a0868e3d5aa4e1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:04:57 +0100 Subject: [PATCH 085/104] changes from main --- dots/.config/kitty/kitty.conf | 3 +++ .../ii/mediaControls/PlayerControl.qml | 2 +- .../ii/scripts/colors/applycolor.sh | 22 ++++++++++++++- .../scripts/colors/terminal/kitty-theme.conf | 27 +++++++++++++++++++ .../thumbnails/generate-thumbnails-magick.sh | 2 +- 5 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 dots/.config/quickshell/ii/scripts/colors/terminal/kitty-theme.conf diff --git a/dots/.config/kitty/kitty.conf b/dots/.config/kitty/kitty.conf index 008bbbe98..97473db05 100644 --- a/dots/.config/kitty/kitty.conf +++ b/dots/.config/kitty/kitty.conf @@ -1,3 +1,6 @@ +# Theming +include ~/.local/state/quickshell/user/generated/terminal/kitty-theme.conf + # Font font_family JetBrains Mono Nerd Font font_size 11.0 diff --git a/dots/.config/quickshell/ii/modules/ii/mediaControls/PlayerControl.qml b/dots/.config/quickshell/ii/modules/ii/mediaControls/PlayerControl.qml index f50769a51..a53255f11 100644 --- a/dots/.config/quickshell/ii/modules/ii/mediaControls/PlayerControl.qml +++ b/dots/.config/quickshell/ii/modules/ii/mediaControls/PlayerControl.qml @@ -77,7 +77,7 @@ Item { // Player instance id: coverArtDownloader property string targetFile: root.artUrl property string artFilePath: root.artFilePath - command: [ "bash", "-c", `[ -f ${artFilePath} ] || curl -sSL '${targetFile}' -o '${artFilePath}'` ] + command: [ "bash", "-c", `[ -f ${artFilePath} ] || curl -4 -sSL '${targetFile}' -o '${artFilePath}'` ] onExited: (exitCode, exitStatus) => { root.downloaded = true } diff --git a/dots/.config/quickshell/ii/scripts/colors/applycolor.sh b/dots/.config/quickshell/ii/scripts/colors/applycolor.sh index ddb93bd20..fd8651c73 100755 --- a/dots/.config/quickshell/ii/scripts/colors/applycolor.sh +++ b/dots/.config/quickshell/ii/scripts/colors/applycolor.sh @@ -27,7 +27,22 @@ IFS=$'\n' colorlist=($colornames) # Array of color names colorvalues=($colorstrings) # Array of color values -apply_term() { +apply_kitty() { + # Check if terminal escape sequence template exists + if [ ! -f "$SCRIPT_DIR/terminal/kitty-theme.conf" ]; then + echo "Template file not found for Kitty theme. Skipping that." + return + fi + # Copy template + mkdir -p "$STATE_DIR"/user/generated/terminal + cp "$SCRIPT_DIR/terminal/kitty-theme.conf" "$STATE_DIR"/user/generated/terminal/kitty-theme.conf + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/${colorlist[$i]} #/${colorvalues[$i]#\#}/g" "$STATE_DIR"/user/generated/terminal/kitty-theme.conf + done +} + +apply_anyterm() { # Check if terminal escape sequence template exists if [ ! -f "$SCRIPT_DIR/terminal/sequences.txt" ]; then echo "Template file not found for Terminal. Skipping that." @@ -52,6 +67,11 @@ apply_term() { done } +apply_term() { + apply_kitty + apply_anyterm +} + apply_qt() { sh "$CONFIG_DIR/scripts/kvantum/materialQT.sh" # generate kvantum theme python "$CONFIG_DIR/scripts/kvantum/changeAdwColors.py" # apply config colors diff --git a/dots/.config/quickshell/ii/scripts/colors/terminal/kitty-theme.conf b/dots/.config/quickshell/ii/scripts/colors/terminal/kitty-theme.conf new file mode 100644 index 000000000..2017b50f6 --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/colors/terminal/kitty-theme.conf @@ -0,0 +1,27 @@ +background #$term0 # + +color0 #$term0 # +color1 #$term1 # +color2 #$term2 # +color3 #$term3 # +color4 #$term4 # +color5 #$term5 # +color6 #$term6 # +color7 #$term7 # +color8 #$term8 # +color9 #$term9 # +color10 #$term10 # +color11 #$term11 # +color12 #$term12 # +color13 #$term13 # +color14 #$term14 # +color15 #$term15 # + +color232 #$term7 # + +cursor #$term7 # + +foreground #$term7 # + +selection_background #$term7 # +selection_foreground #$term0 # diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/generate-thumbnails-magick.sh b/dots/.config/quickshell/ii/scripts/thumbnails/generate-thumbnails-magick.sh index a5c858132..f109f90de 100755 --- a/dots/.config/quickshell/ii/scripts/thumbnails/generate-thumbnails-magick.sh +++ b/dots/.config/quickshell/ii/scripts/thumbnails/generate-thumbnails-magick.sh @@ -36,7 +36,7 @@ urlencode() { for ((i=0; i<${#str}; i++)); do c="${str:$i:1}" case "$c" in - [a-zA-Z0-9.~_-]|/) encoded+="$c" ;; + [a-zA-Z0-9.~_-]|/|'('|')'|'*') encoded+="$c" ;; *) printf -v hex '%%%02X' "'${c}'"; encoded+="$hex" ;; esac done From 6393092e63e3ce5db9bdfe58f1137125066e69fb Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:17:40 +0100 Subject: [PATCH 086/104] hefty: bar: resources indicator: add more stats --- .../ii/modules/common/config/HeftyConfig.qml | 11 +- .../widgets/CombinedCircularProgress.qml | 75 +++++++ .../modules/common/widgets/ConfigSwitch.qml | 1 + .../modules/common/widgets/StyledButton.qml | 7 + .../widgets/StyledCombinedProgressBar.qml | 9 +- .../hefty/topLayer/bar/HBarGroupContainer.qml | 8 +- .../hefty/topLayer/bar/widgets/HResources.qml | 187 +++++++++++++++++- 7 files changed, 283 insertions(+), 15 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/CombinedCircularProgress.qml diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 6284263d0..2275e3704 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -4,11 +4,18 @@ import Quickshell.Io JsonObject { property JsonObject bar: JsonObject { - property list leftWidgets: ["HWindowInfo"] + property list leftWidgets: ["HLeftSidebarButton"] property list centerLeftWidgets: ["HTime"] property list centerWidgets: ["HWorkspaces"] property list centerRightWidgets: ["HResources"] - property list rightWidgets: [] + property list rightWidgets: ["HSystemIndicators"] property bool m3ExpressiveGrouping: true + + property JsonObject resources: JsonObject { + property bool showMemory: false + property bool showRam: false + property bool showSwap: false + property bool showCpu: false + } } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CombinedCircularProgress.qml b/dots/.config/quickshell/ii/modules/common/widgets/CombinedCircularProgress.qml new file mode 100644 index 000000000..ed8b4808f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/CombinedCircularProgress.qml @@ -0,0 +1,75 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Shapes +import qs.modules.common + +AbstractCombinedProgressBar { + id: root + + property int implicitSize: 30 + property int lineWidth: 2 + property real gapAngle: 360 / 18 + + valueHighlights: [Appearance.colors.colPrimary, Appearance.colors.colTertiary] + valueTroughs: [Appearance.colors.colSecondaryContainer, Appearance.colors.colTertiaryContainer] + + property bool enableAnimation: true + property int animationDuration: 800 + property var easingType: Easing.OutCubic + + implicitWidth: implicitSize + implicitHeight: implicitSize + + readonly property real centerX: root.width / 2 + readonly property real centerY: root.height / 2 + readonly property real arcRadius: root.implicitSize / 2 - root.lineWidth + readonly property real startAngle: -90 + + background: Item { + implicitWidth: root.implicitSize + implicitHeight: root.implicitSize + } + + function isNegligibleSegment(seg: var): bool { + const range = seg[1] - seg[0]; + return range < 1 / 360; // TODO make this less arbitrary + } + + Repeater { + model: root.visualSegments + delegate: Shape { + id: segShape + required property int index + required property var modelData + + property bool negligible: root.isNegligibleSegment(modelData) + property bool atStart: index == 0 + property bool atEnd: index == root.visualSegments.length - 1 + property real displaySegStart: { + var i = index; + while ((i > 0 && root.isNegligibleSegment(root.visualSegments[i - 1]))) + i--; + return root.visualSegments[i][0]; + } + + anchors.fill: parent + layer.enabled: true + layer.smooth: true + preferredRendererType: Shape.CurveRenderer + ShapePath { + strokeColor: segShape.negligible ? "transparent" : root.segmentColors[segShape.index % root.segmentColors.length] + strokeWidth: segShape.negligible ? 0 : root.lineWidth + capStyle: ShapePath.RoundCap + fillColor: "transparent" + PathAngleArc { + centerX: root.centerX + centerY: root.centerY + radiusX: root.arcRadius + radiusY: root.arcRadius + startAngle: root.startAngle + 360 * segShape.displaySegStart + root.gapAngle / 2 + sweepAngle: 360 * (segShape.modelData[1] - segShape.displaySegStart) - root.gapAngle + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml index 53e6a27c0..d1e2baf4d 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml @@ -33,6 +33,7 @@ RippleButton { } StyledSwitch { id: switchWidget + focusPolicy: Qt.NoFocus down: root.down Layout.fillWidth: false checked: root.checked diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml index ebd176a5a..18ec4c853 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledButton.qml @@ -22,6 +22,13 @@ Button { hoverEnabled: true opacity: root.enabled ? 1 : 0.5 + Behavior on colBackground { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + Behavior on colForeground { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + HoverHandler { cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml index 15478a145..5f9e9f478 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledCombinedProgressBar.qml @@ -33,10 +33,6 @@ AbstractCombinedProgressBar { required property var modelData visible: !root.isNegligibleSegment(modelData) - anchors { - top: parent.top - bottom: parent.bottom - } property bool atStart: index == 0 property bool atEnd: index == root.visualSegments.length - 1 property real displaySegStart: { // swallow previous segments if they're "negligible" @@ -46,6 +42,11 @@ AbstractCombinedProgressBar { return root.visualSegments[i][0] } + anchors { + top: parent.top + bottom: parent.bottom + } + x: { var result = root.availableWidth * displaySegStart; if (!atStart) result += root.valueBarGap / 2; diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml index 91202f218..e2b013263 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarGroupContainer.qml @@ -56,7 +56,13 @@ Item { property Item contentItem: GridLayout { id: layout columns: C.Config.options.bar.vertical ? 1 : -1 - anchors.centerIn: parent + anchors { + fill: parent + leftMargin: root.vertical ? 0 : root.padding + rightMargin: root.vertical ? 0 : root.padding + topMargin: root.vertical ? root.padding : 0 + bottomMargin: root.vertical ? root.padding : 0 + } property real spacing: 4 columnSpacing: spacing rowSpacing: spacing diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 676aa6421..08960fc33 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -54,19 +54,92 @@ HBarWidgetWithPopout { W.BoxLayout { id: contentGrid vertical: root.vertical - anchors.fill: parent + property int visibleChildren: children.filter(child => child.shown).length + property bool hasResourceIndicators: visibleChildren > 1 || (visibleChildren > 0 && !S.Battery.available) + anchors { + fill: parent + leftMargin: !root.vertical ? (hasResourceIndicators ? 1 : 4) : 0 + topMargin: root.vertical ? 2 : 0 + rightMargin: !root.vertical ? (root.endSide ? 1 : (battLoader.visible ? 0 : -3)) : 0 + bottomMargin: root.vertical ? (root.endSide ? 4 : 2) : 0 + } + spacing: 4 - Battery { - Layout.leftMargin: !root.vertical ? (root.startSide ? 8 : 6) : 0 - Layout.rightMargin: !root.vertical ? (root.endSide ? 0 : -3) : 0 - Layout.bottomMargin: root.vertical ? (root.endSide ? 4 : 2) : 0 - Layout.topMargin: root.vertical ? 2 : 0 + AlignedFadeLoader { + shown: C.Config.options.hefty.bar.resources.showMemory || ( + !battLoader.visible + && !C.Config.options.hefty.bar.resources.showRam + && !C.Config.options.hefty.bar.resources.showSwap + && !C.Config.options.hefty.bar.resources.showCpu + ) + sourceComponent: Memory {} + } + + AlignedFadeLoader { + shown: C.Config.options.hefty.bar.resources.showRam + sourceComponent: RamOnly {} + } + + AlignedFadeLoader { + shown: C.Config.options.hefty.bar.resources.showSwap + sourceComponent: SwapOnly {} + } + + AlignedFadeLoader { + shown: C.Config.options.hefty.bar.resources.showCpu + sourceComponent: Cpu {} + } + + AlignedFadeLoader { + id: battLoader + shown: S.Battery.available Layout.fillWidth: root.vertical Layout.fillHeight: !root.vertical + sourceComponent: Battery {} } } } + component AlignedFadeLoader: W.FadeLoader { + Layout.alignment: root.vertical ? Qt.AlignHCenter : Qt.AlignVCenter + } + + component Memory: SysResourceProgress { + valueWeights: [S.ResourceUsage.memoryTotal, S.ResourceUsage.swapTotal] + values: [S.ResourceUsage.memoryUsedPercentage, S.ResourceUsage.swapUsedPercentage] + centerChar: S.Translation.tr("Memory")[0] + } + + component RamOnly: SysResourceProgress { + values: [S.ResourceUsage.memoryUsedPercentage] + centerChar: S.Translation.tr("RAM")[0] + } + + component SwapOnly: SysResourceProgress { + values: [S.ResourceUsage.swapUsedPercentage] + centerChar: S.Translation.tr("Swap")[0] + } + + component Cpu: SysResourceProgress { + values: [S.ResourceUsage.cpuUsage] + centerChar: S.Translation.tr("CPU")[0] + } + + component SysResourceProgress: W.CombinedCircularProgress { + id: sysResProg + implicitSize: 22 + valueHighlights: [C.Appearance.colors.colPrimary] + valueTroughs: [C.Appearance.colors.colSecondaryContainer] + property string centerChar: "" + + W.StyledText { + renderType: Text.QtRendering + anchors.centerIn: parent + text: sysResProg.centerChar + font.pixelSize: 9 + } + } + component Battery: Item { implicitWidth: !root.vertical ? battShape.implicitWidth : battShape.implicitHeight implicitHeight: !root.vertical ? battShape.implicitHeight : battShape.implicitWidth @@ -105,7 +178,7 @@ HBarWidgetWithPopout { bottomMargin: (parent.height - height) / 2 } rotation: 180 * root.vertical - spacing: 0 + spacing: 4 W.MaterialSymbol { id: boltIcon @@ -125,6 +198,7 @@ HBarWidgetWithPopout { return ""; } iconSize: C.Appearance.font.pixelSize.small + renderType: Text.QtRendering // Better than Native for small sizes font.weight: Font.DemiBold visible: text != "" } @@ -149,9 +223,12 @@ HBarWidgetWithPopout { } component SysInfoPopupContent: W.ChoreographerLoader { + id: sysInfoPopupContent + property bool showSettings: false + sourceComponent: W.ChoreographerGridLayout { id: popupRoot - rowSpacing: 8 + rowSpacing: 10 onShownChanged: { if (shown) { @@ -159,6 +236,51 @@ HBarWidgetWithPopout { } } + W.FlyFadeEnterChoreographable { + z: 1 + Layout.fillWidth: true + Item { + anchors { + left: parent.left + right: parent.right + } + implicitHeight: popupHeaderLayout.implicitHeight + ColumnLayout { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + spacing: 10 + + RowLayout { + id: popupHeaderLayout + Layout.fillWidth: true + W.StyledText { + Layout.fillWidth: true + text: S.Translation.tr("Resources") + elide: Text.ElideRight + font.pixelSize: C.Appearance.font.pixelSize.title + } + + W.StyledIconButton { + implicitSize: C.Appearance.rounding.normal * 2 + text: "settings" + iconSize: 20 + onClicked: sysInfoPopupContent.showSettings = !sysInfoPopupContent.showSettings; + checked: sysInfoPopupContent.showSettings + } + } + + W.FadeLoader { + shown: sysInfoPopupContent.showSettings + Layout.fillWidth: true + sourceComponent: SysInfoPopupConfig {} + } + } + } + } + W.FlyFadeEnterChoreographable { Layout.fillWidth: true @@ -354,6 +476,55 @@ HBarWidgetWithPopout { } } + component SysInfoPopupConfig: Rectangle { + implicitWidth: sysConfLayout.implicitWidth + implicitHeight: sysConfLayout.implicitHeight + radius: C.Appearance.rounding.normal + color: C.Appearance.colors.colLayer4Base + + W.StyledRectangularShadow { + target: parent + z: -1 + } + + ColumnLayout { + id: sysConfLayout + anchors.fill: parent + W.ConfigSwitch { + buttonIcon: "pie_chart" + text: S.Translation.tr("Show memory usage") + checked: C.Config.options.hefty.bar.resources.showMemory + onCheckedChanged: { + C.Config.options.hefty.bar.resources.showMemory = checked; + } + } + W.ConfigSwitch { + buttonIcon: "memory" + text: S.Translation.tr("Show physical memory usage") + checked: C.Config.options.hefty.bar.resources.showRam + onCheckedChanged: { + C.Config.options.hefty.bar.resources.showRam = checked; + } + } + W.ConfigSwitch { + buttonIcon: "swap_horiz" + text: S.Translation.tr("Show swap") + checked: C.Config.options.hefty.bar.resources.showSwap + onCheckedChanged: { + C.Config.options.hefty.bar.resources.showSwap = checked; + } + } + W.ConfigSwitch { + buttonIcon: "planner_review" + text: S.Translation.tr("Show CPU usage") + checked: C.Config.options.hefty.bar.resources.showCpu + onCheckedChanged: { + C.Config.options.hefty.bar.resources.showCpu = checked; + } + } + } + } + component BigSmallTextPair: RowLayout { id: txtPair property string materialSymbol: "" From ad922ac3680e5826aa26dcaee7f6ee7b2b8e2523 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:00:25 +0100 Subject: [PATCH 087/104] hefty: bar: make popups not move while open --- .../bar/HBarWidgetShapeBackground.qml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 52ef16406..0d7b8622a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -65,14 +65,23 @@ Shapes.ShapeCanvas { const maxOffset = maxPopupY - yInGlobal; return maxOffset; } + property bool lockPopupX: false + property bool lockPopupY: false readonly property real popupXOffset: { + // print("popupXOffset", popupXOffset, "lock", lockPopupX) + if (bgShape.lockPopupX) return; + // if (bgShape.showPopup) lockPopupX = true; if (!vertical) return Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset); else return atBottom ? -(popupShape.width + spacing) : (containerShape.width + spacing); } + onPopupXOffsetChanged: if (bgShape.showPopup) lockPopupX = true; readonly property real popupYOffset: { + if (bgShape.lockPopupY) return; + // if (bgShape.showPopup) lockPopupY = true; if (!vertical) return atBottom ? -(popupShape.height + spacing) : (containerShape.height + spacing); else return Math.min(Math.max(-(popupHeight - containerShape.height) / 2, minPopupYOffset), maxPopupYOffset) } + onPopupYOffsetChanged: if (bgShape.showPopup) lockPopupY = true; // Positioning readonly property real popupContentOffsetBase: -baseMargin + popupPadding @@ -111,7 +120,18 @@ Shapes.ShapeCanvas { } property bool popupHiding: false onShowPopupChanged: popupHiding = true - onProgressChanged: if (progress == 1) popupHiding = false + onProgressChanged: { + if (progress == 1) { + popupHiding = false + if (showPopup) { + lockPopupX = true; + lockPopupY = true; + } else { + lockPopupX = false; + lockPopupY = false; + } + } + } color: showPopup || popupHiding ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 xOffset: { if (!vertical) return showPopup ? Math.max(-popupXOffset, 0) : 0; From 495e205934a852b424575ed46832a6004edc0313 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:00:49 +0100 Subject: [PATCH 088/104] hefty: bar: resources: make battery bolt icon not weird --- .../ii/modules/hefty/topLayer/bar/widgets/HResources.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 08960fc33..7abda0bda 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -198,7 +198,6 @@ HBarWidgetWithPopout { return ""; } iconSize: C.Appearance.font.pixelSize.small - renderType: Text.QtRendering // Better than Native for small sizes font.weight: Font.DemiBold visible: text != "" } From b3d15cbae10ca9a726ff1580488dc5acef1f1052 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:01:07 +0100 Subject: [PATCH 089/104] music recognition script fix from main --- .../musicRecognition/recognize-music.sh | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh b/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh index 183432b97..63b3e99ed 100755 --- a/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh +++ b/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh @@ -9,16 +9,16 @@ TMP_RAW="$TMP_PATH/recording.raw" TMP_MP3="$TMP_PATH/recording.mp3" while getopts "i:t:s:" opt; do - case $opt in - i) INTERVAL=$OPTARG ;; - t) TOTAL_DURATION=$OPTARG ;; - s) SOURCE_TYPE=$OPTARG ;; - *) exit 1 ;; - esac + case $opt in + i) INTERVAL=$OPTARG ;; + t) TOTAL_DURATION=$OPTARG ;; + s) SOURCE_TYPE=$OPTARG ;; + *) exit 1 ;; + esac done if [ "$SOURCE_TYPE" = "monitor" ]; then MONITOR_SOURCE=$(pactl get-default-sink).monitor -elif [ "$SOURCE_TYPE" = "input" ]; then + elif [ "$SOURCE_TYPE" = "input" ]; then MONITOR_SOURCE=$(pactl info | grep "Default Source:" | awk '{print $3}' || true) else echo "Invalid source type" @@ -47,14 +47,14 @@ while true; do sleep "$INTERVAL" CURRENT_TIME=$(date +%s) ELAPSED=$((CURRENT_TIME - START_TIME)) - + if (( ELAPSED >= TOTAL_DURATION )); then exit 0 fi - + ffmpeg -f s16le -ar 44100 -ac 2 -i "$TMP_RAW" -acodec libmp3lame -y -hide_banner -loglevel error "$TMP_MP3" 2>/dev/null - RESULT=$(songrec audio-file-to-recognized-song "$TMP_MP3" 2>/dev/null || true) - + RESULT=$(songrec recognize --json "$TMP_MP3" 2>/dev/null || true) + if echo "$RESULT" | grep -q '"matches": \[' && [ ${#RESULT} -gt $MIN_VALID_RESULT_LENGTH ]; then echo "$RESULT" exit 0 From 3cac4206f9343db756c4ac8ac53b614f4b57182b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:30:56 +0100 Subject: [PATCH 090/104] hefty: more subtle focus grab darkening --- .../quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml index b403f8001..613aa55f9 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/HTopLayerPanel.qml @@ -40,7 +40,7 @@ PanelWindow { anchors.fill: parent shown: GlobalFocusGrab.active sourceComponent: Rectangle { - opacity: 0.4 + opacity: 0.2 color: Appearance.m3colors.m3scrim } } From c538505f940ab25f0c1661426af8432b4cfca8d6 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:31:28 +0100 Subject: [PATCH 091/104] hefty: bar: resources: dont keep settings open --- .../ii/modules/hefty/topLayer/bar/widgets/HResources.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index 7abda0bda..f4faf3f8f 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -223,11 +223,11 @@ HBarWidgetWithPopout { component SysInfoPopupContent: W.ChoreographerLoader { id: sysInfoPopupContent - property bool showSettings: false sourceComponent: W.ChoreographerGridLayout { id: popupRoot rowSpacing: 10 + property bool showSettings: false onShownChanged: { if (shown) { @@ -266,13 +266,13 @@ HBarWidgetWithPopout { implicitSize: C.Appearance.rounding.normal * 2 text: "settings" iconSize: 20 - onClicked: sysInfoPopupContent.showSettings = !sysInfoPopupContent.showSettings; - checked: sysInfoPopupContent.showSettings + onClicked: popupRoot.showSettings = !popupRoot.showSettings; + checked: popupRoot.showSettings } } W.FadeLoader { - shown: sysInfoPopupContent.showSettings + shown: popupRoot.showSettings Layout.fillWidth: true sourceComponent: SysInfoPopupConfig {} } From 3ca94d1e70b8283f400deddf4e25c544721809bf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 18 Mar 2026 08:23:44 +0100 Subject: [PATCH 092/104] make game mode persistent --- dots/.config/hypr/hyprland.conf | 9 +- .../hypr/hyprland/shellOverrides/main.conf | 0 .../models/quickToggles/GameModeToggle.qml | 31 +++++-- .../ii/scripts/hyprland/hyprconfigurator.py | 90 +++++++++++++++++++ .../quickshell/ii/services/HyprlandConfig.qml | 51 +++++++++++ 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 dots/.config/hypr/hyprland/shellOverrides/main.conf create mode 100755 dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py create mode 100644 dots/.config/quickshell/ii/services/HyprlandConfig.qml diff --git a/dots/.config/hypr/hyprland.conf b/dots/.config/hypr/hyprland.conf index e40c6c14a..f51e0fbcc 100644 --- a/dots/.config/hypr/hyprland.conf +++ b/dots/.config/hypr/hyprland.conf @@ -5,8 +5,11 @@ $qsConfig = ii exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND submap = global # This is required for catchall to work -# Defaults +# Environment variables source=hyprland/env.conf +source=custom/env.conf + +# Defaults source=hyprland/execs.conf source=hyprland/general.conf source=hyprland/rules.conf @@ -14,7 +17,6 @@ source=hyprland/colors.conf source=hyprland/keybinds.conf # Custom -source=custom/env.conf source=custom/execs.conf source=custom/general.conf source=custom/rules.conf @@ -23,3 +25,6 @@ source=custom/keybinds.conf # nwg-displays support source=workspaces.conf source=monitors.conf + +# Shell overrides +source=hyprland/shellOverrides/main.conf diff --git a/dots/.config/hypr/hyprland/shellOverrides/main.conf b/dots/.config/hypr/hyprland/shellOverrides/main.conf new file mode 100644 index 000000000..e69de29bb diff --git a/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml b/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml index d8ba7402b..a1d6db88e 100644 --- a/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml +++ b/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml @@ -1,11 +1,6 @@ import QtQuick -import Quickshell import Quickshell.Io -import qs import qs.services -import qs.modules.common -import qs.modules.common.functions -import qs.modules.common.widgets QuickToggleModel { id: root @@ -14,11 +9,29 @@ QuickToggleModel { icon: "gamepad" mainAction: () => { - root.toggled = !root.toggled + root.toggled = !root.toggled; if (root.toggled) { - Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`]) + HyprlandConfig.setMany({ + "animations:enabled": 0, + "decoration:shadow:enabled": 0, + "decoration:blur:enabled": 0, + "general:gaps_in": 0, + "general:gaps_out": 0, + "general:border_size": 1, + "decoration:rounding": 0, + "general:allow_tearing": 1 + }); } else { - Quickshell.execDetached(["hyprctl", "reload"]) + HyprlandConfig.resetMany([ // + "animations:enabled", // + "decoration:shadow:enabled", // + "decoration:blur:enabled", // + "general:gaps_in", // + "general:gaps_out", // + "general:border_size", // + "decoration:rounding", // + "general:allow_tearing", // + ]); } } Process { @@ -26,7 +39,7 @@ QuickToggleModel { running: true command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`] onExited: (exitCode, exitStatus) => { - root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit + root.toggled = exitCode !== 0; // Inverted because enabled = nonzero exit } } tooltipText: Translation.tr("Game mode") diff --git a/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py b/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py new file mode 100755 index 000000000..7d582a821 --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py @@ -0,0 +1,90 @@ +#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@"" +import argparse +import re +import os + +def edit_hyprland_config(file_path, set_args, reset_args): + try: + with open(file_path, 'r') as file: + lines = file.readlines() + except FileNotFoundError: + print(f"Error: File '{file_path}' not found.") + return + + set_dict = {k: v for k, v in set_args} if set_args else {} + reset_set = set(reset_args) if reset_args else set() + + new_lines = [] + found_keys = set() + + patterns = {} + for k in list(set_dict.keys()) + list(reset_set): + patterns[k] = re.compile(rf'^\s*{re.escape(k)}\s*=') + + for line in lines: + matched = False + + # Check if line matches a key to be reset + for key in reset_set: + if patterns[key].match(line): + matched = True + break + + if matched: + continue + + # Check if line matches a key to be set + for key, value in set_dict.items(): + if patterns[key].match(line): + new_line = f"{key} = {value}\n" + new_lines.append(new_line) + found_keys.add(key) + matched = True + break + + if matched: + continue + + new_lines.append(line) + + if set_dict: + for key, value in set_dict.items(): + if key not in found_keys: + if new_lines and not new_lines[-1].endswith('\n'): + new_lines[-1] += '\n' + new_lines.append(f"{key} = {value}\n") + + with open(file_path, 'w') as file: + file.writelines(new_lines) + + for key in reset_set: + print(f"Removed '{key}' from '{file_path}'") + for key, value in set_dict.items(): + print(f"Updated '{file_path}' with {key} = {value}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Edit a Hyprland config file.") + parser.add_argument("--file", default="~/.config/hypr/hyprland.conf", help="Path to the Hyprland config file (default: ~/.config/hypr/hyprland.conf).") + + parser.add_argument("--set", nargs=2, action="append", metavar=("KEY", "VALUE"), help="Set a configuration key to a value.") + parser.add_argument("--reset", action="append", metavar="KEY", help="Remove a configuration key.") + + args = parser.parse_args() + + file_path = os.path.expanduser(args.file) + + raw_set_args = args.set or [] + reset_args = args.reset or [] + + set_args = [] + for key, value in raw_set_args: + if value == "[[EMPTY]]": + reset_args.append(key) + else: + set_args.append((key, value)) + + if not set_args and not reset_args: + print("Error: Must specify at least one key to set or reset.") + else: + edit_hyprland_config(file_path, set_args, reset_args) + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/services/HyprlandConfig.qml b/dots/.config/quickshell/ii/services/HyprlandConfig.qml new file mode 100644 index 000000000..50cbe9d9b --- /dev/null +++ b/dots/.config/quickshell/ii/services/HyprlandConfig.qml @@ -0,0 +1,51 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io + +import qs.modules.common +import qs.modules.common.functions + +/** + * Configs Hyprland + */ +Singleton { + id: root + + readonly property string configuratorScriptPath: Quickshell.shellPath("scripts/hyprland/hyprconfigurator.py") + readonly property string shellOverridesPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/hyprland/shellOverrides/main.conf`) + + function set(key: string, value: var) { + Quickshell.execDetached(["bash", "-c", // + `${root.configuratorScriptPath} --file ${root.shellOverridesPath} --set "${key}" "${value}"` // + ]) + } + + function setMany(entries: var) { + let args = "" + for (let key in entries) { + args += `--set "${key}" "${entries[key]}" ` + } + Quickshell.execDetached(["bash", "-c", // + `${root.configuratorScriptPath} --file ${root.shellOverridesPath} ${args}` // + ]) + } + + function reset(key: string) { + Quickshell.execDetached(["bash", "-c", // + `${root.configuratorScriptPath} --file ${root.shellOverridesPath} --reset "${key}"` // + ]) + } + + function resetMany(keys: var) { + let args = "" + for (let i = 0; i < keys.length; i++) { + args += `--reset "${keys[i]}" ` + } + Quickshell.execDetached(["bash", "-c", // + `${root.configuratorScriptPath} --file ${root.shellOverridesPath} ${args}` // + ]) + } +} From 0c69a344d54dfa793d86072a616aeda279aec37d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 08:47:41 +0100 Subject: [PATCH 093/104] sessionwarnings: pacman: only check critical operations --- dots/.config/quickshell/ii/services/SessionWarnings.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/services/SessionWarnings.qml b/dots/.config/quickshell/ii/services/SessionWarnings.qml index c698d28c7..5a9e796a3 100644 --- a/dots/.config/quickshell/ii/services/SessionWarnings.qml +++ b/dots/.config/quickshell/ii/services/SessionWarnings.qml @@ -23,7 +23,7 @@ Singleton { Process { id: detectPackageManagerProc - command: ["bash", "-c", "pidof pacman yay paru dnf zypper apt apx xbps snap apk yum epsi pikman"] + command: ["bash", "-c", "pidof yay paru dnf zypper apt apx xbps snap apk yum epsi pikman || ls /var/lib/pacman/db.lck"] onExited: (exitCode, exitStatus) => { root.packageManagerRunning = (exitCode === 0); } From 08b7b393cbff1e86d1417f2dcb07941d3c5bdf60 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:30:07 +0100 Subject: [PATCH 094/104] changes from main --- .../aiChat/AnnotationSourceButton.qml | 9 ++++----- .../ii/scripts/hyprland/hyprconfigurator.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AnnotationSourceButton.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AnnotationSourceButton.qml index bd75a5d42..828cc0356 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AnnotationSourceButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AnnotationSourceButton.qml @@ -1,10 +1,9 @@ -import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions -import qs.services import QtQuick import QtQuick.Layouts -import Quickshell.Hyprland + +import qs +import qs.modules.common +import qs.modules.common.widgets RippleButton { id: root diff --git a/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py b/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py index 7d582a821..3c5961273 100755 --- a/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py +++ b/dots/.config/quickshell/ii/scripts/hyprland/hyprconfigurator.py @@ -2,6 +2,7 @@ import argparse import re import os +import tempfile def edit_hyprland_config(file_path, set_args, reset_args): try: @@ -54,8 +55,19 @@ def edit_hyprland_config(file_path, set_args, reset_args): new_lines[-1] += '\n' new_lines.append(f"{key} = {value}\n") - with open(file_path, 'w') as file: - file.writelines(new_lines) + dir_name = os.path.dirname(os.path.abspath(file_path)) + temp_path = None + try: + with tempfile.NamedTemporaryFile(mode='w', dir=dir_name, delete=False) as temp_file: + temp_file.writelines(new_lines) + temp_path = temp_file.name + os.chmod(temp_path, os.stat(file_path).st_mode) + os.replace(temp_path, file_path) + except Exception as e: + if temp_path and os.path.exists(temp_path): + os.remove(temp_path) + print(f"Error saving file: {e}") + return for key in reset_set: print(f"Removed '{key}' from '{file_path}'") From 5ac40d544510eddf187d7d9cc727a24b2e52ee21 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:31:30 +0100 Subject: [PATCH 095/104] refactor gamemodetoggle's config option fetching --- .../models/hyprland/HyprlandConfigOption.qml | 63 +++++++++++++++++++ .../models/quickToggles/GameModeToggle.qml | 15 +++-- .../quickshell/ii/services/HyprlandConfig.qml | 16 ++++- 3 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/models/hyprland/HyprlandConfigOption.qml diff --git a/dots/.config/quickshell/ii/modules/common/models/hyprland/HyprlandConfigOption.qml b/dots/.config/quickshell/ii/modules/common/models/hyprland/HyprlandConfigOption.qml new file mode 100644 index 000000000..0422aab02 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/hyprland/HyprlandConfigOption.qml @@ -0,0 +1,63 @@ +pragma ComponentBehavior: Bound +import QtQml +import QtQuick +import Quickshell.Io +import qs.services +import "../" + +NestableObject { + id: root + + required property string key + property alias fetching: fetchProc.running + property bool set + property var value + + Component.onCompleted: fetch() + + Connections { + target: HyprlandConfig + function onReloaded() { + root.fetch(); + } + } + + function fetch() { + fetchProc.command = fetchProc.baseCommand.concat([root.key]); + fetchProc.running = true; + } + + function setValue(newValue) { + HyprlandConfig.set(root.key, newValue) + } + + function reset() { + HyprlandConfig.reset(root.key) + } + + Process { + id: fetchProc + property list baseCommand: ["hyprctl", "getoption", "-j"] + stdout: StdioCollector { + onStreamFinished: { + if (text == "no such option") + return; + try { + const obj = JSON.parse(text); + // Note that the value is returned as "": + // It's the only field that isn't always in the same key so we put it in an else + for (const key in obj) { + if (key == "option") + continue; + else if (key == "set") + root.set = obj[key]; + else + root.value = obj[key]; + } + } catch (e) { + console.log(`[HyprlandConfigOption] Failed to fetch option "${root.key}":\n - Output: ${text.trim()}\n - Error: ${e}`); + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml b/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml index a1d6db88e..79dd98665 100644 --- a/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml +++ b/dots/.config/quickshell/ii/modules/common/models/quickToggles/GameModeToggle.qml @@ -1,11 +1,12 @@ import QtQuick import Quickshell.Io +import qs.modules.common.models.hyprland import qs.services QuickToggleModel { id: root name: Translation.tr("Game mode") - toggled: toggled + toggled: !confOpt.value icon: "gamepad" mainAction: () => { @@ -34,13 +35,11 @@ QuickToggleModel { ]); } } - Process { - id: fetchActiveState - running: true - command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`] - onExited: (exitCode, exitStatus) => { - root.toggled = exitCode !== 0; // Inverted because enabled = nonzero exit - } + + HyprlandConfigOption { + id: confOpt + key: "animations:enabled" } + tooltipText: Translation.tr("Game mode") } diff --git a/dots/.config/quickshell/ii/services/HyprlandConfig.qml b/dots/.config/quickshell/ii/services/HyprlandConfig.qml index 50cbe9d9b..cf5eb0e30 100644 --- a/dots/.config/quickshell/ii/services/HyprlandConfig.qml +++ b/dots/.config/quickshell/ii/services/HyprlandConfig.qml @@ -3,7 +3,7 @@ pragma ComponentBehavior: Bound import QtQuick import Quickshell -import Quickshell.Io +import Quickshell.Hyprland import qs.modules.common import qs.modules.common.functions @@ -14,6 +14,8 @@ import qs.modules.common.functions Singleton { id: root + signal reloaded() + readonly property string configuratorScriptPath: Quickshell.shellPath("scripts/hyprland/hyprconfigurator.py") readonly property string shellOverridesPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/hyprland/shellOverrides/main.conf`) @@ -39,7 +41,7 @@ Singleton { ]) } - function resetMany(keys: var) { + function resetMany(keys: list) { let args = "" for (let i = 0; i < keys.length; i++) { args += `--reset "${keys[i]}" ` @@ -48,4 +50,14 @@ Singleton { `${root.configuratorScriptPath} --file ${root.shellOverridesPath} ${args}` // ]) } + + Connections { + target: Hyprland + + function onRawEvent(event) { + if (event.name == "configreloaded") { + root.reloaded() + } + } + } } From 1225c014e84fc60f6230c2b12b0c8410465b757b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:34:32 +0100 Subject: [PATCH 096/104] add anti-flashbang using shader --- .../quickToggles/AntiFlashbangToggle.qml | 4 +- .../nightLight/NightLightDialog.qml | 26 ++++++++-- .../services/HyprlandAntiFlashbangShader.qml | 38 +++++++++++++++ .../anti-flashbang.glsl | 47 +++++++++++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 dots/.config/quickshell/ii/services/HyprlandAntiFlashbangShader.qml create mode 100644 dots/.config/quickshell/ii/services/hyprlandAntiFlashbangShader/anti-flashbang.glsl diff --git a/dots/.config/quickshell/ii/modules/common/models/quickToggles/AntiFlashbangToggle.qml b/dots/.config/quickshell/ii/modules/common/models/quickToggles/AntiFlashbangToggle.qml index 088434ad7..64f8c4e72 100644 --- a/dots/.config/quickshell/ii/modules/common/models/quickToggles/AntiFlashbangToggle.qml +++ b/dots/.config/quickshell/ii/modules/common/models/quickToggles/AntiFlashbangToggle.qml @@ -8,10 +8,10 @@ QuickToggleModel { name: Translation.tr("Anti-flashbang") tooltipText: Translation.tr("Anti-flashbang") icon: "flash_off" - toggled: Config.options.light.antiFlashbang.enable + toggled: HyprlandAntiFlashbangShader.enabled mainAction: () => { - Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable; + HyprlandAntiFlashbangShader.toggle() } hasMenu: true } diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/nightLight/NightLightDialog.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/nightLight/NightLightDialog.qml index f0286fc38..e6f7509f2 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/nightLight/NightLightDialog.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/nightLight/NightLightDialog.qml @@ -42,7 +42,7 @@ WindowDialog { right: parent.right } iconSize: Appearance.font.pixelSize.larger - buttonIcon: "lightbulb" + buttonIcon: "check" text: Translation.tr("Enable now") checked: Hyprsunset.active onCheckedChanged: { @@ -102,14 +102,32 @@ WindowDialog { right: parent.right } iconSize: Appearance.font.pixelSize.larger - buttonIcon: "flash_off" - text: Translation.tr("Enable") + buttonIcon: "filter" + text: Translation.tr("Content adjustment") + checked: HyprlandAntiFlashbangShader.enabled + onCheckedChanged: { + if (checked) HyprlandAntiFlashbangShader.enable() + else HyprlandAntiFlashbangShader.disable() + } + StyledToolTip { + text: Translation.tr("Dims screen content as needed.

Pros: Immediately responsive
Cons: Expensive and can hurt color accuracy

Uses a Hyprland screen shader") + } + } + + ConfigSwitch { + anchors { + left: parent.left + right: parent.right + } + iconSize: Appearance.font.pixelSize.larger + buttonIcon: "light_mode" + text: Translation.tr("Brightness adjustment") checked: Config.options.light.antiFlashbang.enable onCheckedChanged: { Config.options.light.antiFlashbang.enable = checked; } StyledToolTip { - text: Translation.tr("Example use case: eroge on one workspace, dark Discord window on another") + text: Translation.tr("Adapts the display (physical screen) brightness

Pros: Less expensive, retains colors
Cons: Not immediately responsive

Adjusts display brightness after each Hyprland IPC event") } } } diff --git a/dots/.config/quickshell/ii/services/HyprlandAntiFlashbangShader.qml b/dots/.config/quickshell/ii/services/HyprlandAntiFlashbangShader.qml new file mode 100644 index 000000000..eea2123b9 --- /dev/null +++ b/dots/.config/quickshell/ii/services/HyprlandAntiFlashbangShader.qml @@ -0,0 +1,38 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell + +import qs.modules.common.models.hyprland + +Singleton { + id: root + + readonly property string shaderPath: Quickshell.shellPath("services/hyprlandAntiFlashbangShader/anti-flashbang.glsl") + property bool enabled: confOpt.value == shaderPath + + function enable() { + HyprlandConfig.setMany({ + "decoration:screen_shader": root.shaderPath, + "debug:damage_tracking": 1, // Turn off dmg tracking to prevent weird flashes. 1 = monitor only + }); + } + + function disable() { + HyprlandConfig.resetMany([ + "decoration:screen_shader", + "debug:damage_tracking" + ]); + } + + function toggle() { + if (root.enabled) disable() + else enable() + } + + HyprlandConfigOption { + id: confOpt + key: "decoration:screen_shader" + } +} diff --git a/dots/.config/quickshell/ii/services/hyprlandAntiFlashbangShader/anti-flashbang.glsl b/dots/.config/quickshell/ii/services/hyprlandAntiFlashbangShader/anti-flashbang.glsl new file mode 100644 index 000000000..32e6ad2ed --- /dev/null +++ b/dots/.config/quickshell/ii/services/hyprlandAntiFlashbangShader/anti-flashbang.glsl @@ -0,0 +1,47 @@ +#version 300 es +precision highp float; + +in vec2 v_texcoord; +uniform sampler2D tex; +out vec4 fragColor; + +float overlayOpacityForBrightness(float x) { + // Note: range 0 to 1 + + // Will a fancy curve help?... I'll have to experiment more at night + // float y = pow(x, 2.0) * 0.75; + // float y = (1.0 - exp(-x))*1.15; + // float y = (1.0 - exp(-pow((x-0.15), 0.6)))*1.18; + float y = x*0.75; + + return min(max(y, 0.001), 1.0); +} + +void main() { + // 1. Get the current pixel color + vec4 pixColor = texture(tex, v_texcoord); + + // 2. Calculate average screen brightness + vec3 totalRGB = vec3(0.0); + float samples = 0.0; + + // We use a nested loop to create a 10x10 grid (100 samples) + // This is dense enough to catch small icons/text but light enough to run fast. + for(float x = 0.05; x < 1.0; x += 0.1) { + for(float y = 0.05; y < 1.0; y += 0.1) { + totalRGB += texture(tex, vec2(x, y)).rgb; + samples++; + } + } + + vec3 avgColor = totalRGB / samples; + float globalBrightness = dot(avgColor, vec3(0.2126, 0.7152, 0.0722)); + + // 3. Get the specific opacity for this brightness level + float opacity = overlayOpacityForBrightness(globalBrightness); + + // 4. Apply the "black overlay" effect + vec3 outColor = mix(pixColor.rgb, vec3(0.0), opacity); + + fragColor = vec4(outColor, pixColor.a); +} From 17a99ec06847d48eeddf1edf0b0c01519cc2cf4d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:46:30 +0100 Subject: [PATCH 097/104] update quickshell --- .../illogical-impulse-quickshell-git/PKGBUILD | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD index fe910ba95..477e26494 100644 --- a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD @@ -1,4 +1,4 @@ -_commit='6e17efab83d3a5ad5d6e59bc08d26095c6660502' +_commit='7511545ee20664e3b8b8d3322c0ffe7567c56f7a' # Useful links: # https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master # https://aur.archlinux.org/packages/quickshell-git @@ -16,16 +16,16 @@ url='https://git.outfoxxed.me/quickshell/quickshell' options=(!strip) license=('LGPL-3.0-only') depends=( + 'cpptrace' + 'jemalloc' + 'mesa' 'qt6-declarative' 'qt6-base' - 'jemalloc' 'qt6-svg' + 'libdrm' 'libpipewire' 'libxcb' 'wayland' - 'libdrm' - 'mesa' - 'google-breakpad' # NOTE: Below are custom dependencies of illogical-impulse qt6-5compat qt6-avif-image-plugin @@ -44,14 +44,15 @@ depends=( syntax-highlighting ) makedepends=( - 'spirv-tools' - 'qt6-shadertools' - 'wayland' - 'wayland-protocols' 'cli11' - 'ninja' 'cmake' 'git' + 'ninja' + 'qt6-shadertools' + 'spirv-tools' + 'vulkan-headers' + 'wayland' + 'wayland-protocols' ) provides=("$_pkgname") conflicts=("$_pkgname") From c4f9b20b23f1a161fb1f76d53711d1790cbdc7ba Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:56:25 +0100 Subject: [PATCH 098/104] hefty: bar: ws: rmb for overview --- .../modules/hefty/topLayer/bar/widgets/HWorkspaces.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml index db4700967..7321f9be7 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWorkspaces.qml @@ -141,6 +141,7 @@ HBarWidgetContainer { z: 3 anchors.fill: parent cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: true property int hoverIndex: { const position = root.vertical ? mouseY : mouseX; @@ -150,8 +151,13 @@ HBarWidgetContainer { function switchWorkspaceToHovered() { Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`) } - onPressed: switchWorkspaceToHovered() - onWheel: event => { + onPressed: (mouse) => { + if (mouse.button == Qt.LeftButton) + switchWorkspaceToHovered() + else if (mouse.button == Qt.RightButton) + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + onWheel: (event) => { if (event.angleDelta.y < 0) Hyprland.dispatch(`workspace r+1`); else if (event.angleDelta.y > 0) From db7caae4b5a027d0c02c15935e94f060dd47febd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:58:38 +0100 Subject: [PATCH 099/104] hefty: bar: adjust battery text spacing --- .../ii/modules/hefty/topLayer/bar/widgets/HResources.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml index f4faf3f8f..535cbfb0b 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HResources.qml @@ -178,7 +178,7 @@ HBarWidgetWithPopout { bottomMargin: (parent.height - height) / 2 } rotation: 180 * root.vertical - spacing: 4 + spacing: 0 W.MaterialSymbol { id: boltIcon From 216a44274e980c2c25b1e10e336767aa2cd905a2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:35:51 +0100 Subject: [PATCH 100/104] change choreographergridlayout back to gridlayout --- .../ii/modules/common/widgets/ChoreographerGridLayout.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml index 1581f0601..76daccad4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ChoreographerGridLayout.qml @@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts -BoxLayout { +GridLayout { id: root - vertical: true + columns: 1 property real totalDuration: 250 property real interval: totalDuration / count From aefc88755a54a69394fc34be25adabc0ae4a2581 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:36:26 +0100 Subject: [PATCH 101/104] hefty: bar: make left sidebar button circular (when alone) --- .../modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml index bc84e7837..35f490619 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HLeftSidebarButton.qml @@ -40,7 +40,9 @@ HBarWidgetWithPopout { vertical: root.vertical atBottom: root.atBottom contentImplicitWidth: 14 - contentImplicitHeight: 18 + contentImplicitHeight: 14 + implicitWidth: 24 + implicitHeight: 30 // note that vertical bar is more thicc than horizz showPopup: false onClicked: GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; From 4fcc12444f646b6c9f62f389d4698f9aba12de5c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:38:14 +0100 Subject: [PATCH 102/104] hefty: bar: window info --- .../quickshell/ii/modules/common/Config.qml | 2 +- .../ii/modules/common/config/HeftyConfig.qml | 2 +- .../widgets/VisuallyCenteredStyledText.qml | 3 + .../hefty/topLayer/bar/HBarContent.qml | 24 +- .../bar/HBarUserFallbackComponentRepeater.qml | 8 +- .../topLayer/bar/widgets/HWindowInfo.qml | 282 ++++++++++++++++++ 6 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWindowInfo.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index f658fa395..a0fed3a99 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -159,8 +159,8 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'" - property string network: "kcmshell6 kcm_networkmanagement" property string manageUser: "kcmshell6 kcm_users" + property string network: "kcmshell6 kcm_networkmanagement" property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string taskManager: "plasma-systemmonitor --page-name Processes" property string terminal: "kitty -1" // This is only for shell actions diff --git a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml index 2275e3704..5bd70b28f 100644 --- a/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml +++ b/dots/.config/quickshell/ii/modules/common/config/HeftyConfig.qml @@ -4,7 +4,7 @@ import Quickshell.Io JsonObject { property JsonObject bar: JsonObject { - property list leftWidgets: ["HLeftSidebarButton"] + property list leftWidgets: ["HLeftSidebarButton", "HWindowInfo"] property list centerLeftWidgets: ["HTime"] property list centerWidgets: ["HWorkspaces"] property list centerRightWidgets: ["HResources"] diff --git a/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml index 1390d2caa..6391be5df 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/VisuallyCenteredStyledText.qml @@ -11,6 +11,9 @@ Item { property alias verticalAlignment: textWidget.verticalAlignment property alias font: textWidget.font property alias color: textWidget.color + property alias elide: textWidget.elide + property alias wrapMode: textWidget.wrapMode + property alias animateChange: textWidget.animateChange // In many cases the baseline is a bit high to accomodate the dangling parts of "g" and "y", // making most text (especiall number-only text) not well-balanced. diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml index 7b1e5d6d3..18c3b4d30 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarContent.qml @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import qs.modules.common as C +import qs.modules.common.widgets as W Item { id: root @@ -19,10 +20,17 @@ Item { id: leftSide anchors.left: parent.left anchors.top: parent.top + anchors.right: !root.vertical ? centerLeftSide.left : parent.right + anchors.bottom: !root.vertical ? parent.bottom : centerLeftSide.top HBarUserFallbackComponentRepeater { componentNames: root.leftWidgets } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } Side { @@ -56,12 +64,20 @@ Item { id: rightSide anchors.right: parent.right anchors.bottom: parent.bottom + anchors.left: !root.vertical ? centerRightSide.right : parent.left + anchors.top: !root.vertical ? parent.top : centerRightSide.bottom + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + HBarUserFallbackComponentRepeater { componentNames: root.rightWidgets } } - component Side: GridLayout { + component Side: W.BoxLayout { anchors { top: !root.vertical ? parent.top : undefined bottom: !root.vertical ? parent.bottom : undefined @@ -73,9 +89,7 @@ Item { rightMargin: root.spacing * !root.vertical } - columns: C.Config.options.bar.vertical ? 1 : -1 - property real spacing: root.spacing - columnSpacing: spacing - rowSpacing: spacing + vertical: C.Config.options.bar.vertical + spacing: root.spacing } } diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml index 7cc44ba89..f0720b829 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarUserFallbackComponentRepeater.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Layouts import Quickshell import qs.modules.common as C import qs.modules.common.widgets as W @@ -48,6 +49,11 @@ Repeater { context: root.context property bool startSide: index === 0 property bool endSide: index === root.model.length - 1 + + Layout.fillWidth: item.Layout.fillWidth + Layout.fillHeight: item.Layout.fillHeight + Layout.maximumWidth: item.Layout.maximumWidth + Layout.maximumHeight: item.Layout.maximumHeight } } @@ -74,4 +80,4 @@ Repeater { } } } -} \ No newline at end of file +} diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWindowInfo.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWindowInfo.qml new file mode 100644 index 000000000..2075d8b65 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/widgets/HWindowInfo.qml @@ -0,0 +1,282 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland + +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import ".." + +HBarWidgetWithPopout { + id: root + + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) + readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + readonly property var activeHyprlandClient: HyprlandData.clientForToplevel(activeWindow) + readonly property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name + readonly property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id) + readonly property bool hasFocusedWindow: (focusingThisMonitor && activeWindow?.activated && biggestWindow) ?? false + + readonly property string primaryText: { + if (root.hasFocusedWindow) + return root.activeWindow?.title; + return (root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${root.monitor?.activeWorkspace?.id ?? 1}`; + } + readonly property string secondaryText: { + if (root.hasFocusedWindow && root.activeWindow?.appId != "" && root.activeWindow?.appId != primaryText) + return root.activeWindow?.appId; + return Translation.tr("Options") + } + + property real fontPixelSize: Appearance.font.pixelSize.smaller + + Layout.maximumWidth: implicitWidth + Layout.fillWidth: true + + popupContentWidth: popupContent.implicitWidth + popupContentHeight: popupContent.implicitHeight + + HBarWidgetContent { + id: contentRoot + Layout.fillWidth: true + Layout.fillHeight: true + vertical: root.vertical + atBottom: root.atBottom + contentImplicitWidth: winTitleContent.implicitWidth + contentImplicitHeight: winTitleContent.implicitHeight + showPopup: false + onClicked: root.showPopup = !root.showPopup; + + WinTitleContent { + id: winTitleContent + } + + WinOptionsPopup { + id: popupContent + anchors { + top: parent.top + topMargin: root.popupContentOffsetY + left: parent.left + leftMargin: root.popupContentOffsetX + } + shown: root.showPopup + } + } + + component WinTitleContent: BoxLayout { + anchors.fill: parent + vertical: root.vertical + spacing: 4 + + Item { + Layout.leftMargin: 4 * !root.vertical + Layout.topMargin: 3 * root.vertical + Layout.bottomMargin: 4 * root.vertical + Layout.alignment: Qt.AlignCenter + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + + AppIcon { + id: appIcon + anchors.centerIn: parent + opacity: 0 + source: Quickshell.iconPath(AppSearch.guessIcon(root.activeWindow?.appId), "image-missing") + implicitSize: 16 + animated: false + } + Circle { + id: iconMask + visible: false + layer.enabled: true + diameter: appIcon.implicitSize + } + Loader { + anchors.fill: appIcon + Colorizer { + id: renderedIcon + implicitWidth: appIcon.implicitWidth + implicitHeight: appIcon.implicitHeight + colorizationColor: Appearance.colors.colOnLayer0 + // colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5 + colorization: 1 + brightness: 0 + source: appIcon + + maskEnabled: true + maskSource: iconMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + + visible: root.activeWindow + } + } + + MaterialSymbol { + anchors.centerIn: parent + visible: !renderedIcon.visible + text: "overview_key" + iconSize: 16 + } + } + + Item { + visible: !root.vertical + Layout.rightMargin: 4 + Layout.alignment: Qt.AlignCenter + Layout.fillHeight: true + // No overflow + Layout.maximumWidth: implicitWidth + Layout.fillWidth: true + // Size + implicitWidth: winText.implicitWidth + implicitHeight: winText.implicitHeight + + FlyFadeEnterChoreographable { + anchors.fill: parent + progress: contentRoot.containsMouse ? 0 : 1 + reverseDirection: true + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + VisuallyCenteredStyledText { + id: winText + height: parent.height + width: parent.width + elide: Text.ElideRight + // Styles & text + font.pixelSize: root.fontPixelSize + text: root.primaryText + } + } + FlyFadeEnterChoreographable { + anchors.fill: parent + progress: contentRoot.containsMouse ? 1 : 0 + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + VisuallyCenteredStyledText { + height: parent.height + width: parent.width + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + // Styles & text + font.pixelSize: root.fontPixelSize + text: root.secondaryText + } + } + } + } + + component WinOptionsPopup: ChoreographerLoader { + sourceComponent: ChoreographerGridLayout { + id: popupRoot + + columns: 3 + rowSpacing: 8 + columnSpacing: 6 + + FlyFadeEnterChoreographable { + Layout.fillWidth: true + Layout.columnSpan: 3 + StyledText { + width: parent.width + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: root.hasFocusedWindow ? Translation.tr("Window options") : Translation.tr("Launch") + // font.pixelSize: Appearance.font.pixelSize.title + } + } + + FlyFadeEnterChoreographable { + visible: !root.hasFocusedWindow + PopupLabeledIconButton { + materialSymbol: "terminal" + text: Translation.tr("Terminal") + onClicked: Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]); + } + } + + FlyFadeEnterChoreographable { + visible: !root.hasFocusedWindow + PopupLabeledIconButton { + materialSymbol: "files" + text: Translation.tr("Files") + onClicked: Qt.openUrlExternally(Directories.home); + } + } + + FlyFadeEnterChoreographable { + visible: !root.hasFocusedWindow + PopupLabeledIconButton { + materialSymbol: "language" + text: Translation.tr("Browser") + // Kinda hacky. Works with Google and DDG at least + onClicked: Qt.openUrlExternally(Config.options.search.engineBaseUrl); + } + } + + FlyFadeEnterChoreographable { + visible: root.hasFocusedWindow + PopupLabeledIconButton { + materialSymbol: "content_copy" + text: Translation.tr("Address") + onClicked: Quickshell.clipboardText = root.activeHyprlandClient.address + } + } + + FlyFadeEnterChoreographable { + visible: root.hasFocusedWindow + PopupLabeledIconButton { + property bool toFloat: !(root.activeHyprlandClient?.floating ?? false) + materialSymbol: toFloat ? "picture_in_picture_center" : "side_navigation" + text: toFloat ? Translation.tr("Float") : Translation.tr("Tile") + onClicked: { + Hyprland.dispatch(`togglefloating address:${root.activeHyprlandClient.address}`) + HyprlandData.updateWindowList() + } + } + } + + FlyFadeEnterChoreographable { + visible: root.hasFocusedWindow + PopupLabeledIconButton { + materialSymbol: "warning" + text: Translation.tr("Kill") + colBackground: Appearance.colors.colError + colForeground: Appearance.colors.colOnError + onClicked: { + Hyprland.dispatch(`killwindow address:${root.activeHyprlandClient.address}`) + HyprlandData.updateWindowList() + } + } + } + } + } + + component PopupLabeledIconButton: Column { + id: licobtn + property string materialSymbol: "circle" + property string text: "Label" + property alias colBackground: btn.colBackground + property alias colForeground: btn.colForeground + spacing: 4 + + signal clicked() + onClicked: root.showPopup = false + + StyledIconButton { + id: btn + implicitWidth: 70 + implicitHeight: 50 + text: licobtn.materialSymbol + iconSize: 24 + colBackground: Appearance.colors.colLayer4 + colForeground: Appearance.colors.colOnLayer4 + onClicked: licobtn.clicked() + } + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + text: licobtn.text + } + } +} From 124ea7b2459c6802a6cd3ed2db047d7e6c02e3ab Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:42:48 +0100 Subject: [PATCH 103/104] hefty: bar: fix popup bg getting clipped --- .../modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index 0d7b8622a..db4762fe8 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -111,12 +111,12 @@ Shapes.ShapeCanvas { } } width: { - if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth; + if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth + popupContentOffsetX * 2) : backgroundWidth; else return bgShape.showPopup ? (containerShape.width + popupShape.width + bgShape.spacing) : containerShape.width; } height: { if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height; - else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight; + else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight + popupContentOffsetY * 2) : backgroundHeight; } property bool popupHiding: false onShowPopupChanged: popupHiding = true From 0a185efcc5538cbcbd4f96bc0b5db1c9b34fd69b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:49:33 +0100 Subject: [PATCH 104/104] Revert "hefty: bar: fix popup bg getting clipped" nvm that sloppy calculation made it worse --- .../modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml index db4762fe8..0d7b8622a 100644 --- a/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml +++ b/dots/.config/quickshell/ii/modules/hefty/topLayer/bar/HBarWidgetShapeBackground.qml @@ -111,12 +111,12 @@ Shapes.ShapeCanvas { } } width: { - if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth + popupContentOffsetX * 2) : backgroundWidth; + if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth; else return bgShape.showPopup ? (containerShape.width + popupShape.width + bgShape.spacing) : containerShape.width; } height: { if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height; - else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight + popupContentOffsetY * 2) : backgroundHeight; + else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight; } property bool popupHiding: false onShowPopupChanged: popupHiding = true