Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
// The contents of the quick settings popover — GNOME's system menu, in order:
|
||||
// toggle grid, detail lists, sliders, footer.
|
||||
//
|
||||
// Kept separate from QuickSettings.qml (the PanelWindow) so it can be dropped
|
||||
// into a FloatingWindow for testing: layer-shell surfaces cannot map outside a
|
||||
// wlroots compositor.
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Networking
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: Theme.popoverWidth
|
||||
implicitHeight: content.implicitHeight + Theme.popoverPadding * 2
|
||||
|
||||
// "" | "wifi" | "bluetooth" | "sink" | "source". Only one detail list is
|
||||
// open at a time, so the panel never grows past the screen.
|
||||
property string expandedSection: ""
|
||||
|
||||
function expand(name: string): void {
|
||||
root.expandedSection = root.expandedSection === name ? "" : name;
|
||||
}
|
||||
|
||||
// Collapse everything — called when the popover closes so it reopens in a
|
||||
// known state.
|
||||
function reset(): void {
|
||||
root.expandedSection = "";
|
||||
}
|
||||
|
||||
// ── System state ────────────────────────────────────────────────────────
|
||||
|
||||
readonly property var wifiDevice: {
|
||||
for (const device of Networking.devices.values) {
|
||||
if (device.type === DeviceType.Wifi)
|
||||
return device;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
readonly property var wiredDevice: {
|
||||
for (const device of Networking.devices.values) {
|
||||
if (device.type === DeviceType.Wired && device.connected)
|
||||
return device;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
readonly property string ssid: {
|
||||
if (!root.wifiDevice || !root.wifiDevice.networks)
|
||||
return "";
|
||||
for (const network of root.wifiDevice.networks.values) {
|
||||
if (network.connected)
|
||||
return network.name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
readonly property var btAdapter: Bluetooth.defaultAdapter
|
||||
|
||||
readonly property string btSummary: {
|
||||
if (!root.btAdapter)
|
||||
return "Unavailable";
|
||||
if (!root.btAdapter.enabled)
|
||||
return "Off";
|
||||
for (const device of Bluetooth.devices.values) {
|
||||
if (device.connected)
|
||||
return device.name || device.address;
|
||||
}
|
||||
return "On";
|
||||
}
|
||||
|
||||
readonly property int cellWidth: (content.width - Theme.itemSpacing) / 2
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.popoverPadding
|
||||
spacing: Theme.itemSpacing
|
||||
|
||||
// ── Toggle grid ─────────────────────────────────────────────────────
|
||||
Grid {
|
||||
columns: 2
|
||||
spacing: Theme.itemSpacing
|
||||
|
||||
Toggle {
|
||||
width: root.cellWidth
|
||||
icon: Networking.wifiEnabled ? "network-wireless-symbolic" : "network-wireless-offline-symbolic"
|
||||
label: "Wi-Fi"
|
||||
active: Networking.wifiEnabled
|
||||
enabled: Networking.wifiHardwareEnabled
|
||||
sublabel: {
|
||||
if (!Networking.wifiEnabled)
|
||||
return "Off";
|
||||
return root.ssid !== "" ? root.ssid : "Not connected";
|
||||
}
|
||||
onToggled: Networking.wifiEnabled = !Networking.wifiEnabled
|
||||
onExpanded: root.expand("wifi")
|
||||
}
|
||||
|
||||
Toggle {
|
||||
width: root.cellWidth
|
||||
icon: root.btAdapter && root.btAdapter.enabled ? "bluetooth-active-symbolic" : "bluetooth-disabled-symbolic"
|
||||
label: "Bluetooth"
|
||||
active: root.btAdapter !== null && root.btAdapter.enabled
|
||||
enabled: root.btAdapter !== null
|
||||
sublabel: root.btSummary
|
||||
onToggled: {
|
||||
if (root.btAdapter)
|
||||
root.btAdapter.enabled = !root.btAdapter.enabled;
|
||||
}
|
||||
onExpanded: root.expand("bluetooth")
|
||||
}
|
||||
|
||||
Toggle {
|
||||
width: root.cellWidth
|
||||
icon: "preferences-desktop-screensaver-symbolic"
|
||||
label: "Caffeine"
|
||||
sublabel: Caffeine.enabled ? "Staying awake" : "Off"
|
||||
active: Caffeine.enabled
|
||||
onToggled: Caffeine.toggle()
|
||||
}
|
||||
|
||||
Toggle {
|
||||
width: root.cellWidth
|
||||
icon: "night-light-symbolic"
|
||||
label: "Night Light"
|
||||
active: NightLight.active
|
||||
sublabel: {
|
||||
if (NightLight.automatic)
|
||||
return NightLight.active ? "Scheduled · on" : "Scheduled";
|
||||
return NightLight.active ? NightLight.temperature + "K" : "Off";
|
||||
}
|
||||
onToggled: NightLight.toggle()
|
||||
// No detail list to open, so the secondary action flips the
|
||||
// schedule on and off instead.
|
||||
onExpanded: NightLight.automatic = !NightLight.automatic
|
||||
}
|
||||
}
|
||||
|
||||
RowButton {
|
||||
width: content.width
|
||||
icon: "preferences-system-time-symbolic"
|
||||
iconFallback: "appointment-soon-symbolic"
|
||||
label: FocusSession.active ? "Focus · " + FocusSession.workspaceLabel : "Start focus session"
|
||||
sublabel: FocusSession.active ? FocusSession.remainingText + (FocusSession.paused ? " · paused" : " remaining") : Settings.focusDurationMinutes + " minutes · current workspace"
|
||||
selected: FocusSession.active
|
||||
onClicked: {
|
||||
ShellState.close();
|
||||
FocusSession.reveal();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Detail lists ────────────────────────────────────────────────────
|
||||
Section {
|
||||
width: content.width
|
||||
expanded: root.expandedSection === "wifi"
|
||||
|
||||
WifiList {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
device: root.wifiDevice
|
||||
active: root.expandedSection === "wifi"
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
width: content.width
|
||||
expanded: root.expandedSection === "bluetooth"
|
||||
|
||||
BluetoothList {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
active: root.expandedSection === "bluetooth"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: content.width
|
||||
height: 1
|
||||
color: Theme.alpha(Theme.fg, 0.1)
|
||||
}
|
||||
|
||||
// ── Sliders ─────────────────────────────────────────────────────────
|
||||
AudioSlider {
|
||||
width: content.width
|
||||
node: Pipewire.defaultAudioSink
|
||||
output: true
|
||||
expanded: root.expandedSection === "sink"
|
||||
onToggleExpanded: root.expand("sink")
|
||||
}
|
||||
|
||||
Section {
|
||||
width: content.width
|
||||
expanded: root.expandedSection === "sink"
|
||||
|
||||
AudioDeviceList {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
output: true
|
||||
}
|
||||
}
|
||||
|
||||
AudioSlider {
|
||||
width: content.width
|
||||
node: Pipewire.defaultAudioSource
|
||||
output: false
|
||||
expanded: root.expandedSection === "source"
|
||||
onToggleExpanded: root.expand("source")
|
||||
}
|
||||
|
||||
Section {
|
||||
width: content.width
|
||||
expanded: root.expandedSection === "source"
|
||||
|
||||
AudioDeviceList {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
output: false
|
||||
}
|
||||
}
|
||||
|
||||
BrightnessControl {
|
||||
width: content.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: content.width
|
||||
height: 1
|
||||
color: Theme.alpha(Theme.fg, 0.1)
|
||||
}
|
||||
|
||||
// ── Footer ──────────────────────────────────────────────────────────
|
||||
Item {
|
||||
width: content.width
|
||||
height: 36
|
||||
|
||||
ThemedIcon {
|
||||
id: avatar
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 20
|
||||
icon: "avatar-default-symbolic"
|
||||
iconFallback: "user-info-symbolic"
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.left: avatar.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: actions.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Quickshell.env("USER") || "user"
|
||||
color: Theme.fg
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Row {
|
||||
id: actions
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
|
||||
IconButton {
|
||||
size: 32
|
||||
iconSize: 17
|
||||
icon: "preferences-system-symbolic"
|
||||
onClicked: {
|
||||
ShellState.close();
|
||||
ShellState.openSettings("home");
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
size: 32
|
||||
iconSize: 17
|
||||
icon: "system-shutdown-symbolic"
|
||||
onClicked: ShellState.open("powermenu")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user