From 2cc720cf2d70a47350b4f1aee9fce317cbfeb346 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sat, 10 Oct 2026 17:54:54 -0400 Subject: [PATCH] control center: battery styles on every tile size --- .../ControlCenter/Components/CcEditChrome.qml | 1 + .../ControlCenter/Components/CcFooter.qml | 5 ++++ .../ControlCenter/Components/CcTileSlot.qml | 2 +- .../Components/QuickTilesGroup.qml | 5 ++++ .../ControlCenter/ControlCenterContent.qml | 25 ++++++++----------- .../ControlCenter/Widgets/BatteryTile.qml | 18 ++++++++++--- .../Activities/ControlCenterExpanded.qml | 1 - quickshell/Widgets/DEditableGridSlot.qml | 9 ++++++- quickshell/Widgets/DGridEditChrome.qml | 15 +++++++++-- 9 files changed, 58 insertions(+), 23 deletions(-) diff --git a/quickshell/Modules/ControlCenter/Components/CcEditChrome.qml b/quickshell/Modules/ControlCenter/Components/CcEditChrome.qml index 384ef1f21..148994196 100644 --- a/quickshell/Modules/ControlCenter/Components/CcEditChrome.qml +++ b/quickshell/Modules/ControlCenter/Components/CcEditChrome.qml @@ -13,6 +13,7 @@ DGridEditChrome { hasOptions: WidgetUtils.hasOptions(widgetData.id) removable: WidgetUtils.isRemovable(widgetData) + removeIcon: "remove" buttonSize: Theme.iconSize hitOverflow: CcMetrics.gridGap / 2 iconSize: PopoutMetrics.chromeIconSize diff --git a/quickshell/Modules/ControlCenter/Components/CcFooter.qml b/quickshell/Modules/ControlCenter/Components/CcFooter.qml index 56d68e2f7..80865401b 100644 --- a/quickshell/Modules/ControlCenter/Components/CcFooter.qml +++ b/quickshell/Modules/ControlCenter/Components/CcFooter.qml @@ -391,6 +391,11 @@ Item { onConfigRequested: anchor => root.configRequested(footerItem.entry.index, footerItem.entry.widget, anchor) } + HoverHandler { + enabled: root.editMode && root.resizePreview === null + cursorShape: footerItem.lifted ? Qt.ClosedHandCursor : Qt.OpenHandCursor + } + DragHandler { id: dragHandler diff --git a/quickshell/Modules/ControlCenter/Components/CcTileSlot.qml b/quickshell/Modules/ControlCenter/Components/CcTileSlot.qml index 07ccce22a..45f04a2d5 100644 --- a/quickshell/Modules/ControlCenter/Components/CcTileSlot.qml +++ b/quickshell/Modules/ControlCenter/Components/CcTileSlot.qml @@ -20,7 +20,7 @@ DEditableGridSlot { readonly property real smallSpanLimit: (1 + CcMetrics.smallRowFraction) / 2 passthrough: tileItem?.passthrough ?? null - z: dragging || resizing || (tileItem?.memberDragging ?? false) ? 1 : 0 + raised: dragging || resizing || (tileItem?.memberDragging ?? false) function reanchor(small) { const shift = ((small ? CcMetrics.smallRowFraction : 1) - smallSpanLimit) * grid.cellWidth; diff --git a/quickshell/Modules/ControlCenter/Components/QuickTilesGroup.qml b/quickshell/Modules/ControlCenter/Components/QuickTilesGroup.qml index b1a128f84..f561ebadf 100644 --- a/quickshell/Modules/ControlCenter/Components/QuickTilesGroup.qml +++ b/quickshell/Modules/ControlCenter/Components/QuickTilesGroup.qml @@ -252,6 +252,11 @@ Item { root.moveLifted(member, centroid.scenePosition); } } + + HoverHandler { + enabled: root.editMode + cursorShape: member.lifted ? Qt.ClosedHandCursor : Qt.OpenHandCursor + } } } } diff --git a/quickshell/Modules/ControlCenter/ControlCenterContent.qml b/quickshell/Modules/ControlCenter/ControlCenterContent.qml index 1bbed746e..253107231 100644 --- a/quickshell/Modules/ControlCenter/ControlCenterContent.qml +++ b/quickshell/Modules/ControlCenter/ControlCenterContent.qml @@ -62,7 +62,6 @@ FocusScope { readonly property int gridColumnCap: host.gridColumnCap ?? CcMetrics.columnCapFor((host.triggerScreen?.width ?? CcMetrics.sheetWidthDefault + Theme.spacingL * 2) - Theme.spacingL * 2) readonly property int gridColumns: host.gridColumns ?? Math.min(CcMetrics.gridColumns, gridColumnCap) readonly property real availableGridHeight: (host.availableHeight ?? (host.triggerScreen?.height ?? CcMetrics.fallbackScreenHeight) - CcMetrics.maxHeightInset) - CcMetrics.sheetPadding * 2 - footerReserve - readonly property vector4d chromeRoom: host.chromeRoom ?? Qt.vector4d(Infinity, Infinity, Infinity, Infinity) readonly property DPanelResizer panelResizer: DPanelResizer { popout: root.host stepWidth: CcMetrics.columnWidth + CcMetrics.gridGap @@ -361,32 +360,28 @@ FocusScope { DGridEditChrome { id: panelChrome - readonly property real screenWidth: root.host.triggerScreen?.width ?? Infinity - readonly property real screenHeight: root.host.triggerScreen?.height ?? Infinity - - function ringOffset(room) { - return Math.min(Theme.spacingS, room - handleThickness / 2 - Theme.outlineWidthFocused); - } + property int resizeSide: 0 anchors.fill: parent - anchors.leftMargin: -(contentInset + ringOffset(Math.min(root.host.alignedX, root.chromeRoom.x))) - anchors.rightMargin: -(contentInset + ringOffset(Math.min(screenWidth - root.host.alignedX - root.width, root.chromeRoom.z))) - anchors.topMargin: -(contentInset + ringOffset(Math.min(root.host.alignedY, root.chromeRoom.y))) - anchors.bottomMargin: -(contentInset + ringOffset(Math.min(screenHeight - root.host.alignedY - root.height, root.chromeRoom.w))) + anchors.margins: -contentInset z: 1 visible: root.host.editMode enabled: detailPage.shownSection === "" && !root.widgetSheetOpen - edgeResize: root.panelResizing || root.panelResizer.sideMovable(-1, root.gridColumns) - cornerResize: root.panelResizing || root.panelResizer.sideMovable(1, root.gridColumns) + edgeResize: root.panelResizing ? resizeSide < 0 : root.panelResizer.sideMovable(-1, root.gridColumns) + cornerResize: root.panelResizing ? resizeSide > 0 : root.panelResizer.sideMovable(1, root.gridColumns) horizontalResize: true removable: false - cornerRadius: Theme.windowRadius + Theme.spacingS + cornerRadius: Theme.windowRadius + cornerRadii: root.surfaceCornerRadii buttonSize: Theme.iconSize iconSize: PopoutMetrics.chromeIconSize resizing: root.panelResizing atDefault: root.gridColumns === Math.min(CcMetrics.defaultColumns, root.gridColumnCap) sizeText: root.gridColumns + "×" + widgetGrid.slotLayout.rows - onResizeStarted: (px, py, signX) => root.panelResizer.begin(px, py, signX) + onResizeStarted: (px, py, signX) => { + resizeSide = signX; + root.panelResizer.begin(px, py, signX); + } onResizeMoved: (px, py) => root.panelResizer.move(px, py) onResizeEnded: root.panelResizer.end() onResizeCanceled: root.panelResizer.cancel() diff --git a/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml b/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml index 570c79ca9..8fac33278 100644 --- a/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml +++ b/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml @@ -12,7 +12,7 @@ CcTile { readonly property bool available: BatteryService.batteryAvailable readonly property bool profileMode: !available && PowerProfileWatcher.available readonly property string meterStyle: widgetData?.batteryStyle ?? "icon" - readonly property bool showsMeter: available && meterStyle !== "icon" && (small || compact) && !docked + readonly property bool showsMeter: available && meterStyle !== "icon" property real meterWidth: 0 iconName: profileMode ? Theme.getPowerProfileIcon(PowerProfileWatcher.currentProfile) : BatteryService.getBatteryIcon() @@ -48,13 +48,24 @@ CcTile { onLiveChanged: syncBatteryRef(live) Component.onCompleted: syncBatteryRef(live) Component.onDestruction: syncBatteryRef(false) - // The meter carries its own charging bolt and would vanish into an active body. active: available && (BatteryService.isCharging || BatteryService.isPluggedIn) && !showsMeter - bodyContent: showsMeter ? meterPill : null + bodyContent: showsMeter && compact ? meterPill : null + iconContent: showsMeter && !compact ? meterIcon : null quickContentWidth: showsMeter ? meterWidth : 0 opensPage: !profileMode showExpand: profileMode + Component { + id: meterIcon + + BatteryMeter { + meterStyle: root.meterStyle + levelColors: true + thickness: CcMetrics.pillMeterThickness + scale: Math.min(1, Math.max(0, root.iconExtent - Theme.spacingXS * 2) / Math.max(1, implicitWidth, implicitHeight)) + } + } + Component { id: meterPill @@ -83,6 +94,7 @@ CcTile { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter thickness: CcMetrics.tallMeterThickness + meterStyle: root.showsMeter ? root.meterStyle : "solid" levelColors: true showNumber: false visible: root.available diff --git a/quickshell/Modules/DIsland/Activities/ControlCenterExpanded.qml b/quickshell/Modules/DIsland/Activities/ControlCenterExpanded.qml index b0e4e7691..443715066 100644 --- a/quickshell/Modules/DIsland/Activities/ControlCenterExpanded.qml +++ b/quickshell/Modules/DIsland/Activities/ControlCenterExpanded.qml @@ -56,7 +56,6 @@ FocusScope { onEditModeChanged: root.controller.setEditing("controlcenter", editMode) - readonly property vector4d chromeRoom: Qt.vector4d(root.sideInset, Theme.spacingXS, root.sideInset, root.bottomInset) readonly property int gridColumnCap: root.controller.controlCenterColumnCap readonly property int gridColumns: root.controller.controlCenterColumns readonly property real availableHeight: root.controller.controlCenterMaxHeight - Theme.spacingXS - root.bottomInset diff --git a/quickshell/Widgets/DEditableGridSlot.qml b/quickshell/Widgets/DEditableGridSlot.qml index 0aaef3749..57bf22427 100644 --- a/quickshell/Widgets/DEditableGridSlot.qml +++ b/quickshell/Widgets/DEditableGridSlot.qml @@ -14,6 +14,7 @@ Item { property var resizeOrigin: null property var dragOrigin: null property Item passthrough: null + property bool raised: dragging || resizing readonly property bool interactionEnabled: resizeOrigin !== null || dragOrigin !== null || !grid.interacting signal resizeRequested(real requestedWidth, real requestedHeight) @@ -77,7 +78,7 @@ Item { visible: slot !== null width: slot ? slot.w : 0 height: slot ? slot.h : 0 - z: dragging || resizing ? 1 : 0 + z: raised ? 2 : hoverTracker.hovered ? 1 : 0 QtObject { id: passthroughMask @@ -144,6 +145,12 @@ Item { } } + // Edit chrome overhangs the gap; the hovered slot paints over its neighbours so its badges stay whole. + HoverHandler { + id: hoverTracker + enabled: root.grid.editMode + } + MouseArea { id: dragArea diff --git a/quickshell/Widgets/DGridEditChrome.qml b/quickshell/Widgets/DGridEditChrome.qml index 27764b992..742bfc9e9 100644 --- a/quickshell/Widgets/DGridEditChrome.qml +++ b/quickshell/Widgets/DGridEditChrome.qml @@ -16,6 +16,7 @@ Item { property bool atDefault: false property bool snapped: false property bool removable: true + property string removeIcon: "close" property bool horizontalResize: false property bool edgeResize: false property bool cornerResize: true @@ -24,6 +25,7 @@ Item { // A child of the tile that keeps its own input in edit mode; resize bands never claim points over it. property Item passthrough: null property real cornerRadius: Theme.cornerRadiusXL + property vector4d cornerRadii: Qt.vector4d(cornerRadius, cornerRadius, cornerRadius, cornerRadius) property real buttonSize: Theme.iconSizeLarge property real iconSize: Theme.iconSizeSmall readonly property real handleThickness: Theme.spacingM @@ -56,7 +58,10 @@ Item { Rectangle { anchors.fill: parent anchors.margins: root.contentInset - radius: root.cornerRadius + topLeftRadius: root.cornerRadii.x + topRightRadius: root.cornerRadii.y + bottomRightRadius: root.cornerRadii.z + bottomLeftRadius: root.cornerRadii.w color: root.dragging || root.resizing ? Theme.withAlpha(Theme.primary, Theme.stateLayerDrag) : "transparent" border.color: Theme.primary border.width: Theme.outlineWidthFocused @@ -97,7 +102,7 @@ Item { height: root.touchTargetSize buttonSize: root.buttonSize iconSize: root.iconSize - iconName: "close" + iconName: root.removeIcon iconColor: Theme.error tooltipText: I18n.tr("Remove") visible: root.removable @@ -241,9 +246,15 @@ Item { QtObject { id: bandMask + function overButton(button: Item, point: point): bool { + return button.visible && button.contains(band.mapToItem(button, point.x, point.y)); + } + function contains(point: point): bool { if (root.hitOverflow >= 0 && (point.x < handleMask.x || point.y < handleMask.y || point.x >= handleMask.x + handleMask.width || point.y >= handleMask.y + handleMask.height)) return false; + if (overButton(removeButton, point) || overButton(optionsButton, point)) + return false; if (root.resizeEdgeWidth >= 0 && !root.horizontalResize) { const local = band.mapToItem(root, point.x, point.y); const fromEdge = handleItem.diagonalFlipped ? local.x - root.contentInset : root.width - root.contentInset - local.x;