import QtQuick import Quickshell import Quickshell.Io import qs.Common import qs.Modules.Plugins import qs.Services PluginComponent { id: root pluginId: "gameBar" pluginService: PluginService readonly property bool overlayOpen: overlay.shouldBeVisible // Widget catalogue. `id` is the persistence key, the rest drives the top bar // toggles and the default placement (fractions of the screen size). readonly property var widgetDefs: [ { "id": "audio", "title": "Audio", "icon": "volume_up", "width": 430, "height": 470, "x": 0.04, "y": 0.14, "defaultVisible": true }, { "id": "apps", "title": "Apps", "icon": "tune", "width": 400, "height": 360, "x": 0.30, "y": 0.14, "defaultVisible": true }, { "id": "media", "title": "Media", "icon": "music_note", "width": 430, "height": 300, "x": 0.04, "y": 0.56, "defaultVisible": true }, { "id": "home", "title": "Home", "icon": "home", "width": 420, "height": 420, "x": 0.56, "y": 0.50, "defaultVisible": false }, { "id": "system", "title": "Performance", "icon": "speed", "width": 380, "height": 330, "x": 0.56, "y": 0.14, "defaultVisible": false } ] function widgetDef(widgetId) { return widgetDefs.find(def => def.id === widgetId) || null; } // ── Visibility ─────────────────────────────────────────────────────────── function savePluginValue(key, value) { if (pluginService) pluginService.savePluginData(pluginId, key, value); } function isWidgetVisible(widgetId) { const stored = pluginData.visibleWidgets; if (stored && stored[widgetId] !== undefined) return stored[widgetId] === true; const def = widgetDef(widgetId); return def ? def.defaultVisible === true : false; } function setWidgetVisible(widgetId, visible) { const stored = Object.assign({}, pluginData.visibleWidgets || {}); stored[widgetId] = visible === true; root.savePluginValue("visibleWidgets", stored); } function toggleWidget(widgetId) { root.setWidgetVisible(widgetId, !root.isWidgetVisible(widgetId)); } // ── Positions ──────────────────────────────────────────────────────────── // Stored per screen as fractions of the screen size, so the same layout // survives monitors of different resolutions (and is re-clamped on load). function screenKey(screen) { if (!screen) return "default"; return SettingsData.getScreenDisplayName(screen) || screen.name || "default"; } function widgetPosition(screen, widgetId) { const layout = pluginData.layout || {}; const perScreen = layout[root.screenKey(screen)]; const saved = perScreen ? perScreen[widgetId] : null; if (saved && saved.x !== undefined && saved.y !== undefined) return saved; const def = widgetDef(widgetId); return def ? ({ "x": def.x, "y": def.y }) : ({ "x": 0.1, "y": 0.15 }); } function saveWidgetPosition(screen, widgetId, fractionX, fractionY) { const layout = JSON.parse(JSON.stringify(pluginData.layout || {})); const key = root.screenKey(screen); if (!layout[key]) layout[key] = {}; layout[key][widgetId] = { "x": fractionX, "y": fractionY }; root.savePluginValue("layout", layout); } function resetLayout() { root.savePluginValue("layout", {}); } // ── Overlay ────────────────────────────────────────────────────────────── function openOverlay() { if (overlay.shouldBeVisible) return; if (typeof PopoutService !== "undefined" && PopoutService) PopoutService.closeControlCenter(); overlay.shouldBeVisible = true; overlay.open(); } function closeOverlay() { if (!overlay.shouldBeVisible) return; overlay.shouldBeVisible = false; overlay.close(); } function toggleOverlay() { if (overlay.shouldBeVisible) root.closeOverlay(); else root.openOverlay(); } IpcHandler { target: "gameBar" enabled: true function toggle() : string { root.toggleOverlay(); return "SUCCESS"; } function open() : string { root.openOverlay(); return "SUCCESS"; } function close() : string { root.closeOverlay(); return "SUCCESS"; } function widget(name: string) : string { if (!root.widgetDef(name)) return "UNKNOWN_WIDGET: " + name; root.toggleWidget(name); return "SUCCESS"; } function homeToggle(entityId: string) : string { if (!entityId) return "USAGE: homeToggle "; const domain = haService.domainOf(entityId); if (["scene", "script", "button", "input_button"].indexOf(domain) !== -1) haService.press(entityId); else haService.toggle(entityId); return "SUCCESS"; } function homeStatus() : string { const picked = Array.isArray(root.pluginData.haEntities) ? root.pluginData.haEntities.length : 0; return "configured=" + haService.configured + " url=" + (haService.baseUrl || "(none)") + " tokenChars=" + haService.token.length + " http=" + haService.lastStatus + " entities=" + haService.entityList.length + " picked=" + picked + " error=" + (haService.lastError || "(none)"); } function homeRefresh() : string { haService.refresh(); return "SUCCESS"; } function resetLayout() : string { root.resetLayout(); return "SUCCESS"; } } readonly property alias homeService: haService GameBarHomeService { id: haService daemon: root } GameBarOverlay { id: overlay daemon: root } Component.onCompleted: { if (pluginService && pluginId) { const instances = Object.assign({}, pluginService.pluginInstances); instances[pluginId] = root; pluginService.pluginInstances = instances; } } Component.onDestruction: { if (pluginService && pluginService.pluginInstances[pluginId] === root) { const instances = Object.assign({}, pluginService.pluginInstances); delete instances[pluginId]; pluginService.pluginInstances = instances; } } }