From 8d6406284f3d452a474e7d447b835156db33bf59 Mon Sep 17 00:00:00 2001 From: SomeElse Date: Mon, 25 May 2026 06:06:33 +0000 Subject: [PATCH] Changed the glass animations to a more bubble-like animation --- bar/modules/Notifications.qml | 46 ++++++---- bar/modules/Tray.qml | 2 +- bar/modules/Volume.qml | 163 ++++++++++++++++------------------ components/PopupCard.qml | 9 +- config/config.qml | 8 +- launcher/Launcher.qml | 46 +--------- polkit/Polkit.qml | 20 ++++- 7 files changed, 136 insertions(+), 158 deletions(-) diff --git a/bar/modules/Notifications.qml b/bar/modules/Notifications.qml index c43be90..4cba989 100644 --- a/bar/modules/Notifications.qml +++ b/bar/modules/Notifications.qml @@ -51,31 +51,47 @@ ModuleChip { property real progressVal: 1.0 property bool slideFromRight: false - property real slideX: slideFromRight ? (width + 20) : -(width + 20) - transform: Translate { id: slideTranslate; x: toastRoot.slideX } + transform: [ + Translate { id: slideTranslate; x: toastRoot.slideX }, + Scale { id: bubbleScale; yScale: 0; origin.y: toastLayer.atBottom ? toastRoot.height : 0 } + ] - Component.onCompleted: entranceSlide.start() + Component.onCompleted: entranceAnim.start() - NumberAnimation { - id: entranceSlide - target: slideTranslate; property: "x" - from: toastRoot.slideX; to: 0 - duration: 300; easing.type: Easing.OutCubic + ParallelAnimation { + id: entranceAnim + NumberAnimation { + target: slideTranslate; property: "x" + from: toastRoot.slideX; to: 0 + duration: 320; easing.type: Easing.OutCubic + } + NumberAnimation { + target: bubbleScale; property: "yScale" + from: 0; to: 1 + duration: 280; easing.type: Easing.OutBack + } } function triggerDismiss() { - if (exitSlide.running) return + if (exitAnim.running) return autoTimer.stop() - exitSlide.start() + exitAnim.start() } - NumberAnimation { - id: exitSlide - target: slideTranslate; property: "x" - to: toastRoot.slideX - duration: 220; easing.type: Easing.InCubic + ParallelAnimation { + id: exitAnim + NumberAnimation { + target: slideTranslate; property: "x" + to: toastRoot.slideX + duration: 250; easing.type: Easing.InCubic + } + NumberAnimation { + target: bubbleScale; property: "yScale" + from: 1; to: 0 + duration: 200; easing.type: Easing.InBack + } onStopped: toastRoot.dismissed(toastRoot.toastId) } diff --git a/bar/modules/Tray.qml b/bar/modules/Tray.qml index 8a24309..f95163a 100644 --- a/bar/modules/Tray.qml +++ b/bar/modules/Tray.qml @@ -252,7 +252,7 @@ ModuleChip { || (submenuCard.visible && trayMenuPopup._submenuParentRef === menuItem) color: active ? root.tmHover : "transparent" - Behavior on color { ColorAnimation { duration: 120 } } + Behavior on color { ColorAnimation { duration: 60 } } RowLayout { anchors.fill: parent diff --git a/bar/modules/Volume.qml b/bar/modules/Volume.qml index 584336f..b92c196 100644 --- a/bar/modules/Volume.qml +++ b/bar/modules/Volume.qml @@ -14,8 +14,16 @@ ModuleChip { property int outputMaxVolume: Cfg.Config.volumeOutputMax property int inputMaxVolume: Cfg.Config.volumeInputMax - ConfigWriter { - id: configWriter + readonly property string configPath: + Quickshell.env("HOME") + "/.config/quickshell/config/config.qml" + + Process { + id: volConfigWriter + running: false + onExited: (code) => { + if (code !== 0) + console.warn("[Volume] Failed to patch config.qml (exit " + code + ")") + } } Timer { @@ -23,15 +31,21 @@ ModuleChip { interval: 800 repeat: false onTriggered: { - configWriter.writeInts([ - { key: "volumeOutputMax", value: root.outputMaxVolume }, - { key: "volumeInputMax", value: root.inputMaxVolume } - ]) + const outMax = Math.round(root.outputMaxVolume).toString() + const inMax = Math.round(root.inputMaxVolume).toString() + const cfgPath = root.configPath + const sh = + "sed -i" + + " -e 's/\\(property int[ ]\\+volumeOutputMax:[ ]\\+\\)[0-9.]\\+/\\1" + outMax + "/'" + + " -e 's/\\(property int[ ]\\+volumeInputMax:[ ]\\+\\)[0-9.]\\+/\\1" + inMax + "/'" + + " " + JSON.stringify(cfgPath) + volConfigWriter.command = ["/bin/sh", "-c", sh] + volConfigWriter.running = true } } - onOutputMaxVolumeChanged: saveDebounce.restart() - onInputMaxVolumeChanged: saveDebounce.restart() + onOutputMaxVolumeChanged: { if (root.outputMaxVolume >= 10) saveDebounce.restart() } + onInputMaxVolumeChanged: { if (root.inputMaxVolume >= 10) saveDebounce.restart() } readonly property var sink: Pipewire.defaultAudioSink readonly property var source: Pipewire.defaultAudioSource @@ -98,22 +112,24 @@ ModuleChip { } width: chipLayout.implicitWidth + 15 + height: Cfg.Config.moduleHeight radius: panelRadius - chipColor: t.volBg - chipColorHovered: t.volBgHover - chipBorderColor: t.volBorder - chipBorderColorHovered: t.volBorderHover - chipHoverEnabled: true - chipTapAction: () => volumePopup.toggle() - popupOpen: volumePopup.popupOpen + color: hoverHandler.hovered ? t.volBgHover : t.volBg + border.color: (hoverHandler.hovered || volumePopup.popupOpen) ? t.volBorderHover : t.volBorder + border.width: borderWidth - HoverHandler { - cursorShape: Qt.PointingHandCursor + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + HoverHandler { + id: hoverHandler + cursorShape: Qt.PointingHandCursor onHoveredChanged: { if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart() } } + TapHandler { onTapped: volumePopup.toggle() } SequentialAnimation { id: shakeAnim @@ -140,29 +156,48 @@ ModuleChip { } } - StandardPopup { + PopupPanel { id: volumePopup ns: "volume" chipRoot: root cardWidth: Cfg.Config.volumePopupWidth - cardColor: t.volPopupBg - cardBorderColor: t.volPopupBorder - cardHeight: popupCol.implicitHeight + 34 - hasKeyboardFocus: true - Item { - id: popupCol - anchors { left: parent.left; right: parent.right; top: parent.top } - anchors.margins: 16 - anchors.topMargin: 18 - implicitWidth: popupContent.implicitWidth - implicitHeight: popupContent.implicitHeight - focus: true + Rectangle { + id: volCard + width: parent.width + height: popupCol.implicitHeight + 32 - ColumnLayout { - id: popupContent - anchors.fill: parent - spacing: 14 + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + + transform: Scale { + yScale: volumePopup.popupOpen ? 1 : 0 + origin.y: root.isTop ? 0 : volCard.height + Behavior on yScale { + NumberAnimation { + duration: Cfg.Config.popupAnimDuration + easing.type: Easing.OutBack + } + enabled: volumePopup.visible + } + } + + opacity: volumePopup.popupOpen ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 200 } } + + radius: Cfg.Config.popupRadius + color: root.t.volPopupBg + border.color: root.t.volPopupBorder + border.width: Cfg.Config.popupBorderWidth + + MouseArea { anchors.fill: parent; propagateComposedEvents: false } + + ColumnLayout { + id: popupCol + anchors { left: parent.left; right: parent.right; top: parent.top } + anchors.margins: 16 + anchors.topMargin: 18 + spacing: 14 VolumeSection { id: outputSection @@ -235,7 +270,6 @@ ModuleChip { borderColor: root.t.volPopupBorder onDecrement: root.outputMaxVolume = Math.max(10, root.outputMaxVolume - 10) onIncrement: root.outputMaxVolume = Math.min(300, root.outputMaxVolume + 10) - onValueSet: root.outputMaxVolume = Math.max(10, Math.min(300, val)) } LimitRow { Layout.fillWidth: true @@ -250,18 +284,11 @@ ModuleChip { borderColor: root.t.volPopupBorder onDecrement: root.inputMaxVolume = Math.max(10, root.inputMaxVolume - 10) onIncrement: root.inputMaxVolume = Math.min(300, root.inputMaxVolume + 10) - onValueSet: root.inputMaxVolume = Math.max(10, Math.min(300, val)) } } Item { height: 2 } } - - MouseArea { - anchors.fill: parent - z: -1 - onClicked: popupCol.forceActiveFocus() } - } } component VolumeSection: ColumnLayout { @@ -308,36 +335,14 @@ ModuleChip { Layout.fillWidth: true renderType: Text.NativeRendering } - TextInput { + Text { text: vs.muted ? "muted" : vs.volume + "%" color: vs.muted ? vs.dimColor : vs.textColor font.pixelSize: 12 font.bold: !vs.muted - horizontalAlignment: TextInput.AlignHCenter - verticalAlignment: TextInput.AlignVCenter + horizontalAlignment: Text.AlignHCenter Layout.preferredWidth: 44 renderType: Text.NativeRendering - readOnly: vs.muted - selectByMouse: true - activeFocusOnPress: true - onAccepted: { - if (!vs.muted) { - let val = parseInt(text.replace("%", "").trim(), 10) - if (!isNaN(val)) vs.sliderMoved(Math.max(0, Math.min(vs.maxVolume, val))) - text = Qt.binding(function() { return vs.muted ? "muted" : vs.volume + "%" }) - } - popupCol.forceActiveFocus() - } - onActiveFocusChanged: { - if (activeFocus && !vs.muted) { - text = vs.volume.toString() - selectAll() - } else if (!activeFocus && !vs.muted) { - let val = parseInt(text.replace("%", "").trim(), 10) - if (!isNaN(val)) vs.sliderMoved(Math.max(0, Math.min(vs.maxVolume, val))) - text = Qt.binding(function() { return vs.muted ? "muted" : vs.volume + "%" }) - } - } } Rectangle { @@ -504,7 +509,6 @@ ModuleChip { signal decrement() signal increment() - signal valueSet(int val) Text { text: lr.icon; color: lr.iconColor; font.pixelSize: 13; font.bold: true; renderType: Text.NativeRendering } Text { @@ -531,32 +535,13 @@ ModuleChip { HoverHandler { id: decHover; cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: { if (lr.value > lr.minValue) lr.decrement() } } } - TextInput { + Text { text: lr.value + "%" color: lr.textColor font.pixelSize: 12; font.bold: true - horizontalAlignment: TextInput.AlignHCenter - verticalAlignment: TextInput.AlignVCenter + horizontalAlignment: Text.AlignHCenter Layout.preferredWidth: 44 renderType: Text.NativeRendering - selectByMouse: true - activeFocusOnPress: true - onAccepted: { - let val = parseInt(text.replace("%", "").trim(), 10) - if (!isNaN(val)) lr.valueSet(Math.max(lr.minValue, Math.min(lr.maxValue, val))) - text = Qt.binding(function() { return lr.value + "%" }) - popupCol.forceActiveFocus() - } - onActiveFocusChanged: { - if (activeFocus) { - text = lr.value.toString() - selectAll() - } else { - let val = parseInt(text.replace("%", "").trim(), 10) - if (!isNaN(val)) lr.valueSet(Math.max(lr.minValue, Math.min(lr.maxValue, val))) - text = Qt.binding(function() { return lr.value + "%" }) - } - } } Rectangle { @@ -576,3 +561,5 @@ ModuleChip { } } } + + diff --git a/components/PopupCard.qml b/components/PopupCard.qml index 2cb6182..7794f5f 100644 --- a/components/PopupCard.qml +++ b/components/PopupCard.qml @@ -14,12 +14,13 @@ Rectangle { anchors.top: isTop ? parent.top : undefined anchors.bottom: isTop ? undefined : parent.bottom - transform: Translate { - y: popupOpen ? 0 : (isTop ? -card.height : card.height) - Behavior on y { + transform: Scale { + yScale: popupOpen ? 1 : 0 + origin.y: isTop ? 0 : card.height + Behavior on yScale { NumberAnimation { duration: Cfg.Config.popupAnimDuration - easing.type: Easing.OutExpo + easing.type: Easing.OutBack } enabled: animEnabled } diff --git a/config/config.qml b/config/config.qml index b1932e7..65ddf83 100644 --- a/config/config.qml +++ b/config/config.qml @@ -21,8 +21,8 @@ QtObject { // ── Position & Dimensions ───────────────────────────────────────────────── property string barPosition: "bottom" property int barHeight: 35 - property int margin: 20 - property int radius: 26 + property int margin: 15 + property int radius: 20 // ── Module Lists ────────────────────────────────────────────────────────── property var leftModules: ["Logo", "Stats", "Crypto"] @@ -43,7 +43,7 @@ QtObject { // ── Popup Styling ───────────────────────────────────────────────────────── property int popupRadius: 18 property int popupBorderWidth: 1 - property int popupAnimDuration: 500 + property int popupAnimDuration: 250 // ── Clock Module ───────────────────────────────────────────────────────── property bool clockExtendEnabled: true // seconds extension on hover for clock @@ -90,7 +90,7 @@ QtObject { // NOTIFICATIONS // ──────────────────────────────────────────────────────────────────────── property bool notificationShakeEnabled: true // bell ringing shake for notifications - property string notificationPosition: "topright" + property string notificationPosition: "bottomright" property int notificationHistorySize: 50 // ── Toast timeouts (milliseconds) ───────────────────────────────────────── diff --git a/launcher/Launcher.qml b/launcher/Launcher.qml index 2291398..7fcf335 100644 --- a/launcher/Launcher.qml +++ b/launcher/Launcher.qml @@ -28,7 +28,6 @@ PanelWindow { property bool _isOpen: false readonly property bool isOpen: _isOpen - property string mode: "drun" property bool _keyboardNav: false property var allItems: [] property bool loading: false @@ -82,7 +81,7 @@ PanelWindow { } function readCache() { - const path = Cfg.Config.launcherCacheDir + "/" + (mode === "drun" ? "drun.txt" : "run.txt") + const path = Cfg.Config.launcherCacheDir + "/drun.txt" dataProc.rawOutput = "" dataProc.command = ["cat", path] dataProc.running = true @@ -140,8 +139,6 @@ PanelWindow { close() } - onModeChanged: if (_isOpen) loadItems() - visible: false color: "transparent" @@ -159,7 +156,6 @@ PanelWindow { id: closeTimer onTriggered: { root.visible = false - root.mode = "drun" root.allItems = [] } } @@ -288,44 +284,6 @@ PanelWindow { } resultsList.positionViewAtIndex(resultsList.currentIndex, ListView.Contain) } - - Keys.onTabPressed: root.mode = root.mode === "drun" ? "run" : "drun" - } - - Rectangle { - height: 28 - width: modeRow.implicitWidth + 10 - radius: 8 - color: t.launcherPill - border.color: t.launcherBorder - border.width: Cfg.Config.popupBorderWidth - - RowLayout { - id: modeRow - anchors.centerIn: parent - spacing: 2 - Repeater { - model: ["drun", "run"] - delegate: Rectangle { - height: 22 - width: label.implicitWidth + 14 - radius: 6 - color: root.mode === modelData ? t.launcherModeActiveBg : "transparent" - Text { - id: label - anchors.centerIn: parent - text: modelData - font.pixelSize: 10 - font.bold: root.mode === modelData - color: root.mode === modelData ? t.launcherModeActiveText : t.launcherDim - } - MouseArea { - anchors.fill: parent - onClicked: root.mode = modelData - } - } - } - } } } } @@ -386,7 +344,7 @@ PanelWindow { Text { anchors.centerIn: parent visible: !appIcon.visible - text: root.mode === "drun" ? "󰣆" : "󰆍" + text: "󰣆" color: resultsList.currentIndex === index && index !== -1 ? t.launcherAccent : t.launcherDim font.pixelSize: 16 } diff --git a/polkit/Polkit.qml b/polkit/Polkit.qml index ee2f201..99b42ff 100644 --- a/polkit/Polkit.qml +++ b/polkit/Polkit.qml @@ -158,6 +158,11 @@ Item { opacity: 0 scale: 0.86 + transform: Scale { + yScale: 1 + origin.y: card.height / 2 + } + Component.onCompleted: { overlay.displayFlow = agent.flow enterAnim.start() @@ -175,6 +180,11 @@ Item { from: 0.86; to: 1.0 duration: 360; easing.type: Easing.OutBack; easing.overshoot: 0.6 } + NumberAnimation { + target: card.transform; property: "yScale" + from: 0; to: 1 + duration: 360; easing.type: Easing.OutBack; easing.overshoot: 0.6 + } } ParallelAnimation { @@ -189,6 +199,11 @@ Item { from: 1.0; to: 0.88 duration: 260; easing.type: Easing.InBack } + NumberAnimation { + target: card.transform; property: "yScale" + from: 1; to: 0 + duration: 260; easing.type: Easing.InBack + } onFinished: { overlay.exiting = false root.windowShouldExist = false @@ -206,8 +221,9 @@ Item { exitAnim.stop() overlay.exiting = false } - card.opacity = 0 - card.scale = 0.86 + card.opacity = 0 + card.scale = 0.86 + card.transform.yScale = 0 enterAnim.restart() } else { if (overlay.displayFlow)