1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-15 10:12:07 -04:00

popout: fix excessive repaints

- Size content window to content size, buffer for shadow
- Add second window for click outside behavior
- User overriding the layer disables the click outside behavior

part of #716
This commit is contained in:
bbedward
2025-11-23 10:49:59 -05:00
parent fd20986cf8
commit 62845b470c
2 changed files with 355 additions and 313 deletions

View File

@@ -1,16 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Modules.ControlCenter
import qs.Modules.ControlCenter.Widgets
import qs.Modules.ControlCenter.Details import qs.Modules.ControlCenter.Details
import qs.Modules.DankBar
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Components import qs.Modules.ControlCenter.Components
@@ -32,39 +23,39 @@ DankPopout {
signal lockRequested signal lockRequested
function collapseAll() { function collapseAll() {
expandedSection = "" expandedSection = "";
expandedWidgetIndex = -1 expandedWidgetIndex = -1;
expandedWidgetData = null expandedWidgetData = null;
} }
onEditModeChanged: { onEditModeChanged: {
if (editMode) { if (editMode) {
collapseAll() collapseAll();
} }
} }
onVisibleChanged: { onVisibleChanged: {
if (!visible) { if (!visible) {
collapseAll() collapseAll();
} }
} }
readonly property color _containerBg: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) readonly property color _containerBg: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
function openWithSection(section) { function openWithSection(section) {
StateUtils.openWithSection(root, section) StateUtils.openWithSection(root, section);
} }
function toggleSection(section) { function toggleSection(section) {
StateUtils.toggleSection(root, section) StateUtils.toggleSection(root, section);
} }
popupWidth: 550 popupWidth: 550
popupHeight: { popupHeight: {
const screenHeight = (triggerScreen?.height ?? 1080) const screenHeight = (triggerScreen?.height ?? 1080);
const maxHeight = screenHeight - 100 const maxHeight = screenHeight - 100;
const contentHeight = contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400 const contentHeight = contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400;
return Math.min(maxHeight, contentHeight) return Math.min(maxHeight, contentHeight);
} }
triggerX: 0 triggerX: 0
triggerY: 0 triggerY: 0
@@ -75,12 +66,16 @@ DankPopout {
property bool credentialsPromptOpen: NetworkService.credentialsRequested property bool credentialsPromptOpen: NetworkService.credentialsRequested
WlrLayershell.keyboardFocus: { customKeyboardFocus: {
if (!shouldBeVisible) return WlrKeyboardFocus.None if (!shouldBeVisible)
if (powerMenuOpen) return WlrKeyboardFocus.None return WlrKeyboardFocus.None;
if (credentialsPromptOpen) return WlrKeyboardFocus.None if (powerMenuOpen)
if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand return WlrKeyboardFocus.None;
return WlrKeyboardFocus.Exclusive if (credentialsPromptOpen)
return WlrKeyboardFocus.None;
if (CompositorService.isHyprland)
return WlrKeyboardFocus.OnDemand;
return WlrKeyboardFocus.Exclusive;
} }
onBackgroundClicked: close() onBackgroundClicked: close()
@@ -89,20 +84,20 @@ DankPopout {
if (shouldBeVisible) { if (shouldBeVisible) {
Qt.callLater(() => { Qt.callLater(() => {
if (NetworkService.activeService) { if (NetworkService.activeService) {
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
} }
if (UserInfoService) if (UserInfoService)
UserInfoService.getUptime() UserInfoService.getUptime();
}) });
} else { } else {
Qt.callLater(() => { Qt.callLater(() => {
if (NetworkService.activeService) { if (NetworkService.activeService) {
NetworkService.activeService.autoRefreshEnabled = false NetworkService.activeService.autoRefreshEnabled = false;
} }
if (BluetoothService.adapter && BluetoothService.adapter.discovering) if (BluetoothService.adapter && BluetoothService.adapter.discovering)
BluetoothService.adapter.discovering = false BluetoothService.adapter.discovering = false;
editMode = false editMode = false;
}) });
} }
} }
@@ -118,9 +113,9 @@ DankPopout {
property alias bluetoothCodecSelector: bluetoothCodecSelector property alias bluetoothCodecSelector: bluetoothCodecSelector
color: { color: {
const transparency = Theme.popupTransparency const transparency = Theme.popupTransparency;
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1);
return Qt.rgba(surface.r, surface.g, surface.b, transparency) return Qt.rgba(surface.r, surface.g, surface.b, transparency);
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
@@ -157,20 +152,20 @@ DankPopout {
onEditModeToggled: root.editMode = !root.editMode onEditModeToggled: root.editMode = !root.editMode
onPowerButtonClicked: { onPowerButtonClicked: {
if (powerMenuModalLoader) { if (powerMenuModalLoader) {
powerMenuModalLoader.active = true powerMenuModalLoader.active = true;
if (powerMenuModalLoader.item) { if (powerMenuModalLoader.item) {
const popoutPos = controlContent.mapToItem(null, 0, 0) const popoutPos = controlContent.mapToItem(null, 0, 0);
const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height) const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height);
powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen) powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen);
} }
} }
} }
onLockRequested: { onLockRequested: {
root.close() root.close();
root.lockRequested() root.lockRequested();
} }
onSettingsButtonClicked: { onSettingsButtonClicked: {
root.close() root.close();
} }
} }
@@ -187,14 +182,14 @@ DankPopout {
screenName: root.triggerScreen?.name || "" screenName: root.triggerScreen?.name || ""
parentScreen: root.triggerScreen parentScreen: root.triggerScreen
onExpandClicked: (widgetData, globalIndex) => { onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex root.expandedWidgetIndex = globalIndex;
root.expandedWidgetData = widgetData root.expandedWidgetData = widgetData;
if (widgetData.id === "diskUsage") { if (widgetData.id === "diskUsage") {
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default")) root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"));
} else if (widgetData.id === "brightnessSlider") { } else if (widgetData.id === "brightnessSlider") {
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default")) root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"));
} else { } else {
root.toggleSection(widgetData.id) root.toggleSection(widgetData.id);
} }
} }
onRemoveWidget: index => widgetModel.removeWidget(index) onRemoveWidget: index => widgetModel.removeWidget(index)
@@ -209,10 +204,10 @@ DankPopout {
popoutContent: controlContent popoutContent: controlContent
availableWidgets: { availableWidgets: {
if (!editMode) if (!editMode)
return [] return [];
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id) const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id);
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets()) const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets());
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id)) return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id));
} }
onAddWidget: widgetId => widgetModel.addWidget(widgetId) onAddWidget: widgetId => widgetModel.addWidget(widgetId)
onResetToDefault: () => widgetModel.resetToDefault() onResetToDefault: () => widgetModel.resetToDefault()
@@ -239,10 +234,10 @@ DankPopout {
id: bluetoothDetail id: bluetoothDetail
onShowCodecSelector: function (device) { onShowCodecSelector: function (device) {
if (contentLoader.item && contentLoader.item.bluetoothCodecSelector) { if (contentLoader.item && contentLoader.item.bluetoothCodecSelector) {
contentLoader.item.bluetoothCodecSelector.show(device) contentLoader.item.bluetoothCodecSelector.show(device);
contentLoader.item.bluetoothCodecSelector.codecSelected.connect(function (deviceAddress, codecName) { contentLoader.item.bluetoothCodecSelector.codecSelected.connect(function (deviceAddress, codecName) {
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName) bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName);
}) });
} }
} }
} }

