1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-16 02:32:09 -04:00

(frame): QOL Control Center & Notification updates

This commit is contained in:
purian23
2026-04-16 00:06:34 -04:00
parent 59160ce5ac
commit c11069d502
10 changed files with 637 additions and 463 deletions

View File

@@ -115,6 +115,32 @@ Singleton {
return true; return true;
} }
function setPopoutBody(claimId, bodyX, bodyY, bodyW, bodyH) {
if (!hasPopoutOwner(claimId))
return false;
if (bodyX !== undefined) {
const nextX = Number(bodyX);
if (!isNaN(nextX) && popoutBodyX !== nextX)
popoutBodyX = nextX;
}
if (bodyY !== undefined) {
const nextY = Number(bodyY);
if (!isNaN(nextY) && popoutBodyY !== nextY)
popoutBodyY = nextY;
}
if (bodyW !== undefined) {
const nextW = Number(bodyW);
if (!isNaN(nextW) && popoutBodyW !== nextW)
popoutBodyW = nextW;
}
if (bodyH !== undefined) {
const nextH = Number(bodyH);
if (!isNaN(nextH) && popoutBodyH !== nextH)
popoutBodyH = nextH;
}
return true;
}
function _cloneDockStates() { function _cloneDockStates() {
const next = {}; const next = {};
for (const screenName in dockStates) for (const screenName in dockStates)

View File

@@ -37,7 +37,7 @@ Item {
Loader { Loader {
id: pluginDetailLoader id: pluginDetailLoader
width: parent.width width: parent.width
height: parent.height - Theme.spacingS height: Math.max(0, parent.height - Theme.spacingS)
y: Theme.spacingS y: Theme.spacingS
active: false active: false
sourceComponent: null sourceComponent: null
@@ -46,7 +46,7 @@ Item {
Loader { Loader {
id: coreDetailLoader id: coreDetailLoader
width: parent.width width: parent.width
height: parent.height - Theme.spacingS height: Math.max(0, parent.height - Theme.spacingS)
y: Theme.spacingS y: Theme.spacingS
active: false active: false
sourceComponent: null sourceComponent: null
@@ -134,7 +134,7 @@ Item {
} }
pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent; pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent;
pluginDetailLoader.active = parent.height > 0; pluginDetailLoader.active = true;
return; return;
} }
@@ -155,19 +155,19 @@ Item {
} }
pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent; pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent;
pluginDetailLoader.active = parent.height > 0; pluginDetailLoader.active = true;
return; return;
} }
if (root.expandedSection.startsWith("diskUsage_")) { if (root.expandedSection.startsWith("diskUsage_")) {
coreDetailLoader.sourceComponent = diskUsageDetailComponent; coreDetailLoader.sourceComponent = diskUsageDetailComponent;
coreDetailLoader.active = parent.height > 0; coreDetailLoader.active = true;
return; return;
} }
if (root.expandedSection.startsWith("brightnessSlider_")) { if (root.expandedSection.startsWith("brightnessSlider_")) {
coreDetailLoader.sourceComponent = brightnessDetailComponent; coreDetailLoader.sourceComponent = brightnessDetailComponent;
coreDetailLoader.active = parent.height > 0; coreDetailLoader.active = true;
return; return;
} }
@@ -192,7 +192,7 @@ Item {
return; return;
} }
coreDetailLoader.active = parent.height > 0; coreDetailLoader.active = true;
} }
Component { Component {

View File

@@ -51,6 +51,35 @@ Column {
return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing); return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing);
} }
readonly property real targetImplicitHeight: {
const rows = layoutResult.rows;
let totalHeight = 0;
for (let i = 0; i < rows.length; i++) {
const widgets = rows[i] || [];
const sliderOnly = widgets.length > 0 && widgets.every(w => {
const id = w.id || "";
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider";
});
totalHeight += sliderOnly ? (editMode ? 56 : 36) : 60;
if (expandedSection !== "" && i === expandedRowIndex)
totalHeight += detailHeightForSection(expandedSection) + Theme.spacingS;
}
totalHeight += Math.max(0, rows.length - 1) * spacing;
return totalHeight;
}
function detailHeightForSection(section) {
if (!section)
return 0;
if (section === "wifi" || section === "bluetooth" || section === "builtin_vpn")
return Math.min(350, _maxDetailHeight);
if (section.startsWith("brightnessSlider_"))
return Math.min(400, _maxDetailHeight);
if (section.startsWith("plugin_"))
return Math.min(250, _maxDetailHeight);
return Math.min(250, _maxDetailHeight);
}
function calculateRowsAndWidgets() { function calculateRowsAndWidgets() {
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex); return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
} }
@@ -179,7 +208,10 @@ Column {
id: detailHost id: detailHost
width: parent.width width: parent.width
maxAvailableHeight: root._maxDetailHeight maxAvailableHeight: root._maxDetailHeight
height: active ? (getDetailHeight(root.expandedSection) + Theme.spacingS) : 0 height: active ? (root.detailHeightForSection(root.expandedSection) + Theme.spacingS) : 0
clip: true
property string retainedSection: ""
property var retainedWidgetData: null
property bool active: { property bool active: {
if (root.expandedSection === "") if (root.expandedSection === "")
return false; return false;
@@ -196,14 +228,47 @@ Column {
return rowIndex === root.expandedRowIndex; return rowIndex === root.expandedRowIndex;
} }
visible: active visible: active || height > 0.5
expandedSection: root.expandedSection expandedSection: active ? root.expandedSection : retainedSection
expandedWidgetData: root.expandedWidgetData expandedWidgetData: active ? root.expandedWidgetData : retainedWidgetData
bluetoothCodecSelector: root.bluetoothCodecSelector bluetoothCodecSelector: root.bluetoothCodecSelector
widgetModel: root.model widgetModel: root.model
collapseCallback: root.requestCollapse collapseCallback: root.requestCollapse
screenName: root.screenName screenName: root.screenName
screenModel: root.screenModel screenModel: root.screenModel
function retainActiveDetail() {
if (!active || !root.expandedSection)
return;
retainedSection = root.expandedSection;
retainedWidgetData = root.expandedWidgetData;
}
onActiveChanged: retainActiveDetail()
onHeightChanged: {
if (!active && height <= 0.5) {
retainedSection = "";
retainedWidgetData = null;
}
}
Connections {
target: root
function onExpandedSectionChanged() {
detailHost.retainActiveDetail();
}
function onExpandedWidgetDataChanged() {
detailHost.retainActiveDetail();
}
}
Behavior on height {
NumberAnimation {
duration: Theme.variantDuration(Theme.popoutAnimationDuration, detailHost.active)
easing.type: Easing.BezierSpline
easing.bezierCurve: detailHost.active ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
}
}
} }
} }
} }

View File

