mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-06 04:29:34 +02:00
147 lines
4.8 KiB
QML
147 lines
4.8 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import qs.Commons
|
|
|
|
// Wraps Noctalia panel entry points, which are plain Item content intended to
|
|
// be hosted inside Noctalia's SmartPanel. We host them as anchored PopupWindows
|
|
// so clicking a Noctalia bar widget behaves like native Omarchy popups instead
|
|
// of opening a tiled app window.
|
|
Item {
|
|
id: root
|
|
|
|
property string panelSource: ""
|
|
property var pluginApi: null
|
|
property var manifest: ({})
|
|
property string omarchyPath: ""
|
|
property var shell: null
|
|
property var pluginRegistry: null
|
|
property var barWidgetRegistry: null
|
|
property bool popupOpen: false
|
|
property bool closingFromShell: false
|
|
|
|
readonly property var anchorItem: pluginApi ? pluginApi.panelAnchorItem : null
|
|
readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null
|
|
readonly property string barPosition: {
|
|
if (shell && shell.barConfig && shell.barConfig.position) return String(shell.barConfig.position)
|
|
return "top"
|
|
}
|
|
|
|
function open(payloadJson) {
|
|
popupOpen = true
|
|
}
|
|
|
|
function close() {
|
|
closingFromShell = true
|
|
popupOpen = false
|
|
if (pluginApi) {
|
|
pluginApi.panelOpenScreen = null
|
|
pluginApi.panelAnchorItem = null
|
|
}
|
|
closingFromShell = false
|
|
}
|
|
|
|
function dismiss() {
|
|
if (!popupOpen) return
|
|
if (pluginApi && typeof pluginApi.closePanel === "function") pluginApi.closePanel(null)
|
|
else if (shell && manifest && manifest.id && typeof shell.hide === "function") shell.hide(manifest.id)
|
|
else close()
|
|
}
|
|
|
|
PopupWindow {
|
|
id: popup
|
|
visible: root.popupOpen
|
|
color: "transparent"
|
|
implicitWidth: Math.max(320, panelLoader.item && panelLoader.item.contentPreferredWidth
|
|
? panelLoader.item.contentPreferredWidth : 420)
|
|
implicitHeight: Math.max(260, panelLoader.item && panelLoader.item.contentPreferredHeight
|
|
? panelLoader.item.contentPreferredHeight : 520)
|
|
|
|
onVisibleChanged: {
|
|
if (!visible && root.popupOpen && !root.closingFromShell) root.dismiss()
|
|
}
|
|
|
|
// Do not use HyprlandFocusGrab here. Noctalia panels are larger,
|
|
// interactive surfaces with tabs and nested Flickables; focus grabs made
|
|
// internal clicks (notably tab switches) look like outside clicks and also
|
|
// interfered with wheel scrolling. Native Omarchy micro-popups can keep
|
|
// focus-grab dismissal, but compat panels behave more like pinned panels.
|
|
anchor {
|
|
id: popupAnchor
|
|
window: root.anchorWindow
|
|
adjustment: PopupAdjustment.Slide
|
|
edges: Edges.Top | Edges.Left
|
|
gravity: Edges.Bottom | Edges.Right
|
|
rect.width: 1
|
|
rect.height: 1
|
|
|
|
onAnchoring: {
|
|
var target = root.anchorItem
|
|
var window = root.anchorWindow
|
|
if (!target || !window) {
|
|
popupAnchor.rect.x = 0
|
|
popupAnchor.rect.y = 0
|
|
return
|
|
}
|
|
|
|
var popupWidth = popup.implicitWidth
|
|
var popupHeight = popup.implicitHeight
|
|
var margin = 8
|
|
var localX = target.width / 2 - popupWidth / 2
|
|
var localY = target.height + margin
|
|
|
|
if (root.barPosition === "bottom") {
|
|
localY = -popupHeight - margin
|
|
} else if (root.barPosition === "left") {
|
|
localX = target.width + margin
|
|
localY = target.height / 2 - popupHeight / 2
|
|
} else if (root.barPosition === "right") {
|
|
localX = -popupWidth - margin
|
|
localY = target.height / 2 - popupHeight / 2
|
|
}
|
|
|
|
var point = window.contentItem.mapFromItem(target, localX, localY)
|
|
popupAnchor.rect.x = Math.round(point.x)
|
|
popupAnchor.rect.y = Math.round(point.y)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Color.mSurface
|
|
border.color: Color.mOutline
|
|
border.width: 1
|
|
radius: 0
|
|
|
|
Loader {
|
|
id: panelLoader
|
|
anchors.fill: parent
|
|
source: root.panelSource
|
|
onLoaded: root.injectPanelProps()
|
|
onStatusChanged: {
|
|
if (status === Loader.Error) {
|
|
console.warn("noctalia panel failed for " + (root.manifest ? root.manifest.id : "") + ":", errorString())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onPluginApiChanged: injectPanelProps()
|
|
onManifestChanged: injectPanelProps()
|
|
|
|
function injectPanelProps() {
|
|
var item = panelLoader.item
|
|
if (!item) return
|
|
if ("pluginApi" in item) item.pluginApi = root.pluginApi
|
|
if ("manifest" in item) item.manifest = root.manifest
|
|
if ("omarchyPath" in item) item.omarchyPath = root.omarchyPath
|
|
if ("shell" in item) item.shell = root.shell
|
|
if ("pluginRegistry" in item) item.pluginRegistry = root.pluginRegistry
|
|
if ("barWidgetRegistry" in item) item.barWidgetRegistry = root.barWidgetRegistry
|
|
if ("screen" in item) {
|
|
var screens = Quickshell.screens
|
|
item.screen = screens && screens.length > 0 ? screens[0] : null
|
|
}
|
|
}
|
|
}
|