From 34ca65a180f8c1a878bdb270ed1518c15fdbdde5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:11:24 +0700 Subject: [PATCH 1/9] background: fix wrong anchor --- .config/quickshell/ii/modules/background/Background.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index dc6bdece1..1200ece5e 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -199,7 +199,7 @@ Scope { ColumnLayout { id: clockColumn - anchors.centerIn: wallpaper + anchors.centerIn: parent spacing: 0 StyledText { From 00984c599b4f04ef8f969b1e904937aab4c1c292 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:51:08 +0700 Subject: [PATCH 2/9] settings: update keep right sidebar loaded note --- .config/quickshell/ii/modules/settings/InterfaceConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 4d86ffff7..e4b712012 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -344,7 +344,7 @@ ContentPage { Config.options.sidebar.keepRightSidebarLoaded = checked; } StyledToolTip { - content: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a different kernel might help with this delay") + content: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help") } } } From 13a0927900ce4a5364e37c5c2f33468bb3af8cec Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:52:39 +0700 Subject: [PATCH 3/9] bar: refractor bar content to new file --- .../ii/modules/bar/ActiveWindow.qml | 10 +- .config/quickshell/ii/modules/bar/Bar.qml | 433 +---------------- .../quickshell/ii/modules/bar/BarContent.qml | 448 ++++++++++++++++++ .config/quickshell/ii/modules/bar/SysTray.qml | 4 - .../quickshell/ii/modules/bar/SysTrayItem.qml | 2 +- .../quickshell/ii/modules/bar/Workspaces.qml | 18 +- .config/quickshell/ii/services/Brightness.qml | 2 +- .config/quickshell/ii/shell.qml | 1 + 8 files changed, 467 insertions(+), 451 deletions(-) create mode 100644 .config/quickshell/ii/modules/bar/BarContent.qml diff --git a/.config/quickshell/ii/modules/bar/ActiveWindow.qml b/.config/quickshell/ii/modules/bar/ActiveWindow.qml index 636b23136..c571b1d02 100644 --- a/.config/quickshell/ii/modules/bar/ActiveWindow.qml +++ b/.config/quickshell/ii/modules/bar/ActiveWindow.qml @@ -4,18 +4,18 @@ import qs.modules.common.widgets import qs import QtQuick import QtQuick.Layouts +import Quickshell import Quickshell.Wayland import Quickshell.Hyprland Item { id: root - required property var bar - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) + 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) + property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name + property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id) implicitWidth: colLayout.implicitWidth @@ -45,7 +45,7 @@ Item { elide: Text.ElideRight text: root.focusingThisMonitor && root.activeWindow?.activated && root.biggestWindow ? root.activeWindow?.title : - (root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${monitor.activeWorkspace?.id}` + (root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${monitor?.activeWorkspace?.id}` } } diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index 7fe8668e1..6d0bc754d 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -18,14 +18,6 @@ Scope { readonly property int osdHideMouseMoveThreshold: 20 property bool showBarBackground: Config.options.bar.showBackground - component VerticalBarSeparator: Rectangle { - Layout.topMargin: Appearance.sizes.baseBarHeight / 3 - Layout.bottomMargin: Appearance.sizes.baseBarHeight / 3 - Layout.fillHeight: true - implicitWidth: 1 - color: Appearance.colors.colOutlineVariant - } - Variants { // For each monitor model: { @@ -63,8 +55,9 @@ Scope { right: true } - Item { // Bar content region + BarContent { id: barContent + anchors { right: parent.right left: parent.left @@ -72,7 +65,6 @@ Scope { bottom: undefined } implicitHeight: Appearance.sizes.barHeight - height: Appearance.sizes.barHeight states: State { name: "bottom" @@ -87,427 +79,6 @@ Scope { } } } - - // Background shadow - Loader { - active: showBarBackground && Config.options.bar.cornerStyle === 1 - anchors.fill: barBackground - sourceComponent: StyledRectangularShadow { - anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor - target: barBackground - } - } - // Background - Rectangle { - id: barBackground - anchors { - fill: parent - margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed - } - color: showBarBackground ? Appearance.colors.colLayer0 : "transparent" - radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0 - border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0 - border.color: Appearance.colors.colLayer0Border - } - - MouseArea { // Left side | scroll to change brightness - id: barLeftSideMouseArea - anchors.left: parent.left - implicitHeight: Appearance.sizes.baseBarHeight - height: Appearance.sizes.barHeight - width: (barRoot.width - middleSection.width) / 2 - property bool hovered: false - property real lastScrollX: 0 - property real lastScrollY: 0 - property bool trackingScroll: false - acceptedButtons: Qt.LeftButton - hoverEnabled: true - propagateComposedEvents: true - onEntered: event => { - barLeftSideMouseArea.hovered = true; - } - onExited: event => { - barLeftSideMouseArea.hovered = false; - barLeftSideMouseArea.trackingScroll = false; - } - onPressed: event => { - if (event.button === Qt.LeftButton) { - GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; - } - } - // Scroll to change brightness - WheelHandler { - onWheel: event => { - if (event.angleDelta.y < 0) - barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness - 0.05); - else if (event.angleDelta.y > 0) - barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness + 0.05); - // Store the mouse position and start tracking - barLeftSideMouseArea.lastScrollX = event.x; - barLeftSideMouseArea.lastScrollY = event.y; - barLeftSideMouseArea.trackingScroll = true; - } - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad - } - onPositionChanged: mouse => { - if (barLeftSideMouseArea.trackingScroll) { - const dx = mouse.x - barLeftSideMouseArea.lastScrollX; - const dy = mouse.y - barLeftSideMouseArea.lastScrollY; - if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { - GlobalStates.osdBrightnessOpen = false; - barLeftSideMouseArea.trackingScroll = false; - } - } - } - Item { - // Left section - anchors.fill: parent - implicitHeight: leftSectionRowLayout.implicitHeight - implicitWidth: leftSectionRowLayout.implicitWidth - - ScrollHint { - reveal: barLeftSideMouseArea.hovered - icon: "light_mode" - tooltipText: Translation.tr("Scroll to change brightness") - side: "left" - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - } - - RowLayout { // Content - id: leftSectionRowLayout - anchors.fill: parent - spacing: 10 - - RippleButton { - // Left sidebar button - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: Appearance.rounding.screenRounding - Layout.fillWidth: false - property real buttonPadding: 5 - implicitWidth: distroIcon.width + buttonPadding * 2 - implicitHeight: distroIcon.height + buttonPadding * 2 - - buttonRadius: Appearance.rounding.full - colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) - colBackgroundHover: Appearance.colors.colLayer1Hover - colRipple: Appearance.colors.colLayer1Active - colBackgroundToggled: Appearance.colors.colSecondaryContainer - colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover - colRippleToggled: Appearance.colors.colSecondaryContainerActive - toggled: GlobalStates.sidebarLeftOpen - property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0 - - onPressed: { - GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; - } - - CustomIcon { - id: distroIcon - anchors.centerIn: parent - width: 19.5 - height: 19.5 - source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic" - colorize: true - color: Appearance.colors.colOnLayer0 - } - } - - ActiveWindow { - visible: barRoot.useShortenedForm === 0 - Layout.rightMargin: Appearance.rounding.screenRounding - Layout.fillWidth: true - Layout.fillHeight: true - bar: barRoot - } - } - } - } - - RowLayout { // Middle section - id: middleSection - anchors.centerIn: parent - spacing: Config.options?.bar.borderless ? 4 : 8 - - BarGroup { - id: leftCenterGroup - Layout.preferredWidth: barRoot.centerSideModuleWidth - Layout.fillHeight: true - - Resources { - alwaysShowAllResources: barRoot.useShortenedForm === 2 - Layout.fillWidth: barRoot.useShortenedForm === 2 - } - - Media { - visible: barRoot.useShortenedForm < 2 - Layout.fillWidth: true - } - } - - VerticalBarSeparator { - visible: Config.options?.bar.borderless - } - - BarGroup { - id: middleCenterGroup - padding: workspacesWidget.widgetPadding - Layout.fillHeight: true - - Workspaces { - id: workspacesWidget - bar: barRoot - Layout.fillHeight: true - MouseArea { - // Right-click to toggle overview - anchors.fill: parent - acceptedButtons: Qt.RightButton - - onPressed: event => { - if (event.button === Qt.RightButton) { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; - } - } - } - } - } - - VerticalBarSeparator { - visible: Config.options?.bar.borderless - } - - MouseArea { - id: rightCenterGroup - implicitWidth: rightCenterGroupContent.implicitWidth - implicitHeight: rightCenterGroupContent.implicitHeight - Layout.preferredWidth: barRoot.centerSideModuleWidth - Layout.fillHeight: true - - onPressed: { - GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; - } - - BarGroup { - id: rightCenterGroupContent - anchors.fill: parent - - ClockWidget { - showDate: (Config.options.bar.verbose && barRoot.useShortenedForm < 2) - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - } - - UtilButtons { - visible: (Config.options.bar.verbose && barRoot.useShortenedForm === 0) - Layout.alignment: Qt.AlignVCenter - } - - BatteryIndicator { - visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery) - Layout.alignment: Qt.AlignVCenter - } - } - } - - VerticalBarSeparator { - visible: Config.options.bar.borderless && Config.options.bar.weather.enable - } - } - - MouseArea { // Right side | scroll to change volume - id: barRightSideMouseArea - - anchors.right: parent.right - implicitHeight: Appearance.sizes.baseBarHeight - height: Appearance.sizes.barHeight - width: (barRoot.width - middleSection.width) / 2 - - property bool hovered: false - property real lastScrollX: 0 - property real lastScrollY: 0 - property bool trackingScroll: false - - acceptedButtons: Qt.LeftButton - hoverEnabled: true - propagateComposedEvents: true - onEntered: event => { - barRightSideMouseArea.hovered = true; - } - onExited: event => { - barRightSideMouseArea.hovered = false; - barRightSideMouseArea.trackingScroll = false; - } - onPressed: event => { - if (event.button === Qt.LeftButton) { - GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; - } else if (event.button === Qt.RightButton) { - MprisController.activePlayer.next(); - } - } - // Scroll to change volume - WheelHandler { - onWheel: event => { - const currentVolume = Audio.value; - const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2; - if (event.angleDelta.y < 0) - Audio.sink.audio.volume -= step; - else if (event.angleDelta.y > 0) - Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step); - // Store the mouse position and start tracking - barRightSideMouseArea.lastScrollX = event.x; - barRightSideMouseArea.lastScrollY = event.y; - barRightSideMouseArea.trackingScroll = true; - } - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad - } - onPositionChanged: mouse => { - if (barRightSideMouseArea.trackingScroll) { - const dx = mouse.x - barRightSideMouseArea.lastScrollX; - const dy = mouse.y - barRightSideMouseArea.lastScrollY; - if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { - GlobalStates.osdVolumeOpen = false; - barRightSideMouseArea.trackingScroll = false; - } - } - } - - Item { - anchors.fill: parent - implicitHeight: rightSectionRowLayout.implicitHeight - implicitWidth: rightSectionRowLayout.implicitWidth - - ScrollHint { - reveal: barRightSideMouseArea.hovered - icon: "volume_up" - tooltipText: Translation.tr("Scroll to change volume") - side: "right" - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - } - - RowLayout { - id: rightSectionRowLayout - anchors.fill: parent - spacing: 5 - layoutDirection: Qt.RightToLeft - - RippleButton { // Right sidebar button - id: rightSidebarButton - - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.rightMargin: Appearance.rounding.screenRounding - Layout.fillWidth: false - - implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2 - implicitHeight: indicatorsRowLayout.implicitHeight + 5 * 2 - - buttonRadius: Appearance.rounding.full - colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) - colBackgroundHover: Appearance.colors.colLayer1Hover - colRipple: Appearance.colors.colLayer1Active - colBackgroundToggled: Appearance.colors.colSecondaryContainer - colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover - colRippleToggled: Appearance.colors.colSecondaryContainerActive - toggled: GlobalStates.sidebarRightOpen - property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0 - - Behavior on colText { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } - - onPressed: { - GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; - } - - RowLayout { - id: indicatorsRowLayout - anchors.centerIn: parent - property real realSpacing: 15 - spacing: 0 - - Revealer { - reveal: Audio.sink?.audio?.muted ?? false - Layout.fillHeight: true - Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0 - Behavior on Layout.rightMargin { - NumberAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - } - MaterialSymbol { - text: "volume_off" - iconSize: Appearance.font.pixelSize.larger - color: rightSidebarButton.colText - } - } - Revealer { - reveal: Audio.source?.audio?.muted ?? false - Layout.fillHeight: true - Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0 - Behavior on Layout.rightMargin { - NumberAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - } - MaterialSymbol { - text: "mic_off" - iconSize: Appearance.font.pixelSize.larger - color: rightSidebarButton.colText - } - } - Loader { - active: HyprlandXkb.layoutCodes.length > 1 - visible: active - Layout.rightMargin: indicatorsRowLayout.realSpacing - sourceComponent: StyledText { - text: HyprlandXkb.currentLayoutCode - font.pixelSize: Appearance.font.pixelSize.small - color: rightSidebarButton.colText - } - } - MaterialSymbol { - Layout.rightMargin: indicatorsRowLayout.realSpacing - text: Network.materialSymbol - iconSize: Appearance.font.pixelSize.larger - color: rightSidebarButton.colText - } - MaterialSymbol { - text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled" - iconSize: Appearance.font.pixelSize.larger - color: rightSidebarButton.colText - } - } - } - - SysTray { - bar: barRoot - visible: barRoot.useShortenedForm === 0 - Layout.fillWidth: false - Layout.fillHeight: true - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - - // Weather - Loader { - Layout.leftMargin: 8 - Layout.fillHeight: true - active: Config.options.bar.weather.enable - sourceComponent: BarGroup { - implicitHeight: Appearance.sizes.baseBarHeight - WeatherBar {} - } - } - } - } - } } // Round decorators diff --git a/.config/quickshell/ii/modules/bar/BarContent.qml b/.config/quickshell/ii/modules/bar/BarContent.qml new file mode 100644 index 000000000..616918197 --- /dev/null +++ b/.config/quickshell/ii/modules/bar/BarContent.qml @@ -0,0 +1,448 @@ +import "./weather" +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import Quickshell.Services.UPower +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions + +Item { // Bar content region + id: root + + property var screen: root.QsWindow.window?.screen + property var brightnessMonitor: Brightness.getMonitorForScreen(screen) + property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen?.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen?.width) ? 1 : 0 + readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth + + component VerticalBarSeparator: Rectangle { + Layout.topMargin: Appearance.sizes.baseBarHeight / 3 + Layout.bottomMargin: Appearance.sizes.baseBarHeight / 3 + Layout.fillHeight: true + implicitWidth: 1 + color: Appearance.colors.colOutlineVariant + } + + // Background shadow + Loader { + active: Config.options.bar.showBackground && Config.options.bar.cornerStyle === 1 + anchors.fill: barBackground + sourceComponent: StyledRectangularShadow { + anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor + target: barBackground + } + } + // Background + Rectangle { + id: barBackground + anchors { + fill: parent + margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed + } + color: Config.options.bar.showBackground ? Appearance.colors.colLayer0 : "transparent" + radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0 + border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0 + border.color: Appearance.colors.colLayer0Border + } + + MouseArea { // Left side | scroll to change brightness + id: barLeftSideMouseArea + anchors.left: parent.left + implicitHeight: Appearance.sizes.baseBarHeight + height: Appearance.sizes.barHeight + width: (root.width - middleSection.width) / 2 + property bool hovered: false + property real lastScrollX: 0 + property real lastScrollY: 0 + property bool trackingScroll: false + acceptedButtons: Qt.LeftButton + hoverEnabled: true + propagateComposedEvents: true + onEntered: event => { + barLeftSideMouseArea.hovered = true; + } + onExited: event => { + barLeftSideMouseArea.hovered = false; + barLeftSideMouseArea.trackingScroll = false; + } + onPressed: event => { + if (event.button === Qt.LeftButton) { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } + } + // Scroll to change brightness + WheelHandler { + onWheel: event => { + if (event.angleDelta.y < 0) + root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness - 0.05); + else if (event.angleDelta.y > 0) + root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness + 0.05); + // Store the mouse position and start tracking + barLeftSideMouseArea.lastScrollX = event.x; + barLeftSideMouseArea.lastScrollY = event.y; + barLeftSideMouseArea.trackingScroll = true; + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + onPositionChanged: mouse => { + if (barLeftSideMouseArea.trackingScroll) { + const dx = mouse.x - barLeftSideMouseArea.lastScrollX; + const dy = mouse.y - barLeftSideMouseArea.lastScrollY; + if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { + GlobalStates.osdBrightnessOpen = false; + barLeftSideMouseArea.trackingScroll = false; + } + } + } + Item { + // Left section + anchors.fill: parent + implicitHeight: leftSectionRowLayout.implicitHeight + implicitWidth: leftSectionRowLayout.implicitWidth + + ScrollHint { + reveal: barLeftSideMouseArea.hovered + icon: "light_mode" + tooltipText: Translation.tr("Scroll to change brightness") + side: "left" + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + + RowLayout { // Content + id: leftSectionRowLayout + anchors.fill: parent + spacing: 10 + + RippleButton { + // Left sidebar button + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: Appearance.rounding.screenRounding + Layout.fillWidth: false + property real buttonPadding: 5 + implicitWidth: distroIcon.width + buttonPadding * 2 + implicitHeight: distroIcon.height + buttonPadding * 2 + + buttonRadius: Appearance.rounding.full + colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) + colBackgroundHover: Appearance.colors.colLayer1Hover + colRipple: Appearance.colors.colLayer1Active + colBackgroundToggled: Appearance.colors.colSecondaryContainer + colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover + colRippleToggled: Appearance.colors.colSecondaryContainerActive + toggled: GlobalStates.sidebarLeftOpen + property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0 + + onPressed: { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } + + CustomIcon { + id: distroIcon + anchors.centerIn: parent + width: 19.5 + height: 19.5 + source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic" + colorize: true + color: Appearance.colors.colOnLayer0 + } + } + + ActiveWindow { + visible: root.useShortenedForm === 0 + Layout.rightMargin: Appearance.rounding.screenRounding + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + RowLayout { // Middle section + id: middleSection + anchors.centerIn: parent + spacing: Config.options?.bar.borderless ? 4 : 8 + + BarGroup { + id: leftCenterGroup + Layout.preferredWidth: root.centerSideModuleWidth + Layout.fillHeight: true + + Resources { + alwaysShowAllResources: root.useShortenedForm === 2 + Layout.fillWidth: root.useShortenedForm === 2 + } + + Media { + visible: root.useShortenedForm < 2 + Layout.fillWidth: true + } + } + + VerticalBarSeparator { + visible: Config.options?.bar.borderless + } + + BarGroup { + id: middleCenterGroup + padding: workspacesWidget.widgetPadding + Layout.fillHeight: true + + Workspaces { + id: workspacesWidget + Layout.fillHeight: true + MouseArea { + // Right-click to toggle overview + anchors.fill: parent + acceptedButtons: Qt.RightButton + + onPressed: event => { + if (event.button === Qt.RightButton) { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } + } + } + } + + VerticalBarSeparator { + visible: Config.options?.bar.borderless + } + + MouseArea { + id: rightCenterGroup + implicitWidth: rightCenterGroupContent.implicitWidth + implicitHeight: rightCenterGroupContent.implicitHeight + Layout.preferredWidth: root.centerSideModuleWidth + Layout.fillHeight: true + + onPressed: { + GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; + } + + BarGroup { + id: rightCenterGroupContent + anchors.fill: parent + + ClockWidget { + showDate: (Config.options.bar.verbose && root.useShortenedForm < 2) + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + } + + UtilButtons { + visible: (Config.options.bar.verbose && root.useShortenedForm === 0) + Layout.alignment: Qt.AlignVCenter + } + + BatteryIndicator { + visible: (root.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery) + Layout.alignment: Qt.AlignVCenter + } + } + } + + VerticalBarSeparator { + visible: Config.options.bar.borderless && Config.options.bar.weather.enable + } + } + + MouseArea { // Right side | scroll to change volume + id: barRightSideMouseArea + + anchors.right: parent.right + implicitHeight: Appearance.sizes.baseBarHeight + height: Appearance.sizes.barHeight + width: (root.width - middleSection.width) / 2 + + property bool hovered: false + property real lastScrollX: 0 + property real lastScrollY: 0 + property bool trackingScroll: false + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + propagateComposedEvents: true + onEntered: event => { + barRightSideMouseArea.hovered = true; + } + onExited: event => { + barRightSideMouseArea.hovered = false; + barRightSideMouseArea.trackingScroll = false; + } + onPressed: event => { + if (event.button === Qt.LeftButton) { + GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; + } else if (event.button === Qt.RightButton) { + MprisController.activePlayer.next(); + } + } + // Scroll to change volume + WheelHandler { + onWheel: event => { + const currentVolume = Audio.value; + const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2; + if (event.angleDelta.y < 0) + Audio.sink.audio.volume -= step; + else if (event.angleDelta.y > 0) + Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step); + // Store the mouse position and start tracking + barRightSideMouseArea.lastScrollX = event.x; + barRightSideMouseArea.lastScrollY = event.y; + barRightSideMouseArea.trackingScroll = true; + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + onPositionChanged: mouse => { + if (barRightSideMouseArea.trackingScroll) { + const dx = mouse.x - barRightSideMouseArea.lastScrollX; + const dy = mouse.y - barRightSideMouseArea.lastScrollY; + if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { + GlobalStates.osdVolumeOpen = false; + barRightSideMouseArea.trackingScroll = false; + } + } + } + + Item { + anchors.fill: parent + implicitHeight: rightSectionRowLayout.implicitHeight + implicitWidth: rightSectionRowLayout.implicitWidth + + ScrollHint { + reveal: barRightSideMouseArea.hovered + icon: "volume_up" + tooltipText: Translation.tr("Scroll to change volume") + side: "right" + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + + RowLayout { + id: rightSectionRowLayout + anchors.fill: parent + spacing: 5 + layoutDirection: Qt.RightToLeft + + RippleButton { // Right sidebar button + id: rightSidebarButton + + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.rightMargin: Appearance.rounding.screenRounding + Layout.fillWidth: false + + implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2 + implicitHeight: indicatorsRowLayout.implicitHeight + 5 * 2 + + buttonRadius: Appearance.rounding.full + colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) + colBackgroundHover: Appearance.colors.colLayer1Hover + colRipple: Appearance.colors.colLayer1Active + colBackgroundToggled: Appearance.colors.colSecondaryContainer + colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover + colRippleToggled: Appearance.colors.colSecondaryContainerActive + toggled: GlobalStates.sidebarRightOpen + property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0 + + Behavior on colText { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + + onPressed: { + GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; + } + + RowLayout { + id: indicatorsRowLayout + anchors.centerIn: parent + property real realSpacing: 15 + spacing: 0 + + Revealer { + reveal: Audio.sink?.audio?.muted ?? false + Layout.fillHeight: true + Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0 + Behavior on Layout.rightMargin { + NumberAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + MaterialSymbol { + text: "volume_off" + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + } + Revealer { + reveal: Audio.source?.audio?.muted ?? false + Layout.fillHeight: true + Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0 + Behavior on Layout.rightMargin { + NumberAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + MaterialSymbol { + text: "mic_off" + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + } + Loader { + active: HyprlandXkb.layoutCodes.length > 1 + visible: active + Layout.rightMargin: indicatorsRowLayout.realSpacing + sourceComponent: StyledText { + text: HyprlandXkb.currentLayoutCode + font.pixelSize: Appearance.font.pixelSize.small + color: rightSidebarButton.colText + } + } + MaterialSymbol { + Layout.rightMargin: indicatorsRowLayout.realSpacing + text: Network.materialSymbol + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + MaterialSymbol { + text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled" + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + } + } + + SysTray { + visible: root.useShortenedForm === 0 + Layout.fillWidth: false + Layout.fillHeight: true + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + // Weather + Loader { + Layout.leftMargin: 8 + Layout.fillHeight: true + active: Config.options.bar.weather.enable + sourceComponent: BarGroup { + implicitHeight: Appearance.sizes.baseBarHeight + WeatherBar {} + } + } + } + } + } +} diff --git a/.config/quickshell/ii/modules/bar/SysTray.qml b/.config/quickshell/ii/modules/bar/SysTray.qml index 34919a3ce..923bc04c7 100644 --- a/.config/quickshell/ii/modules/bar/SysTray.qml +++ b/.config/quickshell/ii/modules/bar/SysTray.qml @@ -8,8 +8,6 @@ import Quickshell.Services.SystemTray Item { id: root - required property var bar - height: parent.height implicitWidth: rowLayout.implicitWidth Layout.leftMargin: Appearance.rounding.screenRounding @@ -25,8 +23,6 @@ Item { SysTrayItem { required property SystemTrayItem modelData - - bar: root.bar item: modelData } diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index 9696c49c4..bc3eaa539 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -10,7 +10,7 @@ import Qt5Compat.GraphicalEffects MouseArea { id: root - required property var bar + property var bar: root.QsWindow.window required property SystemTrayItem item property bool targetMenuOpen: false property int trayItemWidth: Appearance.font.pixelSize.larger diff --git a/.config/quickshell/ii/modules/bar/Workspaces.qml b/.config/quickshell/ii/modules/bar/Workspaces.qml index 8758fe52a..b07dc9d18 100644 --- a/.config/quickshell/ii/modules/bar/Workspaces.qml +++ b/.config/quickshell/ii/modules/bar/Workspaces.qml @@ -13,12 +13,12 @@ import Quickshell.Widgets import Qt5Compat.GraphicalEffects Item { - required property var bar + id: root property bool borderless: Config.options.bar.borderless - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) + readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) property list workspaceOccupied: [] property int widgetPadding: 4 property int workspaceButtonWidth: 26 @@ -26,7 +26,7 @@ Item { property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown + property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown // Function to update workspaceOccupied function updateWorkspaceOccupied() { @@ -87,8 +87,8 @@ Item { implicitWidth: workspaceButtonWidth implicitHeight: workspaceButtonWidth radius: Appearance.rounding.full - property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index)) - property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2)) + property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index)) + property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2)) property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full @@ -98,7 +98,7 @@ Item { bottomRightRadius: radiusRight color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0 + opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -188,7 +188,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) text: `${button.workspaceValue}` elide: Text.ElideRight - color: (monitor.activeWorkspace?.id == button.workspaceValue) ? + color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) @@ -208,7 +208,7 @@ Item { width: workspaceButtonWidth * 0.18 height: width radius: width / 2 - color: (monitor.activeWorkspace?.id == button.workspaceValue) ? + color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) diff --git a/.config/quickshell/ii/services/Brightness.qml b/.config/quickshell/ii/services/Brightness.qml index 927a10c06..74ef562b2 100644 --- a/.config/quickshell/ii/services/Brightness.qml +++ b/.config/quickshell/ii/services/Brightness.qml @@ -1,7 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound -// From https://github.com/caelestia-dots/shell/ (`quickshell` branch) with modifications. +// From https://github.com/caelestia-dots/shell with modifications. // License: GPLv3 import Quickshell diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 7380da583..43b8171cd 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -22,6 +22,7 @@ import "./modules/screenCorners/" import "./modules/session/" import "./modules/sidebarLeft/" import "./modules/sidebarRight/" + import QtQuick import QtQuick.Controls import QtQuick.Layouts From 839593b11ecd679d9000f8576c02768af6eb80f9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 17:31:58 +0700 Subject: [PATCH 4/9] add konsole konfig --- .config/konsolerc | 11 +++++++++++ .local/share/icons/quickshell.svg | 1 - .local/share/konsole/Profile 1.profile | 12 ++++++++++++ install.sh | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .config/konsolerc delete mode 120000 .local/share/icons/quickshell.svg create mode 100644 .local/share/konsole/Profile 1.profile diff --git a/.config/konsolerc b/.config/konsolerc new file mode 100644 index 000000000..31a3b7571 --- /dev/null +++ b/.config/konsolerc @@ -0,0 +1,11 @@ +[Desktop Entry] +DefaultProfile=Profile 1.profile + +[General] +ConfigVersion=1 + +[KonsoleWindow] +UseSingleInstance=true + +[UiSettings] +ColorScheme= diff --git a/.local/share/icons/quickshell.svg b/.local/share/icons/quickshell.svg deleted file mode 120000 index 8dc05d01c..000000000 --- a/.local/share/icons/quickshell.svg +++ /dev/null @@ -1 +0,0 @@ -illogical-impulse.svg \ No newline at end of file diff --git a/.local/share/konsole/Profile 1.profile b/.local/share/konsole/Profile 1.profile new file mode 100644 index 000000000..956403964 --- /dev/null +++ b/.local/share/konsole/Profile 1.profile @@ -0,0 +1,12 @@ +[Appearance] +ColorScheme=MaterialYou + +[General] +Command=/bin/fish +Environment=COLORTERM=truecolor +Name=Profile 1 +Parent=FALLBACK/ + +[Keyboard] +KeyBindings=default + diff --git a/install.sh b/install.sh index ae9891e98..b61fdac2f 100755 --- a/install.sh +++ b/install.sh @@ -233,6 +233,7 @@ esac # since the files here come from different places, not only about one program. # v rsync -av ".local/bin/" "$XDG_BIN_HOME" # No longer needed since scripts are no longer in ~/.local/bin v rsync -av ".local/share/icons/" "${XDG_DATA_HOME:-$HOME/.local/share}"/icons/ +v rsync -av ".local/share/konsole/" "${XDG_DATA_HOME:-$HOME/.local/share}"/konsole/ # Prevent hyprland from not fully loaded sleep 1 From 71d0ac4c5e042bff4db2b4e5f03f9d714043cd85 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 18:12:44 +0700 Subject: [PATCH 5/9] make circular progresses use shape instead of canvas --- .../common/widgets/CircularProgress.qml | 110 +++++++++--------- 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml b/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml index 7ff2724fb..6373ad9c2 100644 --- a/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml +++ b/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml @@ -2,6 +2,7 @@ // License: LGPL-3.0 - A copy can be found in `licenses` folder of repo import QtQuick +import QtQuick.Shapes import qs.modules.common /** @@ -15,7 +16,7 @@ Item { property real value: 0 property color primaryColor: Appearance.m3colors.m3onSecondaryContainer property color secondaryColor: Appearance.colors.colSecondaryContainer - property real gapAngle: Math.PI / 9 + property real gapAngle: 180 / 9 property bool fill: false property int fillOverflow: 2 property bool enableAnimation: true @@ -25,73 +26,66 @@ Item { width: size height: size - signal animationFinished(); + property real degree: value * 360 + property real centerX: root.width / 2 + property real centerY: root.height / 2 + property real arcRadius: root.size / 2 - root.lineWidth + property real startAngle: -90 + + Behavior on degree { + enabled: root.enableAnimation + NumberAnimation { + duration: root.animationDuration + easing.type: root.easingType + } - onValueChanged: { - canvas.degree = value * 360; - } - onPrimaryColorChanged: { - canvas.requestPaint(); - } - onSecondaryColorChanged: { - canvas.requestPaint(); } - Canvas { - id: canvas - - property real degree: 0 - + Loader { + active: root.fill anchors.fill: parent - antialiasing: true - - onDegreeChanged: { - requestPaint(); + + sourceComponent: Rectangle { + radius: 9999 + color: root.secondaryColor } + } - onPaint: { - var ctx = getContext("2d"); - var x = root.width / 2; - var y = root.height / 2; - var radius = root.size / 2 - root.lineWidth; - var startAngle = (Math.PI / 180) * 270; - var fullAngle = (Math.PI / 180) * (270 + 360); - var progressAngle = (Math.PI / 180) * (270 + degree); - var epsilon = 0.01; // Small angle in radians - - ctx.reset(); - if (root.fill) { - ctx.fillStyle = root.secondaryColor; - ctx.beginPath(); - ctx.arc(x, y, radius + fillOverflow, startAngle, fullAngle); - ctx.fill(); + Shape { + anchors.fill: parent + layer.enabled: true + layer.smooth: true + preferredRendererType: Shape.CurveRenderer + ShapePath { + id: secondaryPath + strokeColor: root.secondaryColor + strokeWidth: root.lineWidth + capStyle: ShapePath.RoundCap + fillColor: "transparent" + PathAngleArc { + centerX: root.centerX + centerY: root.centerY + radiusX: root.arcRadius + radiusY: root.arcRadius + startAngle: root.startAngle - root.gapAngle + sweepAngle: -(360 - root.degree - 2 * root.gapAngle) } - ctx.lineCap = 'round'; - ctx.lineWidth = root.lineWidth; - - // Secondary - ctx.beginPath(); - ctx.arc(x, y, radius, progressAngle + gapAngle, fullAngle - gapAngle); - ctx.strokeStyle = root.secondaryColor; - ctx.stroke(); - - // Primary (value indication) - var endAngle = progressAngle + (value > 0 ? 0 : epsilon); - ctx.beginPath(); - ctx.arc(x, y, radius, startAngle, endAngle); - ctx.strokeStyle = root.primaryColor; - ctx.stroke(); } - - Behavior on degree { - enabled: root.enableAnimation - NumberAnimation { - duration: root.animationDuration - easing.type: root.easingType + ShapePath { + id: primaryPath + strokeColor: root.primaryColor + strokeWidth: root.lineWidth + capStyle: ShapePath.RoundCap + fillColor: "transparent" + PathAngleArc { + centerX: root.centerX + centerY: root.centerY + radiusX: root.arcRadius + radiusY: root.arcRadius + startAngle: root.startAngle + sweepAngle: root.degree } - } - } } From 3eb7d8ab5846258888ea62a9cc24f9a0580c3d5d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 18:13:12 +0700 Subject: [PATCH 6/9] background: remove unecessary Scope --- .../ii/modules/background/Background.qml | 488 +++++++++--------- 1 file changed, 243 insertions(+), 245 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 1200ece5e..32bbe54a0 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -12,278 +12,276 @@ import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland -Scope { + +Variants { id: root readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition readonly property real fixedClockX: Config.options.background.clockX readonly property real fixedClockY: Config.options.background.clockY + model: Quickshell.screens - Variants { - model: Quickshell.screens + PanelWindow { + id: bgRoot - PanelWindow { - id: bgRoot + required property var modelData - required property var modelData + // Hide when fullscreen + readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + property bool focusingThisMonitor: HyprlandData.activeWorkspace.monitor == monitor.name + visible: !(activeWindow?.fullscreen && activeWindow?.activated && focusingThisMonitor) - // Hide when fullscreen - readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - property bool focusingThisMonitor: HyprlandData.activeWorkspace.monitor == monitor.name - visible: !(activeWindow?.fullscreen && activeWindow?.activated && focusingThisMonitor) + // Workspaces + property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) + property list relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id) + property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1 + property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10 + // Wallpaper + property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") + || Config.options.background.wallpaperPath.endsWith(".webm") + || Config.options.background.wallpaperPath.endsWith(".mkv") + || Config.options.background.wallpaperPath.endsWith(".avi") + || Config.options.background.wallpaperPath.endsWith(".mov") + property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath + property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom + property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated + property int wallpaperWidth: modelData.width // Some reasonable init value, to be updated + property int wallpaperHeight: modelData.height // Some reasonable init value, to be updated + property real movableXSpace: (Math.min(wallpaperWidth * effectiveWallpaperScale, screen.width * preferredWallpaperScale) - screen.width) / 2 + property real movableYSpace: (Math.min(wallpaperHeight * effectiveWallpaperScale, screen.height * preferredWallpaperScale) - screen.height) / 2 + // Position + property real clockX: (modelData.width / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.width) + property real clockY: (modelData.height / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.height) + property var textHorizontalAlignment: clockX < screen.width / 3 ? Text.AlignLeft : + (clockX > screen.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter) + // Colors + property color dominantColor: Appearance.colors.colPrimary + property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 + property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) - // Workspaces - property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) - property list relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id) - property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1 - property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10 - // Wallpaper - property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") - || Config.options.background.wallpaperPath.endsWith(".webm") - || Config.options.background.wallpaperPath.endsWith(".mkv") - || Config.options.background.wallpaperPath.endsWith(".avi") - || Config.options.background.wallpaperPath.endsWith(".mov") - property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath - property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom - property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated - property int wallpaperWidth: modelData.width // Some reasonable init value, to be updated - property int wallpaperHeight: modelData.height // Some reasonable init value, to be updated - property real movableXSpace: (Math.min(wallpaperWidth * effectiveWallpaperScale, screen.width * preferredWallpaperScale) - screen.width) / 2 - property real movableYSpace: (Math.min(wallpaperHeight * effectiveWallpaperScale, screen.height * preferredWallpaperScale) - screen.height) / 2 - // Position - property real clockX: (modelData.width / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.width) - property real clockY: (modelData.height / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.height) - property var textHorizontalAlignment: clockX < screen.width / 3 ? Text.AlignLeft : - (clockX > screen.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter) - // Colors - property color dominantColor: Appearance.colors.colPrimary - property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 - property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + // Layer props + screen: modelData + exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: GlobalStates.screenLocked ? WlrLayer.Top : WlrLayer.Bottom + // WlrLayershell.layer: WlrLayer.Bottom + WlrLayershell.namespace: "quickshell:background" + anchors { + top: true + bottom: true + left: true + right: true + } + color: "transparent" - // Layer props - screen: modelData - exclusionMode: ExclusionMode.Ignore - WlrLayershell.layer: GlobalStates.screenLocked ? WlrLayer.Top : WlrLayer.Bottom - // WlrLayershell.layer: WlrLayer.Bottom - WlrLayershell.namespace: "quickshell:background" + onWallpaperPathChanged: { + bgRoot.updateZoomScale() + // Clock position gets updated after zoom scale is updated + } + + // Wallpaper zoom scale + function updateZoomScale() { + getWallpaperSizeProc.path = bgRoot.wallpaperPath + getWallpaperSizeProc.running = true; + } + Process { + id: getWallpaperSizeProc + property string path: bgRoot.wallpaperPath + command: [ "magick", "identify", "-format", "%w %h", path ] + stdout: StdioCollector { + id: wallpaperSizeOutputCollector + onStreamFinished: { + const output = wallpaperSizeOutputCollector.text + const [width, height] = output.split(" ").map(Number); + bgRoot.wallpaperWidth = width + bgRoot.wallpaperHeight = height + bgRoot.effectiveWallpaperScale = Math.max(1, Math.min( + bgRoot.preferredWallpaperScale, + width / bgRoot.screen.width, + height / bgRoot.screen.height + )); + + bgRoot.updateClockPosition() + } + } + } + + // Clock positioning + function updateClockPosition() { + // Somehow all this manual setting is needed to make the proc correctly use the new values + leastBusyRegionProc.path = bgRoot.wallpaperPath + leastBusyRegionProc.contentWidth = clock.implicitWidth + leastBusyRegionProc.contentHeight = clock.implicitHeight + leastBusyRegionProc.horizontalPadding = (effectiveWallpaperScale - 1) / 2 * screen.width + 100 + leastBusyRegionProc.verticalPadding = (effectiveWallpaperScale - 1) / 2 * screen.height + 100 + leastBusyRegionProc.running = false; + leastBusyRegionProc.running = true; + } + Process { + id: leastBusyRegionProc + property string path: bgRoot.wallpaperPath + property int contentWidth: 300 + property int contentHeight: 300 + property int horizontalPadding: bgRoot.movableXSpace + property int verticalPadding: bgRoot.movableYSpace + command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), + "--screen-width", bgRoot.screen.width, + "--screen-height", bgRoot.screen.height, + "--width", contentWidth, + "--height", contentHeight, + "--horizontal-padding", horizontalPadding, + "--vertical-padding", verticalPadding, + path + ] + stdout: StdioCollector { + id: leastBusyRegionOutputCollector + onStreamFinished: { + const output = leastBusyRegionOutputCollector.text + // console.log("[Background] Least busy region output:", output) + if (output.length === 0) return; + const parsedContent = JSON.parse(output) + bgRoot.clockX = parsedContent.center_x + bgRoot.clockY = parsedContent.center_y + bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary + } + } + } + + // Wallpaper + Image { + id: wallpaper + visible: !bgRoot.wallpaperIsVideo + property real value // 0 to 1, for offset + value: { + // Range = groups that workspaces span on + const chunkSize = Config?.options.bar.workspaces.shown ?? 10; + const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize; + const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize; + const range = upper - lower; + return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace.id - lower) / range) : 0.5) + + (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar) + - (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar) + } + property real effectiveValue: Math.max(0, Math.min(1, value)) + x: -(bgRoot.movableXSpace) - (effectiveValue - 0.5) * 2 * bgRoot.movableXSpace + y: -(bgRoot.movableYSpace) + source: bgRoot.wallpaperPath + fillMode: Image.PreserveAspectCrop + Behavior on x { + NumberAnimation { + duration: 600 + easing.type: Easing.OutCubic + } + } + sourceSize { + width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale + height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale + } + } + + // The clock + Item { + id: clock anchors { - top: true - bottom: true - left: true - right: true - } - color: "transparent" - - onWallpaperPathChanged: { - bgRoot.updateZoomScale() - // Clock position gets updated after zoom scale is updated + left: wallpaper.left + top: wallpaper.top + leftMargin: ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - (wallpaper.effectiveValue * bgRoot.movableXSpace) + topMargin: ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) + Behavior on leftMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on topMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } } - // Wallpaper zoom scale - function updateZoomScale() { - getWallpaperSizeProc.path = bgRoot.wallpaperPath - getWallpaperSizeProc.running = true; - } - Process { - id: getWallpaperSizeProc - property string path: bgRoot.wallpaperPath - command: [ "magick", "identify", "-format", "%w %h", path ] - stdout: StdioCollector { - id: wallpaperSizeOutputCollector - onStreamFinished: { - const output = wallpaperSizeOutputCollector.text - const [width, height] = output.split(" ").map(Number); - bgRoot.wallpaperWidth = width - bgRoot.wallpaperHeight = height - bgRoot.effectiveWallpaperScale = Math.max(1, Math.min( - bgRoot.preferredWallpaperScale, - width / bgRoot.screen.width, - height / bgRoot.screen.height - )); + implicitWidth: clockColumn.implicitWidth + implicitHeight: clockColumn.implicitHeight - bgRoot.updateClockPosition() + ColumnLayout { + id: clockColumn + anchors.centerIn: parent + spacing: 0 + + StyledText { + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 90 + weight: Font.Bold } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: DateTime.time } - } - - // Clock positioning - function updateClockPosition() { - // Somehow all this manual setting is needed to make the proc correctly use the new values - leastBusyRegionProc.path = bgRoot.wallpaperPath - leastBusyRegionProc.contentWidth = clock.implicitWidth - leastBusyRegionProc.contentHeight = clock.implicitHeight - leastBusyRegionProc.horizontalPadding = (effectiveWallpaperScale - 1) / 2 * screen.width + 100 - leastBusyRegionProc.verticalPadding = (effectiveWallpaperScale - 1) / 2 * screen.height + 100 - leastBusyRegionProc.running = false; - leastBusyRegionProc.running = true; - } - Process { - id: leastBusyRegionProc - property string path: bgRoot.wallpaperPath - property int contentWidth: 300 - property int contentHeight: 300 - property int horizontalPadding: bgRoot.movableXSpace - property int verticalPadding: bgRoot.movableYSpace - command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), - "--screen-width", bgRoot.screen.width, - "--screen-height", bgRoot.screen.height, - "--width", contentWidth, - "--height", contentHeight, - "--horizontal-padding", horizontalPadding, - "--vertical-padding", verticalPadding, - path - ] - stdout: StdioCollector { - id: leastBusyRegionOutputCollector - onStreamFinished: { - const output = leastBusyRegionOutputCollector.text - // console.log("[Background] Least busy region output:", output) - if (output.length === 0) return; - const parsedContent = JSON.parse(output) - bgRoot.clockX = parsedContent.center_x - bgRoot.clockY = parsedContent.center_y - bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary + StyledText { + Layout.fillWidth: true + Layout.topMargin: -5 + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: Font.DemiBold } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: DateTime.date } } - // Wallpaper - Image { - id: wallpaper - visible: !bgRoot.wallpaperIsVideo - property real value // 0 to 1, for offset - value: { - // Range = groups that workspaces span on - const chunkSize = Config?.options.bar.workspaces.shown ?? 10; - const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize; - const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize; - const range = upper - lower; - return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace.id - lower) / range) : 0.5) - + (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar) - - (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar) - } - property real effectiveValue: Math.max(0, Math.min(1, value)) - x: -(bgRoot.movableXSpace) - (effectiveValue - 0.5) * 2 * bgRoot.movableXSpace - y: -(bgRoot.movableYSpace) - source: bgRoot.wallpaperPath - fillMode: Image.PreserveAspectCrop - Behavior on x { - NumberAnimation { - duration: 600 - easing.type: Easing.OutCubic - } - } - sourceSize { - width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale - height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale - } - } - - // The clock - Item { - id: clock + RowLayout { anchors { - left: wallpaper.left - top: wallpaper.top - leftMargin: ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - (wallpaper.effectiveValue * bgRoot.movableXSpace) - topMargin: ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) - Behavior on leftMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - Behavior on topMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } + top: clockColumn.bottom + left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined + right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined + horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined + topMargin: 5 + leftMargin: -5 + rightMargin: -5 } - - implicitWidth: clockColumn.implicitWidth - implicitHeight: clockColumn.implicitHeight - - ColumnLayout { - id: clockColumn - anchors.centerIn: parent - spacing: 0 - - StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 90 - weight: Font.Bold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: DateTime.time - } - StyledText { - Layout.fillWidth: true - Layout.topMargin: -5 - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: DateTime.date - } - } - - RowLayout { - anchors { - top: clockColumn.bottom - left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined - right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined - horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined - topMargin: 5 - leftMargin: -5 - rightMargin: -5 - } - opacity: GlobalStates.screenLocked ? 1 : 0 - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } - MaterialSymbol { - text: "lock" - Layout.fillWidth: false - iconSize: Appearance.font.pixelSize.huge - color: bgRoot.colText - } - StyledText { - Layout.fillWidth: false - text: "Locked" - color: bgRoot.colText - font { - pixelSize: Appearance.font.pixelSize.larger - } - } - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } - - } - } - - // Password prompt - StyledText { - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - bottomMargin: 30 - } - opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0 - scale: opacity + opacity: GlobalStates.screenLocked ? 1 : 0 visible: opacity > 0 Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } - text: "Enter password" - color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3) - font { - pixelSize: Appearance.font.pixelSize.normal + Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } + MaterialSymbol { + text: "lock" + Layout.fillWidth: false + iconSize: Appearance.font.pixelSize.huge + color: bgRoot.colText } + StyledText { + Layout.fillWidth: false + text: "Locked" + color: bgRoot.colText + font { + pixelSize: Appearance.font.pixelSize.larger + } + } + Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } + + } + } + + // Password prompt + StyledText { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: 30 + } + opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0 + scale: opacity + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + text: "Enter password" + color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3) + font { + pixelSize: Appearance.font.pixelSize.normal } } } From 87f7bc28a3ef2a9f5139f7dd61b9a2707027ae19 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 18:17:01 +0700 Subject: [PATCH 7/9] chores: remove unnecessary import, suppress init null warnings --- .config/quickshell/ii/modules/background/Background.qml | 4 ++-- .config/quickshell/ii/shell.qml | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 32bbe54a0..f960adf8c 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -27,7 +27,7 @@ Variants { // Hide when fullscreen readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - property bool focusingThisMonitor: HyprlandData.activeWorkspace.monitor == monitor.name + property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor.name visible: !(activeWindow?.fullscreen && activeWindow?.activated && focusingThisMonitor) // Workspaces @@ -156,7 +156,7 @@ Variants { const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize; const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize; const range = upper - lower; - return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace.id - lower) / range) : 0.5) + return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace?.id - lower) / range) : 0.5) + (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar) - (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar) } diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 43b8171cd..e46d7a439 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -24,8 +24,6 @@ import "./modules/sidebarLeft/" import "./modules/sidebarRight/" import QtQuick -import QtQuick.Controls -import QtQuick.Layouts import QtQuick.Window import Quickshell import "./services/" From 0708070764cf0a15642b646ae0d21cae0c5fedab Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:54:01 +0700 Subject: [PATCH 8/9] circular progress: use implicitSize instead of size note: the credit is removed because the widget has been rewritten to use Shape instead of Canvas --- .../ii/modules/bar/BatteryIndicator.qml | 6 ++--- .config/quickshell/ii/modules/bar/Media.qml | 6 ++--- .../quickshell/ii/modules/bar/Resource.qml | 6 ++--- .../common/widgets/CircularProgress.qml | 25 ++++++++----------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/BatteryIndicator.qml b/.config/quickshell/ii/modules/bar/BatteryIndicator.qml index 72cc932bf..fa5d5daf4 100644 --- a/.config/quickshell/ii/modules/bar/BatteryIndicator.qml +++ b/.config/quickshell/ii/modules/bar/BatteryIndicator.qml @@ -43,9 +43,9 @@ Item { Layout.alignment: Qt.AlignVCenter lineWidth: 2 value: percentage - size: 26 - secondaryColor: (isLow && !isCharging) ? batteryLowBackground : Appearance.colors.colSecondaryContainer - primaryColor: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer + implicitSize: 26 + colSecondary: (isLow && !isCharging) ? batteryLowBackground : Appearance.colors.colSecondaryContainer + colPrimary: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer fill: (isLow && !isCharging) MaterialSymbol { diff --git a/.config/quickshell/ii/modules/bar/Media.qml b/.config/quickshell/ii/modules/bar/Media.qml index f3e70a803..2ce02aee6 100644 --- a/.config/quickshell/ii/modules/bar/Media.qml +++ b/.config/quickshell/ii/modules/bar/Media.qml @@ -53,9 +53,9 @@ Item { Layout.leftMargin: rowLayout.spacing lineWidth: 2 value: activePlayer?.position / activePlayer?.length - size: 26 - secondaryColor: Appearance.colors.colSecondaryContainer - primaryColor: Appearance.m3colors.m3onSecondaryContainer + implicitSize: 26 + colSecondary: Appearance.colors.colSecondaryContainer + colPrimary: Appearance.m3colors.m3onSecondaryContainer enableAnimation: false MaterialSymbol { diff --git a/.config/quickshell/ii/modules/bar/Resource.qml b/.config/quickshell/ii/modules/bar/Resource.qml index eb3683d83..12c799111 100644 --- a/.config/quickshell/ii/modules/bar/Resource.qml +++ b/.config/quickshell/ii/modules/bar/Resource.qml @@ -21,9 +21,9 @@ Item { Layout.alignment: Qt.AlignVCenter lineWidth: 2 value: percentage - size: 26 - secondaryColor: Appearance.colors.colSecondaryContainer - primaryColor: Appearance.m3colors.m3onSecondaryContainer + implicitSize: 26 + colSecondary: Appearance.colors.colSecondaryContainer + colPrimary: Appearance.m3colors.m3onSecondaryContainer enableAnimation: false MaterialSymbol { diff --git a/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml b/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml index 6373ad9c2..e1b2effaf 100644 --- a/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml +++ b/.config/quickshell/ii/modules/common/widgets/CircularProgress.qml @@ -1,6 +1,3 @@ -// From https://github.com/rafzby/circular-progressbar with modifications -// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo - import QtQuick import QtQuick.Shapes import qs.modules.common @@ -11,25 +8,25 @@ import qs.modules.common Item { id: root - property int size: 30 + property int implicitSize: 30 property int lineWidth: 2 property real value: 0 - property color primaryColor: Appearance.m3colors.m3onSecondaryContainer - property color secondaryColor: Appearance.colors.colSecondaryContainer - property real gapAngle: 180 / 9 + property color colPrimary: Appearance.m3colors.m3onSecondaryContainer + property color colSecondary: Appearance.colors.colSecondaryContainer + property real gapAngle: 360 / 18 property bool fill: false property int fillOverflow: 2 property bool enableAnimation: true - property int animationDuration: 1000 + property int animationDuration: 800 property var easingType: Easing.OutCubic - width: size - height: size + implicitWidth: implicitSize + implicitHeight: implicitSize property real degree: value * 360 property real centerX: root.width / 2 property real centerY: root.height / 2 - property real arcRadius: root.size / 2 - root.lineWidth + property real arcRadius: root.implicitSize / 2 - root.lineWidth property real startAngle: -90 Behavior on degree { @@ -47,7 +44,7 @@ Item { sourceComponent: Rectangle { radius: 9999 - color: root.secondaryColor + color: root.colSecondary } } @@ -58,7 +55,7 @@ Item { preferredRendererType: Shape.CurveRenderer ShapePath { id: secondaryPath - strokeColor: root.secondaryColor + strokeColor: root.colSecondary strokeWidth: root.lineWidth capStyle: ShapePath.RoundCap fillColor: "transparent" @@ -73,7 +70,7 @@ Item { } ShapePath { id: primaryPath - strokeColor: root.primaryColor + strokeColor: root.colPrimary strokeWidth: root.lineWidth capStyle: ShapePath.RoundCap fillColor: "transparent" From f8d162d9952371b05e0b09572aab03cf3dee1804 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 3 Aug 2025 20:40:52 +0700 Subject: [PATCH 9/9] RoundCorner: rewrite to use Shape instead of Canvas --- .config/quickshell/ii/modules/bar/Bar.qml | 4 +- .../ii/modules/common/widgets/RoundCorner.qml | 87 ++++++++++--------- .../modules/screenCorners/ScreenCorners.qml | 2 +- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index 6d0bc754d..c08133ad6 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -111,7 +111,7 @@ Scope { left: parent.left } - size: Appearance.rounding.screenRounding + implicitSize: Appearance.rounding.screenRounding color: showBarBackground ? Appearance.colors.colLayer0 : "transparent" corner: RoundCorner.CornerEnum.TopLeft @@ -130,7 +130,7 @@ Scope { top: !Config.options.bar.bottom ? parent.top : undefined bottom: Config.options.bar.bottom ? parent.bottom : undefined } - size: Appearance.rounding.screenRounding + implicitSize: Appearance.rounding.screenRounding color: showBarBackground ? Appearance.colors.colLayer0 : "transparent" corner: RoundCorner.CornerEnum.TopRight diff --git a/.config/quickshell/ii/modules/common/widgets/RoundCorner.qml b/.config/quickshell/ii/modules/common/widgets/RoundCorner.qml index 6fba4b92d..9cda87ca5 100644 --- a/.config/quickshell/ii/modules/common/widgets/RoundCorner.qml +++ b/.config/quickshell/ii/modules/common/widgets/RoundCorner.qml @@ -1,4 +1,5 @@ -import QtQuick 2.9 +import QtQuick +import QtQuick.Shapes Item { id: root @@ -6,55 +7,57 @@ Item { enum CornerEnum { TopLeft, TopRight, BottomLeft, BottomRight } property var corner: RoundCorner.CornerEnum.TopLeft // Default to TopLeft - property int size: 25 + property int implicitSize: 25 property color color: "#000000" - onColorChanged: { - canvas.requestPaint(); - } - onCornerChanged: { - canvas.requestPaint(); - } - - implicitWidth: size - implicitHeight: size - - Canvas { - id: canvas + implicitWidth: implicitSize + implicitHeight: implicitSize + Shape { anchors.fill: parent - antialiasing: true - - onPaint: { - var ctx = getContext("2d"); - var r = root.size; - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.beginPath(); - switch (root.corner) { - case RoundCorner.CornerEnum.TopLeft: - ctx.arc(r, r, r, Math.PI, 3 * Math.PI / 2); - ctx.lineTo(0, 0); - break; - case RoundCorner.CornerEnum.TopRight: - ctx.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI); - ctx.lineTo(r, 0); - break; - case RoundCorner.CornerEnum.BottomLeft: - ctx.arc(r, 0, r, Math.PI / 2, Math.PI); - ctx.lineTo(0, r); - break; - case RoundCorner.CornerEnum.BottomRight: - ctx.arc(0, 0, r, 0, Math.PI / 2); - ctx.lineTo(r, r); - break; + layer.enabled: true + layer.smooth: true + preferredRendererType: Shape.CurveRenderer + + ShapePath { + id: shapePath + strokeWidth: 0 + + fillColor: root.color + startX: switch (root.corner) { + case RoundCorner.CornerEnum.TopLeft: return 0; + case RoundCorner.CornerEnum.TopRight: return root.implicitSize; + case RoundCorner.CornerEnum.BottomLeft: return 0; + case RoundCorner.CornerEnum.BottomRight: return root.implicitSize; + } + startY: switch (root.corner) { + case RoundCorner.CornerEnum.TopLeft: return 0; + case RoundCorner.CornerEnum.TopRight: return 0; + case RoundCorner.CornerEnum.BottomLeft: return root.implicitSize; + case RoundCorner.CornerEnum.BottomRight: return root.implicitSize; + } + PathAngleArc { + moveToStart: false + centerX: root.implicitSize - shapePath.startX + centerY: root.implicitSize - shapePath.startY + radiusX: root.implicitSize + radiusY: root.implicitSize + startAngle: switch (root.corner) { + case RoundCorner.CornerEnum.TopLeft: return 180; + case RoundCorner.CornerEnum.TopRight: return -90; + case RoundCorner.CornerEnum.BottomLeft: return 90; + case RoundCorner.CornerEnum.BottomRight: return 0; + } + sweepAngle: 90 + } + PathLine { + x: shapePath.startX + y: shapePath.startY } - ctx.closePath(); - ctx.fillStyle = root.color; - ctx.fill(); } } - Behavior on size { + Behavior on implicitSize { animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) } diff --git a/.config/quickshell/ii/modules/screenCorners/ScreenCorners.qml b/.config/quickshell/ii/modules/screenCorners/ScreenCorners.qml index 5bfed5cde..99dab8292 100644 --- a/.config/quickshell/ii/modules/screenCorners/ScreenCorners.qml +++ b/.config/quickshell/ii/modules/screenCorners/ScreenCorners.qml @@ -35,7 +35,7 @@ Scope { implicitHeight: cornerWidget.implicitHeight RoundCorner { id: cornerWidget - size: Appearance.rounding.screenRounding + implicitSize: Appearance.rounding.screenRounding corner: cornerPanelWindow.corner } }