Changed the glass animations to a more bubble-like animation

This commit is contained in:
SomeElse
2026-05-25 06:06:33 +00:00
parent 390525ac72
commit 8d6406284f
7 changed files with 136 additions and 158 deletions

View File

@@ -51,31 +51,47 @@ ModuleChip {
property real progressVal: 1.0 property real progressVal: 1.0
property bool slideFromRight: false property bool slideFromRight: false
property real slideX: slideFromRight ? (width + 20) : -(width + 20) 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 { ParallelAnimation {
id: entranceSlide id: entranceAnim
target: slideTranslate; property: "x" NumberAnimation {
from: toastRoot.slideX; to: 0 target: slideTranslate; property: "x"
duration: 300; easing.type: Easing.OutCubic 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() { function triggerDismiss() {
if (exitSlide.running) return if (exitAnim.running) return
autoTimer.stop() autoTimer.stop()
exitSlide.start() exitAnim.start()
} }
NumberAnimation { ParallelAnimation {
id: exitSlide id: exitAnim
target: slideTranslate; property: "x" NumberAnimation {
to: toastRoot.slideX target: slideTranslate; property: "x"
duration: 220; easing.type: Easing.InCubic 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) onStopped: toastRoot.dismissed(toastRoot.toastId)
} }

View File

@@ -252,7 +252,7 @@ ModuleChip {
|| (submenuCard.visible || (submenuCard.visible
&& trayMenuPopup._submenuParentRef === menuItem) && trayMenuPopup._submenuParentRef === menuItem)
color: active ? root.tmHover : "transparent" color: active ? root.tmHover : "transparent"
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 60 } }
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent

View File

@@ -14,8 +14,16 @@ ModuleChip {
property int outputMaxVolume: Cfg.Config.volumeOutputMax property int outputMaxVolume: Cfg.Config.volumeOutputMax
property int inputMaxVolume: Cfg.Config.volumeInputMax property int inputMaxVolume: Cfg.Config.volumeInputMax
ConfigWriter { readonly property string configPath:
id: configWriter 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 { Timer {
@@ -23,15 +31,21 @@ ModuleChip {
interval: 800 interval: 800
repeat: false repeat: false
onTriggered: { onTriggered: {
configWriter.writeInts([ const outMax = Math.round(root.outputMaxVolume).toString()
{ key: "volumeOutputMax", value: root.outputMaxVolume }, const inMax = Math.round(root.inputMaxVolume).toString()
{ key: "volumeInputMax", value: root.inputMaxVolume } 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() onOutputMaxVolumeChanged: { if (root.outputMaxVolume >= 10) saveDebounce.restart() }
onInputMaxVolumeChanged: saveDebounce.restart() onInputMaxVolumeChanged: { if (root.inputMaxVolume >= 10) saveDebounce.restart() }
readonly property var sink: Pipewire.defaultAudioSink readonly property var sink: Pipewire.defaultAudioSink
readonly property var source: Pipewire.defaultAudioSource readonly property var source: Pipewire.defaultAudioSource
@@ -98,22 +112,24 @@ ModuleChip {
} }
width: chipLayout.implicitWidth + 15 width: chipLayout.implicitWidth + 15
height: Cfg.Config.moduleHeight
radius: panelRadius radius: panelRadius
chipColor: t.volBg color: hoverHandler.hovered ? t.volBgHover : t.volBg
chipColorHovered: t.volBgHover border.color: (hoverHandler.hovered || volumePopup.popupOpen) ? t.volBorderHover : t.volBorder
chipBorderColor: t.volBorder border.width: borderWidth
chipBorderColorHovered: t.volBorderHover
chipHoverEnabled: true
chipTapAction: () => volumePopup.toggle()
popupOpen: volumePopup.popupOpen
HoverHandler { Behavior on color { ColorAnimation { duration: 150 } }
cursorShape: Qt.PointingHandCursor Behavior on border.color { ColorAnimation { duration: 150 } }
HoverHandler {
id: hoverHandler
cursorShape: Qt.PointingHandCursor
onHoveredChanged: { onHoveredChanged: {
if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart() if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart()
} }
} }
TapHandler { onTapped: volumePopup.toggle() }
SequentialAnimation { SequentialAnimation {
id: shakeAnim id: shakeAnim
@@ -140,29 +156,48 @@ ModuleChip {
} }
} }
StandardPopup { PopupPanel {
id: volumePopup id: volumePopup
ns: "volume" ns: "volume"
chipRoot: root chipRoot: root
cardWidth: Cfg.Config.volumePopupWidth cardWidth: Cfg.Config.volumePopupWidth
cardColor: t.volPopupBg
cardBorderColor: t.volPopupBorder
cardHeight: popupCol.implicitHeight + 34
hasKeyboardFocus: true
Item { Rectangle {
id: popupCol id: volCard
anchors { left: parent.left; right: parent.right; top: parent.top } width: parent.width
anchors.margins: 16 height: popupCol.implicitHeight + 32
anchors.topMargin: 18
implicitWidth: popupContent.implicitWidth
implicitHeight: popupContent.implicitHeight
focus: true
ColumnLayout { anchors.top: root.isTop ? parent.top : undefined
id: popupContent anchors.bottom: root.isTop ? undefined : parent.bottom
anchors.fill: parent
spacing: 14 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 { VolumeSection {
id: outputSection id: outputSection
@@ -235,7 +270,6 @@ ModuleChip {
borderColor: root.t.volPopupBorder borderColor: root.t.volPopupBorder
onDecrement: root.outputMaxVolume = Math.max(10, root.outputMaxVolume - 10) onDecrement: root.outputMaxVolume = Math.max(10, root.outputMaxVolume - 10)
onIncrement: root.outputMaxVolume = Math.min(300, root.outputMaxVolume + 10) onIncrement: root.outputMaxVolume = Math.min(300, root.outputMaxVolume + 10)
onValueSet: root.outputMaxVolume = Math.max(10, Math.min(300, val))
} }
LimitRow { LimitRow {
Layout.fillWidth: true Layout.fillWidth: true
@@ -250,18 +284,11 @@ ModuleChip {
borderColor: root.t.volPopupBorder borderColor: root.t.volPopupBorder
onDecrement: root.inputMaxVolume = Math.max(10, root.inputMaxVolume - 10) onDecrement: root.inputMaxVolume = Math.max(10, root.inputMaxVolume - 10)
onIncrement: root.inputMaxVolume = Math.min(300, 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 } Item { height: 2 }
} }
MouseArea {
anchors.fill: parent
z: -1
onClicked: popupCol.forceActiveFocus()
} }
}
} }
component VolumeSection: ColumnLayout { component VolumeSection: ColumnLayout {
@@ -308,36 +335,14 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
renderType: Text.NativeRendering renderType: Text.NativeRendering
} }
TextInput { Text {
text: vs.muted ? "muted" : vs.volume + "%" text: vs.muted ? "muted" : vs.volume + "%"
color: vs.muted ? vs.dimColor : vs.textColor color: vs.muted ? vs.dimColor : vs.textColor
font.pixelSize: 12 font.pixelSize: 12
font.bold: !vs.muted font.bold: !vs.muted
horizontalAlignment: TextInput.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
Layout.preferredWidth: 44 Layout.preferredWidth: 44
renderType: Text.NativeRendering 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 { Rectangle {
@@ -504,7 +509,6 @@ ModuleChip {
signal decrement() signal decrement()
signal increment() signal increment()
signal valueSet(int val)
Text { text: lr.icon; color: lr.iconColor; font.pixelSize: 13; font.bold: true; renderType: Text.NativeRendering } Text { text: lr.icon; color: lr.iconColor; font.pixelSize: 13; font.bold: true; renderType: Text.NativeRendering }
Text { Text {
@@ -531,32 +535,13 @@ ModuleChip {
HoverHandler { id: decHover; cursorShape: Qt.PointingHandCursor } HoverHandler { id: decHover; cursorShape: Qt.PointingHandCursor }
TapHandler { onTapped: { if (lr.value > lr.minValue) lr.decrement() } } TapHandler { onTapped: { if (lr.value > lr.minValue) lr.decrement() } }
} }
TextInput { Text {
text: lr.value + "%" text: lr.value + "%"
color: lr.textColor color: lr.textColor
font.pixelSize: 12; font.bold: true font.pixelSize: 12; font.bold: true
horizontalAlignment: TextInput.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
Layout.preferredWidth: 44 Layout.preferredWidth: 44
renderType: Text.NativeRendering 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 { Rectangle {
@@ -576,3 +561,5 @@ ModuleChip {
} }
} }
} }

View File

@@ -14,12 +14,13 @@ Rectangle {
anchors.top: isTop ? parent.top : undefined anchors.top: isTop ? parent.top : undefined
anchors.bottom: isTop ? undefined : parent.bottom anchors.bottom: isTop ? undefined : parent.bottom
transform: Translate { transform: Scale {
y: popupOpen ? 0 : (isTop ? -card.height : card.height) yScale: popupOpen ? 1 : 0
Behavior on y { origin.y: isTop ? 0 : card.height
Behavior on yScale {
NumberAnimation { NumberAnimation {
duration: Cfg.Config.popupAnimDuration duration: Cfg.Config.popupAnimDuration
easing.type: Easing.OutExpo easing.type: Easing.OutBack
} }
enabled: animEnabled enabled: animEnabled
} }

View File

@@ -21,8 +21,8 @@ QtObject {
// ── Position & Dimensions ───────────────────────────────────────────────── // ── Position & Dimensions ─────────────────────────────────────────────────
property string barPosition: "bottom" property string barPosition: "bottom"
property int barHeight: 35 property int barHeight: 35
property int margin: 20 property int margin: 15
property int radius: 26 property int radius: 20
// ── Module Lists ────────────────────────────────────────────────────────── // ── Module Lists ──────────────────────────────────────────────────────────
property var leftModules: ["Logo", "Stats", "Crypto"] property var leftModules: ["Logo", "Stats", "Crypto"]
@@ -43,7 +43,7 @@ QtObject {
// ── Popup Styling ───────────────────────────────────────────────────────── // ── Popup Styling ─────────────────────────────────────────────────────────
property int popupRadius: 18 property int popupRadius: 18
property int popupBorderWidth: 1 property int popupBorderWidth: 1
property int popupAnimDuration: 500 property int popupAnimDuration: 250
// ── Clock Module ───────────────────────────────────────────────────────── // ── Clock Module ─────────────────────────────────────────────────────────
property bool clockExtendEnabled: true // seconds extension on hover for clock property bool clockExtendEnabled: true // seconds extension on hover for clock
@@ -90,7 +90,7 @@ QtObject {
// NOTIFICATIONS // NOTIFICATIONS
// ──────────────────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────────────────
property bool notificationShakeEnabled: true // bell ringing shake for notifications property bool notificationShakeEnabled: true // bell ringing shake for notifications
property string notificationPosition: "topright" property string notificationPosition: "bottomright"
property int notificationHistorySize: 50 property int notificationHistorySize: 50
// ── Toast timeouts (milliseconds) ───────────────────────────────────────── // ── Toast timeouts (milliseconds) ─────────────────────────────────────────

View File

@@ -28,7 +28,6 @@ PanelWindow {
property bool _isOpen: false property bool _isOpen: false
readonly property bool isOpen: _isOpen readonly property bool isOpen: _isOpen
property string mode: "drun"
property bool _keyboardNav: false property bool _keyboardNav: false
property var allItems: [] property var allItems: []
property bool loading: false property bool loading: false
@@ -82,7 +81,7 @@ PanelWindow {
} }
function readCache() { function readCache() {
const path = Cfg.Config.launcherCacheDir + "/" + (mode === "drun" ? "drun.txt" : "run.txt") const path = Cfg.Config.launcherCacheDir + "/drun.txt"
dataProc.rawOutput = "" dataProc.rawOutput = ""
dataProc.command = ["cat", path] dataProc.command = ["cat", path]
dataProc.running = true dataProc.running = true
@@ -140,8 +139,6 @@ PanelWindow {
close() close()
} }
onModeChanged: if (_isOpen) loadItems()
visible: false visible: false
color: "transparent" color: "transparent"
@@ -159,7 +156,6 @@ PanelWindow {
id: closeTimer id: closeTimer
onTriggered: { onTriggered: {
root.visible = false root.visible = false
root.mode = "drun"
root.allItems = [] root.allItems = []
} }
} }
@@ -288,44 +284,6 @@ PanelWindow {
} }
resultsList.positionViewAtIndex(resultsList.currentIndex, ListView.Contain) 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 { Text {
anchors.centerIn: parent anchors.centerIn: parent
visible: !appIcon.visible visible: !appIcon.visible
text: root.mode === "drun" ? "󰣆" : "󰆍" text: "󰣆"
color: resultsList.currentIndex === index && index !== -1 ? t.launcherAccent : t.launcherDim color: resultsList.currentIndex === index && index !== -1 ? t.launcherAccent : t.launcherDim
font.pixelSize: 16 font.pixelSize: 16
} }

View File

@@ -158,6 +158,11 @@ Item {
opacity: 0 opacity: 0
scale: 0.86 scale: 0.86
transform: Scale {
yScale: 1
origin.y: card.height / 2
}
Component.onCompleted: { Component.onCompleted: {
overlay.displayFlow = agent.flow overlay.displayFlow = agent.flow
enterAnim.start() enterAnim.start()
@@ -175,6 +180,11 @@ Item {
from: 0.86; to: 1.0 from: 0.86; to: 1.0
duration: 360; easing.type: Easing.OutBack; easing.overshoot: 0.6 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 { ParallelAnimation {
@@ -189,6 +199,11 @@ Item {
from: 1.0; to: 0.88 from: 1.0; to: 0.88
duration: 260; easing.type: Easing.InBack duration: 260; easing.type: Easing.InBack
} }
NumberAnimation {
target: card.transform; property: "yScale"
from: 1; to: 0
duration: 260; easing.type: Easing.InBack
}
onFinished: { onFinished: {
overlay.exiting = false overlay.exiting = false
root.windowShouldExist = false root.windowShouldExist = false
@@ -206,8 +221,9 @@ Item {
exitAnim.stop() exitAnim.stop()
overlay.exiting = false overlay.exiting = false
} }
card.opacity = 0 card.opacity = 0
card.scale = 0.86 card.scale = 0.86
card.transform.yScale = 0
enterAnim.restart() enterAnim.restart()
} else { } else {
if (overlay.displayFlow) if (overlay.displayFlow)