From 9fc0d26eb5010c15d22623cde59a85e83d1a2cbf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 15 Aug 2025 22:17:27 +0700 Subject: [PATCH] vertical bar --- .config/quickshell/ii/modules/bar/SysTray.qml | 21 +- .../quickshell/ii/modules/bar/SysTrayItem.qml | 12 +- .../ii/modules/common/Appearance.qml | 24 +- .../quickshell/ii/modules/common/Config.qml | 1 + .../common/widgets/ClippedProgressBar.qml | 28 +- .../ii/modules/common/widgets/StyledPopup.qml | 29 +- .../ii/modules/settings/InterfaceConfig.qml | 67 +++- .../modules/verticalBar/BatteryIndicator.qml | 66 ++++ .../ii/modules/verticalBar/Resource.qml | 43 ++ .../ii/modules/verticalBar/Resources.qml | 41 ++ .../ii/modules/verticalBar/VerticalBar.qml | 248 ++++++++++++ .../verticalBar/VerticalBarContent.qml | 369 ++++++++++++++++++ .../modules/verticalBar/VerticalBarGroup.qml | 37 ++ .../verticalBar/VerticalClockWidget.qml | 43 ++ .../ii/modules/verticalBar/Workspaces.qml | 313 +++++++++++++++ .config/quickshell/ii/shell.qml | 5 +- .config/quickshell/ii/welcome.qml | 67 ++-- 17 files changed, 1334 insertions(+), 80 deletions(-) create mode 100644 .config/quickshell/ii/modules/verticalBar/BatteryIndicator.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/Resource.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/Resources.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/VerticalBar.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/VerticalBarGroup.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml create mode 100644 .config/quickshell/ii/modules/verticalBar/Workspaces.qml diff --git a/.config/quickshell/ii/modules/bar/SysTray.qml b/.config/quickshell/ii/modules/bar/SysTray.qml index 923bc04c..e315ff56 100644 --- a/.config/quickshell/ii/modules/bar/SysTray.qml +++ b/.config/quickshell/ii/modules/bar/SysTray.qml @@ -4,19 +4,18 @@ import QtQuick import QtQuick.Layouts import Quickshell.Services.SystemTray -// TODO: More fancy animation Item { id: root + implicitWidth: gridLayout.implicitWidth + implicitHeight: gridLayout.implicitHeight + property bool vertical: false - height: parent.height - implicitWidth: rowLayout.implicitWidth - Layout.leftMargin: Appearance.rounding.screenRounding - - RowLayout { - id: rowLayout - + GridLayout { + id: gridLayout + columns: root.vertical ? 1 : -1 anchors.fill: parent - spacing: 15 + rowSpacing: 10 + columnSpacing: 15 Repeater { model: SystemTray.items @@ -24,12 +23,14 @@ Item { SysTrayItem { required property SystemTrayItem modelData item: modelData + Layout.fillHeight: !root.vertical + Layout.fillWidth: root.vertical } } StyledText { - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter font.pixelSize: Appearance.font.pixelSize.larger color: Appearance.colors.colSubtext text: "•" diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index bc3eaa53..d95a74ae 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -13,11 +13,10 @@ MouseArea { property var bar: root.QsWindow.window required property SystemTrayItem item property bool targetMenuOpen: false - property int trayItemWidth: Appearance.font.pixelSize.larger acceptedButtons: Qt.LeftButton | Qt.RightButton - Layout.fillHeight: true - implicitWidth: trayItemWidth + implicitWidth: 20 + implicitHeight: 20 onClicked: (event) => { switch (event.button) { case Qt.LeftButton: @@ -35,10 +34,11 @@ MouseArea { menu: root.item.menu anchor.window: bar - anchor.rect.x: root.x + bar.width - anchor.rect.y: root.y + anchor.rect.x: root.x + (Config.options.bar.vertical ? 0 : bar.width) + anchor.rect.y: root.y + (Config.options.bar.vertical ? bar.height : 0) anchor.rect.height: root.height - anchor.edges: Edges.Bottom + anchor.rect.width: root.width + anchor.edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) } IconImage { diff --git a/.config/quickshell/ii/modules/common/Appearance.qml b/.config/quickshell/ii/modules/common/Appearance.qml index 938d54c4..1e5ca689 100644 --- a/.config/quickshell/ii/modules/common/Appearance.qml +++ b/.config/quickshell/ii/modules/common/Appearance.qml @@ -329,19 +329,23 @@ Singleton { property real barCenterSideModuleWidthHellaShortened: 190 property real barShortenScreenWidthThreshold: 1200 // Shorten if screen width is at most this value property real barHellaShortenScreenWidthThreshold: 1000 // Shorten even more... - property real sidebarWidth: 460 - property real sidebarWidthExtended: 750 - property real osdWidth: 200 - property real mediaControlsWidth: 440 - property real mediaControlsHeight: 160 - property real notificationPopupWidth: 410 - property real searchWidthCollapsed: 260 - property real searchWidth: 450 - property real hyprlandGapsOut: 5 property real elevationMargin: 10 property real fabShadowRadius: 5 property real fabHoveredShadowRadius: 7 + property real hyprlandGapsOut: 5 + property real mediaControlsWidth: 440 + property real mediaControlsHeight: 160 + property real notificationPopupWidth: 410 + property real osdWidth: 200 + property real searchWidthCollapsed: 260 + property real searchWidth: 450 + property real sidebarWidth: 460 + property real sidebarWidthExtended: 750 + property real baseVerticalBarWidth: 46 + property real verticalBarWidth: Config.options.bar.cornerStyle === 1 ? + (baseVerticalBarWidth + root.sizes.hyprlandGapsOut * 2) : baseVerticalBarWidth + property real verticalBarGroupWidth: 38 } - syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light" + syntaxHighlightingTheme: root.m3colors.darkmode ? "Monokai" : "ayu Light" } diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 653a8d91..abcaaf80 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -143,6 +143,7 @@ Singleton { property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons property bool showBackground: true property bool verbose: true + property bool vertical: false property JsonObject resources: JsonObject { property bool alwaysShowSwap: true property bool alwaysShowCpu: false diff --git a/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml b/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml index 66c14bb6..b47b49ba 100644 --- a/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml +++ b/.config/quickshell/ii/modules/common/widgets/ClippedProgressBar.qml @@ -10,6 +10,7 @@ import Qt5Compat.GraphicalEffects */ ProgressBar { id: root + property bool vertical: false property real valueBarWidth: 30 property real valueBarHeight: 18 property color highlightColor: Appearance?.colors.colOnSecondaryContainer ?? "#685496" @@ -45,13 +46,36 @@ ProgressBar { visible: false Rectangle { + id: progressFill anchors { - left: parent.left top: parent.top bottom: parent.bottom + left: parent.left + right: undefined } - radius: Appearance.rounding.unsharpen width: parent.width * root.visualPosition + height: parent.height + + states: State { + name: "vertical" + when: root.vertical + AnchorChanges { + target: progressFill + anchors { + top: undefined + bottom: parent.bottom + left: parent.left + right: parent.right + } + } + PropertyChanges { + target: progressFill + width: parent.width + height: parent.height * root.visualPosition + } + } + + radius: Appearance.rounding.unsharpen color: root.highlightColor } } diff --git a/.config/quickshell/ii/modules/common/widgets/StyledPopup.qml b/.config/quickshell/ii/modules/common/widgets/StyledPopup.qml index 4958a1c6..bc399aa3 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledPopup.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledPopup.qml @@ -17,9 +17,10 @@ LazyLoader { id: popupWindow color: "transparent" - anchors.left: true - anchors.top: !Config.options.bar.bottom - anchors.bottom: Config.options.bar.bottom + anchors.left: !Config.options.bar.vertical || (Config.options.bar.vertical && !Config.options.bar.bottom) + anchors.right: Config.options.bar.vertical && Config.options.bar.bottom + anchors.top: Config.options.bar.vertical || (!Config.options.bar.vertical && !Config.options.bar.bottom) + anchors.bottom: !Config.options.bar.vertical && Config.options.bar.bottom implicitWidth: popupBackground.implicitWidth + Appearance.sizes.hyprlandGapsOut * 2 implicitHeight: popupBackground.implicitHeight + Appearance.sizes.hyprlandGapsOut * 2 @@ -27,12 +28,22 @@ LazyLoader { exclusionMode: ExclusionMode.Ignore exclusiveZone: 0 margins { - left: root.QsWindow?.mapFromItem( - root.hoverTarget, - (root.hoverTarget.width - popupBackground.implicitWidth) / 2, 0 - ).x - top: Config?.options.bar.bottom ? 0 : Appearance.sizes.barHeight - bottom: Config?.options.bar.bottom ? Appearance.sizes.barHeight : 0 + left: { + if (!Config.options.bar.vertical) return root.QsWindow?.mapFromItem( + root.hoverTarget, + (root.hoverTarget.width - popupBackground.implicitWidth) / 2, 0 + ).x; + return Appearance.sizes.barHeight + } + top: { + if (!Config.options.bar.vertical) return Appearance.sizes.barHeight; + return root.QsWindow?.mapFromItem( + root.hoverTarget, + (root.hoverTarget.height - popupBackground.implicitHeight) / 2, 0 + ).y; + } + right: Appearance.sizes.barHeight + bottom: Appearance.sizes.barHeight } WlrLayershell.namespace: "quickshell:popup" WlrLayershell.layer: WlrLayer.Overlay diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 88cbc623..eb5eaf29 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -72,26 +72,53 @@ ContentPage { ContentSection { title: Translation.tr("Bar") - ConfigSelectionArray { - currentValue: Config.options.bar.cornerStyle - configOptionName: "bar.cornerStyle" - onSelected: newValue => { - Config.options.bar.cornerStyle = newValue; - } - options: [ - { - displayName: Translation.tr("Hug"), - value: 0 - }, - { - displayName: Translation.tr("Float"), - value: 1 - }, - { - displayName: Translation.tr("Plain rectangle"), - value: 2 + ConfigRow { + ContentSubsection { + title: "Corner style" + + ConfigSelectionArray { + currentValue: Config.options.bar.cornerStyle + configOptionName: "bar.cornerStyle" + onSelected: newValue => { + Config.options.bar.cornerStyle = newValue; // Update local copy + } + options: [ + { + displayName: Translation.tr("Hug"), + value: 0 + }, + { + displayName: Translation.tr("Float"), + value: 1 + }, + { + displayName: Translation.tr("Plain rectangle"), + value: 2 + } + ] } - ] + } + + ContentSubsection { + title: "Bar layout" + ConfigSelectionArray { + currentValue: Config.options.bar.vertical + configOptionName: "bar.vertical" + onSelected: newValue => { + Config.options.bar.vertical = newValue; + } + options: [ + { + displayName: Translation.tr("Horizontal"), + value: false + }, + { + displayName: Translation.tr("Vertical"), + value: true + }, + ] + } + } } ContentSubsection { @@ -106,7 +133,7 @@ ContentPage { } } ConfigSwitch { - text: Translation.tr("Place at the bottom") + text: Translation.tr("Place at the bottom/right") checked: Config.options.bar.bottom onCheckedChanged: { Config.options.bar.bottom = checked; diff --git a/.config/quickshell/ii/modules/verticalBar/BatteryIndicator.qml b/.config/quickshell/ii/modules/verticalBar/BatteryIndicator.qml new file mode 100644 index 00000000..470214f4 --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/BatteryIndicator.qml @@ -0,0 +1,66 @@ +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts +import "./../bar" as Bar + +MouseArea { + id: root + property bool borderless: Config.options.bar.borderless + readonly property var chargeState: Battery.chargeState + readonly property bool isCharging: Battery.isCharging + readonly property bool isPluggedIn: Battery.isPluggedIn + readonly property real percentage: Battery.percentage + readonly property bool isLow: percentage <= Config.options.battery.low / 100 + + implicitHeight: batteryProgress.implicitHeight + implicitWidth: Appearance.sizes.verticalBarGroupWidth + + hoverEnabled: true + + ClippedProgressBar { + id: batteryProgress + anchors.centerIn: parent + vertical: true + valueBarWidth: 21 + valueBarHeight: 40 + value: percentage + highlightColor: (isLow && !isCharging) ? Appearance.m3colors.m3error : Appearance.colors.colOnSecondaryContainer + + font { + pixelSize: text.length > 2 ? 11 : 13 + weight: text.length > 2 ? Font.Medium : Font.DemiBold + } + + textMask: Item { + anchors.centerIn: parent + width: batteryProgress.valueBarWidth + height: batteryProgress.valueBarHeight + + ColumnLayout { + anchors.centerIn: parent + spacing: 0 + + MaterialSymbol { + id: boltIcon + Layout.alignment: Qt.AlignHCenter + fill: 1 + text: isCharging ? "bolt" : "battery_android_full" + iconSize: Appearance.font.pixelSize.normal + visible: percentage < 1 + } + StyledText { + Layout.alignment: Qt.AlignHCenter + font: batteryProgress.font + text: batteryProgress.text + } + } + } + } + + Bar.BatteryPopup { + id: batteryPopup + hoverTarget: root + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/Resource.qml b/.config/quickshell/ii/modules/verticalBar/Resource.qml new file mode 100644 index 00000000..4334f8cb --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/Resource.qml @@ -0,0 +1,43 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts +import Quickshell + +Item { + id: root + required property string iconName + required property double percentage + implicitHeight: resourceProgress.implicitHeight + implicitWidth: Appearance.sizes.verticalBarWidth + + // Helper function to format KB to GB + function formatKB(kb) { + return (kb / (1024 * 1024)).toFixed(1) + " GB"; + } + + ClippedFilledCircularProgress { + id: resourceProgress + anchors.centerIn: parent + value: percentage + + MaterialSymbol { + font.weight: Font.Medium + fill: 1 + text: root.iconName + iconSize: 13 + color: Appearance.colors.colOnSecondaryContainer + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + enabled: root.visible + } + +} diff --git a/.config/quickshell/ii/modules/verticalBar/Resources.qml b/.config/quickshell/ii/modules/verticalBar/Resources.qml new file mode 100644 index 00000000..900bb167 --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/Resources.qml @@ -0,0 +1,41 @@ +import qs.services +import QtQuick +import QtQuick.Layouts +import "../bar" as Bar + +MouseArea { + id: root + property bool alwaysShowAllResources: false + implicitHeight: columnLayout.implicitHeight + implicitWidth: columnLayout.implicitWidth + hoverEnabled: true + + ColumnLayout { + id: columnLayout + spacing: 10 + anchors.fill: parent + + Resource { + Layout.alignment: Qt.AlignHCenter + iconName: "memory" + percentage: ResourceUsage.memoryUsedPercentage + } + + Resource { + Layout.alignment: Qt.AlignHCenter + iconName: "swap_horiz" + percentage: ResourceUsage.swapUsedPercentage + } + + Resource { + Layout.alignment: Qt.AlignHCenter + iconName: "planner_review" + percentage: ResourceUsage.cpuUsage + } + + } + + Bar.ResourcesPopup { + hoverTarget: root + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml new file mode 100644 index 00000000..2f6aa6de --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml @@ -0,0 +1,248 @@ +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 + +Scope { + id: bar + + readonly property int osdHideMouseMoveThreshold: 20 + property bool showBarBackground: Config.options.bar.showBackground + + Variants { + // For each monitor + model: { + const screens = Quickshell.screens; + const list = Config.options.bar.screenList; + if (!list || list.length === 0) + return screens; + return screens.filter(screen => list.includes(screen.name)); + } + LazyLoader { + id: barLoader + active: GlobalStates.barOpen && !GlobalStates.screenLocked + required property ShellScreen modelData + component: PanelWindow { // Bar window + id: barRoot + screen: barLoader.modelData + + property var brightnessMonitor: Brightness.getMonitorForScreen(barLoader.modelData) + + Timer { + id: showBarTimer + interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100) + repeat: false + onTriggered: { + barRoot.superShow = true + } + } + Connections { + target: GlobalStates + function onSuperDownChanged() { + if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return; + if (GlobalStates.superDown) showBarTimer.restart(); + else { + showBarTimer.stop(); + barRoot.superShow = false; + } + } + } + property bool superShow: false + property bool mustShow: hoverRegion.containsMouse || superShow + exclusionMode: ExclusionMode.Ignore + exclusiveZone: (Config?.options.bar.autoHide.enable && (!mustShow || !Config?.options.bar.autoHide.pushWindows)) ? 0 : + Appearance.sizes.baseVerticalBarWidth + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0) + WlrLayershell.namespace: "quickshell:verticalBar" + // WlrLayershell.layer: WlrLayer.Overlay // TODO enable this when bar can hide when fullscreen + implicitWidth: Appearance.sizes.verticalBarWidth + Appearance.rounding.screenRounding + mask: Region { + item: hoverMaskRegion + } + color: "transparent" + + anchors { + left: !Config.options.bar.bottom + right: Config.options.bar.bottom + top: true + bottom: true + } + + MouseArea { + id: hoverRegion + hoverEnabled: true + anchors.fill: parent + + Item { + id: hoverMaskRegion + anchors { + fill: barContent + leftMargin: -1 + rightMargin: -1 + } + } + + VerticalBarContent { + id: barContent + + implicitWidth: Appearance.sizes.verticalBarWidth + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: undefined + leftMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.verticalBarWidth : 0 + rightMargin: 0 + } + Behavior on anchors.leftMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on anchors.rightMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + states: State { + name: "right" + when: Config.options.bar.bottom + AnchorChanges { + target: barContent + anchors { + top: parent.top + bottom: parent.bottom + left: undefined + right: parent.right + } + } + PropertyChanges { + target: barContent + anchors.topMargin: 0 + anchors.rightMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.barHeight : 0 + } + } + } + + // Round decorators + Loader { + id: roundDecorators + anchors { + top: parent.top + bottom: parent.bottom + left: barContent.right + right: undefined + } + width: Appearance.rounding.screenRounding + active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug + + states: State { + name: "right" + when: Config.options.bar.bottom + AnchorChanges { + target: roundDecorators + anchors { + top: parent.top + bottom: parent.bottom + left: undefined + right: barContent.left + } + } + } + + sourceComponent: Item { + implicitHeight: Appearance.rounding.screenRounding + RoundCorner { + id: topCorner + anchors { + left: parent.left + right: parent.right + top: parent.top + } + + implicitSize: Appearance.rounding.screenRounding + color: showBarBackground ? Appearance.colors.colLayer0 : "transparent" + + corner: RoundCorner.CornerEnum.TopLeft + states: State { + name: "bottom" + when: Config.options.bar.bottom + PropertyChanges { + topCorner.corner: RoundCorner.CornerEnum.TopRight + } + } + } + RoundCorner { + id: bottomCorner + anchors { + bottom: parent.bottom + left: !Config.options.bar.bottom ? parent.left : undefined + right: Config.options.bar.bottom ? parent.right : undefined + } + implicitSize: Appearance.rounding.screenRounding + color: showBarBackground ? Appearance.colors.colLayer0 : "transparent" + + corner: RoundCorner.CornerEnum.BottomLeft + states: State { + name: "bottom" + when: Config.options.bar.bottom + PropertyChanges { + bottomCorner.corner: RoundCorner.CornerEnum.BottomRight + } + } + } + } + } + } + } + } + } + + IpcHandler { + target: "bar" + + function toggle(): void { + GlobalStates.barOpen = !GlobalStates.barOpen + } + + function close(): void { + GlobalStates.barOpen = false + } + + function open(): void { + GlobalStates.barOpen = true + } + } + + GlobalShortcut { + name: "barToggle" + description: "Toggles bar on press" + + onPressed: { + GlobalStates.barOpen = !GlobalStates.barOpen; + } + } + + GlobalShortcut { + name: "barOpen" + description: "Opens bar on press" + + onPressed: { + GlobalStates.barOpen = true; + } + } + + GlobalShortcut { + name: "barClose" + description: "Closes bar on press" + + onPressed: { + GlobalStates.barOpen = false; + } + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml new file mode 100644 index 00000000..ccb953e4 --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml @@ -0,0 +1,369 @@ +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 +import "../bar" as Bar + +Item { // Bar content region + id: root + + property var screen: root.QsWindow.window?.screen + property var brightnessMonitor: Brightness.getMonitorForScreen(screen) + + component HorizontalBarSeparator: Rectangle { + Layout.leftMargin: Appearance.sizes.baseBarHeight / 3 + Layout.rightMargin: Appearance.sizes.baseBarHeight / 3 + Layout.fillWidth: true + implicitHeight: 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 { // Top section | scroll to change brightness + id: barTopSectionMouseArea + anchors.top: parent.top + implicitHeight: topSectionColumnLayout.implicitHeight + implicitWidth: Appearance.sizes.baseVerticalBarWidth + height: (root.height - middleSection.height) / 2 + width: Appearance.sizes.verticalBarWidth + 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 => { + barTopSectionMouseArea.hovered = true; + } + onExited: event => { + barTopSectionMouseArea.hovered = false; + barTopSectionMouseArea.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 + barTopSectionMouseArea.lastScrollX = event.x; + barTopSectionMouseArea.lastScrollY = event.y; + barTopSectionMouseArea.trackingScroll = true; + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + onPositionChanged: mouse => { + if (barTopSectionMouseArea.trackingScroll) { + const dx = mouse.x - barTopSectionMouseArea.lastScrollX; + const dy = mouse.y - barTopSectionMouseArea.lastScrollY; + if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { + GlobalStates.osdBrightnessOpen = false; + barTopSectionMouseArea.trackingScroll = false; + } + } + } + + ColumnLayout { // Content + id: topSectionColumnLayout + anchors.fill: parent + spacing: 10 + + RippleButton { // Left sidebar button + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + Layout.topMargin: (Appearance.sizes.baseVerticalBarWidth - implicitWidth) / 2 + Appearance.sizes.hyprlandGapsOut + Layout.fillHeight: false + property real buttonPadding: 5 + implicitWidth: distroIcon.width + buttonPadding * 2 + implicitHeight: distroIcon.height + buttonPadding * 2 + + buttonRadius: Appearance.rounding.full + colBackground: barTopSectionMouseArea.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 : `${Config.options.bar.topLeftIcon}-symbolic` + colorize: true + color: Appearance.colors.colOnLayer0 + } + } + + Item { + Layout.fillHeight: true + } + + } + } + + ColumnLayout { // Middle section + id: middleSection + anchors.centerIn: parent + spacing: 4 + + VerticalBarGroup { + padding: 8 + Resources { + Layout.fillWidth: true + Layout.fillHeight: false + } + } + + HorizontalBarSeparator { + visible: Config.options?.bar.borderless + } + + VerticalBarGroup { + id: middleCenterGroup + padding: 6 + Layout.fillHeight: true + + Workspaces { + id: workspacesWidget + MouseArea { + // Right-click to toggle overview + anchors.fill: parent + acceptedButtons: Qt.RightButton + + onPressed: event => { + if (event.button === Qt.RightButton) { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } + } + } + } + + HorizontalBarSeparator { + visible: Config.options?.bar.borderless + } + + VerticalBarGroup { + padding: 8 + + VerticalClockWidget { + Layout.fillWidth: true + Layout.fillHeight: false + } + + BatteryIndicator { + Layout.fillWidth: true + Layout.fillHeight: false + } + } + } + + MouseArea { // Bottom section | scroll to change volume + id: barBottomSectionMouseArea + + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + implicitWidth: Appearance.sizes.baseVerticalBarWidth + implicitHeight: bottomSectionColumnLayout.implicitHeight + width: Appearance.sizes.verticalBarWidth + + 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 => { + barBottomSectionMouseArea.hovered = true; + } + onExited: event => { + barBottomSectionMouseArea.hovered = false; + barBottomSectionMouseArea.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 + barBottomSectionMouseArea.lastScrollX = event.x; + barBottomSectionMouseArea.lastScrollY = event.y; + barBottomSectionMouseArea.trackingScroll = true; + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + onPositionChanged: mouse => { + if (barBottomSectionMouseArea.trackingScroll) { + const dx = mouse.x - barBottomSectionMouseArea.lastScrollX; + const dy = mouse.y - barBottomSectionMouseArea.lastScrollY; + if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) { + GlobalStates.osdVolumeOpen = false; + barBottomSectionMouseArea.trackingScroll = false; + } + } + } + + ColumnLayout { + id: bottomSectionColumnLayout + anchors.fill: parent + spacing: 4 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + Bar.SysTray { + vertical: true + Layout.fillWidth: true + Layout.fillHeight: false + } + + RippleButton { // Right sidebar button + id: rightSidebarButton + + Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter + Layout.bottomMargin: Appearance.rounding.screenRounding + Layout.fillHeight: false + + implicitHeight: indicatorsColumnLayout.implicitHeight + 4 * 2 + implicitWidth: indicatorsColumnLayout.implicitWidth + 6 * 2 + + buttonRadius: Appearance.rounding.full + colBackground: barBottomSectionMouseArea.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; + } + + ColumnLayout { + id: indicatorsColumnLayout + anchors.centerIn: parent + property real realSpacing: 6 + spacing: 0 + + Revealer { + vertical: true + reveal: Audio.sink?.audio?.muted ?? false + Layout.fillWidth: true + Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0 + Behavior on Layout.bottomMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + MaterialSymbol { + text: "volume_off" + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + } + Revealer { + vertical: true + reveal: Audio.source?.audio?.muted ?? false + Layout.fillWidth: true + Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0 + Behavior on Layout.topMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + MaterialSymbol { + text: "mic_off" + iconSize: Appearance.font.pixelSize.larger + color: rightSidebarButton.colText + } + } + Loader { + active: HyprlandXkb.layoutCodes.length > 1 + visible: active + Layout.bottomMargin: indicatorsColumnLayout.realSpacing + sourceComponent: StyledText { + text: HyprlandXkb.currentLayoutCode + font.pixelSize: Appearance.font.pixelSize.small + color: rightSidebarButton.colText + } + } + MaterialSymbol { + Layout.bottomMargin: indicatorsColumnLayout.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 + } + } + } + } + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarGroup.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarGroup.qml new file mode 100644 index 00000000..ef88aafb --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarGroup.qml @@ -0,0 +1,37 @@ +import qs.modules.common +import QtQuick +import QtQuick.Layouts + +Item { + id: root + property real padding: 5 + implicitWidth: Appearance.sizes.baseVerticalBarWidth + width: Appearance.sizes.verticalBarWidth + implicitHeight: columnLayout.implicitHeight + padding * 2 + default property alias items: columnLayout.children + + Rectangle { + id: background + anchors { + fill: parent + leftMargin: 4 + rightMargin: 4 + } + color: Config.options?.bar.borderless ? "transparent" : Appearance.colors.colLayer1 + radius: Appearance.rounding.small + } + + ColumnLayout { + id: columnLayout + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + bottom: parent.bottom + topMargin: root.padding + bottomMargin: root.padding + } + spacing: 12 + + // Children defined by `items` prop + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml b/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml new file mode 100644 index 00000000..1c19f282 --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml @@ -0,0 +1,43 @@ +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts +import "../bar" as Bar + +Item { + id: root + property bool borderless: Config.options.bar.borderless + implicitHeight: clockColumn.implicitHeight + implicitWidth: Appearance.sizes.verticalBarWidth + + ColumnLayout { + id: clockColumn + anchors.centerIn: parent + spacing: 0 + + Repeater { + model: DateTime.time.split(/[: ]/) + delegate: StyledText { + required property string modelData + Layout.alignment: Qt.AlignHCenter + font.pixelSize: modelData.match(/am|pm/i) ? + Appearance.font.pixelSize.smaller // Smaller "am"/"pm" text + : Appearance.font.pixelSize.large + color: Appearance.colors.colOnLayer1 + text: modelData.padStart(2, "0") + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + + Bar.ClockWidgetTooltip { + hoverTarget: mouseArea + } + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/Workspaces.qml b/.config/quickshell/ii/modules/verticalBar/Workspaces.qml new file mode 100644 index 00000000..715bd162 --- /dev/null +++ b/.config/quickshell/ii/modules/verticalBar/Workspaces.qml @@ -0,0 +1,313 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import Quickshell.Widgets +import Qt5Compat.GraphicalEffects + +Item { + id: root + 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 workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) + property list workspaceOccupied: [] + property int workspaceButtonWidth: 26 + 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) % Config.options.bar.workspaces.shown + + implicitHeight: columnLayout.implicitHeight + columnLayout.spacing * 2 + implicitWidth: Appearance.sizes.verticalBarWidth + + property bool showNumbers: false + Timer { + id: showNumbersTimer + interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100) + repeat: false + onTriggered: { + root.showNumbers = true + } + } + Connections { + target: GlobalStates + function onSuperDownChanged() { + if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return; + if (GlobalStates.superDown) showNumbersTimer.restart(); + else { + showNumbersTimer.stop(); + root.showNumbers = false; + } + } + function onSuperReleaseMightTriggerChanged() { + showNumbersTimer.stop() + } + } + + // Function to update workspaceOccupied + function updateWorkspaceOccupied() { + workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => { + return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1); + }) + } + + // Occupied workspace updates + Component.onCompleted: updateWorkspaceOccupied() + Connections { + target: Hyprland.workspaces + function onValuesChanged() { + updateWorkspaceOccupied(); + } + } + Connections { + target: Hyprland + function onFocusedWorkspaceChanged() { + updateWorkspaceOccupied(); + } + } + onWorkspaceGroupChanged: { + updateWorkspaceOccupied(); + } + + // Scroll to switch workspaces + WheelHandler { + onWheel: (event) => { + if (event.angleDelta.y < 0) + Hyprland.dispatch(`workspace r+1`); + else if (event.angleDelta.y > 0) + Hyprland.dispatch(`workspace r-1`); + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.BackButton + onPressed: (event) => { + if (event.button === Qt.BackButton) { + Hyprland.dispatch(`togglespecialworkspace`); + } + } + } + + // Workspaces - background + ColumnLayout { + id: columnLayout + z: 1 + + spacing: 0 + anchors { + top: parent.top + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + implicitWidth: Appearance.sizes.verticalBarWidth + + Repeater { + model: Config.options.bar.workspaces.shown + + Rectangle { + z: 1 + implicitWidth: workspaceButtonWidth + implicitHeight: workspaceButtonWidth + radius: (width / 2) + property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index)) + property var nextOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2)) + property var radiusTop: previousOccupied ? 0 : (width / 2) + property var radiusBottom: nextOccupied ? 0 : (width / 2) + + topLeftRadius: radiusTop + topRightRadius: radiusTop + bottomLeftRadius: radiusBottom + bottomRightRadius: radiusBottom + + color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) + opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0 + + Behavior on opacity { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on radiusTop { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on radiusBottom { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + + } + } + } + + // Active workspace + Rectangle { + z: 2 + // Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight + property real activeWorkspaceMargin: 2 + implicitWidth: workspaceButtonWidth - activeWorkspaceMargin * 2 + radius: Appearance.rounding.full + color: Appearance.colors.colPrimary + anchors.horizontalCenter: parent.horizontalCenter + + property real idx1: workspaceIndexInGroup + property real idx2: workspaceIndexInGroup + y: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin + implicitHeight: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2 + + Behavior on activeWorkspaceMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on idx1 { // Leading anim + NumberAnimation { + duration: 100 + easing.type: Easing.OutSine + } + } + Behavior on idx2 { // Following anim + NumberAnimation { + duration: 300 + easing.type: Easing.OutSine + } + } + } + + // Workspaces - numbers + ColumnLayout { + id: columnLayoutNumbers + z: 3 + + spacing: 0 + anchors.fill: parent + implicitWidth: Appearance.sizes.verticalBarWidth + + Repeater { + model: Config.options.bar.workspaces.shown + + Button { + id: button + property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1 + Layout.fillWidth: true + onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`) + height: workspaceButtonWidth + + background: Item { + id: workspaceButtonBackground + implicitWidth: workspaceButtonWidth + implicitHeight: workspaceButtonWidth + property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue) + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + + StyledText { // Workspace number text + opacity: root.showNumbers + || ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers)) + || (root.showNumbers && !Config.options?.bar.workspaces.showAppIcons) + ) ? 1 : 0 + z: 3 + + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) + text: `${button.workspaceValue}` + elide: Text.ElideRight + color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + Appearance.m3colors.m3onPrimary : + (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : + Appearance.colors.colOnLayer1Inactive) + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + Rectangle { // Dot instead of ws number + id: wsDot + opacity: (Config.options?.bar.workspaces.alwaysShowNumbers + || root.showNumbers + || (Config.options?.bar.workspaces.showAppIcons && workspaceButtonBackground.biggestWindow) + ) ? 0 : 1 + visible: opacity > 0 + anchors.centerIn: parent + 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) + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + Item { // Main app icon + anchors.centerIn: parent + width: workspaceButtonWidth + height: workspaceButtonWidth + opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : + (workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? + 1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0 + visible: opacity > 0 + IconImage { + id: mainAppIcon + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? + (workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked + anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? + (workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked + + source: workspaceButtonBackground.mainAppIconSource + implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on anchors.bottomMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on anchors.rightMargin { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitSize { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + + Loader { + active: Config.options.bar.workspaces.monochromeIcons + anchors.fill: mainAppIcon + sourceComponent: Item { + Desaturate { + id: desaturatedIcon + visible: false // There's already color overlay + anchors.fill: parent + source: mainAppIcon + desaturation: 0.8 + } + ColorOverlay { + anchors.fill: desaturatedIcon + source: desaturatedIcon + color: ColorUtils.transparentize(wsDot.color, 0.9) + } + } + } + } + } + + + } + + } + + } + +} diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index e46d7a43..8186d1a5 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 "./modules/verticalBar/" import QtQuick import QtQuick.Window @@ -47,6 +48,7 @@ ShellRoot { property bool enableSession: true property bool enableSidebarLeft: true property bool enableSidebarRight: true + property bool enableVerticalBar: true // Force initialization of some singletons Component.onCompleted: { @@ -56,7 +58,7 @@ ShellRoot { MaterialThemeLoader.reapplyTheme() } - LazyLoader { active: enableBar; component: Bar {} } + LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} } LazyLoader { active: enableBackground; component: Background {} } LazyLoader { active: enableCheatsheet; component: Cheatsheet {} } LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} } @@ -72,5 +74,6 @@ ShellRoot { LazyLoader { active: enableSession; component: Session {} } LazyLoader { active: enableSidebarLeft; component: SidebarLeft {} } LazyLoader { active: enableSidebarRight; component: SidebarRight {} } + LazyLoader { active: enableVerticalBar && Config.ready && Config.options.bar.vertical; component: VerticalBar {} } } diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index 1406ff0d..b353b77c 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -134,29 +134,52 @@ ApplicationWindow { ContentSection { title: Translation.tr("Bar") - ContentSubsection { - title: "Corner style" + ConfigRow { + ContentSubsection { + title: "Corner style" - ConfigSelectionArray { - currentValue: Config.options.bar.cornerStyle - configOptionName: "bar.cornerStyle" - onSelected: newValue => { - Config.options.bar.cornerStyle = newValue; // Update local copy - } - options: [ - { - displayName: Translation.tr("Hug"), - value: 0 - }, - { - displayName: Translation.tr("Float"), - value: 1 - }, - { - displayName: Translation.tr("Plain rectangle"), - value: 2 + ConfigSelectionArray { + currentValue: Config.options.bar.cornerStyle + configOptionName: "bar.cornerStyle" + onSelected: newValue => { + Config.options.bar.cornerStyle = newValue; // Update local copy } - ] + options: [ + { + displayName: Translation.tr("Hug"), + value: 0 + }, + { + displayName: Translation.tr("Float"), + value: 1 + }, + { + displayName: Translation.tr("Plain rectangle"), + value: 2 + } + ] + } + } + + ContentSubsection { + title: "Bar layout" + ConfigSelectionArray { + currentValue: Config.options.bar.vertical + configOptionName: "bar.vertical" + onSelected: newValue => { + Config.options.bar.vertical = newValue; + } + options: [ + { + displayName: Translation.tr("Horizontal"), + value: false + }, + { + displayName: Translation.tr("Vertical"), + value: true + }, + ] + } } } @@ -169,7 +192,7 @@ ApplicationWindow { } } ConfigSwitch { - text: Translation.tr("Place at the bottom") + text: Translation.tr("Place at the bottom/right") checked: Config.options.bar.bottom onCheckedChanged: { Config.options.bar.bottom = checked;