View File

@@ -1,18 +1,15 @@
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { Item {
id: root id: root
property string layerNamespace: "dms:popout" property string layerNamespace: "dms:popout"
WlrLayershell.namespace: layerNamespace
property alias content: contentLoader.sourceComponent property alias content: contentLoader.sourceComponent
property alias contentLoader: contentLoader property alias contentLoader: contentLoader
property real popupWidth: 400 property real popupWidth: 400
@@ -28,24 +25,34 @@ PanelWindow {
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
property bool shouldBeVisible: false property bool shouldBeVisible: false
property var customKeyboardFocus: null
property real storedBarThickness: Theme.barHeight - 4 property real storedBarThickness: Theme.barHeight - 4
property real storedBarSpacing: 4 property real storedBarSpacing: 4
property var storedBarConfig: null property var storedBarConfig: null
property var adjacentBarInfo: ({ "topBar": 0, "bottomBar": 0, "leftBar": 0, "rightBar": 0 }) property var adjacentBarInfo: ({
"topBar": 0,
visible: false "bottomBar": 0,
"leftBar": 0,
"rightBar": 0
})
property var screen: null
readonly property real effectiveBarThickness: { readonly property real effectiveBarThickness: {
const padding = storedBarConfig ? (storedBarConfig.innerPadding !== undefined ? storedBarConfig.innerPadding : 4) : 4 const padding = storedBarConfig ? (storedBarConfig.innerPadding !== undefined ? storedBarConfig.innerPadding : 4) : 4;
return Math.max(26 + padding * 0.6, Theme.barHeight - 4 - (8 - padding)) + storedBarSpacing return Math.max(26 + padding * 0.6, Theme.barHeight - 4 - (8 - padding)) + storedBarSpacing;
} }
readonly property var barBounds: { readonly property var barBounds: {
if (!root.screen) { if (!screen)
return { "x": 0, "y": 0, "width": 0, "height": 0, "wingSize": 0 } return {
} "x": 0,
return SettingsData.getBarBounds(root.screen, effectiveBarThickness, effectiveBarPosition, storedBarConfig) "y": 0,
"width": 0,
"height": 0,
"wingSize": 0
};
return SettingsData.getBarBounds(screen, effectiveBarThickness, effectiveBarPosition, storedBarConfig);
} }
readonly property real barX: barBounds.x readonly property real barX: barBounds.x
@@ -58,50 +65,57 @@ PanelWindow {
signal popoutClosed signal popoutClosed
signal backgroundClicked signal backgroundClicked
property int effectiveBarPosition: 0
property real effectiveBarBottomGap: 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;
} }
function setTriggerPosition(x, y, width, section, screen, barPosition, barThickness, barSpacing, barConfig) { function setTriggerPosition(x, y, width, section, targetScreen, barPosition, barThickness, barSpacing, barConfig) {
triggerX = x triggerX = x;
triggerY = y triggerY = y;
triggerWidth = width triggerWidth = width;
triggerSection = section triggerSection = section;
root.screen = screen screen = targetScreen;
storedBarThickness = barThickness !== undefined ? barThickness : (Theme.barHeight - 4) storedBarThickness = barThickness !== undefined ? barThickness : (Theme.barHeight - 4);
storedBarSpacing = barSpacing !== undefined ? barSpacing : 4 storedBarSpacing = barSpacing !== undefined ? barSpacing : 4;
storedBarConfig = barConfig storedBarConfig = barConfig;
const pos = barPosition !== undefined ? barPosition : 0 const pos = barPosition !== undefined ? barPosition : 0;
const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : 0) : 0 const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : 0) : 0;
// Get adjacent bar info for proper positioning adjacentBarInfo = SettingsData.getAdjacentBarInfo(targetScreen, pos, barConfig);
adjacentBarInfo = SettingsData.getAdjacentBarInfo(screen, pos, barConfig) setBarContext(pos, bottomGap);
}
setBarContext(pos, bottomGap) readonly property bool useBackgroundWindow: {
const layerOverride = Quickshell.env("DMS_POPOUT_LAYER");
return !layerOverride || layerOverride === "overlay";
} }
function open() { function open() {
closeTimer.stop() if (!screen)
shouldBeVisible = true return;
visible = true closeTimer.stop();
PopoutManager.showPopout(root) shouldBeVisible = true;
opened() if (useBackgroundWindow)
backgroundWindow.visible = true;
contentWindow.visible = true;
PopoutManager.showPopout(root);
opened();
} }
function close() { function close() {
shouldBeVisible = false shouldBeVisible = false;
PopoutManager.popoutChanged() PopoutManager.popoutChanged();
closeTimer.restart() closeTimer.restart();
} }
function toggle() { function toggle() {
if (shouldBeVisible) shouldBeVisible ? close() : open();
close()
else
open()
} }
Timer { Timer {
@@ -109,32 +123,95 @@ PanelWindow {
interval: animationDuration interval: animationDuration
onTriggered: { onTriggered: {
if (!shouldBeVisible) { if (!shouldBeVisible) {
visible = false contentWindow.visible = false;
PopoutManager.hidePopout(root) if (useBackgroundWindow)
popoutClosed() backgroundWindow.visible = false;
PopoutManager.hidePopout(root);
popoutClosed();
} }
} }
} }
color: "transparent" readonly property real screenWidth: screen ? screen.width : 0
WlrLayershell.layer: { readonly property real screenHeight: screen ? screen.height : 0
switch (Quickshell.env("DMS_POPOUT_LAYER")) { readonly property real dpr: screen ? CompositorService.getScreenScale(screen) : 1
case "bottom":
return WlrLayershell.Bottom readonly property real shadowBuffer: 5
case "overlay": readonly property real alignedWidth: Theme.px(popupWidth, dpr)
return WlrLayershell.Overlay readonly property real alignedHeight: Theme.px(popupHeight, dpr)
case "background":
return WlrLayershell.Background readonly property real alignedX: Theme.snap((() => {
const useAutoGaps = storedBarConfig?.popupGapsAuto !== undefined ? storedBarConfig.popupGapsAuto : true;
const manualGapValue = storedBarConfig?.popupGapsManual !== undefined ? storedBarConfig.popupGapsManual : 4;
const popupGap = useAutoGaps ? Math.max(4, storedBarSpacing) : manualGapValue;
switch (effectiveBarPosition) {
case SettingsData.Position.Left:
return Math.max(popupGap, Math.min(screenWidth - popupWidth - popupGap, triggerX));
case SettingsData.Position.Right:
return Math.max(popupGap, Math.min(screenWidth - popupWidth - popupGap, triggerX - popupWidth));
default: default:
return WlrLayershell.Top const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
const minX = adjacentBarInfo.leftBar > 0 ? adjacentBarInfo.leftBar : popupGap;
const maxX = screenWidth - popupWidth - (adjacentBarInfo.rightBar > 0 ? adjacentBarInfo.rightBar : popupGap);
return Math.max(minX, Math.min(maxX, rawX));
} }
})(), dpr)
readonly property real alignedY: Theme.snap((() => {
const useAutoGaps = storedBarConfig?.popupGapsAuto !== undefined ? storedBarConfig.popupGapsAuto : true;
const manualGapValue = storedBarConfig?.popupGapsManual !== undefined ? storedBarConfig.popupGapsManual : 4;
const popupGap = useAutoGaps ? Math.max(4, storedBarSpacing) : manualGapValue;
switch (effectiveBarPosition) {
case SettingsData.Position.Bottom:
return Math.max(popupGap, Math.min(screenHeight - popupHeight - popupGap, triggerY - popupHeight));
case SettingsData.Position.Top:
return Math.max(popupGap, Math.min(screenHeight - popupHeight - popupGap, triggerY));
default:
const rawY = triggerY - (popupHeight / 2);
const minY = adjacentBarInfo.topBar > 0 ? adjacentBarInfo.topBar : popupGap;
const maxY = screenHeight - popupHeight - (adjacentBarInfo.bottomBar > 0 ? adjacentBarInfo.bottomBar : popupGap);
return Math.max(minY, Math.min(maxY, rawY));
} }
})(), dpr)
readonly property real maskX: {
const triggeringBarX = (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? barWidth : 0;
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
return Math.max(triggeringBarX, adjacentLeftBar);
}
readonly property real maskY: {
const triggeringBarY = (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? barHeight : 0;
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
return Math.max(triggeringBarY, adjacentTopBar);
}
readonly property real maskWidth: {
const triggeringBarRight = (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? barWidth : 0;
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar);
return Math.max(100, screenWidth - maskX - rightExclusion);
}
readonly property real maskHeight: {
const triggeringBarBottom = (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? barHeight : 0;
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar);
return Math.max(100, screenHeight - maskY - bottomExclusion);
}
PanelWindow {
id: backgroundWindow
screen: root.screen
visible: false
color: "transparent"
WlrLayershell.namespace: root.layerNamespace + ":background"
WlrLayershell.layer: WlrLayershell.Top
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: { WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
if (!shouldBeVisible) return WlrKeyboardFocus.None
if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand
return WlrKeyboardFocus.Exclusive
}
anchors { anchors {
top: true top: true
@@ -143,114 +220,83 @@ PanelWindow {
bottom: true bottom: true
} }
readonly property real screenWidth: root.screen.width
readonly property real screenHeight: root.screen.height
readonly property real dpr: CompositorService.getScreenScale(root.screen)
readonly property real alignedWidth: Theme.px(popupWidth, dpr)
readonly property real alignedHeight: Theme.px(popupHeight, dpr)
property int effectiveBarPosition: 0
property real effectiveBarBottomGap: 0
readonly property real alignedX: Theme.snap((() => {
const useAutoGaps = storedBarConfig?.popupGapsAuto !== undefined ? storedBarConfig.popupGapsAuto : true
const manualGapValue = storedBarConfig?.popupGapsManual !== undefined ? storedBarConfig.popupGapsManual : 4
const popupGap = useAutoGaps ? Math.max(4, storedBarSpacing) : manualGapValue
let rawX = 0
if (effectiveBarPosition === SettingsData.Position.Left) {
rawX = triggerX
} else if (effectiveBarPosition === SettingsData.Position.Right) {
rawX = triggerX - popupWidth
} else {
rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2)
const minX = adjacentBarInfo.leftBar > 0 ? adjacentBarInfo.leftBar : popupGap
const maxX = screenWidth - popupWidth - (adjacentBarInfo.rightBar > 0 ? adjacentBarInfo.rightBar : popupGap)
return Math.max(minX, Math.min(maxX, rawX))
}
return Math.max(popupGap, Math.min(screenWidth - popupWidth - popupGap, rawX))
})(), dpr)
readonly property real alignedY: Theme.snap((() => {
const useAutoGaps = storedBarConfig?.popupGapsAuto !== undefined ? storedBarConfig.popupGapsAuto : true
const manualGapValue = storedBarConfig?.popupGapsManual !== undefined ? storedBarConfig.popupGapsManual : 4
const popupGap = useAutoGaps ? Math.max(4, storedBarSpacing) : manualGapValue
let rawY = 0
if (effectiveBarPosition === SettingsData.Position.Bottom) {
rawY = triggerY - popupHeight
} else if (effectiveBarPosition === SettingsData.Position.Top) {
rawY = triggerY
} else {
rawY = triggerY - (popupHeight / 2)
const minY = adjacentBarInfo.topBar > 0 ? adjacentBarInfo.topBar : popupGap
const maxY = screenHeight - popupHeight - (adjacentBarInfo.bottomBar > 0 ? adjacentBarInfo.bottomBar : popupGap)
return Math.max(minY, Math.min(maxY, rawY))
}
return Math.max(popupGap, Math.min(screenHeight - popupHeight - popupGap, rawY))
})(), dpr)
readonly property real maskX: {
const triggeringBarX = (effectiveBarPosition === SettingsData.Position.Left && root.barWidth > 0) ? root.barWidth : 0
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0
return Math.max(triggeringBarX, adjacentLeftBar)
}
readonly property real maskY: {
const triggeringBarY = (effectiveBarPosition === SettingsData.Position.Top && root.barHeight > 0) ? root.barHeight : 0
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0
return Math.max(triggeringBarY, adjacentTopBar)
}
readonly property real maskWidth: {
const triggeringBarRight = (effectiveBarPosition === SettingsData.Position.Right && root.barWidth > 0) ? root.barWidth : 0
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0
const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar)
return Math.max(100, root.width - maskX - rightExclusion)
}
readonly property real maskHeight: {
const triggeringBarBottom = (effectiveBarPosition === SettingsData.Position.Bottom && root.barHeight > 0) ? root.barHeight : 0
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0
const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar)
return Math.max(100, root.height - maskY - bottomExclusion)
}
mask: Region { mask: Region {
item: Rectangle { item: Rectangle {
x: root.maskX x: root.maskX
y: root.maskY y: root.maskY
width: root.maskWidth width: shouldBeVisible ? root.maskWidth : 0
height: root.maskHeight height: shouldBeVisible ? root.maskHeight : 0
} }
} }
MouseArea { MouseArea {
x: maskX x: root.maskX
y: maskY y: root.maskY
width: maskWidth width: root.maskWidth
height: maskHeight height: root.maskHeight
z: -1
enabled: shouldBeVisible enabled: shouldBeVisible
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => { onClicked: mouse => {
const clickX = mouse.x + maskX const clickX = mouse.x + root.maskX;
const clickY = mouse.y + maskY const clickY = mouse.y + root.maskY;
const outsideContent = clickX < alignedX || clickX > alignedX + alignedWidth || const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.alignedY || clickY > root.alignedY + root.alignedHeight;
clickY < alignedY || clickY > alignedY + alignedHeight
if (!outsideContent) return if (!outsideContent)
return;
backgroundClicked() backgroundClicked();
} }
} }
}
PanelWindow {
id: contentWindow
screen: root.screen
visible: false
color: "transparent"
WlrLayershell.namespace: root.layerNamespace
WlrLayershell.layer: {
switch (Quickshell.env("DMS_POPOUT_LAYER")) {
case "bottom":
return WlrLayershell.Bottom;
case "top":
return WlrLayershell.Top;
case "background":
return WlrLayershell.Background;
default:
return WlrLayershell.Overlay;
}
}
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
if (customKeyboardFocus !== null)
return customKeyboardFocus;
if (!shouldBeVisible)
return WlrKeyboardFocus.None;
if (CompositorService.isHyprland)
return WlrKeyboardFocus.OnDemand;
return WlrKeyboardFocus.Exclusive;
}
anchors {
left: true
top: true
}
WlrLayershell.margins {
left: root.alignedX - shadowBuffer
top: root.alignedY - shadowBuffer
}
implicitWidth: root.alignedWidth + (shadowBuffer * 2)
implicitHeight: root.alignedHeight + (shadowBuffer * 2)
Item { Item {
id: contentContainer id: contentContainer
x: alignedX x: shadowBuffer
y: alignedY y: shadowBuffer
width: alignedWidth width: root.alignedWidth
height: alignedHeight height: root.alignedHeight
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
@@ -269,9 +315,9 @@ PanelWindow {
Connections { Connections {
target: root target: root
function onShouldBeVisibleChanged() { function onShouldBeVisibleChanged() {
contentContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : contentContainer.offsetX, root.dpr) contentContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : contentContainer.offsetX, root.dpr);
contentContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : contentContainer.offsetY, root.dpr) contentContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : contentContainer.offsetY, root.dpr);
contentContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed contentContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed;
} }
} }
@@ -344,8 +390,8 @@ PanelWindow {
shadowBlur: Math.max(0, Math.min(1, contentWrapper.shadowBlurPx / bgShadowLayer.blurMax)) shadowBlur: Math.max(0, Math.min(1, contentWrapper.shadowBlurPx / bgShadowLayer.blurMax))
shadowScale: 1 + (2 * contentWrapper.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height)) shadowScale: 1 + (2 * contentWrapper.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
shadowColor: { shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, contentWrapper.effectiveShadowAlpha) return Theme.withAlpha(baseColor, contentWrapper.effectiveShadowAlpha);
} }
} }
@@ -364,7 +410,7 @@ PanelWindow {
Loader { Loader {
id: contentLoader id: contentLoader
anchors.fill: parent anchors.fill: parent
active: root.visible active: contentWindow.visible
asynchronous: false asynchronous: false
} }
} }
@@ -378,8 +424,9 @@ PanelWindow {
focus: true focus: true
Keys.onPressed: event => { Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
close() close();
event.accepted = true event.accepted = true;
}
} }
} }
} }