summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/dms/Modules/DankBar/DankBarWindow.qml
diff options
context:
space:
mode:
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.qml1058
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
+ }
+ }
+ }
+ }
+ }
+}