Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
// One app's worth of notifications in the date menu, GNOME-style: a header
|
||||
// carrying the app's identity and a clear button, over a collapsible stack of
|
||||
// cards.
|
||||
//
|
||||
// The header is the disclosure control in its entirety — a 4px chevron is a
|
||||
// poor click target, and GNOME lets you hit the whole row.
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.modules.notifications
|
||||
import qs.modules.quicksettings
|
||||
import qs.widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
// One entry from Notifs.groups: { app, icon, desktopEntry, items }.
|
||||
required property var group
|
||||
|
||||
property bool expanded: true
|
||||
|
||||
signal toggled
|
||||
signal cleared
|
||||
|
||||
spacing: 4
|
||||
|
||||
// The app icon the notifications themselves declared, falling back to the
|
||||
// desktop entry — same resolution order NotificationCard uses.
|
||||
readonly property string iconSource: {
|
||||
if (root.group.icon)
|
||||
return Quickshell.iconPath(root.group.icon, true);
|
||||
const entry = DesktopEntries.byId(root.group.desktopEntry);
|
||||
return entry ? Quickshell.iconPath(entry.icon, true) : "";
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: header
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cardRadius - 4
|
||||
border.width: 0
|
||||
color: headerMouse.containsMouse ? Theme.alpha(Theme.fg, 0.07) : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: headerMouse.containsMouse ? Theme.durFast : Theme.durNormal
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggled()
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 6
|
||||
anchors.right: clearButton.left
|
||||
anchors.rightMargin: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 7
|
||||
|
||||
IconImage {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitSize: 15
|
||||
asynchronous: true
|
||||
source: root.iconSource
|
||||
visible: source !== ""
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.group.app
|
||||
color: Theme.fgDim
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
// A bare count next to a collapsed group is the only way to know
|
||||
// how much is hidden, so it is always shown once there is more
|
||||
// than one.
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: countLabel.implicitWidth + 12
|
||||
height: 16
|
||||
radius: Theme.pillRadius
|
||||
border.width: 0
|
||||
visible: root.group.items.length > 1
|
||||
color: Theme.alpha(Theme.fg, 0.12)
|
||||
|
||||
Text {
|
||||
id: countLabel
|
||||
anchors.centerIn: parent
|
||||
text: root.group.items.length
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.features: Theme.tabularFigures
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: clearButton
|
||||
anchors.right: chevron.left
|
||||
anchors.rightMargin: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 22
|
||||
iconSize: 12
|
||||
tint: Theme.fgMuted
|
||||
icon: "edit-clear-all-symbolic"
|
||||
iconFallback: "window-close-symbolic"
|
||||
onClicked: root.cleared()
|
||||
}
|
||||
|
||||
ThemedIcon {
|
||||
id: chevron
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 12
|
||||
icon: "pan-down-symbolic"
|
||||
iconFallback: "go-down-symbolic"
|
||||
tint: Theme.fgMuted
|
||||
|
||||
// Pointing down when open, right when closed — the standard
|
||||
// disclosure direction, animated only on the click that caused it.
|
||||
rotation: root.expanded ? 0 : -90
|
||||
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
duration: Theme.durNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section animates its own height and hides itself at zero, which also
|
||||
// collapses the spacing the parent Column would otherwise leave behind.
|
||||
Section {
|
||||
width: parent.width
|
||||
expanded: root.expanded
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: root.group.items
|
||||
|
||||
NotificationCard {
|
||||
required property var modelData
|
||||
|
||||
width: parent.width
|
||||
compact: true
|
||||
notification: modelData
|
||||
onDismissed: Notifs.dismiss(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user