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 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)
}

View File

@@ -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

View File

@@ -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
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 {
}
}
}

View File

@@ -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
}

View File

@@ -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) ─────────────────────────────────────────

View File

@@ -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
}

View File

@@ -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)