diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml b/raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml new file mode 100644 index 0000000..d8b0216 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml @@ -0,0 +1,1058 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Common +import qs.Services + +PanelWindow { + id: barWindow + + required property var rootWindow + required property var barConfig + property var modelData: item + property var hyprlandOverviewLoader: rootWindow ? rootWindow.hyprlandOverviewLoader : null + + property var leftWidgetsModel + property var centerWidgetsModel + property var rightWidgetsModel + + property var controlCenterButtonRef: null + property var clockButtonRef: null + + function triggerControlCenter() { + controlCenterLoader.active = true; + if (!controlCenterLoader.item) { + return; + } + + if (controlCenterButtonRef && controlCenterLoader.item.setTriggerPosition) { + const screenPos = controlCenterButtonRef.mapToItem(null, 0, 0); + const barPosition = axis?.edge === "left" ? 2 : (axis?.edge === "right" ? 3 : (axis?.edge === "top" ? 0 : 1)); + const pos = SettingsData.getPopupTriggerPosition(screenPos, barWindow.screen, barWindow.effectiveBarThickness, controlCenterButtonRef.width, barConfig?.spacing ?? 4, barPosition, barConfig); + const section = controlCenterButtonRef.section || "right"; + controlCenterLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, barConfig?.spacing ?? 4, barConfig); + } else { + controlCenterLoader.item.triggerScreen = barWindow.screen; + } + + controlCenterLoader.item.toggle(); + if (controlCenterLoader.item.shouldBeVisible && NetworkService.wifiEnabled) { + NetworkService.scanWifi(); + } + } + + function triggerWallpaperBrowser() { + dankDashPopoutLoader.active = true; + if (!dankDashPopoutLoader.item) { + return; + } + + let section = "center"; + if (clockButtonRef && clockButtonRef.visualContent && dankDashPopoutLoader.item.setTriggerPosition) { + // Calculate barPosition from axis.edge + const barPosition = axis?.edge === "left" ? 2 : (axis?.edge === "right" ? 3 : (axis?.edge === "top" ? 0 : 1)); + section = clockButtonRef.section || "center"; + + let triggerPos, triggerWidth; + if (section === "center") { + const centerSection = barWindow.isVertical ? (barWindow.axis?.edge === "left" ? topBarContent.vCenterSection : topBarContent.vCenterSection) : topBarContent.hCenterSection; + if (centerSection) { + if (barWindow.isVertical) { + const centerY = centerSection.height / 2; + triggerPos = centerSection.mapToItem(null, 0, centerY); + triggerWidth = centerSection.height; + } else { + triggerPos = centerSection.mapToItem(null, 0, 0); + triggerWidth = centerSection.width; + } + } else { + triggerPos = clockButtonRef.visualContent.mapToItem(null, 0, 0); + triggerWidth = clockButtonRef.visualWidth; + } + } else { + triggerPos = clockButtonRef.visualContent.mapToItem(null, 0, 0); + triggerWidth = clockButtonRef.visualWidth; + } + + const pos = SettingsData.getPopupTriggerPosition(triggerPos, barWindow.screen, barWindow.effectiveBarThickness, triggerWidth, barConfig?.spacing ?? 4, barPosition, barConfig); + dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, barConfig?.spacing ?? 4, barConfig); + } else { + dankDashPopoutLoader.item.triggerScreen = barWindow.screen; + } + + PopoutManager.requestPopout(dankDashPopoutLoader.item, 2, (barConfig?.id ?? "default") + "-" + section + "-2"); + } + + readonly property var dBarLayer: { + switch (Quickshell.env("DMS_DANKBAR_LAYER")) { + case "bottom": + return WlrLayer.Bottom; + case "overlay": + return WlrLayer.Overlay; + case "background": + return WlrLayer.background; + default: + return WlrLayer.Top; + } + } + + property var blurRegion: null + property var _blurWidgetItems: [] + + function registerBlurWidget(item) { + if (_blurWidgetItems.indexOf(item) >= 0) + return; + _blurWidgetItems = _blurWidgetItems.concat([item]); + _blurRebuildTimer.restart(); + } + + function unregisterBlurWidget(item) { + const idx = _blurWidgetItems.indexOf(item); + if (idx < 0) + return; + const arr = _blurWidgetItems.slice(); + arr.splice(idx, 1); + _blurWidgetItems = arr; + _blurRebuildTimer.restart(); + } + + Timer { + id: _blurRebuildTimer + interval: 1 + onTriggered: barBlur.rebuild() + } + + Item { + id: barBlur + visible: false + + readonly property bool barHasTransparency: barWindow._backgroundAlpha > 0 && barWindow._backgroundAlpha < 1 + + function rebuild() { + teardown(); + if (!BlurService.enabled || !BlurService.available) + return; + + const widgets = barWindow._blurWidgetItems.filter(w => w && w.visible && w.width > 0 && w.height > 0); + const hasBar = barHasTransparency; + if (!hasBar && widgets.length === 0) + return; + + const cr = Theme.cornerRadius; + let qml = 'import QtQuick; import Quickshell; Region {'; + for (let i = 0; i < widgets.length; i++) { + qml += ` property Item w${i}; Region { item: w${i}; radius: ${cr} }`; + } + qml += '}'; + + try { + const region = Qt.createQmlObject(qml, barWindow, "BarBlurRegion"); + + if (hasBar) { + region.x = Qt.binding(() => topBarMouseArea.x + barUnitInset.x + topBarSlide.x); + region.y = Qt.binding(() => topBarMouseArea.y + barUnitInset.y + topBarSlide.y); + region.width = Qt.binding(() => barUnitInset.width); + region.height = Qt.binding(() => barUnitInset.height); + region.radius = Qt.binding(() => barBackground.rt); + } + + for (let i = 0; i < widgets.length; i++) { + region[`w${i}`] = widgets[i]; + } + + barWindow.BackgroundEffect.blurRegion = region; + barWindow.blurRegion = region; + } catch (e) { + console.warn("BarBlur: Failed to create blur region:", e); + } + } + + function teardown() { + if (!barWindow.blurRegion) + return; + try { + barWindow.BackgroundEffect.blurRegion = null; + } catch (e) {} + barWindow.blurRegion.destroy(); + barWindow.blurRegion = null; + } + + onBarHasTransparencyChanged: _blurRebuildTimer.restart() + + Connections { + target: BlurService + function onEnabledChanged() { + barBlur.rebuild(); + } + } + + Connections { + target: topBarSlide + function onXChanged() { + if (barWindow.blurRegion) + barWindow.blurRegion.changed(); + } + function onYChanged() { + if (barWindow.blurRegion) + barWindow.blurRegion.changed(); + } + } + + Component.onCompleted: rebuild() + Component.onDestruction: teardown() + } + + WlrLayershell.layer: dBarLayer + WlrLayershell.namespace: "dms:bar" + + signal colorPickerRequested + + onColorPickerRequested: rootWindow.colorPickerRequested() + + property alias axis: axis + + AxisContext { + id: axis + edge: { + switch (barConfig?.position ?? 0) { + case SettingsData.Position.Top: + return "top"; + case SettingsData.Position.Bottom: + return "bottom"; + case SettingsData.Position.Left: + return "left"; + case SettingsData.Position.Right: + return "right"; + default: + return "top"; + } + } + } + + readonly property bool isVertical: axis.isVertical + + property bool gothCornersEnabled: barConfig?.gothCornersEnabled ?? false + property real wingtipsRadius: barConfig?.gothCornerRadiusOverride ? (barConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius + readonly property real _wingR: Math.max(0, wingtipsRadius) + readonly property color _surfaceContainer: Theme.surfaceContainer + readonly property string _barId: barConfig?.id ?? "default" + property real _backgroundAlpha: barConfig?.transparency ?? 1.0 + readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha) + + function _updateBackgroundAlpha() { + const live = SettingsData.barConfigs.find(c => c.id === _barId); + _backgroundAlpha = (live ?? barConfig)?.transparency ?? 1.0; + } + readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen) + + // Shadow buffer: extra window space for shadow to render beyond bar bounds + readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (barConfig?.shadowIntensity ?? 0) > 0 + readonly property real _shadowBuffer: { + if (!_shadowActive) + return 0; + const hasOverride = (barConfig?.shadowIntensity ?? 0) > 0; + if (hasOverride) { + const blur = (barConfig.shadowIntensity ?? 0) * 0.2; + const offset = blur * 0.5; + return Theme.snap(Math.max(16, blur + offset + 8), _dpr); + } + return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr); + } + + property string screenName: modelData.name + + property bool hasMaximizedToplevel: false + property bool hasFullscreenToplevel: false + property bool shouldHideForWindows: false + + function _updateHasMaximizedToplevel() { + if (!(barConfig?.maximizeDetection ?? true)) { + hasMaximizedToplevel = false; + return; + } + if (!CompositorService.isHyprland && !CompositorService.isNiri) { + hasMaximizedToplevel = false; + return; + } + + const filtered = CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName); + for (let i = 0; i < filtered.length; i++) { + if (filtered[i]?.maximized) { + hasMaximizedToplevel = true; + return; + } + } + hasMaximizedToplevel = false; + } + + function _updateHasFullscreenToplevel() { + if (!CompositorService.isHyprland) { + hasFullscreenToplevel = false; + return; + } + + const filtered = CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName); + for (let i = 0; i < filtered.length; i++) { + if (filtered[i]?.fullscreen) { + // On niri, fullscreen windows in inactive columns should not hide the bar + if (CompositorService.isNiri && !filtered[i]?.activated) + continue; + hasFullscreenToplevel = true; + return; + } + } + hasFullscreenToplevel = false; + } + + function _updateShouldHideForWindows() { + if (!(barConfig?.showOnWindowsOpen ?? false)) { + shouldHideForWindows = false; + return; + } + if (!(barConfig?.autoHide ?? false)) { + shouldHideForWindows = false; + return; + } + if (!CompositorService.isNiri && !CompositorService.isHyprland) { + shouldHideForWindows = false; + return; + } + + if (CompositorService.isNiri) { + let currentWorkspaceId = null; + for (let i = 0; i < NiriService.allWorkspaces.length; i++) { + const ws = NiriService.allWorkspaces[i]; + if (ws.output === screenName && ws.is_active) { + currentWorkspaceId = ws.id; + break; + } + } + + if (currentWorkspaceId === null) { + shouldHideForWindows = false; + return; + } + + let hasTiled = false; + let hasFloatingTouchingBar = false; + const pos = barConfig?.position ?? 0; + const barThickness = barWindow.effectiveBarThickness + (barConfig?.spacing ?? 4); + + for (let i = 0; i < NiriService.windows.length; i++) { + const win = NiriService.windows[i]; + if (win.workspace_id !== currentWorkspaceId) + continue; + + if (!win.is_floating) { + hasTiled = true; + continue; + } + + const tilePos = win.layout?.tile_pos_in_workspace_view; + const winSize = win.layout?.window_size || win.layout?.tile_size; + if (!tilePos || !winSize) + continue; + + switch (pos) { + case SettingsData.Position.Top: + if (tilePos[1] < barThickness) + hasFloatingTouchingBar = true; + break; + case SettingsData.Position.Bottom: + const screenHeight = barWindow.screen?.height ?? 0; + if (tilePos[1] + winSize[1] > screenHeight - barThickness) + hasFloatingTouchingBar = true; + break; + case SettingsData.Position.Left: + if (tilePos[0] < barThickness) + hasFloatingTouchingBar = true; + break; + case SettingsData.Position.Right: + const screenWidth = barWindow.screen?.width ?? 0; + if (tilePos[0] + winSize[0] > screenWidth - barThickness) + hasFloatingTouchingBar = true; + break; + } + } + + shouldHideForWindows = hasTiled || hasFloatingTouchingBar; + return; + } + + const filtered = CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName); + shouldHideForWindows = filtered.length > 0; + } + + property real effectiveSpacing: hasMaximizedToplevel ? 0 : (barConfig?.spacing ?? 4) + + Behavior on effectiveSpacing { + enabled: barWindow.visible + NumberAnimation { + duration: Theme.shortDuration + easing.type: Easing.OutCubic + } + } + + readonly property int notificationCount: NotificationService.notifications.length + readonly property real effectiveBarThickness: Theme.snap(Math.max(barWindow.widgetThickness + (barConfig?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))), _dpr) + readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + (barConfig?.innerPadding ?? 4) * 0.6), _dpr) + + readonly property bool hasAdjacentTopBar: { + if (barConfig?.autoHide ?? false) + return false; + if (!isVertical) + return false; + return SettingsData.barConfigs.some(bc => { + if (!bc.enabled || bc.id === barConfig?.id) + return false; + if (bc.autoHide) + return false; + if (!(bc.visible ?? true)) + return false; + if (bc.position !== SettingsData.Position.Top && bc.position !== 0) + return false; + const onThisScreen = bc.screenPreferences.includes(screenName) || bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all"); + if (!onThisScreen) + return false; + if (bc.showOnLastDisplay && screenName !== barWindow.screenName) + return false; + return true; + }); + } + + readonly property bool hasAdjacentBottomBar: { + if (barConfig?.autoHide ?? false) + return false; + if (!isVertical) + return false; + const result = SettingsData.barConfigs.some(bc => { + if (!bc.enabled || bc.id === barConfig?.id) + return false; + if (bc.autoHide) + return false; + if (!(bc.visible ?? true)) + return false; + if (bc.position !== SettingsData.Position.Bottom && bc.position !== 1) + return false; + const onThisScreen = bc.screenPreferences.includes(screenName) || bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all"); + if (!onThisScreen) + return false; + if (bc.showOnLastDisplay && screenName !== barWindow.screenName) + return false; + return true; + }); + return result; + } + + readonly property bool hasAdjacentLeftBar: { + if (barConfig?.autoHide ?? false) + return false; + if (isVertical) + return false; + const result = SettingsData.barConfigs.some(bc => { + if (!bc.enabled || bc.id === barConfig?.id) + return false; + if (bc.autoHide) + return false; + if (!(bc.visible ?? true)) + return false; + if (bc.position !== SettingsData.Position.Left && bc.position !== 2) + return false; + const onThisScreen = bc.screenPreferences.includes(screenName) || bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all"); + if (!onThisScreen) + return false; + if (bc.showOnLastDisplay && screenName !== barWindow.screenName) + return false; + return true; + }); + return result; + } + + readonly property bool hasAdjacentRightBar: { + if (barConfig?.autoHide ?? false) + return false; + if (isVertical) + return false; + const result = SettingsData.barConfigs.some(bc => { + if (!bc.enabled || bc.id === barConfig?.id) + return false; + if (bc.autoHide) + return false; + if (!(bc.visible ?? true)) + return false; + if (bc.position !== SettingsData.Position.Right && bc.position !== 3) + return false; + const onThisScreen = bc.screenPreferences.includes(screenName) || bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all"); + if (!onThisScreen) + return false; + if (bc.showOnLastDisplay && screenName !== barWindow.screenName) + return false; + return true; + }); + return result; + } + + screen: modelData + implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0 + implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0 + color: "transparent" + + property var nativeInhibitor: null + + Component.onCompleted: { + if (SettingsData.forceStatusBarLayoutRefresh) { + SettingsData.forceStatusBarLayoutRefresh.connect(() => { + Qt.callLater(() => { + stackContainer.visible = false; + Qt.callLater(() => { + stackContainer.visible = true; + }); + }); + }); + } + + updateGpuTempConfig(); + _updateBackgroundAlpha(); + _updateHasMaximizedToplevel(); + _updateShouldHideForWindows(); + + inhibitorInitTimer.start(); + } + + Timer { + id: inhibitorInitTimer + interval: 300 + repeat: false + onTriggered: { + if (SessionService.nativeInhibitorAvailable) { + createNativeInhibitor(); + } + } + } + + Connections { + target: PluginService + function onPluginLoaded(pluginId) { + console.info("DankBar: Plugin loaded:", pluginId); + SettingsData.widgetDataChanged(); + } + function onPluginUnloaded(pluginId) { + console.info("DankBar: Plugin unloaded:", pluginId); + SettingsData.widgetDataChanged(); + } + } + + function updateGpuTempConfig() { + const leftWidgets = barConfig?.leftWidgets || []; + const centerWidgets = barConfig?.centerWidgets || []; + const rightWidgets = barConfig?.rightWidgets || []; + const allWidgets = [...leftWidgets, ...centerWidgets, ...rightWidgets]; + + const hasGpuTempWidget = allWidgets.some(widget => { + const widgetId = typeof widget === "string" ? widget : widget.id; + const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false); + return widgetId === "gpuTemp" && widgetEnabled; + }); + + DgopService.gpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled || SessionData.nonNvidiaGpuTempEnabled; + DgopService.nvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled; + DgopService.nonNvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nonNvidiaGpuTempEnabled; + } + + function createNativeInhibitor() { + if (!SessionService.nativeInhibitorAvailable) { + return; + } + + try { + const qmlString = ` + import QtQuick + import Quickshell.Wayland + + IdleInhibitor { + enabled: false + } + `; + + nativeInhibitor = Qt.createQmlObject(qmlString, barWindow, "DankBar.NativeInhibitor"); + nativeInhibitor.window = barWindow; + nativeInhibitor.enabled = Qt.binding(() => SessionService.idleInhibited); + nativeInhibitor.enabledChanged.connect(function () { + if (SessionService.idleInhibited !== nativeInhibitor.enabled) { + SessionService.idleInhibited = nativeInhibitor.enabled; + SessionService.inhibitorChanged(); + } + }); + } catch (e) { + nativeInhibitor = null; + } + } + + Connections { + function onBarConfigChanged() { + barWindow.updateGpuTempConfig(); + barWindow._updateBackgroundAlpha(); + barWindow._updateHasMaximizedToplevel(); + barWindow._updateShouldHideForWindows(); + } + + target: rootWindow + } + + Connections { + target: SettingsData + function onBarConfigsChanged() { + barWindow._updateBackgroundAlpha(); + } + } + + Connections { + target: CompositorService + function onToplevelsChanged() { + barWindow._updateHasMaximizedToplevel(); + barWindow._updateHasFullscreenToplevel(); + barWindow._updateShouldHideForWindows(); + } + } + + Connections { + target: NiriService + function onAllWorkspacesChanged() { + barWindow._updateHasMaximizedToplevel(); + barWindow._updateHasFullscreenToplevel(); + barWindow._updateShouldHideForWindows(); + } + } + + Connections { + function onNvidiaGpuTempEnabledChanged() { + barWindow.updateGpuTempConfig(); + } + + function onNonNvidiaGpuTempEnabledChanged() { + barWindow.updateGpuTempConfig(); + } + + target: SessionData + } + + readonly property int barPos: barConfig?.position ?? 0 + + anchors.top: !isVertical ? (barPos === SettingsData.Position.Top) : true + anchors.bottom: !isVertical ? (barPos === SettingsData.Position.Bottom) : true + anchors.left: !isVertical ? true : (barPos === SettingsData.Position.Left) + anchors.right: !isVertical ? true : (barPos === SettingsData.Position.Right) + + exclusiveZone: (!(barConfig?.visible ?? true) || topBarCore.autoHide) ? -1 : (barWindow.effectiveBarThickness + effectiveSpacing + (barConfig?.bottomGap ?? 0)) + + Item { + id: inputMask + + readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr) + + readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false) + readonly property bool effectiveVisible: (barConfig?.visible ?? true) || inOverviewWithShow + readonly property bool showing: effectiveVisible && (topBarCore.reveal || inOverviewWithShow || !topBarCore.autoHide) + + readonly property int maskThickness: showing ? barThickness : 1 + + x: { + if (!axis.isVertical) { + return 0; + } else { + switch (barPos) { + case SettingsData.Position.Left: + return 0; + case SettingsData.Position.Right: + return parent.width - maskThickness; + default: + return 0; + } + } + } + y: { + if (axis.isVertical) { + return 0; + } else { + switch (barPos) { + case SettingsData.Position.Top: + return 0; + case SettingsData.Position.Bottom: + return parent.height - maskThickness; + default: + return 0; + } + } + } + width: axis.isVertical ? maskThickness : parent.width + height: axis.isVertical ? parent.height : maskThickness + } + + readonly property bool clickThroughEnabled: barConfig?.clickThrough ?? false + + readonly property var _leftSection: topBarContent ? (barWindow.isVertical ? topBarContent.vLeftSection : topBarContent.hLeftSection) : null + readonly property var _centerSection: topBarContent ? (barWindow.isVertical ? topBarContent.vCenterSection : topBarContent.hCenterSection) : null + readonly property var _rightSection: topBarContent ? (barWindow.isVertical ? topBarContent.vRightSection : topBarContent.hRightSection) : null + readonly property real _revealProgress: topBarSlide.x + topBarSlide.y + + function sectionRect(section, isCenter, _dep) { + if (!section) + return { + "x": 0, + "y": 0, + "w": 0, + "h": 0 + }; + + const pos = section.mapToItem(barWindow.contentItem, 0, 0); + const implW = section.implicitWidth || 0; + const implH = section.implicitHeight || 0; + + const offsetX = isCenter && !barWindow.isVertical ? (section.width - implW) / 2 : 0; + const offsetY = !barWindow.isVertical ? (section.height - implH) / 2 : (isCenter ? (section.height - implH) / 2 : 0); + + const edgePad = 2; + return { + "x": pos.x + offsetX - edgePad, + "y": pos.y + offsetY - edgePad, + "w": implW + edgePad * 2, + "h": implH + edgePad * 2 + }; + } + + mask: Region { + item: clickThroughEnabled ? null : inputMask + + Region { + readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._leftSection, false, barWindow._revealProgress) : { + "x": 0, + "y": 0, + "w": 0, + "h": 0 + } + x: r.x + y: r.y + width: r.w + height: r.h + } + + Region { + readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._centerSection, true, barWindow._revealProgress) : { + "x": 0, + "y": 0, + "w": 0, + "h": 0 + } + x: r.x + y: r.y + width: r.w + height: r.h + } + + Region { + readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._rightSection, false, barWindow._revealProgress) : { + "x": 0, + "y": 0, + "w": 0, + "h": 0 + } + x: r.x + y: r.y + width: r.w + height: r.h + } + + Region { + readonly property bool active: barWindow.clickThroughEnabled && !inputMask.showing + x: active ? inputMask.x : 0 + y: active ? inputMask.y : 0 + width: active ? inputMask.width : 0 + height: active ? inputMask.height : 0 + } + } + + Item { + id: topBarCore + anchors.fill: parent + layer.enabled: false + + property bool autoHide: barConfig?.autoHide ?? false + property bool revealSticky: false + + Timer { + id: revealHold + interval: barWindow.clickThroughEnabled ? Math.max((barConfig?.autoHideDelay ?? 250) * 6, 1500) : (barConfig?.autoHideDelay ?? 250) + repeat: false + onTriggered: { + if (!topBarMouseArea.containsMouse && !topBarCore.hasActivePopout) { + topBarCore.revealSticky = false; + } + } + } + + property bool reveal: { + const inOverviewWithShow = CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false); + if (inOverviewWithShow) + return true; + + if (barWindow.hasFullscreenToplevel) + return false; + + const showOnWindowsSetting = barConfig?.showOnWindowsOpen ?? false; + if (showOnWindowsSetting && autoHide && (CompositorService.isNiri || CompositorService.isHyprland)) { + if (barWindow.shouldHideForWindows) + return topBarMouseArea.containsMouse || hasActivePopout || revealSticky; + return true; + } + + if (CompositorService.isNiri && NiriService.inOverview) + return topBarMouseArea.containsMouse || hasActivePopout || revealSticky; + + return (barConfig?.visible ?? true) && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky); + } + + property bool hasActivePopout: false + + onHasActivePopoutChanged: evaluateReveal() + + function updateActivePopoutState() { + if (!barWindow.screen) + return; + const screenName = barWindow.screen.name; + const activePopout = PopoutManager.currentPopoutsByScreen[screenName]; + const activeTrayMenu = TrayMenuManager.activeTrayMenus[screenName]; + const trayOpen = rootWindow.systemTrayMenuOpen; + + const hasVisiblePopout = activePopout && activePopout.shouldBeVisible; + topBarCore.hasActivePopout = !!(hasVisiblePopout || activeTrayMenu || trayOpen); + } + + Connections { + target: PopoutManager + function onPopoutChanged() { + topBarCore.updateActivePopoutState(); + } + } + + Connections { + target: TrayMenuManager + function onActiveTrayMenusChanged() { + topBarCore.updateActivePopoutState(); + } + } + + Connections { + function onBarConfigChanged() { + topBarCore.autoHide = barConfig?.autoHide ?? false; + } + + target: rootWindow + } + + function evaluateReveal() { + if (!autoHide) + return; + + if (topBarMouseArea.containsMouse || hasActivePopout) { + revealSticky = true; + revealHold.stop(); + return; + } + + revealHold.restart(); + } + + Connections { + target: topBarMouseArea + function onContainsMouseChanged() { + topBarCore.evaluateReveal(); + } + } + + Connections { + target: PopoutManager + function onPopoutOpening() { + topBarCore.evaluateReveal(); + } + } + + MouseArea { + id: topBarMouseArea + y: !barWindow.isVertical ? (barPos === SettingsData.Position.Bottom ? parent.height - height : 0) : 0 + x: barWindow.isVertical ? (barPos === SettingsData.Position.Right ? parent.width - width : 0) : 0 + height: !barWindow.isVertical ? Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr) : undefined + width: barWindow.isVertical ? Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr) : undefined + anchors { + left: !barWindow.isVertical ? parent.left : (barPos === SettingsData.Position.Left ? parent.left : undefined) + right: !barWindow.isVertical ? parent.right : (barPos === SettingsData.Position.Right ? parent.right : undefined) + top: barWindow.isVertical ? parent.top : undefined + bottom: barWindow.isVertical ? parent.bottom : undefined + } + readonly property bool inOverview: CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false) + hoverEnabled: (barConfig?.autoHide ?? false) && !inOverview && !topBarCore.hasActivePopout + acceptedButtons: Qt.NoButton + enabled: (barConfig?.autoHide ?? false) && !inOverview + + Item { + id: topBarContainer + anchors.fill: parent + + transform: Translate { + id: topBarSlide + x: barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (barPos === SettingsData.Position.Right ? barWindow.implicitWidth : -barWindow.implicitWidth), barWindow._dpr) : 0 + y: !barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (barPos === SettingsData.Position.Bottom ? barWindow.implicitHeight : -barWindow.implicitHeight), barWindow._dpr) : 0 + + Behavior on x { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Easing.OutCubic + } + } + + Behavior on y { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Easing.OutCubic + } + } + } + + Item { + id: barUnitInset + property int spacingPx: Theme.px(barWindow.effectiveSpacing, barWindow._dpr) + anchors.fill: parent + anchors.leftMargin: !barWindow.isVertical ? spacingPx : (axis.edge === "left" ? spacingPx : 0) + anchors.rightMargin: !barWindow.isVertical ? spacingPx : (axis.edge === "right" ? spacingPx : 0) + anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? 0 : spacingPx) : (axis.outerVisualEdge() === "bottom" ? 0 : spacingPx) + anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? 0 : spacingPx) : (axis.outerVisualEdge() === "bottom" ? spacingPx : 0) + + BarCanvas { + id: barBackground + barWindow: barWindow + axis: axis + barConfig: barWindow.barConfig + } + + MouseArea { + id: scrollArea + anchors.fill: parent + acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 + + property real touchpadAccumulatorY: 0 + property real touchpadAccumulatorX: 0 + property real mouseAccumulatorY: 0 + property real mouseAccumulatorX: 0 + property bool actionInProgress: false + + Timer { + id: cooldownTimer + interval: 100 + onTriggered: parent.actionInProgress = false + } + + function handleScrollAction(behavior, direction) { + switch (behavior) { + case "workspace": + topBarContent.switchWorkspace(direction); + return true; + case "column": + if (!CompositorService.isNiri) + return false; + if (direction > 0) + NiriService.moveColumnRight(); + else + NiriService.moveColumnLeft(); + return true; + default: + return false; + } + } + + onWheel: wheel => { + if (!(barConfig?.scrollEnabled ?? true) || actionInProgress) { + wheel.accepted = false; + return; + } + + const deltaY = wheel.angleDelta.y; + const deltaX = wheel.angleDelta.x; + const isTouchpadY = wheel.pixelDelta && wheel.pixelDelta.y !== 0; + const isTouchpadX = wheel.pixelDelta && wheel.pixelDelta.x !== 0; + const xBehavior = barConfig?.scrollXBehavior ?? "column"; + const yBehavior = barConfig?.scrollYBehavior ?? "workspace"; + const reverse = SettingsData.reverseScrolling ? -1 : 1; + + if (CompositorService.isNiri && xBehavior !== "none" && Math.abs(deltaX) > Math.abs(deltaY)) { + if (isTouchpadX) { + touchpadAccumulatorX += deltaX; + if (Math.abs(touchpadAccumulatorX) >= 500) { + const direction = touchpadAccumulatorX * reverse < 0 ? 1 : -1; + if (handleScrollAction(xBehavior, direction)) { + actionInProgress = true; + cooldownTimer.restart(); + } + touchpadAccumulatorX = 0; + } + } else { + mouseAccumulatorX += deltaX; + if (Math.abs(mouseAccumulatorX) >= 120) { + const direction = mouseAccumulatorX * reverse < 0 ? 1 : -1; + if (handleScrollAction(xBehavior, direction)) { + actionInProgress = true; + cooldownTimer.restart(); + } + mouseAccumulatorX = 0; + } + } + wheel.accepted = false; + return; + } + + if (yBehavior === "none") { + wheel.accepted = false; + return; + } + + if (isTouchpadY) { + touchpadAccumulatorY += deltaY; + if (Math.abs(touchpadAccumulatorY) >= 500) { + const direction = touchpadAccumulatorY * reverse < 0 ? 1 : -1; + if (handleScrollAction(yBehavior, direction)) { + actionInProgress = true; + cooldownTimer.restart(); + } + touchpadAccumulatorY = 0; + } + } else { + mouseAccumulatorY += deltaY; + if (Math.abs(mouseAccumulatorY) >= 120) { + const direction = mouseAccumulatorY * reverse < 0 ? 1 : -1; + if (handleScrollAction(yBehavior, direction)) { + actionInProgress = true; + cooldownTimer.restart(); + } + mouseAccumulatorY = 0; + } + } + + wheel.accepted = false; + } + } + + DankBarContent { + id: topBarContent + barWindow: barWindow + rootWindow: barWindow.rootWindow + barConfig: barWindow.barConfig + leftWidgetsModel: barWindow.leftWidgetsModel + centerWidgetsModel: barWindow.centerWidgetsModel + rightWidgetsModel: barWindow.rightWidgetsModel + } + } + } + } + } +} |