mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-27 21:47:50 -04:00
cc: replace detail pages with bands (1.6 style)
port 2.0
(cherry picked from commit 290360428e)
This commit is contained in:
1 parent
433a84d824
commit
3f75012b7c
15 files changed
+204
-222
No files matched your search
+1
-1
Submodule dux-qml-common updated: c6e4a6bce2...e261e339c4.
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
+57
-156
@@ -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") {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user