cc: replace detail pages with bands (1.6 style)

port 2.0

(cherry picked from commit 290360428e)
This commit is contained in:
bbedward authored and dux-ci[bot] committed 2026-10-27 16:48:01 +00:00
1 parent 433a84d824
commit 3f75012b7c
15 files changed
+204 -222

No files matched your search

+31
View File
@@ -171,6 +171,37 @@ function shrinkRows(layout, shrinkable, unit) {
});
}
// Opens a full-width band of `extent` at the cut under the slot at `index`, or under the last row when there is
// none, pushing every slot below the cut down by exactly that much. The extent carries the band's own gaps, so an
// animated extent moves the rows without a step at either end. The cut sits below any taller neighbour straddling
// the slot's bottom edge.
function insertBand(layout, index, extent) {
if (extent <= 0)
return layout;
const slots = layout.slots;
const anchor = slots[index] ?? null;
let cut = anchor ? anchor.y + anchor.h : layout.totalHeight;
for (let moved = true; moved && anchor; ) {
moved = false;
for (const slot of slots) {
if (!slot || slot.y >= cut || slot.y + slot.h <= cut)
continue;
cut = slot.y + slot.h;
moved = true;
}
}
const shifted = slots.map(slot => slot && slot.y >= cut ? Object.assign({}, slot, {
"y": slot.y + extent
}) : slot);
const below = slots.some(slot => slot && slot.y >= cut);
return Object.assign({}, layout, {
"slots": shifted,
"bandY": cut,
"bandExtent": extent,
"totalHeight": (below ? layout.totalHeight : cut) + extent
});
}
function rowAt(layout, y) {
const pitch = (layout.rowUnit + layout.gap) * layout.step;
const tops = layout.bandTops;
+4
View File
@@ -335,6 +335,10 @@ Singleton {
property real foregroundLayerTransparency: Spec.SPEC.foregroundLayerTransparency.def
property real blurLayerOutlineOpacity: Spec.SPEC.blurLayerOutlineOpacity.def
onBlurLayerOutlineOpacityChanged: saveSettings()
property string tooltipSurfaceColor: Spec.SPEC.tooltipSurfaceColor.def
onTooltipSurfaceColorChanged: saveSettings()
property string tooltipSurfaceCustomColor: Spec.SPEC.tooltipSurfaceCustomColor.def
onTooltipSurfaceCustomColorChanged: saveSettings()
property bool blurBorderEnabled: Spec.SPEC.blurBorderEnabled.def
onBlurBorderEnabledChanged: saveSettings()
property string blurBorderColor: Spec.SPEC.blurBorderColor.def
+4 -2
View File
@@ -578,13 +578,15 @@ var LOCAL_SPEC = {
id: "brightnessSlider",
enabled: true,
w: 4,
h: 1
h: 1,
small: true
},
{
id: "volumeSlider",
enabled: true,
w: 4,
h: 1
h: 1,
small: true
},
{
id: "wifi",
+14 -4
View File
@@ -190,9 +190,8 @@ function parse(root, jsonObj) {
// would wipe values set by detection processes on every reload.
if (SPEC[k].persist === false)
continue;
if (!(k in jsonObj)) {
root[k] = Util.cloneDef(SPEC[k].def);
}
if (!(k in jsonObj))
assign(root, k, Util.cloneDef(SPEC[k].def));
}
for (var k in jsonObj) {
@@ -203,7 +202,16 @@ function parse(root, jsonObj) {
var raw = jsonObj[k];
var spec = SPEC[k];
var coerce = spec.coerce;
root[k] = coerce ? (coerce(raw) !== undefined ? coerce(raw) : root[k]) : raw;
assign(root, k, coerce ? (coerce(raw) !== undefined ? coerce(raw) : root[k]) : raw);
}
}
// A QML root throws on an undeclared property; a shared spec key the shell has not declared must not abort the load.
function assign(root, key, value) {
try {
root[key] = value;
} catch (e) {
console.warn("settings: no SettingsData property for spec key, skipped:", key);
}
}
@@ -216,6 +224,8 @@ function toJson(root, setKeys) {
if (k === "pluginSettings")
continue;
var value = root[k];
if (value === undefined)
continue;
if (k === "desktopWidgetInstances")
value = withoutInstancePositions(value);
if (k === "builtInPluginSettings")
@@ -68,8 +68,9 @@ Singleton {
readonly property real smallRowFraction: 0.5
readonly property real shapeButtonSize: Theme.iconSize
readonly property real detailDialogInset: Theme.spacingL
readonly property real detailDialogPadding: Theme.spacingS
readonly property real detailHeaderHeight: Theme.iconButtonSize
readonly property real detailTitleSize: Theme.fontSizeLarge
readonly property real detailMinContentHeight: Theme.listItemTwoLineHeight * 3
readonly property real headerActionSize: Theme.buttonHeightXS
readonly property real headerActionIconSize: Theme.iconSizeMedium
@@ -134,9 +135,8 @@ Singleton {
readonly property bool animationsEnabled: !SettingsData.reduceMotion && Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
function preferredDetailHeight(section, pluginHeight) {
if (!section)
return 0;
// The 1.6 detail heights: a detail never shows less than it did there, whatever the grid leaves free.
function detailHeightFloor(section, pluginHeight) {
if (section.startsWith("plugin_"))
return pluginHeight > 0 ? pluginHeight : detailHeightDefault;
if (section.startsWith("brightnessSlider_"))
@@ -145,13 +145,8 @@ Singleton {
case "wifi":
case "network":
case "bluetooth":
case "battery":
case "builtin_vpn":
case "builtin_tailscale":
case "audioOutput":
case "audioInput":
case "diskUsage":
case "runningApps":
return detailHeightList;
default:
return detailHeightDefault;
@@ -18,8 +18,7 @@ FocusScope {
property string screenName: ""
property string screenModel: ""
property var transientSurfaceTracker: null
property real topInset: 0
property vector4d cornerRadii: Qt.vector4d(Theme.windowRadius, Theme.windowRadius, Theme.windowRadius, Theme.windowRadius)
property real maximumHeight: 0
property real coverage: 0
property var runningToplevels: []
@@ -31,17 +30,21 @@ FocusScope {
signal portSelectorRequested(var node)
property string shownSection: ""
property bool enterPending: false
readonly property bool animationsEnabled: CcMetrics.animationsEnabled && !SettingsData.reduceMotion
readonly property bool transitioning: enterPending || enterAnimation.running
readonly property var pageItem: pageLoader.item
readonly property real pageHeight: (pageItem?.implicitHeight ?? 0) > 0 ? pageItem.implicitHeight : CcMetrics.preferredDetailHeight(shownSection, pageItem?.preferredHeight ?? 0)
readonly property real contentHeight: pageHeight
readonly property real chromeHeight: header.height + CcMetrics.detailDialogPadding * 2
readonly property real maximumHeight: height - topInset - CcMetrics.detailDialogInset
// plugin detail content may not scroll itself, so the panel grows to fit it
readonly property bool pageScrollsItself: !shownSection.startsWith("plugin_")
readonly property real minimumHeight: chromeHeight + (pageScrollsItself ? Math.min(contentHeight, CcMetrics.detailMinContentHeight) : pageHeight)
readonly property real pageHeight: {
const own = pageItem?.implicitHeight ?? 0;
if (own > 0)
return own;
const preferred = pageItem?.preferredHeight ?? 0;
return preferred > 0 ? preferred : CcMetrics.detailHeightDefault;
}
// The band's extent includes the gap above and below its card, so they grow and shrink with it.
readonly property real chromeHeight: CcMetrics.gridGap * 2 + CcMetrics.detailHeaderHeight + CcMetrics.detailDialogPadding * 2
readonly property real contentCap: Math.max(CcMetrics.detailHeightFloor(shownSection, pageItem?.preferredHeight ?? 0), maximumHeight - chromeHeight)
readonly property real targetHeight: section === "" ? 0 : chromeHeight + Math.min(pageHeight, contentCap)
readonly property bool transitioning: heightAnimation.running
// Only IPC can ask for a plugin section whose tile is not placed, so there is nothing to show.
readonly property bool pageEmpty: pageItem?.empty === true
readonly property string title: {
const own = pageItem?.title ?? "";
if (own)
@@ -49,48 +52,37 @@ FocusScope {
const parsed = Sections.parse(shownSection);
return model?.getWidgetForId(parsed.base)?.text ?? "";
}
height: targetHeight
visible: shownSection !== ""
opacity: 0
layer.enabled: enterPending || enterAnimation.running || exitAnimation.running
layer.smooth: true
Accessible.role: Accessible.Dialog
clip: true
Accessible.role: Accessible.Grouping
Accessible.name: title
// Same timing as the popout's own resize, so the surface and the content move as one.
Behavior on height {
enabled: CcMetrics.animationsEnabled
NumberAnimation {
id: heightAnimation
duration: CcMetrics.transitionDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.standard
}
}
onPageEmptyChanged: {
if (pageEmpty && section !== "")
Qt.callLater(collapseRequested);
}
function finishClose() {
if (section !== "")
if (section !== "" || shownSection === "")
return;
pageLoader.sourceComponent = null;
shownSection = "";
dismissed();
}
function containsItem(item) {
for (let ancestor = item; ancestor; ancestor = ancestor.parent) {
if (ancestor === root)
return true;
}
return false;
}
function moveFocus(backwards) {
const current = root.Window.window?.activeFocusItem ?? root;
const next = current.nextItemInFocusChain(!backwards);
const target = root.containsItem(next) ? next : backwards ? closeButton : root.nextItemInFocusChain(true);
(root.containsItem(target) ? target : closeButton).forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
}
Shortcut {
sequence: "Tab"
enabled: root.section !== "" && root.activeFocus
onActivated: root.moveFocus(false)
}
Shortcut {
sequences: ["Backtab", "Shift+Tab"]
enabled: root.section !== "" && root.activeFocus
onActivated: root.moveFocus(true)
}
function dismissTransient() {
const item = pageItem;
if (!item || typeof item.dismissTransient !== "function")
@@ -129,132 +121,52 @@ FocusScope {
}
onSectionChanged: {
enterPending = false;
enterAnimation.stop();
exitAnimation.stop();
if (section === "") {
if (!animationsEnabled) {
root.opacity = 0;
if (!CcMetrics.animationsEnabled)
finishClose();
return;
}
exitBlocker.forceActiveFocus();
exitAnimation.start();
return;
}
const opening = shownSection === "";
enterPending = opening && animationsEnabled;
root.opacity = enterPending ? 0 : 1;
dialogSurface.entryScale = enterPending ? CcMetrics.popupEnterScale : 1;
shownSection = section;
pageLoader.sourceComponent = _componentFor(section);
(pageItem ?? root).forceActiveFocus();
}
Connections {
target: root.Window.window
enabled: root.enterPending
function onFrameSwapped() {
root.enterPending = false;
enterAnimation.start();
}
}
ParallelAnimation {
id: enterAnimation
NumberAnimation {
target: root
property: "opacity"
to: 1
duration: CcMetrics.fadeDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
NumberAnimation {
target: dialogSurface
property: "entryScale"
to: 1
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
}
}
NumberAnimation {
id: exitAnimation
target: root
property: "opacity"
to: 0
duration: CcMetrics.fadeDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
onFinished: root.finishClose()
onHeightChanged: {
if (section === "" && height <= 0.5)
finishClose();
}
Rectangle {
anchors.fill: parent
topLeftRadius: root.cornerRadii.x
topRightRadius: root.cornerRadii.y
bottomRightRadius: root.cornerRadii.z
bottomLeftRadius: root.cornerRadii.w
color: Theme.scrimColor
opacity: Theme.scrimAlpha
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.AllButtons
onClicked: {
if (root.section !== "")
root.backRequested();
}
onWheel: wheel => wheel.accepted = true
}
Rectangle {
id: dialogSurface
x: CcMetrics.detailDialogInset
y: root.topInset + Math.max(0, (root.maximumHeight - height) / 2)
width: Math.max(0, root.width - CcMetrics.detailDialogInset * 2)
height: Math.max(0, Math.min(root.chromeHeight + root.contentHeight, root.maximumHeight))
radius: Theme.cornerRadiusXL
Behavior on height {
enabled: root.animationsEnabled && !root.transitioning && root.section !== ""
NumberAnimation {
duration: Theme.expressiveDurations.expressiveFastSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.standard
}
}
y: CcMetrics.gridGap
width: parent.width
height: Math.max(0, parent.height - CcMetrics.gridGap * 2)
radius: CcMetrics.tallTileRadius
color: CcMetrics.dialogColor
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
property real entryScale: 1
scale: Math.min(1, entryScale)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
}
Item {
id: panel
anchors.fill: parent
anchors.margins: CcMetrics.detailDialogPadding
opacity: CcMetrics.hideCoveredContent ? 1 - root.coverage : 1
opacity: root.section !== "" ? (CcMetrics.hideCoveredContent ? 1 - root.coverage : 1) : 0
Behavior on opacity {
enabled: CcMetrics.animationsEnabled
NumberAnimation {
duration: CcMetrics.fadeDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
}
Item {
id: header
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: CcMetrics.pageHeaderHeight
height: CcMetrics.detailHeaderHeight
StyledText {
anchors.left: parent.left
@@ -263,7 +175,8 @@ FocusScope {
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
text: root.title
font.pixelSize: CcMetrics.pageTitleSize
font.pixelSize: CcMetrics.detailTitleSize
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
@@ -308,18 +221,6 @@ FocusScope {
}
}
MouseArea {
id: exitBlocker
anchors.fill: parent
visible: root.section === ""
hoverEnabled: true
acceptedButtons: Qt.AllButtons
onWheel: wheel => wheel.accepted = true
Keys.onPressed: event => event.accepted = true
Keys.onReleased: event => event.accepted = true
}
Connections {
target: root.pageItem
ignoreUnknownSignals: true
@@ -4,6 +4,7 @@ import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.ControlCenter
import "../utils/sections.js" as Sections
import "../utils/widgets.js" as WidgetUtils
import "../../../Common/GridLayout.js" as GridUtils
@@ -16,6 +17,17 @@ DEditableGrid {
property int columns: CcMetrics.gridColumns
property real availableHeight: CcMetrics.fallbackScreenHeight - CcMetrics.maxHeightInset
readonly property int maximumRows: CcMetrics.rowCapFor(availableHeight, cellWidth - CcMetrics.gridGap)
// The detail band opens under this tile's row and pushes the rows below it down. While a band is shown the
// slots follow its animated height frame by frame instead of running their own motion, so one animation drives
// both; the section is the band's own, which it keeps until it has fully collapsed.
property string expandedSection: ""
property real bandHeight: 0
property real bandTargetHeight: 0
property bool bandMoving: false
readonly property int expandedIndex: indexForSection(expandedSection)
readonly property real bandY: slotLayout.bandY ?? 0
readonly property real baseHeight: baseLayout.totalHeight
readonly property real targetHeight: GridUtils.insertBand(baseLayout, expandedIndex, expandedSection !== "" ? bandTargetHeight : 0).totalHeight
signal expandClicked(var widgetData)
signal removeWidget(int index)
@@ -44,11 +56,14 @@ DEditableGrid {
readonly property var shownIndices: savedWidgets.reduce((indices, widget, i) => !WidgetUtils.inGroup(widget, groupIds) && WidgetUtils.isShown(widget) && model?.componentForWidget(widget) ? indices.concat([i]) : indices, [])
sourceItems: shownIndices.map(i => Object.assign({}, savedWidgets[i], sizeWithHiddenTwin(i)))
slotLayout: {
readonly property var baseLayout: {
const items = clamped(layoutItems);
const layout = GridUtils.packCards(items, placementOrder, columns, width, CcMetrics.gridGap, cellWidth - CcMetrics.gridGap, I18n.isRtl, null, CcMetrics.gridStep, true);
return GridUtils.shrinkRows(layout, items.map(item => WidgetUtils.isSmall(item, item.h)), CcMetrics.smallRowHeight);
}
// Whole pixels keep the rows crisp while they ride the band's animation.
slotLayout: GridUtils.insertBand(baseLayout, expandedIndex, Math.round(bandHeight))
holdLayout: bandMoving
gravity: true
rowStep: CcMetrics.gridStep
packer: items => GridUtils.packCells(clamped(items), placementOrder, columns, null, CcMetrics.gridStep, true).cells
@@ -60,6 +75,17 @@ DEditableGrid {
memberDragging = false;
}
// A grouped member expands under its group's tile.
function indexForSection(section) {
if (section === "")
return -1;
const widget = Sections.widgetForSection(section);
const target = widget?.group ? savedWidgets.find(other => other?.id === WidgetUtils.GROUP_ID && other.instanceId === widget.group) : widget;
if (!target)
return -1;
return sourceItems.findIndex(item => item.id === target.id && (item.instanceId || "") === (target.instanceId || ""));
}
function clamped(items) {
return items.map(widget => Object.assign({}, widget, WidgetUtils.clampSize(widget, columns, maximumRows)));
}
@@ -10,6 +10,7 @@ Item {
readonly property var instance: builtinInstance ?? pluginHost.instance
readonly property real preferredHeight: instance?.ccDetailHeight ?? 0
readonly property bool empty: (instance?.ccDetailContent ?? null) === null
readonly property string title: instance?.ccWidgetPrimaryText || PluginService.loadedPlugins[pluginId]?.name || PluginService.availablePluginsList.find(p => p.id === pluginId)?.name || ""
PluginInstanceHost {
@@ -24,12 +24,13 @@ FocusScope {
readonly property bool pageOpen: (host.expandedSection ?? "") !== ""
readonly property real gridHeight: widgetGrid.gridHeight
readonly property real gridTargetHeight: widgetGrid.targetHeight
// Reserved from the grid in both modes so the island growing into edit mode never reflows tiles.
readonly property real editRoom: host.editRoom ?? 0
readonly property real chromeHeight: (host.editRoomShown ?? false) ? editRoom : 0
readonly property bool stripAbove: host.stripAbove ?? false
readonly property bool widgetSheetOpen: widgetSheetLoader.item?.active ?? false
readonly property real coveredAmount: Math.max(detailPage.opacity, widgetSheetLoader.item?.progress ?? 0)
readonly property real coveredAmount: Math.max(widgetSheetLoader.item?.progress ?? 0, codecSelectorLoader.item?.presence ?? 0, portSelectorLoader.item?.presence ?? 0)
property bool widgetSheetRequested: false
property int toplevelRevision: 0
readonly property var runningToplevels: {
@@ -46,13 +47,8 @@ FocusScope {
readonly property real dragRoom: widgetGrid.dragging && (host.growsFromTop ?? true) && widgetGrid.pinnedHeight + newRowUnit <= availableGridHeight ? newRowUnit : 0
readonly property real newRowUnit: CcMetrics.gridGap + widgetGrid.slotLayout.rowUnit
// Held during a drag so the trash never moves under the pointer.
readonly property real heldGridHeight: widgetGrid.dragging ? widgetGrid.pinnedHeight + dragRoom : gridHeight
readonly property real targetImplicitHeight: {
const total = CcMetrics.sheetPadding * 2 + heldGridHeight;
if (detailPage.shownSection === "")
return total;
return Math.max(total, detailPage.topInset + detailPage.minimumHeight + CcMetrics.detailDialogInset);
}
readonly property real heldGridHeight: widgetGrid.dragging ? widgetGrid.pinnedHeight + dragRoom : gridTargetHeight
readonly property real targetImplicitHeight: CcMetrics.sheetPadding * 2 + heldGridHeight
property Item detailReturnFocus: null
property var pageHistory: []
// One string so Discard and undo can compare and restore the whole session state at once.
@@ -115,13 +111,13 @@ FocusScope {
return;
if (host.expandedSection)
pageHistory = pageHistory.concat([host.expandedSection]);
else if (detailPage.shownSection === "")
else if (detailBand.shownSection === "")
detailReturnFocus = root.Window.window?.activeFocusItem ?? null;
host.expandedSection = section;
}
function goBack() {
if (detailPage.dismissTransient())
if (detailBand.dismissTransient())
return;
if (pageHistory.length > 0) {
const previous = pageHistory[pageHistory.length - 1];
@@ -132,6 +128,14 @@ FocusScope {
host.collapseAll();
}
// Followed while the band grows, so it stays in view however the popout and the grid settle around it.
function revealDetail() {
if (detailBand.section === "")
return;
const top = mainColumn.y + body.y + detailBand.y;
contentFlickable.revealRange(top, top + detailBand.height);
}
function openWidgetPage(widgetData) {
const section = Sections.sectionFor(widgetData);
if (host.expandedSection === section) {
@@ -366,7 +370,7 @@ FocusScope {
anchors.bottomMargin: -contentInset - (editActions.joined && !root.stripAbove ? editActions.height : 0)
z: 1
visible: root.host.editMode
enabled: detailPage.shownSection === "" && !root.widgetSheetOpen
enabled: detailBand.shownSection === "" && !root.widgetSheetOpen
edgeResize: root.panelResizing ? resizeSide < 0 : root.panelResizer.sideMovable(-1, root.gridColumns)
cornerResize: root.panelResizing ? resizeSide > 0 : root.panelResizer.sideMovable(1, root.gridColumns)
horizontalResize: true
@@ -451,7 +455,11 @@ FocusScope {
anchors.top: parent.top
editMode: root.host.editMode
model: widgetModel
live: root.host.shouldBeVisible && detailPage.shownSection === ""
live: root.host.shouldBeVisible
expandedSection: detailBand.shownSection
bandHeight: detailBand.height
bandTargetHeight: detailBand.targetHeight
bandMoving: detailBand.shownSection !== ""
screenName: root.host.triggerScreen?.name || ""
tapToClose: root.host.headerTogglesClose ?? false
runningToplevels: root.runningToplevels
@@ -481,6 +489,37 @@ FocusScope {
loader.item.openFromControlCenter(bounds, root.host.screen);
}
}
CcDetailBand {
id: detailBand
x: 0
y: widgetGrid.bandY
width: parent.width
section: root.host.expandedSection ?? ""
maximumHeight: root.availableGridHeight - widgetGrid.baseHeight
coverage: Math.max(codecSelectorLoader.item?.presence ?? 0, portSelectorLoader.item?.presence ?? 0)
model: widgetModel
screenName: root.host.triggerScreen?.name || ""
screenModel: root.host.triggerScreen?.model || ""
transientSurfaceTracker: root.host.transientSurfaceTracker
runningToplevels: root.runningToplevels
onCodecSelectorRequested: device => root.showCodecSelector(device)
onPortSelectorRequested: node => root.showPortSelector(node)
onBackRequested: root.goBack()
onCollapseRequested: root.host.collapseAll()
onCloseRequested: root.host.close()
onHeightChanged: root.revealDetail()
onDismissed: {
const target = root.detailReturnFocus;
root.detailReturnFocus = null;
if (target?.visible && target.enabled) {
target.forceActiveFocus();
return;
}
root.forceActiveFocus();
}
}
}
}
}
@@ -495,7 +534,7 @@ FocusScope {
// Below the edit rings so the shoulder fill never covers them.
z: -1
editMode: root.host.editMode
enabled: detailPage.shownSection === "" && !root.widgetSheetOpen
enabled: detailBand.shownSection === "" && !root.widgetSheetOpen
dragging: root.gridDragWidget !== null
dragRemovable: WidgetUtils.isRemovable(root.gridDragWidget)
overTrash: root.overTrash
@@ -514,36 +553,6 @@ FocusScope {
onCancelRequested: root.cancelEdit()
}
CcDetailPage {
id: detailPage
z: 1
anchors.fill: parent
section: root.host.expandedSection ?? ""
topInset: CcMetrics.sheetPadding
cornerRadii: root.surfaceCornerRadii
coverage: Math.max(codecSelectorLoader.item?.presence ?? 0, portSelectorLoader.item?.presence ?? 0)
model: widgetModel
screenName: root.host.triggerScreen?.name || ""
screenModel: root.host.triggerScreen?.model || ""
transientSurfaceTracker: root.host.transientSurfaceTracker
onCodecSelectorRequested: device => root.showCodecSelector(device)
onPortSelectorRequested: node => root.showPortSelector(node)
runningToplevels: root.runningToplevels
onBackRequested: root.goBack()
onCollapseRequested: root.host.collapseAll()
onCloseRequested: root.host.close()
onDismissed: {
const target = root.detailReturnFocus;
root.detailReturnFocus = null;
if (target?.visible && target.enabled) {
target.forceActiveFocus();
return;
}
root.forceActiveFocus();
}
}
Loader {
id: codecSelectorLoader
@@ -10,7 +10,7 @@ DPopout {
layerNamespace: "dms:control-center"
fullHeightSurface: true
resizeCurve: Theme.expressiveCurves.standard
resizeDuration: Theme.expressiveDurations.expressiveFastSpatial
resizeDuration: CcMetrics.transitionDuration
resizeMotion: true
resizing: contentLoader.item?.panelResizing ?? false
surfacePadding: CcMetrics.editOverflow
@@ -4,6 +4,7 @@ import qs.Modules.ControlCenter
import qs.Modules.Settings.Widgets
SettingsRow {
minHeight: Theme.listItemHeight
paddingH: CcMetrics.rowPaddingH
paddingV: CcMetrics.rowPaddingV
rowColor: CcMetrics.rowColor
@@ -137,7 +137,7 @@ function clampSize(widget, columns, rows = Infinity) {
function addWidget(widgetId, columns) {
const widgets = Common.SettingsData.controlCenterWidgets.slice();
const widget = defaultWidget(widgetId, columns);
if (BUTTON_IDS.includes(widgetId))
if (BUTTON_IDS.includes(widgetId) || isSliderWidget(widgetId))
widget.small = true;
if (widgetId === "diskUsage") {
+2
View File
@@ -16,6 +16,8 @@ Item {
property real placeholderRadius: Theme.cornerRadiusXL
property bool animationsEnabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
property bool animateLayout: false
// Slots take layout changes as they come, for a layout that an animation elsewhere already drives frame by frame.
property bool holdLayout: false
property int draggingSourceIndex: -1
property var dragCell: null
property var sizePreview: null
+4 -4
View File
@@ -112,7 +112,7 @@ Item {
}
Behavior on width {
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout && !root.grid.holdLayout
NumberAnimation {
duration: Theme.expressiveDurations.expressiveFastSpatial
easing.type: Easing.BezierSpline
@@ -121,7 +121,7 @@ Item {
}
Behavior on height {
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout && !root.grid.holdLayout
NumberAnimation {
duration: Theme.expressiveDurations.expressiveFastSpatial
easing.type: Easing.BezierSpline
@@ -130,7 +130,7 @@ Item {
}
Behavior on x {
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout && !root.grid.holdLayout
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
@@ -139,7 +139,7 @@ Item {
}
Behavior on y {
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout
enabled: !root.dragging && root.resizeOrigin === null && root.grid.animationsEnabled && root.grid.animateLayout && !root.grid.holdLayout
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline