Merge status bar and launcher into morphing top bar

This commit is contained in:
2026-02-25 03:09:27 -08:00
parent ea9f96dab7
commit 86cf291663
7 changed files with 436 additions and 340 deletions

172
TopBar.qml Normal file
View File

@@ -0,0 +1,172 @@
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.Wayland
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Shapes
import "." as Shell
PanelWindow {
id: root
anchors {
top: true
}
implicitWidth: 1024
implicitHeight: 200
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
color: "transparent"
property bool up: false
IpcHandler {
target: "topbar"
function open() { root.open() }
function close() { root.close() }
function toggle() {
root.up ? close() : open()
}
}
function open() {
background.index = 0
background.entry.start()
}
function close() {
background.exit.start()
}
Shell.BarShape {
id: background
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
color: "#222222"
radius: 16
width: children[0].width + radius*2
height: children[0].height
component DefaultTransition: Behavior {
enabled: root.up
NumberAnimation {
duration: 500
easing.type: Easing.OutQuint
}
}
DefaultTransition on width { }
DefaultTransition on height { }
property int index: 0
HoverHandler { onHoveredChanged: if (!hovered) {
root.close()
}}
onIndexChanged: {
width = children[index].width + radius*2
height = children[index].height
for (const [i, child] of children.entries()) {
if (i === index) {
if (root.up) child.fadeIn.start()
else child.opacity = 1
}
else {
if (root.up) child.fadeOut.start()
else child.opacity = 0
}
}
}
component Display: Item {
id: display
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
width: children[0].width
height: children[0].height
opacity: 0
property var fadeIn: SequentialAnimation {
PauseAnimation {duration: 200}
NumberAnimation {
target: display
property: "opacity"
to: 1
duration: 300
easing.type: Easing.OutQuint
}
onStarted: fadeOut.stop()
}
property var fadeOut: NumberAnimation {
target: display
property: "opacity"
to: 0
duration: 300
onStarted: fadeIn.stop()
easing.type: Easing.OutQuint
}
property var entry: NumberAnimation {
target: display
property: "anchors.topMargin"
to: 0
duration: 300
easing.type: Easing.OutQuint
onStarted: exit.stop()
}
property var exit: NumberAnimation {
target: display
property: "anchors.topMargin"
to: -height
duration: 300
easing.type: Easing.InQuint
onStarted: entry.stop()
}
}
Display { opacity: 1; Shell.Status {
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
} }
Display { Shell.Launcher {
onShowChanged: if (show) background.index = 1; else background.index = 0
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
} }
property var entry: NumberAnimation {
target: background
property: "height"
to: background.children[background.index].height
duration: 300
easing.type: Easing.OutQuint
onStarted: {
root.implicitHeight = 200
background.exit.stop()
root.up = true
for (const child of background.children) {
child.entry.start()
}
}
}
property var exit: NumberAnimation {
target: background
property: "height"
to: 0
duration: 300
easing.type: Easing.InQuint
onStarted: {
background.entry.stop()
for (const child of background.children) {
child.exit.start()
}
}
onFinished: {
root.implicitHeight = 1
root.up = false
background.index = 0
}
}
}
HoverHandler { onHoveredChanged: if (hovered) {
root.open()
}}
}