pragma ComponentBehavior: Bound import QtQuick import qs.Common import qs.Modules.ControlCenter.Components import qs.Modules.ControlCenter.Models import qs.Modules.ControlCenter.Details import qs.Widgets import "./utils/sections.js" as Sections FocusScope { id: root required property var host LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true readonly property bool pageOpen: (host.expandedSection ?? "") !== "" readonly property real gridHeight: widgetGrid.gridHeight readonly property real bodyHeight: detailPage.shownSection !== "" ? Math.max(gridHeight, detailPage.preferredHeight) : gridHeight readonly property real targetImplicitHeight: { let total = CcMetrics.sheetPadding * 2 + headerPane.height + Theme.spacingS + bodyHeight; if (host.editMode) total += Theme.spacingS + editControls.height; return total; } property var pageHistory: [] readonly property bool panelResizing: panelResizer.resizing readonly property real editGutter: host.editMode ? PopoutMetrics.editOverflow : 0 readonly property DankPanelResizer panelResizer: DankPanelResizer { popout: root.host gutter: root.editGutter stepWidth: CcMetrics.sheetWidthStep widthFor: step => CcMetrics.sheetWidthForStep(step) currentStep: () => CcMetrics.sheetStepFor(CcMetrics.sheetWidth) minStep: CcMetrics.sheetStepMin maxStep: CcMetrics.sheetStepMax onPreview: step => CcMetrics.sheetPreviewWidth = CcMetrics.sheetWidthForStep(step) onCommitted: step => { SettingsData.set("controlCenterWidth", CcMetrics.sheetWidthForStep(step)); CcMetrics.sheetPreviewWidth = 0; } onCanceled: CcMetrics.sheetPreviewWidth = 0 } implicitHeight: targetImplicitHeight focus: true function navigateTo(section) { if (section === host.expandedSection) return; if (host.expandedSection) pageHistory = pageHistory.concat([host.expandedSection]); host.expandedSection = section; } function goBack() { if (detailPage.dismissTransient()) return; if (pageHistory.length > 0) { const previous = pageHistory[pageHistory.length - 1]; pageHistory = pageHistory.slice(0, -1); host.expandedSection = previous; return; } host.collapseAll(); } function openWidgetPage(widgetData) { const section = Sections.sectionFor(widgetData); if (host.expandedSection === section) { goBack(); return; } navigateTo(section); } function showCodecSelector(device) { presentSheet(codecSelectorLoader, device); } function showPortSelector(node) { presentSheet(portSelectorLoader, node); } function presentSheet(loader, target) { loader.active = true; const sheet = loader.item; if (!sheet) return; sheet.show(target); if (!sheet.shown) loader.active = false; } function releaseSheet(loader) { if (loader.item?.shown) return; loader.active = false; } function openConfigOverlay(index, widgetData, anchor) { if (widgetData.id === "brightnessSlider") { openWidgetPage(widgetData); return; } configOverlayLoader.active = true; const overlay = configOverlayLoader.item; if (!overlay) return; overlay.open(index, widgetData, anchor); } function releaseConfigOverlay() { if (configOverlayLoader.item?.visible) return; configOverlayLoader.active = false; } Keys.onEscapePressed: event => { if (configOverlayLoader.item?.visible) { configOverlayLoader.item.close(); event.accepted = true; return; } if (pageOpen) { goBack(); event.accepted = true; return; } host.close(); event.accepted = true; } readonly property string expandedSection: host.expandedSection ?? "" readonly property bool editMode: host.editMode onExpandedSectionChanged: { if (expandedSection !== "") return; pageHistory = []; forceActiveFocus(); } onEditModeChanged: { if (editMode) host.collapseAll(); else panelResizer.cancel(); forceActiveFocus(); } DankGridEditChrome { id: panelChrome anchors.fill: parent anchors.margins: PopoutMetrics.panelChromeInset - contentInset z: 1 visible: root.host.editMode edgeResize: true horizontalResize: true removable: false edgeBandWidth: PopoutMetrics.panelResizeBand cornerRadius: Math.max(0, Theme.windowRadius - PopoutMetrics.panelChromeInset) buttonSize: Theme.iconSize iconSize: PopoutMetrics.chromeIconSize resizing: root.panelResizing atDefault: CcMetrics.sheetWidth === CcMetrics.sheetWidthDefault sizeText: Math.round(CcMetrics.sheetWidth) + " " + I18n.tr("px", "Cursor size unit, pixels") onResizeStarted: (px, py, signX) => root.panelResizer.begin(px, py, signX) onResizeMoved: (px, py) => root.panelResizer.move(px, py) onResizeEnded: root.panelResizer.end() onResizeCanceled: root.panelResizer.cancel() } WidgetModel { id: widgetModel } Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, Theme.scrimAlpha) opacity: root.host.powerMenuOpen ? 1 : 0 visible: opacity > 0 z: CcMetrics.overlayZ Behavior on opacity { enabled: CcMetrics.animationsEnabled NumberAnimation { duration: CcMetrics.fadeDuration easing.type: Easing.BezierSpline easing.bezierCurve: Theme.expressiveCurves.expressiveEffects } } } DankFlickable { id: contentFlickable anchors.fill: parent clip: true contentWidth: width contentHeight: Math.max(height, mainColumn.implicitHeight + CcMetrics.sheetPadding * 2) interactive: contentHeight > height Column { id: mainColumn width: CcMetrics.sheetWidth - CcMetrics.sheetPadding * 2 x: CcMetrics.sheetPadding + root.editGutter y: CcMetrics.sheetPadding spacing: Theme.spacingS HeaderPane { id: headerPane width: parent.width editMode: root.host.editMode live: root.host.shouldBeVisible tapToClose: root.host.headerTogglesClose ?? false onHeaderTapped: root.host.close() onEditModeToggled: root.host.editMode = !root.host.editMode onPowerButtonClicked: { const loader = root.host.powerMenuModalLoader; if (!loader) return; loader.active = true; if (!loader.item) return; const bounds = Qt.rect(root.host.alignedX, root.host.alignedY, root.host.popupWidth, root.host.popupHeight); loader.item.openFromControlCenter(bounds, root.host.screen); } onLockRequested: { root.host.close(); root.host.lockRequested(); } onSettingsButtonClicked: root.host.openSettings() } Item { id: body width: parent.width height: root.bodyHeight CcTileGrid { id: widgetGrid anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top editMode: root.host.editMode model: widgetModel live: root.host.shouldBeVisible screenName: root.host.triggerScreen?.name || "" opacity: root.pageOpen ? 0 : 1 visible: opacity > 0 enabled: !root.pageOpen onExpandClicked: widgetData => root.openWidgetPage(widgetData) onRemoveWidget: index => widgetModel.removeWidget(index) onConfigRequested: (index, widgetData, anchor) => root.openConfigOverlay(index, widgetData, anchor) onColorPickerRequested: root.host.openColorPicker() Behavior on opacity { enabled: CcMetrics.animationsEnabled NumberAnimation { duration: CcMetrics.fadeDuration easing.type: Easing.BezierSpline easing.bezierCurve: Theme.expressiveCurves.expressiveEffects } } } CcDetailPage { id: detailPage anchors.fill: parent section: root.host.expandedSection ?? "" model: widgetModel screenName: root.host.triggerScreen?.name || "" screenModel: root.host.triggerScreen?.model || "" onCodecSelectorRequested: device => root.showCodecSelector(device) onPortSelectorRequested: node => root.showPortSelector(node) onBackRequested: root.goBack() onCollapseRequested: root.host.collapseAll() } } EditControls { id: editControls width: parent.width visible: root.host.editMode popupScreen: root.host.screen popoutX: root.host.alignedX popoutY: root.host.alignedY popoutWidth: root.host.alignedWidth popoutHeight: root.host.alignedHeight availableWidgets: { if (!root.host.editMode) return []; const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id); const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets()); return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id)); } onAddWidget: widgetId => widgetModel.addWidget(widgetId) onResetToDefault: () => widgetModel.resetToDefault() onClearAll: () => widgetModel.clearAll() } } } Loader { id: codecSelectorLoader anchors.fill: parent z: CcMetrics.overlayZ active: false sourceComponent: BluetoothCodecSelector { onDismissed: Qt.callLater(root.releaseSheet, codecSelectorLoader) } } Loader { id: portSelectorLoader anchors.fill: parent z: CcMetrics.overlayZ active: false sourceComponent: AudioPortSelector { onDismissed: Qt.callLater(root.releaseSheet, portSelectorLoader) } } Loader { id: configOverlayLoader anchors.fill: parent z: CcMetrics.overlayZ active: false sourceComponent: WidgetConfigOverlay { onVisibleChanged: { if (visible) return; Qt.callLater(root.releaseConfigOverlay); } } } }