Files
Panama/config/dot/quickshell/modules/dock/DockPickOverlay.qml
T

172 lines
5.6 KiB
QML

// "Add application to dock", reachable without opening Settings.
//
// The Settings page is the place to curate the whole dock -- reorder it, unpin
// things, change how it hides. Adding one application is a single decision made
// while looking at the dock, and routing it through a settings window means
// finding the page, then the card, then the box. This is that box, on its own.
//
// It embeds the same DockAppPicker the Dock page uses rather than growing a
// second search: the exclusion of already-pinned applications, the icon lookup
// and the "nothing until you type" behavior are all already there, and two
// copies of them would drift.
import Quickshell
import Quickshell.Wayland
import QtQuick
import qs.config
import qs.services
import qs.modules.settings
import qs.widgets
PanelWindow {
id: root
readonly property bool open: ShellState.dockPickerOpen
// The one place a pin is added from outside the Settings page. Returns
// false for an id nothing installs, so the IPC caller hears about a typo
// instead of the dock quietly gaining a hole -- DockBody drops a pin it
// cannot resolve, so a bad id is invisible at runtime.
function pin(id: string): bool {
const wanted = String(id ?? "").trim();
if (!wanted || !DesktopEntries.byId(wanted))
return false;
const stored = Settings.dockPinned;
if (stored.indexOf(wanted) >= 0)
return true;
DesktopPreferences.set("dockPinned", stored.concat([wanted]));
return true;
}
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
exclusiveZone: 0
exclusionMode: ExclusionMode.Ignore
// Blurred by the `^qs-popover` rule in hypr/rules.lua; the scrim is painted
// here rather than added to that rule, the same way the cheatsheet does it.
WlrLayershell.namespace: "qs-popover-dock-picker"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: root.open
? WlrKeyboardFocus.Exclusive
: WlrKeyboardFocus.None
// Stays mapped for the length of the close animation, or it vanishes
// instantly and only the opening is ever seen.
property bool mapped: false
visible: root.mapped
onOpenChanged: {
if (root.open) {
unmapTimer.stop();
root.mapped = true;
picker.grab();
} else {
unmapTimer.restart();
}
}
Timer {
id: unmapTimer
interval: Theme.durNormal
onTriggered: root.mapped = false
}
Rectangle {
anchors.fill: parent
color: Theme.alpha(Theme.bgDark, Theme.overlayAlpha)
opacity: root.open ? 1 : 0
Behavior on opacity { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutCubic } }
MouseArea {
anchors.fill: parent
onClicked: ShellState.close()
}
}
Rectangle {
id: card
anchors.horizontalCenter: parent.horizontalCenter
// High rather than centred: the list grows downwards as you type, and a
// centred card walks up the screen while you are reading it.
y: Math.round(parent.height * 0.18)
width: Math.min(root.width - 120, 520)
height: header.height + picker.implicitHeight + 56
radius: Theme.popoverRadius
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
border.width: 1
border.color: Theme.alpha(Theme.fg, 0.1)
opacity: root.open ? 1 : 0
scale: root.open ? 1 : 0.98
Behavior on opacity { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutCubic } }
Behavior on scale { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutCubic } }
PrismEdge {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
inset: Theme.popoverRadius
}
// Clicks on the card itself must not fall through to the scrim.
MouseArea { anchors.fill: parent }
Item {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 22
height: title.implicitHeight
Text {
id: title
text: "Add application to dock"
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.DemiBold
}
Text {
anchors.right: parent.right
anchors.verticalCenter: title.verticalCenter
text: "Esc to close"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
}
}
DockAppPicker {
id: picker
anchors.top: header.bottom
anchors.topMargin: 16
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 22
anchors.rightMargin: 22
pinned: Settings.dockPinned
onPicked: id => {
root.pin(id);
ShellState.close();
}
}
}
// A Shortcut rather than Keys.onEscapePressed on an item: the search box
// owns Escape while it has focus (it clears the query first), and a key
// handler on an ancestor would never see the second press.
Shortcut {
sequence: "Escape"
enabled: root.open
onActivated: ShellState.close()
}
}