Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
// The dock — a replacement for Dash-to-Dock configured as: bottom edge, 48px
|
||||
// icons, intellihide against all windows, dot running-indicators, show-apps
|
||||
// button at the leading edge.
|
||||
//
|
||||
// Intellihide means the dock reserves no space at all (exclusiveZone 0) and
|
||||
// floats over windows, appearing when the pointer reaches the bottom edge or
|
||||
// when nothing is in the way.
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import qs.services
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
// Set by Variants when instantiated per-screen. Plain rather than
|
||||
// `required` so the dock can also be created standalone while testing;
|
||||
// Variants injects into the existing property either way.
|
||||
property var modelData: null
|
||||
screen: root.modelData
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
|
||||
// A dock that reserved space would not be intellihiding.
|
||||
exclusiveZone: 0
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
// Matched by the `qs-dock` layer rule in hypr/rules.lua — do not rename.
|
||||
WlrLayershell.namespace: "qs-dock"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
// ── Geometry ────────────────────────────────────────────────────────────
|
||||
// Height = room for the tooltip above the bar + the bar + the gap under it.
|
||||
readonly property int revealStripHeight: 3
|
||||
readonly property int bottomMargin: Theme.barGap
|
||||
readonly property int tooltipSpace: 34
|
||||
|
||||
implicitHeight: tooltipSpace + body.implicitHeight + bottomMargin
|
||||
|
||||
// ── Intellihide ─────────────────────────────────────────────────────────
|
||||
// Hyprland does not expose live toplevel geometry, so exact overlap cannot
|
||||
// be computed. "Is anything on this workspace at all" is the robust proxy,
|
||||
// and it is what Dash-to-Dock's all-windows intellihide felt like in
|
||||
// practice: an empty workspace keeps the dock out.
|
||||
readonly property bool workspaceOccupied: {
|
||||
const ws = Hyprland.focusedWorkspace;
|
||||
return !!ws && ws.toplevels.values.length > 0;
|
||||
}
|
||||
|
||||
readonly property bool wantRevealed: !Settings.dockAutohide || !workspaceOccupied || pointer.hovered
|
||||
|
||||
property bool revealed: true
|
||||
|
||||
onWantRevealedChanged: {
|
||||
if (wantRevealed) {
|
||||
hideTimer.stop();
|
||||
revealTimer.restart();
|
||||
} else {
|
||||
revealTimer.stop();
|
||||
hideTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: revealTimer
|
||||
interval: Settings.dockRevealDelayMs
|
||||
onTriggered: root.revealed = true
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: Settings.dockHideDelayMs
|
||||
onTriggered: root.revealed = false
|
||||
}
|
||||
|
||||
// Other modules (the bar, the capture overlay) read this.
|
||||
onRevealedChanged: ShellState.dockRevealed = revealed
|
||||
|
||||
// wantRevealed's first evaluation emits no change signal when it lands on
|
||||
// false (the default), so the initial state has to be taken explicitly —
|
||||
// otherwise a shell started on a busy workspace would leave the dock up.
|
||||
Component.onCompleted: {
|
||||
revealed = wantRevealed;
|
||||
ShellState.dockRevealed = revealed;
|
||||
}
|
||||
|
||||
// ── Input region ────────────────────────────────────────────────────────
|
||||
// Revealed: the bar plus everything below it, so crossing the gap under the
|
||||
// dock does not count as leaving. Hidden: a sliver along the screen edge,
|
||||
// which is the only thing that can still trigger the dock — every other
|
||||
// click passes straight through to the window underneath.
|
||||
mask: Region {
|
||||
item: maskItem
|
||||
}
|
||||
|
||||
Item {
|
||||
id: surface
|
||||
anchors.fill: parent
|
||||
|
||||
// Reports the pointer anywhere inside the input region. A HoverHandler
|
||||
// rather than a MouseArea because it keeps reporting while the icons'
|
||||
// own MouseAreas are hovered.
|
||||
HoverHandler {
|
||||
id: pointer
|
||||
}
|
||||
|
||||
Item {
|
||||
id: maskItem
|
||||
x: root.revealed ? body.x : 0
|
||||
y: root.revealed ? body.y : surface.height - root.revealStripHeight
|
||||
width: root.revealed ? body.width : surface.width
|
||||
height: root.revealed ? surface.height - body.y : root.revealStripHeight
|
||||
}
|
||||
|
||||
DockBody {
|
||||
id: body
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// Slides off the bottom edge when hidden.
|
||||
y: root.revealed ? root.tooltipSpace : surface.height
|
||||
opacity: root.revealed ? 1 : 0
|
||||
|
||||
// Asymmetric on purpose. Revealing is a response to something the
|
||||
// user just did, so it has to feel immediate — any delay there
|
||||
// reads as lag. Hiding is not a response to anything, so it can
|
||||
// take its time and stay calm in peripheral vision.
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: root.revealed ? Theme.durDockReveal : Theme.durNormal
|
||||
easing.type: root.revealed ? Easing.OutQuint : Easing.InCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: root.revealed ? Theme.durDockReveal : Theme.durNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user