@@ -20,19 +20,53 @@ DankPopout {
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var expandedWidgetData: null property var expandedWidgetData: null
property bool powerMenuOpen: powerMenuModalLoader?.item?.shouldBeVisible ?? false property bool powerMenuOpen: powerMenuModalLoader?.item?.shouldBeVisible ?? false
property real targetPopupHeight: 400
property bool _heightUpdatePending: false
signal lockRequested signal lockRequested
function _maxPopupHeight() {
const screenHeight = (triggerScreen?.height ?? 1080);
return screenHeight - 100;
}
function _contentTargetHeight() {
const item = contentLoader.item;
if (!item)
return 400;
const naturalHeight = item.targetImplicitHeight !== undefined ? item.targetImplicitHeight : item.implicitHeight;
return Math.max(300, naturalHeight + 20);
}
function updateTargetPopupHeight() {
const target = Math.min(_maxPopupHeight(), _contentTargetHeight());
if (Math.abs(targetPopupHeight - target) < 0.5)
return;
targetPopupHeight = target;
}
function queueTargetPopupHeightUpdate() {
if (_heightUpdatePending)
return;
_heightUpdatePending = true;
Qt.callLater(() => {
_heightUpdatePending = false;
updateTargetPopupHeight();
});
}
function collapseAll() { function collapseAll() {
expandedSection = ""; expandedSection = "";
expandedWidgetIndex = -1; expandedWidgetIndex = -1;
expandedWidgetData = null; expandedWidgetData = null;
queueTargetPopupHeightUpdate();
} }
onEditModeChanged: { onEditModeChanged: {
if (editMode) { if (editMode) {
collapseAll(); collapseAll();
} }
queueTargetPopupHeightUpdate();
} }
onVisibleChanged: { onVisibleChanged: {
@@ -52,12 +86,7 @@ DankPopout {
} }
popupWidth: 550 popupWidth: 550
popupHeight: { popupHeight: targetPopupHeight
const screenHeight = (triggerScreen?.height ?? 1080);
const maxHeight = screenHeight - 100;
const contentHeight = contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400;
return Math.min(maxHeight, contentHeight);
}
triggerWidth: 80 triggerWidth: 80
positioning: "" positioning: ""
screen: triggerScreen screen: triggerScreen
@@ -95,6 +124,7 @@ DankPopout {
onShouldBeVisibleChanged: { onShouldBeVisibleChanged: {
if (shouldBeVisible) { if (shouldBeVisible) {
collapseAll(); collapseAll();
queueTargetPopupHeightUpdate();
Qt.callLater(() => { Qt.callLater(() => {
if (NetworkService.activeService) if (NetworkService.activeService)
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled; NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
@@ -111,6 +141,28 @@ DankPopout {
} }
} }
onExpandedSectionChanged: queueTargetPopupHeightUpdate()
onExpandedWidgetIndexChanged: queueTargetPopupHeightUpdate()
onTriggerScreenChanged: queueTargetPopupHeightUpdate()
Connections {
target: contentLoader
function onLoaded() {
root.queueTargetPopupHeightUpdate();
}
}
Connections {
target: contentLoader.item
ignoreUnknownSignals: true
function onTargetImplicitHeightChanged() {
root.queueTargetPopupHeightUpdate();
}
function onImplicitHeightChanged() {
root.queueTargetPopupHeightUpdate();
}
}
WidgetModel { WidgetModel {
id: widgetModel id: widgetModel
} }
@@ -122,7 +174,13 @@ DankPopout {
LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true LayoutMirroring.childrenInherit: true
implicitHeight: mainColumn.implicitHeight + Theme.spacingM readonly property real targetImplicitHeight: {
let total = headerPane.implicitHeight + Theme.spacingS + widgetGrid.targetImplicitHeight;
if (editControls.visible)
total += Theme.spacingS + editControls.height;
return total + Theme.spacingM;
}
implicitHeight: targetImplicitHeight
property alias bluetoothCodecSelector: bluetoothCodecSelector property alias bluetoothCodecSelector: bluetoothCodecSelector
color: "transparent" color: "transparent"
@@ -145,84 +203,94 @@ DankPopout {
} }
} }
Column { DankFlickable {
id: mainColumn id: contentFlickable
width: parent.width - Theme.spacingL * 2 anchors.fill: parent
x: Theme.spacingL clip: true
y: Theme.spacingL contentWidth: width
spacing: Theme.spacingS contentHeight: Math.max(height, mainColumn.implicitHeight + Theme.spacingM)
interactive: contentHeight > height
HeaderPane { Column {
id: headerPane id: mainColumn
width: parent.width width: contentFlickable.width - Theme.spacingL * 2
editMode: root.editMode x: Theme.spacingL
onEditModeToggled: root.editMode = !root.editMode y: Theme.spacingL
onPowerButtonClicked: { spacing: Theme.spacingS
if (powerMenuModalLoader) {
powerMenuModalLoader.active = true; HeaderPane {
if (powerMenuModalLoader.item) { id: headerPane
const bounds = Qt.rect(root.alignedX, root.alignedY, root.popupWidth, root.popupHeight); width: parent.width
powerMenuModalLoader.item.openFromControlCenter(bounds, root.screen); editMode: root.editMode
onEditModeToggled: root.editMode = !root.editMode
onPowerButtonClicked: {
if (powerMenuModalLoader) {
powerMenuModalLoader.active = true;
if (powerMenuModalLoader.item) {
const bounds = Qt.rect(root.alignedX, root.alignedY, root.popupWidth, root.popupHeight);
powerMenuModalLoader.item.openFromControlCenter(bounds, root.screen);
}
} }
} }
} onLockRequested: {
onLockRequested: { root.close();
root.close(); root.lockRequested();
root.lockRequested(); }
} onSettingsButtonClicked: {
onSettingsButtonClicked: { root.close();
root.close();
}
}
DragDropGrid {
id: widgetGrid
width: parent.width
editMode: root.editMode
maxPopoutHeight: {
const screenHeight = (root.triggerScreen?.height ?? 1080);
return screenHeight - 100 - Theme.spacingL - headerPane.height - Theme.spacingS;
}
expandedSection: root.expandedSection
expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData
model: widgetModel
bluetoothCodecSelector: bluetoothCodecSelector
colorPickerModal: root.colorPickerModal
screenName: root.triggerScreen?.name || ""
screenModel: root.triggerScreen?.model || ""
parentScreen: root.triggerScreen
onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex;
root.expandedWidgetData = widgetData;
if (widgetData.id === "diskUsage") {
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"));
} else if (widgetData.id === "brightnessSlider") {
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"));
} else {
root.toggleSection(widgetData.id);
} }
} }
onRemoveWidget: index => widgetModel.removeWidget(index)
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
onToggleWidgetSize: index => widgetModel.toggleWidgetSize(index)
onCollapseRequested: root.collapseAll()
}
EditControls { DragDropGrid {
width: parent.width id: widgetGrid
visible: editMode width: parent.width
popoutContent: controlContent editMode: root.editMode
availableWidgets: { maxPopoutHeight: {
if (!editMode) const screenHeight = (root.triggerScreen?.height ?? 1080);
return []; return screenHeight - 100 - Theme.spacingL - headerPane.implicitHeight - Theme.spacingS;
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id); }
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets()); expandedSection: root.expandedSection
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id)); expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData
model: widgetModel
bluetoothCodecSelector: bluetoothCodecSelector
colorPickerModal: root.colorPickerModal
screenName: root.triggerScreen?.name || ""
screenModel: root.triggerScreen?.model || ""
parentScreen: root.triggerScreen
onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex;
root.expandedWidgetData = widgetData;
if (widgetData.id === "diskUsage") {
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"));
} else if (widgetData.id === "brightnessSlider") {
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"));
} else {
root.toggleSection(widgetData.id);
}
}
onRemoveWidget: index => widgetModel.removeWidget(index)
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
onToggleWidgetSize: index => widgetModel.toggleWidgetSize(index)
onCollapseRequested: root.collapseAll()
}
EditControls {
id: editControls
width: parent.width
visible: editMode
popoutContent: controlContent
availableWidgets: {
if (!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()
} }
onAddWidget: widgetId => widgetModel.addWidget(widgetId)
onResetToDefault: () => widgetModel.resetToDefault()
onClearAll: () => widgetModel.clearAll()
} }
} }

View File

@@ -16,8 +16,7 @@ DankListView {
property bool listInitialized: false property bool listInitialized: false
property int swipingCardIndex: -1 property int swipingCardIndex: -1
property real swipingCardOffset: 0 property real swipingCardOffset: 0
property real __pendingStableHeight: 0 property bool _stableHeightUpdatePending: false
property real __heightUpdateThreshold: 20
readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0 readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0
readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1) readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1)
readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1) readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1)
@@ -27,51 +26,52 @@ DankListView {
Qt.callLater(() => { Qt.callLater(() => {
if (listView) { if (listView) {
listView.listInitialized = true; listView.listInitialized = true;
listView.stableContentHeight = listView.contentHeight; listView.syncStableContentHeight(false);
} }
}); });
} }
Timer { function targetContentHeight() {
id: heightUpdateDebounce if (count <= 0)
interval: Theme.mediumDuration + 20 return contentHeight;
repeat: false
onTriggered: { let total = topMargin + bottomMargin + Math.max(0, count - 1) * spacing;
if (!listView.isAnimatingExpansion && Math.abs(listView.__pendingStableHeight - listView.stableContentHeight) > listView.__heightUpdateThreshold) { for (let i = 0; i < count; i++) {
listView.stableContentHeight = listView.__pendingStableHeight; const item = itemAtIndex(i);
} if (!item || item.nonAnimHeight === undefined)
return contentHeight;
total += item.nonAnimHeight;
} }
return Math.max(0, total);
}
function syncStableContentHeight(useTarget) {
const nextHeight = useTarget ? targetContentHeight() : contentHeight;
if (Math.abs(nextHeight - stableContentHeight) <= 0.5)
return;
stableContentHeight = nextHeight;
}
function queueStableContentHeightUpdate(useTarget) {
if (_stableHeightUpdatePending)
return;
_stableHeightUpdatePending = true;
Qt.callLater(() => {
_stableHeightUpdatePending = false;
syncStableContentHeight(useTarget || isAnimatingExpansion);
});
} }
onContentHeightChanged: { onContentHeightChanged: {
if (!isAnimatingExpansion) { if (!isAnimatingExpansion)
__pendingStableHeight = contentHeight; queueStableContentHeightUpdate(false);
if (Math.abs(contentHeight - stableContentHeight) > __heightUpdateThreshold) {
heightUpdateDebounce.restart();
} else {
stableContentHeight = contentHeight;
}
}
} }
onIsAnimatingExpansionChanged: { onIsAnimatingExpansionChanged: {
if (isAnimatingExpansion) { if (isAnimatingExpansion) {
heightUpdateDebounce.stop(); syncStableContentHeight(true);
let delta = 0;
for (let i = 0; i < count; i++) {
const item = itemAtIndex(i);
if (item && item.children[0] && item.children[0].isAnimating) {
const targetDelegateHeight = item.children[0].targetHeight + listView.delegateShadowGutter;
delta += targetDelegateHeight - item.height;
}
}
const targetHeight = contentHeight + delta;
// During expansion, always update immediately without threshold check
stableContentHeight = targetHeight;
} else { } else {
__pendingStableHeight = contentHeight; queueStableContentHeightUpdate(false);
heightUpdateDebounce.stop();
stableContentHeight = __pendingStableHeight;
} }
} }
@@ -148,11 +148,14 @@ DankListView {
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(listView.swipingCardOffset) / width * 0.02 : 1.0 readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(listView.swipingCardOffset) / width * 0.02 : 1.0
readonly property real swipeFadeStartOffset: width * 0.75 readonly property real swipeFadeStartOffset: width * 0.75
readonly property real swipeFadeDistance: Math.max(1, width - swipeFadeStartOffset) readonly property real swipeFadeDistance: Math.max(1, width - swipeFadeStartOffset)
readonly property real nonAnimHeight: notificationCard.targetHeight + listView.delegateShadowGutter
Component.onCompleted: { Component.onCompleted: {
Qt.callLater(() => { Qt.callLater(() => {
if (delegateRoot) if (delegateRoot) {
delegateRoot.__delegateInitialized = true; delegateRoot.__delegateInitialized = true;
listView.queueStableContentHeightUpdate(listView.isAnimatingExpansion);
}
}); });
} }
@@ -180,6 +183,7 @@ DankListView {
onIsAnimatingChanged: { onIsAnimatingChanged: {
if (isAnimating) { if (isAnimating) {
listView.isAnimatingExpansion = true; listView.isAnimatingExpansion = true;
listView.syncStableContentHeight(true);
} else { } else {
Qt.callLater(() => { Qt.callLater(() => {
if (!notificationCard || !listView) if (!notificationCard || !listView)
@@ -197,6 +201,13 @@ DankListView {
} }
} }
onTargetHeightChanged: {
if (isAnimating || listView.isAnimatingExpansion)
listView.syncStableContentHeight(true);
else
listView.queueStableContentHeightUpdate(false);
}
isGroupSelected: { isGroupSelected: {
if (!keyboardController || !keyboardController.keyboardNavigationActive || !listView.keyboardActive) if (!keyboardController || !keyboardController.keyboardNavigationActive || !listView.keyboardActive)
return false; return false;

View File

@@ -16,6 +16,12 @@ Rectangle {
property bool userInitiatedExpansion: false property bool userInitiatedExpansion: false
property bool isAnimating: false property bool isAnimating: false
property bool animateExpansion: true property bool animateExpansion: true
property bool _retainedExpandedContent: false
property bool _clipAnimatedContent: false
property real expandedContentOpacity: expanded ? 1 : 0
property real collapsedContentOpacity: expanded ? 0 : 1
readonly property bool renderExpandedContent: expanded || _retainedExpandedContent
readonly property bool renderCollapsedContent: !expanded
property bool isGroupSelected: false property bool isGroupSelected: false
property int selectedNotificationIndex: -1 property int selectedNotificationIndex: -1
@@ -57,6 +63,14 @@ Rectangle {
}); });
} }
function expansionMotionDuration() {
return root.connectedFrameMode ? Theme.variantDuration(Theme.popoutAnimationDuration, root.expanded) : (root.expanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration);
}
function expansionMotionCurve() {
return root.connectedFrameMode ? (root.expanded ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : Theme.expressiveCurves.emphasized;
}
Behavior on scale { Behavior on scale {
enabled: listLevelScaleAnimationsEnabled enabled: listLevelScaleAnimationsEnabled
NumberAnimation { NumberAnimation {
@@ -66,6 +80,7 @@ Rectangle {
} }
Behavior on shadowBlurPx { Behavior on shadowBlurPx {
enabled: !root.connectedFrameMode
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
@@ -73,6 +88,7 @@ Rectangle {
} }
Behavior on shadowOffsetXPx { Behavior on shadowOffsetXPx {
enabled: !root.connectedFrameMode
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
@@ -80,6 +96,7 @@ Rectangle {
} }
Behavior on shadowOffsetYPx { Behavior on shadowOffsetYPx {
enabled: !root.connectedFrameMode
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
@@ -94,6 +111,24 @@ Rectangle {
} }
} }
Behavior on expandedContentOpacity {
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
NumberAnimation {
duration: root.expansionMotionDuration()
easing.type: Easing.BezierSpline
easing.bezierCurve: root.expansionMotionCurve()
}
}
Behavior on collapsedContentOpacity {
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
NumberAnimation {
duration: root.expansionMotionDuration()
easing.type: Easing.BezierSpline
easing.bezierCurve: root.expansionMotionCurve()
}
}
color: { color: {
if (isGroupSelected && keyboardNavigationActive) { if (isGroupSelected && keyboardNavigationActive) {
return Theme.primaryPressed; return Theme.primaryPressed;
@@ -129,7 +164,31 @@ Rectangle {
} }
return 0; return 0;
} }
clip: false clip: connectedFrameMode && _clipAnimatedContent
onExpandedChanged: {
if (connectedFrameMode && __initialized && userInitiatedExpansion && animateExpansion)
_clipAnimatedContent = true;
if (expanded) {
_retainedExpandedContent = false;
return;
}
if (connectedFrameMode && __initialized && userInitiatedExpansion && animateExpansion)
_retainedExpandedContent = true;
}
onHeightChanged: {
if (Math.abs(height - targetHeight) > 0.5)
return;
_clipAnimatedContent = false;
if (!expanded && _retainedExpandedContent)
_retainedExpandedContent = false;
}
onExpandedContentOpacityChanged: {
if (!expanded && _retainedExpandedContent && expandedContentOpacity <= 0.01)
_retainedExpandedContent = false;
}
HoverHandler { HoverHandler {
id: cardHoverHandler id: cardHoverHandler
@@ -149,7 +208,7 @@ Rectangle {
shadowOffsetX: root.shadowOffsetXPx shadowOffsetX: root.shadowOffsetXPx
shadowOffsetY: root.shadowOffsetYPx shadowOffsetY: root.shadowOffsetYPx
shadowColor: root.shadowElevation ? Theme.elevationShadowColor(root.shadowElevation) : "transparent" shadowColor: root.shadowElevation ? Theme.elevationShadowColor(root.shadowElevation) : "transparent"
shadowEnabled: root.shadowsAllowed shadowEnabled: root.shadowsAllowed && !root.connectedFrameMode
} }
Rectangle { Rectangle {
@@ -189,7 +248,8 @@ Rectangle {
anchors.leftMargin: Theme.spacingL anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin
height: collapsedContentHeight + extraHeight height: collapsedContentHeight + extraHeight
visible: !expanded visible: renderCollapsedContent
opacity: root.collapsedContentOpacity
DankCircularImage { DankCircularImage {
id: iconContainer id: iconContainer
@@ -388,7 +448,8 @@ Rectangle {
anchors.leftMargin: Theme.spacingL anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL anchors.rightMargin: Theme.spacingL
spacing: compactMode ? Theme.spacingXS : Theme.spacingS spacing: compactMode ? Theme.spacingXS : Theme.spacingS
visible: expanded visible: renderExpandedContent
opacity: root.expandedContentOpacity
Item { Item {
width: parent.width width: parent.width
@@ -831,7 +892,8 @@ Rectangle {
} }
Row { Row {
visible: !expanded visible: renderCollapsedContent
opacity: root.collapsedContentOpacity
anchors.right: clearButton.visible ? clearButton.left : parent.right anchors.right: clearButton.visible ? clearButton.left : parent.right
anchors.rightMargin: clearButton.visible ? contentSpacing : Theme.spacingL anchors.rightMargin: clearButton.visible ? contentSpacing : Theme.spacingL
anchors.top: collapsedContent.bottom anchors.top: collapsedContent.bottom
@@ -887,7 +949,8 @@ Rectangle {
property bool isHovered: false property bool isHovered: false
readonly property int actionCount: (notificationGroup?.latestNotification?.actions || []).length readonly property int actionCount: (notificationGroup?.latestNotification?.actions || []).length
visible: !expanded && actionCount < 3 visible: renderCollapsedContent && actionCount < 3
opacity: root.collapsedContentOpacity
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.spacingL anchors.rightMargin: Theme.spacingL
anchors.top: collapsedContent.bottom anchors.top: collapsedContent.bottom
@@ -918,7 +981,7 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
visible: !expanded && (notificationGroup?.count || 0) > 1 && !descriptionExpanded visible: renderCollapsedContent && (notificationGroup?.count || 0) > 1 && !descriptionExpanded
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.userInitiatedExpansion = true; root.userInitiatedExpansion = true;
@@ -962,15 +1025,17 @@ Rectangle {
Behavior on height { Behavior on height {
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
NumberAnimation { NumberAnimation {
duration: root.connectedFrameMode ? Theme.variantDuration(Theme.popoutAnimationDuration, root.expanded) : (root.expanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration) duration: root.expansionMotionDuration()
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: root.connectedFrameMode ? (root.expanded ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : Theme.expressiveCurves.emphasized easing.bezierCurve: root.expansionMotionCurve()
onRunningChanged: { onRunningChanged: {
if (running) { if (running) {
root.isAnimating = true; root.isAnimating = true;
} else { } else {
root.isAnimating = false; root.isAnimating = false;
root.userInitiatedExpansion = false; root.userInitiatedExpansion = false;
root._retainedExpandedContent = false;
root._clipAnimatedContent = false;
} }
} }
} }

View File

@@ -14,6 +14,7 @@ DankPopout {
property real stablePopupHeight: 400 property real stablePopupHeight: 400
property real _lastAlignedContentHeight: -1 property real _lastAlignedContentHeight: -1
property bool _pendingSizedOpen: false property bool _pendingSizedOpen: false
property bool _heightUpdatePending: false
function updateStablePopupHeight() { function updateStablePopupHeight() {
const item = contentLoader.item; const item = contentLoader.item;
@@ -30,6 +31,16 @@ DankPopout {
stablePopupHeight = target; stablePopupHeight = target;
} }
function queueStablePopupHeightUpdate() {
if (_heightUpdatePending)
return;
_heightUpdatePending = true;
Qt.callLater(() => {
_heightUpdatePending = false;
updateStablePopupHeight();
});
}
NotificationKeyboardController { NotificationKeyboardController {
id: keyboardController id: keyboardController
listView: null listView: null
@@ -128,7 +139,7 @@ DankPopout {
Connections { Connections {
target: contentLoader.item target: contentLoader.item
function onImplicitHeightChanged() { function onImplicitHeightChanged() {
root.updateStablePopupHeight(); root.queueStablePopupHeightUpdate();
} }
} }

View File

@@ -537,21 +537,21 @@ PanelWindow {
Behavior on shadowBlurPx { Behavior on shadowBlurPx {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on shadowOffsetX { Behavior on shadowOffsetX {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on shadowOffsetY { Behavior on shadowOffsetY {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
@@ -566,7 +566,7 @@ PanelWindow {
shadowOffsetX: content.shadowOffsetX shadowOffsetX: content.shadowOffsetX
shadowOffsetY: content.shadowOffsetY shadowOffsetY: content.shadowOffsetY
shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent" shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed && !win.connectedFrameMode
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr)) layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically layer.textureMirroring: ShaderEffectSource.MirrorVertically
@@ -578,39 +578,39 @@ PanelWindow {
sourceRect.radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius sourceRect.radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
sourceRect.color: win.connectedFrameMode ? Theme.popupLayerColor(Theme.surfaceContainer) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) sourceRect.color: win.connectedFrameMode ? Theme.popupLayerColor(Theme.surfaceContainer) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
sourceRect.antialiasing: true sourceRect.antialiasing: true
sourceRect.layer.enabled: win.connectedFrameMode sourceRect.layer.enabled: false
sourceRect.layer.smooth: true sourceRect.layer.textureSize: Qt.size(0, 0)
sourceRect.layer.textureSize: win.connectedFrameMode && win.dpr > 1 ? Qt.size(Math.ceil(sourceRect.width * win.dpr), Math.ceil(sourceRect.height * win.dpr)) : Qt.size(0, 0)
sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08) sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0 sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
}
Rectangle { // Keep critical accent outside shadow rendering so connected mode still shows it.
x: bgShadowLayer.sourceRect.x Rectangle {
y: bgShadowLayer.sourceRect.y x: content.cardInset
width: bgShadowLayer.sourceRect.width y: content.cardInset
height: bgShadowLayer.sourceRect.height width: Math.max(0, content.width - content.cardInset * 2)
radius: bgShadowLayer.sourceRect.radius height: Math.max(0, content.height - content.cardInset * 2)
visible: notificationData && notificationData.urgency === NotificationUrgency.Critical radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
opacity: 1 visible: win.notificationData && win.notificationData.urgency === NotificationUrgency.Critical
clip: true opacity: 1
clip: true
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
GradientStop { GradientStop {
position: 0 position: 0
color: Theme.primary color: Theme.primary
} }
GradientStop { GradientStop {
position: 0.02 position: 0.02
color: Theme.primary color: Theme.primary
} }
GradientStop { GradientStop {
position: 0.021 position: 0.021
color: "transparent" color: "transparent"
}
} }
} }
} }

View File

@@ -36,6 +36,7 @@ QtObject {
property var pendingDestroys: [] property var pendingDestroys: []
property int destroyDelayMs: 100 property int destroyDelayMs: 100
property bool _chromeSyncPending: false property bool _chromeSyncPending: false
property bool _syncingVisibleNotifications: false
readonly property real chromeOpenProgressThreshold: 0.10 readonly property real chromeOpenProgressThreshold: 0.10
readonly property real chromeReleaseTailStart: 0.90 readonly property real chromeReleaseTailStart: 0.90
readonly property real chromeReleaseDropProgress: 0.995 readonly property real chromeReleaseDropProgress: 0.995
@@ -160,6 +161,7 @@ QtObject {
function _sync(newWrappers) { function _sync(newWrappers) {
let needsReposition = false; let needsReposition = false;
_syncingVisibleNotifications = true;
for (const p of popupWindows.slice()) { for (const p of popupWindows.slice()) {
if (!_isValidWindow(p) || p.exiting) if (!_isValidWindow(p) || p.exiting)
continue; continue;
@@ -171,10 +173,10 @@ QtObject {
} }
for (const w of newWrappers) { for (const w of newWrappers) {
if (w && !_hasWindowFor(w) && _isFocusedScreen()) { if (w && !_hasWindowFor(w) && _isFocusedScreen()) {
_insertAtTop(w); needsReposition = _insertAtTop(w, true) || needsReposition;
needsReposition = false;
} }
} }
_syncingVisibleNotifications = false;
if (needsReposition) if (needsReposition)
_repositionAll(); _repositionAll();
} }
@@ -183,9 +185,9 @@ QtObject {
return (p.alignedHeight || p.implicitHeight || (baseNotificationHeight - popupSpacing)) + popupSpacing; return (p.alignedHeight || p.implicitHeight || (baseNotificationHeight - popupSpacing)) + popupSpacing;
} }
function _insertAtTop(wrapper) { function _insertAtTop(wrapper, deferReposition) {
if (!wrapper) if (!wrapper)
return; return false;
const notificationId = wrapper?.notification ? wrapper.notification.id : ""; const notificationId = wrapper?.notification ? wrapper.notification.id : "";
const win = popupComponent.createObject(null, { const win = popupComponent.createObject(null, {
"notificationData": wrapper, "notificationData": wrapper,
@@ -194,15 +196,17 @@ QtObject {
"screen": manager.modelData "screen": manager.modelData
}); });
if (!win) if (!win)
return; return false;
if (!win.hasValidData) { if (!win.hasValidData) {
win.destroy(); win.destroy();
return; return false;
} }
popupWindows.unshift(win); popupWindows.unshift(win);
_repositionAll(); if (!deferReposition)
_repositionAll();
if (!sweeper.running) if (!sweeper.running)
sweeper.start(); sweeper.start();
return true;
} }
function _repositionAll() { function _repositionAll() {
@@ -321,6 +325,10 @@ QtObject {
if (!rect || p !== trailing || !p.popupChromeReleaseProgress) if (!rect || p !== trailing || !p.popupChromeReleaseProgress)
return rect; return rect;
// Keep maxed-stack chrome anchored while a replacement tail exits.
if (p.exiting && p.notificationData?.removedByLimit && _layoutWindows().length > 0)
return rect;
const progress = _chromeReleaseTailProgress(p.popupChromeReleaseProgress()); const progress = _chromeReleaseTailProgress(p.popupChromeReleaseProgress());
if (progress <= 0) if (progress <= 0)
return rect; return rect;
@@ -489,17 +497,34 @@ QtObject {
_scheduleNotificationChromeSync(); _scheduleNotificationChromeSync();
} }
// Coalesce resize repositioning; exit-path moves remain immediate.
property bool _repositionPending: false
function _queueReposition() {
if (_repositionPending)
return;
_repositionPending = true;
Qt.callLater(_flushReposition);
}
function _flushReposition() {
_repositionPending = false;
_repositionAll();
}
function _onPopupHeightChanged(p) { function _onPopupHeightChanged(p) {
if (!p || p.exiting || p._isDestroying) if (!p || p.exiting || p._isDestroying)
return; return;
if (popupWindows.indexOf(p) === -1) if (popupWindows.indexOf(p) === -1)
return; return;
_repositionAll(); _queueReposition();
} }
function _onPopupExitStarted(p) { function _onPopupExitStarted(p) {
if (!p || popupWindows.indexOf(p) === -1) if (!p || popupWindows.indexOf(p) === -1)
return; return;
if (_syncingVisibleNotifications)
return;
_repositionAll(); _repositionAll();
} }

View File

@@ -32,8 +32,6 @@ Item {
property bool fullHeightSurface: false property bool fullHeightSurface: false
property bool _primeContent: false property bool _primeContent: false
property bool _resizeActive: false property bool _resizeActive: false
property real _surfaceMarginLeft: 0
property real _surfaceW: 0
property string _chromeClaimId: "" property string _chromeClaimId: ""
property int _connectedChromeSerial: 0 property int _connectedChromeSerial: 0
property real _chromeAnimTravelX: 1 property real _chromeAnimTravelX: 1
@@ -49,6 +47,8 @@ Item {
"rightBar": 0 "rightBar": 0
}) })
property var screen: null property var screen: null
// Connected resize uses one full-screen surface; body-sized regions are masks.
readonly property bool useBackgroundWindow: false
readonly property real effectiveBarThickness: { readonly property real effectiveBarThickness: {
if (Theme.isConnectedEffect) if (Theme.isConnectedEffect)
@@ -118,12 +118,6 @@ Item {
} }
readonly property string effectiveShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection readonly property string effectiveShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
// Snapshot mask geometry to prevent background damage on bar updates
property real _frozenMaskX: 0
property real _frozenMaskY: 0
property real _frozenMaskWidth: 0
property real _frozenMaskHeight: 0
function setBarContext(position, bottomGap) { function setBarContext(position, bottomGap) {
effectiveBarPosition = position !== undefined ? position : 0; effectiveBarPosition = position !== undefined ? position : 0;
effectiveBarBottomGap = bottomGap !== undefined ? bottomGap : 0; effectiveBarBottomGap = bottomGap !== undefined ? bottomGap : 0;
@@ -181,7 +175,7 @@ Item {
if (barSide !== "top" && barSide !== "bottom") if (barSide !== "top" && barSide !== "bottom")
return contentContainer.animY; return contentContainer.animY;
const extent = Math.max(0, root.alignedHeight); const extent = Math.max(0, root.renderedAlignedHeight);
const progress = Math.min(1, Math.abs(contentContainer.animY) / Math.max(1, _chromeAnimTravelY)); const progress = Math.min(1, Math.abs(contentContainer.animY) / Math.max(1, _chromeAnimTravelY));
const offset = Theme.snap(extent * progress, root.dpr); const offset = Theme.snap(extent * progress, root.dpr);
return contentContainer.animY < 0 ? -offset : offset; return contentContainer.animY < 0 ? -offset : offset;
@@ -193,9 +187,9 @@ Item {
"visible": visible, "visible": visible,
"barSide": contentContainer.connectedBarSide, "barSide": contentContainer.connectedBarSide,
"bodyX": root.alignedX, "bodyX": root.alignedX,
"bodyY": root.alignedY, "bodyY": root.renderedAlignedY,
"bodyW": root.alignedWidth, "bodyW": root.alignedWidth,
"bodyH": root.alignedHeight, "bodyH": root.renderedAlignedHeight,
"animX": _connectedChromeAnimX(), "animX": _connectedChromeAnimX(),
"animY": _connectedChromeAnimY(), "animY": _connectedChromeAnimY(),
"screen": root.screen ? root.screen.name : "", "screen": root.screen ? root.screen.name : "",
@@ -258,16 +252,28 @@ Item {
ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined); ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
} }
function _syncPopoutBody() {
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
return;
if (!contentWindow.visible && !shouldBeVisible)
return;
ConnectedModeState.setPopoutBody(_chromeClaimId, root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
}
function _flushFullSync() { function _flushFullSync() {
_fullSyncPending = false; _fullSyncPending = false;
_syncPopoutChromeState(); if (root && typeof root._syncPopoutChromeState === "function")
root._syncPopoutChromeState();
} }
function _queueFullSync() { function _queueFullSync() {
if (_fullSyncPending) if (_fullSyncPending)
return; return;
_fullSyncPending = true; _fullSyncPending = true;
Qt.callLater(root._flushFullSync); Qt.callLater(() => {
if (root && typeof root._flushFullSync === "function")
root._flushFullSync();
});
} }
onAlignedXChanged: _queueFullSync() onAlignedXChanged: _queueFullSync()
@@ -275,6 +281,8 @@ Item {
onAlignedWidthChanged: _queueFullSync() onAlignedWidthChanged: _queueFullSync()
onContentAnimXChanged: _syncPopoutAnim("x") onContentAnimXChanged: _syncPopoutAnim("x")
onContentAnimYChanged: _syncPopoutAnim("y") onContentAnimYChanged: _syncPopoutAnim("y")
onRenderedAlignedYChanged: _syncPopoutBody()
onRenderedAlignedHeightChanged: _syncPopoutBody()
onScreenChanged: _syncPopoutChromeState() onScreenChanged: _syncPopoutChromeState()
onEffectiveBarPositionChanged: _syncPopoutChromeState() onEffectiveBarPositionChanged: _syncPopoutChromeState()
@@ -306,18 +314,10 @@ Item {
} }
} }
readonly property bool useBackgroundWindow: !CompositorService.isHyprland || CompositorService.useHyprlandFocusGrab
readonly property bool frameOwnsConnectedChrome: SettingsData.connectedFrameModeActive readonly property bool frameOwnsConnectedChrome: SettingsData.connectedFrameModeActive
&& !!root.screen && !!root.screen
&& SettingsData.isScreenInPreferences(root.screen, SettingsData.frameScreenPreferences) && SettingsData.isScreenInPreferences(root.screen, SettingsData.frameScreenPreferences)
function updateSurfacePosition() {
if (useBackgroundWindow && shouldBeVisible) {
_surfaceMarginLeft = alignedX - shadowBuffer;
_surfaceW = alignedWidth + shadowBuffer * 2;
}
}
property bool animationsEnabled: true property bool animationsEnabled: true
function open() { function open() {
@@ -328,16 +328,8 @@ Item {
animationsEnabled = false; animationsEnabled = false;
_primeContent = true; _primeContent = true;
// Snapshot mask geometry
_frozenMaskX = maskX;
_frozenMaskY = maskY;
_frozenMaskWidth = maskWidth;
_frozenMaskHeight = maskHeight;
if (_lastOpenedScreen !== null && _lastOpenedScreen !== screen) { if (_lastOpenedScreen !== null && _lastOpenedScreen !== screen) {
contentWindow.visible = false; contentWindow.visible = false;
if (useBackgroundWindow)
backgroundWindow.visible = false;
} }
_lastOpenedScreen = screen; _lastOpenedScreen = screen;
@@ -355,19 +347,12 @@ Item {
_chromeClaimId = ""; _chromeClaimId = "";
} }
if (useBackgroundWindow) {
_surfaceMarginLeft = alignedX - shadowBuffer;
_surfaceW = alignedWidth + shadowBuffer * 2;
backgroundWindow.visible = true;
}
contentWindow.visible = true; contentWindow.visible = true;
Qt.callLater(() => { Qt.callLater(() => {
animationsEnabled = true; animationsEnabled = true;
shouldBeVisible = true; shouldBeVisible = true;
if (shouldBeVisible && screen) { if (shouldBeVisible && screen) {
if (useBackgroundWindow)
backgroundWindow.visible = true;
contentWindow.visible = true; contentWindow.visible = true;
PopoutManager.showPopout(root); PopoutManager.showPopout(root);
opened(); opened();
@@ -413,8 +398,6 @@ Item {
if (!shouldBeVisible) { if (!shouldBeVisible) {
isClosing = false; isClosing = false;
contentWindow.visible = false; contentWindow.visible = false;
if (useBackgroundWindow)
backgroundWindow.visible = false;
PopoutManager.hidePopout(root); PopoutManager.hidePopout(root);
popoutClosed(); popoutClosed();
} }
@@ -536,6 +519,27 @@ Item {
readonly property real shadowBuffer: Theme.snap(shadowRenderPadding + shadowMotionPadding, dpr) readonly property real shadowBuffer: Theme.snap(shadowRenderPadding + shadowMotionPadding, dpr)
readonly property real alignedWidth: Theme.px(popupWidth, dpr) readonly property real alignedWidth: Theme.px(popupWidth, dpr)
readonly property real alignedHeight: Theme.px(popupHeight, dpr) readonly property real alignedHeight: Theme.px(popupHeight, dpr)
property real renderedAlignedY: alignedY
property real renderedAlignedHeight: alignedHeight
readonly property bool renderedGeometryGrowing: alignedHeight >= renderedAlignedHeight
Behavior on renderedAlignedY {
enabled: root.animationsEnabled && contentWindow.visible && root.shouldBeVisible
NumberAnimation {
duration: Theme.variantDuration(root.animationDuration, root.renderedGeometryGrowing)
easing.type: Easing.BezierSpline
easing.bezierCurve: root.renderedGeometryGrowing ? root.animationEnterCurve : root.animationExitCurve
}
}
Behavior on renderedAlignedHeight {
enabled: root.animationsEnabled && contentWindow.visible && root.shouldBeVisible
NumberAnimation {
duration: Theme.variantDuration(root.animationDuration, root.renderedGeometryGrowing)
easing.type: Easing.BezierSpline
easing.bezierCurve: root.renderedGeometryGrowing ? root.animationEnterCurve : root.animationExitCurve
}
}
readonly property real connectedAnchorX: { readonly property real connectedAnchorX: {
if (!Theme.isConnectedEffect) if (!Theme.isConnectedEffect)
return triggerX; return triggerX;
@@ -572,7 +576,7 @@ Item {
} }
onAlignedHeightChanged: { onAlignedHeightChanged: {
_syncPopoutChromeState(); _queueFullSync();
if (!suspendShadowWhileResizing || !shouldBeVisible) if (!suspendShadowWhileResizing || !shouldBeVisible)
return; return;
_resizeActive = true; _resizeActive = true;
@@ -661,117 +665,6 @@ Item {
return Math.max(100, screenHeight - maskY - bottomExclusion); return Math.max(100, screenHeight - maskY - bottomExclusion);
} }
PanelWindow {
id: backgroundWindow
screen: root.screen
visible: false
color: "transparent"
Component.onCompleted: {
if (typeof updatesEnabled !== "undefined" && !root.overlayContent)
updatesEnabled = false;
}
WlrLayershell.namespace: root.layerNamespace + ":background"
WlrLayershell.layer: WlrLayershell.Top
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
anchors {
top: true
left: true
right: true
bottom: true
}
mask: Region {
item: maskRect
Region {
item: contentExclusionRect
intersection: Intersection.Subtract
}
}
Rectangle {
id: maskRect
visible: false
color: "transparent"
x: root._frozenMaskX
y: root._frozenMaskY
width: (backgroundWindow.visible && backgroundInteractive) ? root._frozenMaskWidth : 0
height: (backgroundWindow.visible && backgroundInteractive) ? root._frozenMaskHeight : 0
}
Item {
id: contentExclusionRect
visible: false
x: root.alignedX
y: root.alignedY
width: root.alignedWidth
height: root.alignedHeight
}
Item {
id: outsideClickCatcher
x: root._frozenMaskX
y: root._frozenMaskY
width: root._frozenMaskWidth
height: root._frozenMaskHeight
enabled: root.shouldBeVisible && root.backgroundInteractive
readonly property real contentLeft: Math.max(0, root.alignedX - x)
readonly property real contentTop: Math.max(0, root.alignedY - y)
readonly property real contentRight: Math.min(width, contentLeft + root.alignedWidth)
readonly property real contentBottom: Math.min(height, contentTop + root.alignedHeight)
MouseArea {
x: 0
y: 0
width: outsideClickCatcher.width
height: Math.max(0, outsideClickCatcher.contentTop)
enabled: parent.enabled
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: root.backgroundClicked()
}
MouseArea {
x: 0
y: outsideClickCatcher.contentBottom
width: outsideClickCatcher.width
height: Math.max(0, outsideClickCatcher.height - outsideClickCatcher.contentBottom)
enabled: parent.enabled
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: root.backgroundClicked()
}
MouseArea {
x: 0
y: outsideClickCatcher.contentTop
width: Math.max(0, outsideClickCatcher.contentLeft)
height: Math.max(0, outsideClickCatcher.contentBottom - outsideClickCatcher.contentTop)
enabled: parent.enabled
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: root.backgroundClicked()
}
MouseArea {
x: outsideClickCatcher.contentRight
y: outsideClickCatcher.contentTop
width: Math.max(0, outsideClickCatcher.width - outsideClickCatcher.contentRight)
height: Math.max(0, outsideClickCatcher.contentBottom - outsideClickCatcher.contentTop)
enabled: parent.enabled
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: root.backgroundClicked()
}
}
Loader {
id: overlayLoader
anchors.fill: parent
active: root.overlayContent !== null && backgroundWindow.visible
sourceComponent: root.overlayContent
}
}
PanelWindow { PanelWindow {
id: contentWindow id: contentWindow
screen: root.screen screen: root.screen
@@ -824,27 +717,37 @@ Item {
return WlrKeyboardFocus.Exclusive; return WlrKeyboardFocus.Exclusive;
} }
readonly property bool _fullHeight: useBackgroundWindow && root.fullHeightSurface readonly property bool _fullHeight: root.fullHeightSurface
anchors { anchors {
left: true left: true
top: true top: true
right: !useBackgroundWindow right: true
bottom: _fullHeight || !useBackgroundWindow bottom: true
} }
WlrLayershell.margins { WlrLayershell.margins {
left: useBackgroundWindow ? root._surfaceMarginLeft : 0 left: 0
top: (useBackgroundWindow && !_fullHeight) ? (root.alignedY - shadowBuffer) : 0 top: 0
} }
implicitWidth: useBackgroundWindow ? root._surfaceW : 0 implicitWidth: 0
implicitHeight: (useBackgroundWindow && !_fullHeight) ? (root.alignedHeight + shadowBuffer * 2) : 0 implicitHeight: 0
mask: useBackgroundWindow ? contentInputMask : null mask: contentInputMask
Region { Region {
id: contentInputMask id: contentInputMask
item: contentMaskRect // Outside-click dismissal needs full-screen input only while interactive.
item: (shouldBeVisible && backgroundInteractive) ? fullScreenMaskItem : contentMaskRect
}
Item {
id: fullScreenMaskItem
visible: false
x: 0
y: 0
width: 32767
height: 32767
} }
Item { Item {
@@ -853,18 +756,18 @@ Item {
x: contentContainer.x - contentContainer.horizontalConnectorExtent x: contentContainer.x - contentContainer.horizontalConnectorExtent
y: contentContainer.y - contentContainer.verticalConnectorExtent y: contentContainer.y - contentContainer.verticalConnectorExtent
width: root.alignedWidth + contentContainer.horizontalConnectorExtent * 2 width: root.alignedWidth + contentContainer.horizontalConnectorExtent * 2
height: root.alignedHeight + contentContainer.verticalConnectorExtent * 2 height: root.renderedAlignedHeight + contentContainer.verticalConnectorExtent * 2
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
enabled: !useBackgroundWindow && shouldBeVisible && backgroundInteractive enabled: shouldBeVisible && backgroundInteractive
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
z: -1 z: -1
onClicked: mouse => { onClicked: mouse => {
const clickX = mouse.x; const clickX = mouse.x;
const clickY = mouse.y; const clickY = mouse.y;
const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.alignedY || clickY > root.alignedY + root.alignedHeight; const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.renderedAlignedY || clickY > root.renderedAlignedY + root.renderedAlignedHeight;
if (!outsideContent) if (!outsideContent)
return; return;
backgroundClicked(); backgroundClicked();
@@ -873,10 +776,10 @@ Item {
Item { Item {
id: contentContainer id: contentContainer
x: useBackgroundWindow ? shadowBuffer : root.alignedX x: root.alignedX
y: (useBackgroundWindow && !contentWindow._fullHeight) ? shadowBuffer : root.alignedY y: root.renderedAlignedY
width: root.alignedWidth width: root.alignedWidth
height: root.alignedHeight height: root.renderedAlignedHeight
readonly property bool barTop: effectiveBarPosition === SettingsData.Position.Top readonly property bool barTop: effectiveBarPosition === SettingsData.Position.Top
readonly property bool barBottom: effectiveBarPosition === SettingsData.Position.Bottom readonly property bool barBottom: effectiveBarPosition === SettingsData.Position.Bottom
@@ -1082,154 +985,147 @@ Item {
return parent.height + clipOversize * 2; return parent.height + clipOversize * 2;
} }
// Roll-out clips a wrapper while content and shadow keep full-size geometry.
Item { Item {
id: aligner id: rollOutAdjuster
readonly property real baseWidth: contentContainer.width readonly property real baseWidth: contentContainer.width
readonly property real baseHeight: contentContainer.height readonly property real baseHeight: contentContainer.height
readonly property bool isRollOut: typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 && Theme.isDirectionalEffect readonly property bool isRollOut: typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 && Theme.isDirectionalEffect
x: (directionalClipMask.x !== 0 ? -directionalClipMask.x : 0) + (isRollOut && contentContainer.barRight ? baseWidth * (1 - contentContainer.scaleValue) : 0) x: directionalClipMask.x !== 0 ? -directionalClipMask.x : 0
y: (directionalClipMask.y !== 0 ? -directionalClipMask.y : 0) + (isRollOut && contentContainer.barBottom ? baseHeight * (1 - contentContainer.scaleValue) : 0) y: directionalClipMask.y !== 0 ? -directionalClipMask.y : 0
width: isRollOut && (contentContainer.barLeft || contentContainer.barRight) ? Math.max(0, baseWidth * contentContainer.scaleValue) : baseWidth width: isRollOut && (contentContainer.barLeft || contentContainer.barRight) ? Math.max(0, baseWidth * contentContainer.scaleValue) : baseWidth
height: isRollOut && (contentContainer.barTop || contentContainer.barBottom) ? Math.max(0, baseHeight * contentContainer.scaleValue) : baseHeight height: isRollOut && (contentContainer.barTop || contentContainer.barBottom) ? Math.max(0, baseHeight * contentContainer.scaleValue) : baseHeight
clip: isRollOut clip: isRollOut
ElevationShadow {
id: shadowSource
readonly property real connectorExtent: Theme.isConnectedEffect ? Theme.connectedCornerRadius : 0
readonly property real extraLeft: Theme.isConnectedEffect && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
readonly property real extraRight: Theme.isConnectedEffect && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
readonly property real extraTop: Theme.isConnectedEffect && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
readonly property real extraBottom: Theme.isConnectedEffect && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
readonly property real bodyX: extraLeft
readonly property real bodyY: extraTop
readonly property real bodyWidth: rollOutAdjuster.baseWidth
readonly property real bodyHeight: rollOutAdjuster.baseHeight
width: rollOutAdjuster.baseWidth + extraLeft + extraRight
height: rollOutAdjuster.baseHeight + extraTop + extraBottom
opacity: contentWrapper.opacity
scale: contentWrapper.scale
x: contentWrapper.x - extraLeft
y: contentWrapper.y - extraTop
level: root.shadowLevel
direction: root.effectiveShadowDirection
fallbackOffset: root.shadowFallbackOffset
targetRadius: contentContainer.surfaceRadius
topLeftRadius: contentContainer.surfaceTopLeftRadius
topRightRadius: contentContainer.surfaceTopRightRadius
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
bottomRightRadius: contentContainer.surfaceBottomRightRadius
targetColor: contentContainer.surfaceColor
borderColor: contentContainer.surfaceBorderColor
borderWidth: contentContainer.surfaceBorderWidth
useCustomSource: Theme.isConnectedEffect
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
Item {
anchors.fill: parent
visible: Theme.isConnectedEffect && !root.frameOwnsConnectedChrome
clip: false
Rectangle {
x: shadowSource.bodyX
y: shadowSource.bodyY
width: shadowSource.bodyWidth
height: shadowSource.bodyHeight
topLeftRadius: contentContainer.surfaceTopLeftRadius
topRightRadius: contentContainer.surfaceTopRightRadius
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
bottomRightRadius: contentContainer.surfaceBottomRightRadius
color: contentContainer.surfaceColor
}
ConnectedCorner {
visible: Theme.isConnectedEffect
barSide: contentContainer.connectedBarSide
placement: "left"
spacing: 0
connectorRadius: Theme.connectedCornerRadius
color: contentContainer.surfaceColor
dpr: root.dpr
x: Theme.snap(contentContainer.connectorX(shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing), root.dpr)
y: Theme.snap(contentContainer.connectorY(shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing), root.dpr)
}
ConnectedCorner {
visible: Theme.isConnectedEffect
barSide: contentContainer.connectedBarSide
placement: "right"
spacing: 0
connectorRadius: Theme.connectedCornerRadius
color: contentContainer.surfaceColor
dpr: root.dpr
x: Theme.snap(contentContainer.connectorX(shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing), root.dpr)
y: Theme.snap(contentContainer.connectorY(shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing), root.dpr)
}
}
}
Item { Item {
id: unrollCounteract id: contentWrapper
x: aligner.isRollOut && contentContainer.barRight ? -(aligner.baseWidth * (1 - contentContainer.scaleValue)) : 0 width: rollOutAdjuster.baseWidth
y: aligner.isRollOut && contentContainer.barBottom ? -(aligner.baseHeight * (1 - contentContainer.scaleValue)) : 0 height: rollOutAdjuster.baseHeight
width: aligner.baseWidth opacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0)
height: aligner.baseHeight visible: opacity > 0
ElevationShadow { scale: rollOutAdjuster.isRollOut ? 1.0 : contentContainer.scaleValue
id: shadowSource x: Theme.snap(contentContainer.animX + (rollOutAdjuster.baseWidth - width) * (1 - scale) * 0.5, root.dpr)
readonly property real connectorExtent: Theme.isConnectedEffect ? Theme.connectedCornerRadius : 0 y: Theme.snap(contentContainer.animY + (rollOutAdjuster.baseHeight - height) * (1 - scale) * 0.5, root.dpr)
readonly property real extraLeft: Theme.isConnectedEffect && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
readonly property real extraRight: Theme.isConnectedEffect && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
readonly property real extraTop: Theme.isConnectedEffect && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
readonly property real extraBottom: Theme.isConnectedEffect && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
readonly property real bodyX: extraLeft
readonly property real bodyY: extraTop
readonly property real bodyWidth: parent.width
readonly property real bodyHeight: parent.height
width: parent.width + extraLeft + extraRight layer.enabled: contentWrapper.opacity < 1
height: parent.height + extraTop + extraBottom layer.smooth: false
opacity: contentWrapper.opacity layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
scale: contentWrapper.scale
x: contentWrapper.x - extraLeft
y: contentWrapper.y - extraTop
level: root.shadowLevel
direction: root.effectiveShadowDirection
fallbackOffset: root.shadowFallbackOffset
targetRadius: contentContainer.surfaceRadius
topLeftRadius: contentContainer.surfaceTopLeftRadius
topRightRadius: contentContainer.surfaceTopRightRadius
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
bottomRightRadius: contentContainer.surfaceBottomRightRadius
targetColor: contentContainer.surfaceColor
borderColor: contentContainer.surfaceBorderColor
borderWidth: contentContainer.surfaceBorderWidth
useCustomSource: Theme.isConnectedEffect
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
Item { Behavior on opacity {
anchors.fill: parent enabled: !Theme.isDirectionalEffect
visible: Theme.isConnectedEffect && !root.frameOwnsConnectedChrome NumberAnimation {
clip: false duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale)
easing.type: Easing.BezierSpline
Rectangle { easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
x: shadowSource.bodyX
y: shadowSource.bodyY
width: shadowSource.bodyWidth
height: shadowSource.bodyHeight
topLeftRadius: contentContainer.surfaceTopLeftRadius
topRightRadius: contentContainer.surfaceTopRightRadius
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
bottomRightRadius: contentContainer.surfaceBottomRightRadius
color: contentContainer.surfaceColor
}
ConnectedCorner {
visible: Theme.isConnectedEffect
barSide: contentContainer.connectedBarSide
placement: "left"
spacing: 0
connectorRadius: Theme.connectedCornerRadius
color: contentContainer.surfaceColor
dpr: root.dpr
x: Theme.snap(contentContainer.connectorX(shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing), root.dpr)
y: Theme.snap(contentContainer.connectorY(shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing), root.dpr)
}
ConnectedCorner {
visible: Theme.isConnectedEffect
barSide: contentContainer.connectedBarSide
placement: "right"
spacing: 0
connectorRadius: Theme.connectedCornerRadius
color: contentContainer.surfaceColor
dpr: root.dpr
x: Theme.snap(contentContainer.connectorX(shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing), root.dpr)
y: Theme.snap(contentContainer.connectorY(shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing), root.dpr)
}
} }
} }
Item { Item {
id: contentWrapper anchors.fill: parent
width: parent.width clip: false
height: parent.height visible: !Theme.isConnectedEffect
opacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0)
visible: opacity > 0
scale: aligner.isRollOut ? 1.0 : contentContainer.scaleValue Rectangle {
x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - scale) * 0.5, root.dpr)
y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - scale) * 0.5, root.dpr)
layer.enabled: contentWrapper.opacity < 1
layer.smooth: false
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
Behavior on opacity {
enabled: !Theme.isDirectionalEffect
NumberAnimation {
duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale)
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
Item {
anchors.fill: parent anchors.fill: parent
clip: false antialiasing: true
visible: !Theme.isConnectedEffect topLeftRadius: contentContainer.surfaceTopLeftRadius
topRightRadius: contentContainer.surfaceTopRightRadius
Rectangle { bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
anchors.fill: parent bottomRightRadius: contentContainer.surfaceBottomRightRadius
antialiasing: true color: contentContainer.surfaceColor
topLeftRadius: contentContainer.surfaceTopLeftRadius border.color: contentContainer.surfaceBorderColor
topRightRadius: contentContainer.surfaceTopRightRadius border.width: contentContainer.surfaceBorderWidth
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
bottomRightRadius: contentContainer.surfaceBottomRightRadius
color: contentContainer.surfaceColor
border.color: contentContainer.surfaceBorderColor
border.width: contentContainer.surfaceBorderWidth
}
} }
}
Loader { Loader {
id: contentLoader id: contentLoader
anchors.fill: parent anchors.fill: parent
active: root._primeContent || shouldBeVisible || contentWindow.visible active: root._primeContent || shouldBeVisible || contentWindow.visible
asynchronous: false asynchronous: false
} }
} // closes contentWrapper }
} // closes unrollCounteract }
} // closes aligner }
} // closes directionalClipMask }
} // closes contentContainer
Item { Item {
id: focusHelper id: focusHelper
@@ -1247,5 +1143,12 @@ Item {
} }
} }
} }
Loader {
id: overlayLoader
anchors.fill: parent
active: root.overlayContent !== null && contentWindow.visible
sourceComponent: root.overlayContent
}
} }
} }