# PopoutService for Plugins ## Overview The `PopoutService` singleton provides plugins with access to all DankMaterialShell popouts and modals. It's automatically injected into plugin widgets and daemons, enabling them to control shell UI elements. ## Automatic Injection The `popoutService` property is automatically injected into: - Widget plugins (loaded in DankBar) - Daemon plugins (background services) - Plugin settings components **Required**: Declare the property in your plugin component: ```qml property var popoutService: null ``` **Note**: Without this declaration, the service cannot be injected and you'll see errors like `Cannot assign to non-existent property "popoutService"` ## API Reference ### Popouts (DankPopout-based) | Component | Open | Close | Toggle | |-----------|------|-------|--------| | Control Center | `openControlCenter()` | `closeControlCenter()` | `toggleControlCenter()` | | Notification Center | `openNotificationCenter()` | `closeNotificationCenter()` | `toggleNotificationCenter()` | | App Drawer | `openAppDrawer()` | `closeAppDrawer()` | `toggleAppDrawer()` | | Process List | `openProcessList()` | `closeProcessList()` | `toggleProcessList()` | | DankDash | `openDankDash(tab)` | `closeDankDash()` | `toggleDankDash(tab)` | | Battery | `openBattery()` | `closeBattery()` | `toggleBattery()` | | VPN | `openVpn()` | `closeVpn()` | `toggleVpn()` | | System Update | `openSystemUpdate()` | `closeSystemUpdate()` | `toggleSystemUpdate()` | ### Modals (DankModal-based) | Modal | Show | Hide | Notes | |-------|------|------|-------| | Settings | `openSettings()` | `closeSettings()` | Full settings interface | | Clipboard History | `openClipboardHistory()` | `closeClipboardHistory()` | Cliphist integration | | Spotlight | `openSpotlight()` | `closeSpotlight()` | Command launcher | | Power Menu | `openPowerMenu()` | `closePowerMenu()` | Also has `togglePowerMenu()` | | Process List Modal | `showProcessListModal()` | `hideProcessListModal()` | Fullscreen version, has `toggleProcessListModal()` | | Color Picker | `showColorPicker()` | `hideColorPicker()` | Theme color selection | | Notification | `showNotificationModal()` | `hideNotificationModal()` | Notification details | | WiFi Password | `showWifiPasswordModal()` | `hideWifiPasswordModal()` | Network authentication | | Network Info | `showNetworkInfoModal()` | `hideNetworkInfoModal()` | Network details | ### Slideouts | Component | Open | Close | Toggle | |-----------|------|-------|--------| | Notepad | `openNotepad()` | `closeNotepad()` | `toggleNotepad()` | ## Usage Examples ### Simple Widget with Popout Control ```qml import QtQuick import qs.Common import qs.Widgets Rectangle { id: root property var popoutService: null width: 100 height: 30 color: Theme.surfaceContainerHigh radius: Theme.cornerRadius MouseArea { anchors.fill: parent onClicked: popoutService?.toggleControlCenter() } StyledText { anchors.centerIn: parent text: "Settings" } } ``` ### Daemon with Event-Driven Popouts ```qml import QtQuick import qs.Services Item { id: root property var popoutService: null Connections { target: NotificationService function onNotificationReceived(notification) { if (notification.urgency === "critical") { popoutService?.openNotificationCenter() } } } Connections { target: BatteryService function onPercentageChanged() { if (BatteryService.percentage < 10 && !BatteryService.isCharging) { popoutService?.openBattery() } } } } ``` ### Widget with Multiple Popout Options ```qml import QtQuick import QtQuick.Controls import qs.Common Rectangle { property var popoutService: null MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: (mouse) => { if (mouse.button === Qt.RightButton) { contextMenu.popup() } else { popoutService?.toggleControlCenter() } } } Menu { id: contextMenu MenuItem { text: "Settings" onClicked: popoutService?.openSettings() } MenuItem { text: "Notifications" onClicked: popoutService?.toggleNotificationCenter() } MenuItem { text: "Power Menu" onClicked: popoutService?.openPowerMenu() } } } ``` ## Implementation Details ### Service Architecture `PopoutService` is a singleton that holds references to popout instances: ```qml // Services/PopoutService.qml Singleton { id: root property var controlCenterPopout: null property var notificationCenterPopout: null // ... other popout references function toggleControlCenter() { controlCenterPopout?.toggle() } // ... other control functions } ``` ### Reference Assignment References are assigned in `DMSShell.qml` when popouts are loaded: ```qml LazyLoader { ControlCenterPopout { id: controlCenterPopout Component.onCompleted: { PopoutService.controlCenterPopout = controlCenterPopout } } } ``` ### Plugin Injection The service is injected in three locations: 1. **DMSShell.qml** (daemon plugins): ```qml Instantiator { delegate: Loader { onLoaded: { if (item) { item.popoutService = PopoutService } } } } ``` 2. **WidgetHost.qml** (widget plugins): ```qml onLoaded: { if (item.popoutService !== undefined) { item.popoutService = PopoutService } } ``` 3. **CenterSection.qml** (center widgets): ```qml onLoaded: { if (item.popoutService !== undefined) { item.popoutService = PopoutService } } ``` 4. **PluginsTab.qml** (settings): ```qml onLoaded: { if (item && typeof PopoutService !== "undefined") { item.popoutService = PopoutService } } ``` ## Best Practices 1. **Use Optional Chaining**: Always use `?.` to handle null cases ```qml popoutService?.toggleControlCenter() ``` 2. **Check Availability**: Some popouts may not be available ```qml if (popoutService && popoutService.controlCenterPopout) { popoutService.toggleControlCenter() } ``` 3. **Lazy Loading**: First access may activate lazy loaders - this is normal 4. **Feature Detection**: Some popouts require specific features ```qml if (BatteryService.batteryAvailable) { popoutService?.openBattery() } ``` 5. **User Intent**: Only trigger popouts based on user actions or critical events ## Example Plugin See `PLUGINS/PopoutControlExample/` for a complete working example that demonstrates: - Widget creation with popout controls - Menu-based popout selection - Proper service usage - Error handling ## Limitations - Popouts are shared across all plugins - avoid conflicts - Some popouts may be compositor or feature-dependent - Lazy-loaded popouts need activation before use - Multi-monitor considerations apply to positioned popouts