pragma ComponentBehavior: Bound import QtQuick import QtQuick.Window import Quickshell import Quickshell.Io import Quickshell.Services.Notifications import "../.." as N Scope { id: root property int layoutFrames: 0 property var audioSliderIdentity: null property var networkInputIdentity: null property var notificationInputIdentity: null function tree(item: var): var { let items = [item]; for (const child of item.children || []) items = items.concat(tree(child)); return items; } function ownedTree(item: var): var { const items = []; function visit(object) { if (!object || items.indexOf(object) >= 0) return; items.push(object); for (const child of object.children || []) visit(child); const data = typeof object.data === "function" ? [] : (object.data || []); for (const datum of data) visit(datum); } visit(item); return items; } function host(): var { return ownedTree(bar).find(function (item) { return item.activeName !== undefined && item.cardFrame !== undefined; }) || null; } function rect(item: var, window: var): var { if (!item || !window) return null; const point = item.mapToItem(window.contentItem, 0, 0); return { "x": point.x, "y": point.y, "width": item.width, "height": item.height }; } function chip(name: string): var { return tree(bar.contentItem).find(function (item) { return item.visible && item.popoutName === name; }) || null; } function chipState(name: string): var { const item = chip(name); return { "rect": rect(item, bar), "hovered": !!item && tree(item).some(function (child) { return child.visible && child.containsMouse === true; }) }; } function panelFor(name: string, objects: var): var { return objects.find(function (item) { if (!item.visible) return false; if (name === "clipboard") return item.pasteRequested !== undefined && item.selectedRow !== undefined; if (name === "launcher") return item.rightWidth !== undefined && item.selectedActions !== undefined; if (name === "audio") return item.expandedSink !== undefined && item.expandedSource !== undefined; if (name === "media") return item.transportActions !== undefined && item.canSeek !== undefined; if (name === "tray") return item.hasItems !== undefined && item.primary !== undefined; if (name === "power") return item.destructiveActions !== undefined && item.safeActions !== undefined; if (name === "connectivity") return item.expandedSsid !== undefined && item.networkState !== undefined; if (name === "notifications") return item.groups !== undefined && item.toggleDnd !== undefined; if (name === "brightness") return item.hasGlobal !== undefined && item.displays !== undefined; if (name === "vault") return item.locked !== undefined && item.focusPrimary !== undefined; if (name === "noko") return item.copiedIndex !== undefined && item.waveTick !== undefined; return false; }) || null; } function named(objects: var, name: string): var { return objects.find(function (item) { return item.visible && item.objectName === name; }) || null; } function accessible(objects: var, name: string): var { return objects.find(function (item) { return item.visible && (item.accessibleName === name || item.Accessible.name === name); }) || null; } function placeholder(objects: var, name: string): var { return objects.find(function (item) { return item.visible && item.placeholderText === name; }) || null; } function button(objects: var, text: string): var { return objects.find(function (item) { return item.visible && item.clicked !== undefined && item.text === text; }) || null; } function control(item: var, window: var): var { if (!item) return null; return { "rect": rect(item, window), "enabled": item.enabled, "focused": item.input ? item.input.activeFocus : item.activeFocus }; } NotificationServer {} N.Theme { id: palette lowPowerMode: true } N.Time { id: clock } N.Format { id: format } N.Usage { id: usage } N.Niri { id: niri } N.Resources { id: resources upower: null powerProfiles: null brightnessService: null } N.Privilege { id: privilege } N.Clipboard { id: clipboard } N.Brightness { id: brightness } N.Vault { id: vault clipboardService: clipboard panelOpen: surfaces.popoutOpen && surfaces.popoutName === "vault" } N.Launcher { id: launcher } N.Audio { id: audio } N.Media { id: media } N.Tray { id: tray } N.Power { id: power } N.Network { id: network } N.Bluetooth { id: bluetooth } N.Noko { id: noko } N.Notifications { id: notifications } N.SurfaceManager { id: surfaces powerService: power mediaService: media trayService: tray } Connections { target: surfaces function onPopoutNameChanged() { root.layoutFrames = 0; } } N.Bar { id: bar theme: palette niriService: niri timeService: clock resourceService: resources notificationService: notifications trayService: tray mediaService: media clipboardService: clipboard vaultService: vault launcherService: launcher audioService: audio brightnessService: brightness networkService: network bluetoothService: bluetooth powerService: power privilegeService: privilege idleService: null lockService: null nokoService: noko surfaces: surfaces usageService: usage caldavService: null formatService: format orientation: "horizontal" FrameAnimation { running: surfaces.popoutOpen && root.layoutFrames < 4 onTriggered: root.layoutFrames++ } } // var parameters keep synthetic-only fixture API out of production type contracts. function externalEvent(event: string, clipboardFixture: var, audioFixture: var, mediaFixture: var, networkFixture: var, notificationFixture: var, brightnessFixture: var): void { if (event === "vertical" || event === "horizontal") bar.orientation = event; else if (event.startsWith("brightness-state:")) brightnessFixture.snapshotOverride = JSON.parse(event.slice("brightness-state:".length)); else if (event === "brightness-update") brightnessFixture.level = 0.25; else if (event === "brightness-unavailable") brightnessFixture.available = false; else if (event === "clipboard-update") clipboardFixture.externalUpdate(); else if (event === "audio-many") audioFixture.manyDevices = true; else if (event === "audio-update") audioFixture.externalUpdate(); else if (event === "media-restrict") mediaFixture.restrictCapabilities(); else if (event === "media-empty" || event === "media-populated") mediaFixture.empty = event === "media-empty"; else if (event === "tray-empty" || event === "tray-populated") tray.empty = event === "tray-empty"; else if (event === "power-desktop" || event === "power-laptop") power.batteryPresent = event === "power-laptop"; else if (event === "power-devices") power.externalDevices = [ { "name": "fixture mouse", "percent": 60 }, { "name": "fixture keyboard", "percent": 90 }, { "name": "fixture headset", "percent": 45 } ]; else if (event === "power-no-devices") power.externalDevices = []; else if (event === "network-update") networkFixture.externalUpdate(); else if (event === "notification-update") notificationFixture.externalUpdate(); } function state(clipboardFixture: var, launcherFixture: var, audioFixture: var, mediaFixture: var, trayFixture: var, powerFixture: var, networkFixture: var, notificationFixture: var, brightnessFixture: var, nokoFixture: var): string { const popoutHost = host(); const objects = popoutHost ? tree(popoutHost.contentItem) : []; const popup = JSON.parse(surfaces.popoutState()); const panel = panelFor(popup.name, objects); const clipboardSearch = named(objects, "searchField"); const launcherSearch = placeholder(objects, "search apps"); const audioSlider = accessible(objects, "fixture output output volume"); const mediaPrevious = accessible(objects, "previous track"); const mediaPlay = accessible(objects, mediaFixture.playing ? "pause" : "play"); const mediaNext = accessible(objects, "next track"); const mediaShuffle = accessible(objects, "shuffle"); const mediaRepeat = accessible(objects, "repeat"); const trayPrimary = accessible(objects, "activate fixture tray"); const traySecondary = accessible(objects, "secondary action for fixture tray"); const trayMenu = accessible(objects, "menu for fixture tray"); const reboot = button(objects, "reboot"); const poweroff = button(objects, "power off"); const networkRow = accessible(objects, "expand fixture-secure") || accessible(objects, "collapse fixture-secure"); const networkInput = placeholder(objects, "passphrase"); const notificationInput = placeholder(objects, "reply to fixture"); const brightnessSlider = accessible(objects, "fixture display brightness"); const vaultSearch = placeholder(objects, "search vault"); const capture = button(objects, noko.listening ? "stop capture" : "start capture"); const historyCopy = button(objects, "copy") || button(objects, "copied"); const vaultSelected = objects.find(item => item.visible && item.modelData !== undefined && item.modelData.id === vault.selectedId && item.active === true && item.clicked !== undefined); const vaultResults = objects.find(item => item.restoringScroll !== undefined && item.revealItem !== undefined); if (audioSlider && !audioSliderIdentity) audioSliderIdentity = audioSlider; if (networkInput && !networkInputIdentity) networkInputIdentity = networkInput; if (notificationInput && !notificationInputIdentity) notificationInputIdentity = notificationInput; return JSON.stringify({ "width": surfaces.screenWidth, "height": surfaces.screenHeight, "orientation": surfaces.orientation, "barWidth": bar.width, "clock": rect(tree(bar.contentItem).find(item => item.visible && item.stacked !== undefined && item.hh !== undefined), bar), "layoutFrames": root.layoutFrames, "barEdge": rect(tree(bar.contentItem).find(item => item.color !== undefined && Qt.colorEqual(item.color, palette.withAlpha(palette.ui.accent, 0.55))), bar), "popup": { "open": popup.open, "windowActive": !!popoutHost && !!popoutHost.contentItem.Window.window && popoutHost.contentItem.Window.window.active, "phase": popup.phase, "name": popup.name, "lastCloseReason": popup.lastCloseReason, "card": popup.popupCardFrame.currentRect }, "panel": rect(panel, popoutHost), "chips": { "clipboard": chipState("clipboard"), "launcher": chipState("launcher"), "audio": chipState("audio"), "media": chipState("media"), "tray": chipState("tray"), "connectivity": chipState("connectivity"), "notifications": chipState("notifications"), "power": chipState("power"), "brightness": chipState("brightness"), "vault": chipState("vault"), "noko": chipState("noko") }, "clipboard": { "queryKnown": surfaces.panelStates.clipboard.query === "needle", "selectedKnown": surfaces.panelStates.clipboard.selectedId === "clip-needle", "visibleRows": panel && panel.rows !== undefined ? panel.rows.length : 0, "search": control(clipboardSearch, popoutHost), "copy": control(named(objects, "clipboardCopy"), popoutHost), "pin": control(named(objects, "clipboardPin"), popoutHost), "copies": clipboardFixture.copyCount, "pins": clipboardFixture.pinCount, "externalUpdates": clipboardFixture.externalUpdates }, "launcher": { "queryKnown": surfaces.panelStates.launcher.query === "terminal", "visibleRows": panel && panel.results !== undefined ? panel.results.length : 0, "search": control(launcherSearch, popoutHost), "activations": launcherFixture.activations, "actionActivations": launcherFixture.actionActivations }, "audio": { "slider": control(audioSlider, popoutHost), "pressed": audioSlider ? audioSlider.pressed : false, "displayedValue": audioSlider ? audioSlider.displayedValue : -1, "sameSlider": !!audioSlider && audioSlider === audioSliderIdentity, "volume": audioFixture.volume, "writes": audioFixture.volumeWrites, "externalUpdates": audioFixture.externalUpdates, "scrollY": panel && popup.name === "audio" ? panel.contentY : -1, "focusedRect": rect(objects.find(item => item.controlFocused === true || (item.moved !== undefined && item.activeFocus)), popoutHost) }, "media": { "emptyState": rect(named(objects, "mediaEmptyState"), popoutHost), "previous": control(mediaPrevious, popoutHost), "play": control(mediaPlay, popoutHost), "next": control(mediaNext, popoutHost), "shuffle": control(mediaShuffle, popoutHost), "repeat": control(mediaRepeat, popoutHost), "actions": mediaFixture.actionCount, "lastWasPlay": mediaFixture.lastAction === "playPause", "restricted": mediaFixture.restricted }, "tray": { "emptyState": rect(named(objects, "trayEmptyState"), popoutHost), "primary": control(trayPrimary, popoutHost), "secondary": control(traySecondary, popoutHost), "menu": control(trayMenu, popoutHost), "primaryCount": trayFixture.primaryCount, "secondaryCount": trayFixture.secondaryCount, "menuCount": trayFixture.menuCount }, "power": { "battery": rect(named(objects, "batteryStatus"), popoutHost), "devices": rect(named(objects, "externalDeviceBatteryChips"), popoutHost), "confirmation": rect(named(objects, "powerConfirmation"), popoutHost), "cancel": control(button(objects, "cancel"), popoutHost), "confirm": control(accessible(objects, `confirm ${panel && popup.name === "power" ? panel.actionLabel(powerFixture.pendingConfirmation) : ""}`), popoutHost), "immediate": powerFixture.immediateCount, "rebootTooltip": reboot ? reboot.tooltipVisible : false, "actions": objects.filter(item => item.visible && item.modelData !== undefined && item.risky !== undefined).map(item => ({ "name": item.modelData, "control": control(item, popoutHost) })), "reboot": control(reboot, popoutHost), "poweroff": control(poweroff, popoutHost), "armed": powerFixture.pendingConfirmation.length > 0, "armedAction": powerFixture.pendingConfirmation, "arms": powerFixture.armCount, "cancels": powerFixture.cancelCount, "confirms": powerFixture.confirmCount }, "connectivity": { "row": control(networkRow, popoutHost), "input": control(networkInput, popoutHost), "hasInput": networkInput ? networkInput.text.length > 0 : false, "inputMasked": networkInput ? networkInput.echoMode === TextInput.Password : false, "sameInput": !!networkInput && networkInput === networkInputIdentity, "expanded": panel && panel.expandedSsid === "fixture-secure", "generation": networkFixture.generation, "connects": networkFixture.connectCount }, "brightness": { "displayIds": panel && popup.name === "brightness" ? panel.displays.map(row => row.id) : [], "setupVisible": !!button(objects, "setup ddc"), "builtIn": control(accessible(objects, "built-in brightness"), popoutHost), "slider": control(brightnessSlider, popoutHost), "value": brightnessSlider ? brightnessSlider.displayedValue : -1, "level": brightnessFixture.level, "writes": brightnessFixture.writes }, "noko": { "capture": control(capture, popoutHost), "copy": control(historyCopy, popoutHost), "copied": panel && popup.name === "noko" && panel.copiedIndex === 0, "listening": noko.listening, "triggers": nokoFixture.triggers, "scrollY": panel && popup.name === "noko" ? panel.scrollView.contentY : -1, "viewport": panel && popup.name === "noko" ? rect(panel.scrollView, popoutHost) : null, "focusedRect": rect(objects.find(item => item.controlFocused === true), popoutHost) }, "vault": { "search": control(vaultSearch, popoutHost), "selected": rect(vaultSelected, popoutHost), "viewport": rect(vaultResults, popoutHost), "selectedId": vault.selectedId, "scrollY": vault.resultScrollPosition, "count": vault.visibleItems.length, "detailEmpty": !vault.detail.id, "queryKnown": vault.query === "absent" }, "notifications": { "input": control(notificationInput, popoutHost), "hasInput": notificationInput ? notificationInput.text.length > 0 : false, "sameInput": !!notificationInput && notificationInput === notificationInputIdentity, "knownDraft": notificationFixture.receivedKnownDraft, "edits": notificationFixture.editCount, "sends": notificationFixture.sendCount, "failed": notificationFixture.replyError.length > 0, "externalUpdates": notificationFixture.externalUpdates } }); } IpcHandler { target: "uiTest" function external(event: string): void { root.externalEvent(event, clipboard, audio, media, network, notifications, brightness); } function snapshot(): string { return root.state(clipboard, launcher, audio, media, tray, power, network, notifications, brightness, noko); } } }