mirror of
https://github.com/Nexoniarz/nexoniarz-config-nixos.git
synced 2026-11-03 04:24:27 +00:00
218 lines
8 KiB
QML
218 lines
8 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Services.Pipewire
|
|
import Quickshell.Services.SystemTray
|
|
import Quickshell.Widgets
|
|
import Quickshell.Wayland
|
|
|
|
PanelWindow {
|
|
id: root
|
|
required property var screen
|
|
property string activePanel: ""
|
|
property bool expanded: false
|
|
|
|
// Keeps the default sink's PwNode subscribed so audio properties
|
|
// (volume/muted) actually receive live updates.
|
|
PwObjectTracker {
|
|
objects: Pipewire.defaultAudioSink ? [Pipewire.defaultAudioSink] : []
|
|
}
|
|
|
|
anchors { top: true; right: true; bottom: true }
|
|
// Collapsed = zero reserved space; expand/collapse is triggered from
|
|
// the taskbar (BottomBar), not a button on this bar itself.
|
|
implicitWidth: expanded ? 220 : 0
|
|
exclusiveZone: expanded ? 220 : 0
|
|
color: Theme.bg
|
|
// Never take keyboard focus — this bar has no text input, and leaving
|
|
// it focusable was stealing the keyboard grab from things like slurp's
|
|
// screenshot selection overlay.
|
|
focusable: false
|
|
|
|
// Only "network" needs the popup's keyboard grab (hotspot/wifi
|
|
// password fields).
|
|
function needsGrab(panel) { return panel === "network"; }
|
|
|
|
function toggle(panel) {
|
|
if (root.activePanel === panel) {
|
|
root.activePanel = "";
|
|
return;
|
|
}
|
|
if (root.activePanel !== "" && root.needsGrab(root.activePanel) !== root.needsGrab(panel)) {
|
|
// See LeftBar.qml's toggle() for why: an xdg_popup's grab
|
|
// can't be toggled on an already-mapped surface, only
|
|
// established fresh — so force a close+reopen when crossing
|
|
// that boundary instead of just changing the panel in place.
|
|
root.activePanel = "";
|
|
Qt.callLater(function () { root.activePanel = panel; });
|
|
return;
|
|
}
|
|
root.activePanel = panel;
|
|
}
|
|
|
|
Column {
|
|
visible: root.expanded
|
|
anchors.top: parent.top
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.topMargin: 14
|
|
spacing: 14
|
|
width: parent.width - 24
|
|
|
|
DigitalClock {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
// System tray (StatusNotifierItem) — background/status apps like
|
|
// Steam, Discord, KDE Connect that register a tray icon. Sits
|
|
// right under the clock, separate from the categorized sections
|
|
// below the divider.
|
|
Row {
|
|
id: trayRow
|
|
visible: SystemTray.items.values.length > 0
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: 12
|
|
|
|
Repeater {
|
|
model: SystemTray.items.values
|
|
delegate: IconImage {
|
|
id: trayIcon
|
|
implicitSize: 18
|
|
source: Quickshell.iconPath(modelData.icon, "")
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: (mouse) => {
|
|
if (mouse.button === Qt.RightButton) {
|
|
if (modelData.hasMenu) {
|
|
var pos = trayIcon.mapToItem(null, 0, trayIcon.height);
|
|
modelData.display(root, pos.x, pos.y);
|
|
} else {
|
|
modelData.secondaryActivate();
|
|
}
|
|
} else {
|
|
modelData.activate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle { width: parent.width; height: 1; color: Theme.borderDim }
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: 6
|
|
|
|
Text {
|
|
text: "CONNECTIVITY"
|
|
color: Theme.fgDim
|
|
font.family: "JetBrainsMono Nerd Font"
|
|
font.pixelSize: 9
|
|
font.bold: true
|
|
}
|
|
SidebarRow {
|
|
label: "Wi-Fi & Network"
|
|
active: root.activePanel === "network"
|
|
onClicked: root.toggle("network")
|
|
}
|
|
SidebarRow {
|
|
label: "Bluetooth"
|
|
active: root.activePanel === "bluetooth"
|
|
onClicked: root.toggle("bluetooth")
|
|
}
|
|
SidebarRow {
|
|
label: "Messages"
|
|
active: root.activePanel === "messages"
|
|
onClicked: root.toggle("messages")
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: 6
|
|
|
|
Text {
|
|
text: "SYSTEM"
|
|
color: Theme.fgDim
|
|
font.family: "JetBrainsMono Nerd Font"
|
|
font.pixelSize: 9
|
|
font.bold: true
|
|
}
|
|
SidebarRow {
|
|
label: "Screenshot"
|
|
active: root.activePanel === "screenshot"
|
|
onClicked: root.toggle("screenshot")
|
|
}
|
|
SidebarRow {
|
|
label: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.audio && Pipewire.defaultAudioSink.audio.muted)
|
|
? "Volume (muted)" : "Volume"
|
|
active: root.activePanel === "volume"
|
|
onClicked: root.toggle("volume")
|
|
}
|
|
SidebarRow {
|
|
label: "Brightness"
|
|
active: root.activePanel === "brightness"
|
|
onClicked: root.toggle("brightness")
|
|
}
|
|
SidebarRow {
|
|
label: "Devices"
|
|
active: root.activePanel === "devices"
|
|
onClicked: root.toggle("devices")
|
|
}
|
|
}
|
|
}
|
|
|
|
SidebarRow {
|
|
visible: root.expanded
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 14
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: parent.width - 24
|
|
label: "Power"
|
|
fg: Theme.danger
|
|
active: root.activePanel === "power"
|
|
onClicked: root.toggle("power")
|
|
}
|
|
|
|
PanelWindow {
|
|
id: flyout
|
|
screen: root.screen
|
|
anchors { top: true; right: true }
|
|
// No margin here on purpose: Hyprland already insets layer-shell
|
|
// surfaces past other surfaces' exclusive zones on the same edge
|
|
// (confirmed via `hyprctl layers` — BottomBar gets the same
|
|
// treatment for free, spanning right up to x=1700 with zero
|
|
// margin of its own). A margin of 220 here was stacking on top
|
|
// of that automatic inset, pushing the flyout 220px further left
|
|
// than intended and leaving a ~220px gap between it and the
|
|
// sidebar.
|
|
visible: root.activePanel !== ""
|
|
implicitWidth: 320
|
|
implicitHeight: content.implicitHeight
|
|
exclusiveZone: 0
|
|
color: "transparent"
|
|
WlrLayershell.layer: WlrLayer.Overlay
|
|
// On-demand layer-shell focus, not an xdg_popup grab: this used to
|
|
// be a PopupWindow with grabFocus, but an xdg_popup grab gets
|
|
// dismissed by the compositor the instant anything else so much as
|
|
// touches focus/surface state elsewhere (confirmed: even an
|
|
// unrelated hyprpaper/mpvpaper surface change was enough), which
|
|
// silently closed the whole flyout AND dropped keyboard input —
|
|
// that's what let keystrokes fall through to whatever window was
|
|
// focused before the flyout opened instead of reaching the text
|
|
// field. OnDemand only claims focus when something inside is
|
|
// actually clicked, and isn't torn down by unrelated surfaces.
|
|
// Panels with no text input don't need it at all.
|
|
WlrLayershell.keyboardFocus: root.needsGrab(root.activePanel)
|
|
? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
|
|
|
RightFlyout {
|
|
id: content
|
|
anchors.fill: parent
|
|
activePanel: root.activePanel
|
|
onRequestClose: root.activePanel = ""
|
|
}
|
|
}
|
|
}
|