Files
DankMaterialShell/quickshell/Modules/ControlCenter/ControlCenterContent.qml
T

352 lines
12 KiB
QML

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 sheetContentWidth: host.sheetContentWidth ?? CcMetrics.sheetWidth
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: root.sheetContentWidth - 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);
}
}
}
}