import QtQuick import QtQuick.Controls import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Widgets import qs.Common import qs.Modules.ControlCenter import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Details import qs.Modules.DankBar import qs.Services import qs.Widgets import qs.Modules.ControlCenter.Components import qs.Modules.ControlCenter.Models import "./utils/state.js" as StateUtils DankPopout { id: root property string expandedSection: "" property var triggerScreen: null property bool editMode: false property int expandedWidgetIndex: -1 property var expandedWidgetData: null signal lockRequested function collapseAll() { expandedSection = "" expandedWidgetIndex = -1 expandedWidgetData = null } onEditModeChanged: { if (editMode) { collapseAll() } } onVisibleChanged: { if (!visible) { collapseAll() } } readonly property color _containerBg: Theme.surfaceContainerHigh function setTriggerPosition(x, y, width, section, screen) { StateUtils.setTriggerPosition(root, x, y, width, section, screen) } function openWithSection(section) { StateUtils.openWithSection(root, section) } function toggleSection(section) { StateUtils.toggleSection(root, section) } popupWidth: 550 popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400) triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing triggerWidth: 80 positioning: "" screen: triggerScreen shouldBeVisible: false visible: shouldBeVisible onShouldBeVisibleChanged: { if (shouldBeVisible) { Qt.callLater(() => { NetworkService.autoRefreshEnabled = NetworkService.wifiEnabled if (UserInfoService) UserInfoService.getUptime() }) } else { Qt.callLater(() => { NetworkService.autoRefreshEnabled = false if (BluetoothService.adapter && BluetoothService.adapter.discovering) BluetoothService.adapter.discovering = false editMode = false }) } } WidgetModel { id: widgetModel } content: Component { Rectangle { id: controlContent implicitHeight: mainColumn.implicitHeight + Theme.spacingM property alias bluetoothCodecSelector: bluetoothCodecSelector color: { const transparency = Theme.popupTransparency const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) return Qt.rgba(surface.r, surface.g, surface.b, transparency) } radius: Theme.cornerRadius border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 0 antialiasing: true smooth: true Column { id: mainColumn width: parent.width - Theme.spacingL * 2 x: Theme.spacingL y: Theme.spacingL spacing: Theme.spacingS HeaderPane { id: headerPane width: parent.width editMode: root.editMode onEditModeToggled: root.editMode = !root.editMode onPowerButtonClicked: { if (powerMenuModalLoader) { powerMenuModalLoader.active = true if (powerMenuModalLoader.item) { const popoutPos = controlContent.mapToItem(null, 0, 0) const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height) powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen) } } } onLockRequested: { root.close() root.lockRequested() } } DragDropGrid { id: widgetGrid width: parent.width editMode: root.editMode expandedSection: root.expandedSection expandedWidgetIndex: root.expandedWidgetIndex expandedWidgetData: root.expandedWidgetData model: widgetModel bluetoothCodecSelector: bluetoothCodecSelector colorPickerModal: root.colorPickerModal onExpandClicked: (widgetData, globalIndex) => { root.expandedWidgetIndex = globalIndex root.expandedWidgetData = widgetData if (widgetData.id === "diskUsage") { root.toggleSection("diskUsage_" + (widgetData.instanceId || "default")) } else { root.toggleSection(widgetData.id) } } onRemoveWidget: (index) => widgetModel.removeWidget(index) onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex) onToggleWidgetSize: (index) => widgetModel.toggleWidgetSize(index) } EditControls { width: parent.width visible: editMode popoutContent: controlContent availableWidgets: { const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id) return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id)) } onAddWidget: (widgetId) => widgetModel.addWidget(widgetId) onResetToDefault: () => widgetModel.resetToDefault() onClearAll: () => widgetModel.clearAll() } } BluetoothCodecSelector { id: bluetoothCodecSelector anchors.fill: parent z: 10000 } } } Component { id: networkDetailComponent NetworkDetail {} } Component { id: bluetoothDetailComponent BluetoothDetail { id: bluetoothDetail onShowCodecSelector: function(device) { if (contentLoader.item && contentLoader.item.bluetoothCodecSelector) { contentLoader.item.bluetoothCodecSelector.show(device) contentLoader.item.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) { bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName) }) } } } } Component { id: audioOutputDetailComponent AudioOutputDetail {} } Component { id: audioInputDetailComponent AudioInputDetail {} } Component { id: batteryDetailComponent BatteryDetail {} } property var colorPickerModal: null property var powerMenuModalLoader: null }