mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-01 19:18:28 -04:00
8af71036cc
default
1212 lines
49 KiB
QML
1212 lines
49 KiB
QML
import QtQuick
|
||
import Quickshell
|
||
import qs.Common
|
||
import qs.Services
|
||
|
||
Item {
|
||
id: barWindow
|
||
readonly property var log: Log.scoped("DankBarBody")
|
||
|
||
required property var hostWindow
|
||
required property var rootWindow
|
||
required property var barConfig
|
||
required property var modelData
|
||
readonly property var screen: modelData
|
||
property var hyprlandOverviewLoader: rootWindow ? rootWindow.hyprlandOverviewLoader : null
|
||
|
||
property var leftWidgetsModel
|
||
property var centerWidgetsModel
|
||
property var rightWidgetsModel
|
||
|
||
readonly property bool barRevealed: inputMask.showing
|
||
|
||
property var controlCenterButtonRef: null
|
||
property var clockButtonRef: null
|
||
property var systemUpdateButtonRef: null
|
||
|
||
function triggerSystemUpdate() {
|
||
const loader = PopoutService.systemUpdateLoader;
|
||
if (!loader)
|
||
return;
|
||
loader.active = true;
|
||
if (!loader.item)
|
||
return;
|
||
const popout = loader.item;
|
||
const barPosition = axis?.edge === "left" ? 2 : (axis?.edge === "right" ? 3 : (axis?.edge === "top" ? 0 : 1));
|
||
if (systemUpdateButtonRef && popout.setTriggerPosition) {
|
||
const screenPos = systemUpdateButtonRef.mapToItem(null, 0, 0);
|
||
const pos = SettingsData.getPopupTriggerPosition(screenPos, barWindow.screen, barWindow.effectiveBarThickness, systemUpdateButtonRef.width, barConfig?.spacing ?? 4, barPosition, barConfig);
|
||
const section = systemUpdateButtonRef.section || "right";
|
||
popout.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, barConfig?.spacing ?? 4, barConfig);
|
||
} else {
|
||
popout.screen = barWindow.screen;
|
||
}
|
||
PopoutManager.requestPopout(popout, undefined, "systemUpdate");
|
||
}
|
||
|
||
function triggerControlCenter() {
|
||
const loader = PopoutService.controlCenterLoader;
|
||
if (!loader)
|
||
return;
|
||
loader.active = true;
|
||
if (!loader.item) {
|
||
return;
|
||
}
|
||
|
||
if (controlCenterButtonRef && loader.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";
|
||
loader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, barConfig?.spacing ?? 4, barConfig);
|
||
} else {
|
||
loader.item.triggerScreen = barWindow.screen;
|
||
}
|
||
|
||
loader.item.toggle();
|
||
if (loader.item.shouldBeVisible && NetworkService.wifiEnabled) {
|
||
NetworkService.scanWifi();
|
||
}
|
||
}
|
||
|
||
function triggerDashTab(tabId) {
|
||
const loader = PopoutService.dankDashPopoutLoader;
|
||
if (!loader)
|
||
return false;
|
||
loader.active = true;
|
||
if (!loader.item) {
|
||
return false;
|
||
}
|
||
|
||
let section = "center";
|
||
if (clockButtonRef && clockButtonRef.visualContent && loader.item.setTriggerPosition) {
|
||
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);
|
||
loader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, barConfig?.spacing ?? 4, barConfig);
|
||
} else {
|
||
loader.item.triggerScreen = barWindow.screen;
|
||
}
|
||
|
||
if (loader.item.requestTab)
|
||
loader.item.requestTab(tabId);
|
||
PopoutManager.requestPopout(loader.item, undefined, (barConfig?.id ?? "default") + "-" + section + "-" + tabId);
|
||
return true;
|
||
}
|
||
|
||
function triggerWallpaperBrowser() {
|
||
triggerDashTab("wallpaper");
|
||
}
|
||
|
||
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()
|
||
}
|
||
|
||
Connections {
|
||
target: barWindow
|
||
function onUsesConnectedFrameChromeChanged() {
|
||
_blurRebuildTimer.restart();
|
||
}
|
||
function onUsesFrameBarChromeChanged() {
|
||
// Rebuild immediately so the bar region never overlaps FrameWindow's during chrome handoff
|
||
barBlur.rebuild();
|
||
}
|
||
function onBarRevealedChanged() {
|
||
barBlur.rebuild();
|
||
}
|
||
}
|
||
|
||
Component {
|
||
id: blurRegionComp
|
||
Region {}
|
||
}
|
||
|
||
Component {
|
||
id: blurSubRegionComp
|
||
Region {
|
||
property Item w
|
||
item: w
|
||
radius: Theme.cornerRadius
|
||
}
|
||
}
|
||
|
||
Component {
|
||
id: blurWingRegionComp
|
||
|
||
// r×r square at a bar end minus a quarter-disc — the swoop BarCanvas paints
|
||
Region {
|
||
id: wingRegion
|
||
|
||
property bool atEnd: false
|
||
|
||
readonly property real r: barBackground.wing
|
||
readonly property real bx: topBarMouseArea.x + barUnitInset.x + topBarSlide.x
|
||
readonly property real by: topBarMouseArea.y + barUnitInset.y + topBarSlide.y
|
||
readonly property real bw: barUnitInset.width
|
||
readonly property real bh: barUnitInset.height
|
||
|
||
x: {
|
||
switch (barPos) {
|
||
case SettingsData.Position.Left:
|
||
return bx + bw;
|
||
case SettingsData.Position.Right:
|
||
return bx - r;
|
||
default:
|
||
return atEnd ? bx + bw - r : bx;
|
||
}
|
||
}
|
||
y: {
|
||
switch (barPos) {
|
||
case SettingsData.Position.Top:
|
||
return by + bh;
|
||
case SettingsData.Position.Bottom:
|
||
return by - r;
|
||
default:
|
||
return atEnd ? by + bh - r : by;
|
||
}
|
||
}
|
||
width: r
|
||
height: r
|
||
|
||
Region {
|
||
intersection: Intersection.Subtract
|
||
radius: wingRegion.r
|
||
width: wingRegion.r * 2
|
||
height: wingRegion.r * 2
|
||
x: {
|
||
switch (barPos) {
|
||
case SettingsData.Position.Left:
|
||
return wingRegion.bx + wingRegion.bw;
|
||
case SettingsData.Position.Right:
|
||
return wingRegion.bx - wingRegion.r * 2;
|
||
default:
|
||
return wingRegion.atEnd ? wingRegion.bx + wingRegion.bw - wingRegion.r * 2 : wingRegion.bx;
|
||
}
|
||
}
|
||
y: {
|
||
switch (barPos) {
|
||
case SettingsData.Position.Top:
|
||
return wingRegion.by + wingRegion.bh;
|
||
case SettingsData.Position.Bottom:
|
||
return wingRegion.by - wingRegion.r * 2;
|
||
default:
|
||
return wingRegion.atEnd ? wingRegion.by + wingRegion.bh - wingRegion.r * 2 : wingRegion.by;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Item {
|
||
id: barBlur
|
||
visible: false
|
||
|
||
readonly property bool barHasTransparency: barWindow._backgroundAlpha > 0 && barWindow._backgroundAlpha < 1
|
||
|
||
function rebuild() {
|
||
teardown();
|
||
if (!BlurService.enabled || !BlurService.available)
|
||
return;
|
||
if (!barWindow.barRevealed && CompositorService.isHyprland)
|
||
return;
|
||
// FrameWindow owns the blur region for the whole edge while the bar wears frame chrome
|
||
if (FrameTransitionState.effectiveFrameEnabled && barWindow.usesFrameBarChrome)
|
||
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 region = blurRegionComp.createObject(barWindow);
|
||
if (!region) {
|
||
log.warn("BarBlur: Failed to create blur region");
|
||
return;
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
const subRegions = [];
|
||
for (let i = 0; i < widgets.length; i++) {
|
||
const sub = blurSubRegionComp.createObject(region, {
|
||
w: widgets[i]
|
||
});
|
||
if (sub)
|
||
subRegions.push(sub);
|
||
}
|
||
|
||
if (hasBar && barBackground.gothEnabled && barWindow._wingR > 0) {
|
||
for (const atEnd of [false, true]) {
|
||
const wing = blurWingRegionComp.createObject(region, {
|
||
atEnd: atEnd
|
||
});
|
||
if (wing)
|
||
subRegions.push(wing);
|
||
}
|
||
}
|
||
|
||
region.regions = subRegions;
|
||
|
||
barWindow.blurRegion = region;
|
||
}
|
||
|
||
function teardown() {
|
||
const old = barWindow.blurRegion;
|
||
if (!old)
|
||
return;
|
||
barWindow.blurRegion = null;
|
||
old.destroy();
|
||
}
|
||
|
||
onBarHasTransparencyChanged: _blurRebuildTimer.restart()
|
||
|
||
Connections {
|
||
target: BlurService
|
||
function onEnabledChanged() {
|
||
barBlur.rebuild();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
target: FrameTransitionState
|
||
function onEffectiveFrameEnabledChanged() {
|
||
barBlur.rebuild();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
target: topBarSlide
|
||
function onXChanged() {
|
||
if (barWindow.blurRegion)
|
||
barWindow.blurRegion.changed();
|
||
}
|
||
function onYChanged() {
|
||
if (barWindow.blurRegion)
|
||
barWindow.blurRegion.changed();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
target: barBackground
|
||
function onGothEnabledChanged() {
|
||
_blurRebuildTimer.restart();
|
||
}
|
||
function onWingChanged() {
|
||
if (barWindow.blurRegion)
|
||
barWindow.blurRegion.changed();
|
||
}
|
||
}
|
||
|
||
Component.onCompleted: rebuild()
|
||
Component.onDestruction: teardown()
|
||
}
|
||
|
||
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
|
||
|
||
readonly property color _surfaceContainer: Theme.surfaceContainer
|
||
readonly property string _barId: barConfig?.id ?? "default"
|
||
property real _backgroundAlpha: barConfig?.transparency ?? 1.0
|
||
readonly property color _bgColor: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? Theme.withAlpha(SettingsData.effectiveFrameColor, SettingsData.frameOpacity) : 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)
|
||
|
||
property string screenName: modelData.name
|
||
|
||
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screenName)
|
||
readonly property bool usesFrameBarChrome: CompositorService.frameWindowVisibleForScreen(screenName)
|
||
readonly property var renderBarConfig: SettingsData.effectiveBarConfigForRender(barConfig, usesFrameBarChrome)
|
||
|
||
property bool gothCornersEnabled: renderBarConfig?.gothCornersEnabled ?? false
|
||
property real wingtipsRadius: renderBarConfig?.gothCornerRadiusOverride ? (renderBarConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
|
||
readonly property real _wingR: Math.max(0, wingtipsRadius)
|
||
|
||
// 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)) || (renderBarConfig?.shadowIntensity ?? 0) > 0
|
||
readonly property real _shadowBuffer: {
|
||
if (!_shadowActive)
|
||
return 0;
|
||
const hasOverride = (renderBarConfig?.shadowIntensity ?? 0) > 0;
|
||
if (hasOverride) {
|
||
const blur = (renderBarConfig.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);
|
||
}
|
||
|
||
// Flatten/spacing collapse for maximized windows only applies to frame-integrated layout
|
||
readonly property bool flattenForMaximizedWindow: !FrameTransitionState.effectiveFrameEnabled || usesFrameBarChrome
|
||
|
||
property bool hasMaximizedToplevel: false
|
||
property bool shouldHideForWindows: false
|
||
|
||
function _updateHasMaximizedToplevel() {
|
||
if (!(barConfig?.maximizeDetection ?? true)) {
|
||
hasMaximizedToplevel = false;
|
||
return;
|
||
}
|
||
if (CompositorService.isMango) {
|
||
const out = MangoService.outputs[screenName];
|
||
const active = new Set((out?.activeTags) || []);
|
||
const wins = MangoService.windows || [];
|
||
for (let i = 0; i < wins.length; i++) {
|
||
const w = wins[i];
|
||
if (!w || w.monitor !== screenName || w.is_minimized)
|
||
continue;
|
||
if (active.size > 0 && !(w.tags || []).some(t => active.has(t)))
|
||
continue;
|
||
if (w.is_maximized || w.is_fullscreen) {
|
||
hasMaximizedToplevel = true;
|
||
return;
|
||
}
|
||
}
|
||
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 _updateShouldHideForWindows() {
|
||
if (!(barConfig?.showOnWindowsOpen ?? false)) {
|
||
shouldHideForWindows = false;
|
||
return;
|
||
}
|
||
if (!(barConfig?.autoHide ?? false)) {
|
||
shouldHideForWindows = false;
|
||
return;
|
||
}
|
||
if (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isMango) {
|
||
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: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? 0 : ((flattenForMaximizedWindow && hasMaximizedToplevel) ? 0 : (barConfig?.spacing ?? 4))
|
||
|
||
Behavior on effectiveSpacing {
|
||
enabled: barWindow.hostWindow?.visible ?? false
|
||
NumberAnimation {
|
||
duration: Theme.shortDuration
|
||
easing.type: Easing.OutCubic
|
||
}
|
||
}
|
||
|
||
readonly property int notificationCount: NotificationService.notifications.length
|
||
readonly property real effectiveBarThickness: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? SettingsData.frameBarSize : Theme.snap(Math.max(barWindow.widgetThickness + (barConfig?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))), _dpr)
|
||
readonly property bool effectiveOpenOnOverview: FrameTransitionState.effectiveFrameEnabled ? SettingsData.frameShowOnOverview : (barConfig?.openOnOverview ?? false)
|
||
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;
|
||
}
|
||
|
||
readonly property real surfaceImplicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||
readonly property real surfaceImplicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||
|
||
Component.onCompleted: {
|
||
updateGpuTempConfig();
|
||
_updateBackgroundAlpha();
|
||
_updateHasMaximizedToplevel();
|
||
_updateShouldHideForWindows();
|
||
}
|
||
|
||
Connections {
|
||
target: PluginService
|
||
function onPluginLoaded(pluginId) {
|
||
log.info("DankBar: Plugin loaded:", pluginId);
|
||
SettingsData.widgetDataChanged();
|
||
}
|
||
function onPluginUnloaded(pluginId) {
|
||
log.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;
|
||
}
|
||
|
||
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._updateShouldHideForWindows();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
target: NiriService
|
||
function onAllWorkspacesChanged() {
|
||
barWindow._updateHasMaximizedToplevel();
|
||
barWindow._updateShouldHideForWindows();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
function onNvidiaGpuTempEnabledChanged() {
|
||
barWindow.updateGpuTempConfig();
|
||
}
|
||
|
||
function onNonNvidiaGpuTempEnabledChanged() {
|
||
barWindow.updateGpuTempConfig();
|
||
}
|
||
|
||
target: SessionData
|
||
}
|
||
|
||
readonly property int barPos: barConfig?.position ?? 0
|
||
|
||
readonly property bool reserveExclusiveWhenAutoHidden: FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome && !!barWindow.screen && SettingsData.isScreenInPreferences(barWindow.screen, SettingsData.frameScreenPreferences)
|
||
|
||
readonly property real surfaceExclusiveZone: (!(barConfig?.visible ?? true) || (topBarCore.autoHide && !barWindow.reserveExclusiveWhenAutoHidden)) ? -1 : (barWindow.effectiveBarThickness + effectiveSpacing + (usesFrameBarChrome ? 0 : (barConfig?.bottomGap ?? 0)))
|
||
|
||
readonly property alias inputMaskItem: inputMask
|
||
|
||
Item {
|
||
id: inputMask
|
||
|
||
readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr)
|
||
|
||
readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview
|
||
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 containsGlobalPoint(gx, gy, padding) {
|
||
const pad = padding !== undefined ? padding : 16;
|
||
if (!inputMask.showing)
|
||
return false;
|
||
const topLeft = inputMask.mapToItem(null, 0, 0);
|
||
return gx >= topLeft.x - pad && gx < topLeft.x + inputMask.width + pad && gy >= topLeft.y - pad && gy < topLeft.y + inputMask.height + pad;
|
||
}
|
||
|
||
function sectionRect(section, isCenter, _dep) {
|
||
if (!section)
|
||
return {
|
||
"x": 0,
|
||
"y": 0,
|
||
"w": 0,
|
||
"h": 0
|
||
};
|
||
|
||
const pos = section.mapToItem(barWindow.hostWindow.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
|
||
};
|
||
}
|
||
|
||
Item {
|
||
id: topBarCore
|
||
anchors.fill: parent
|
||
layer.enabled: false
|
||
|
||
property bool autoHide: barConfig?.autoHide ?? false
|
||
property bool revealSticky: false
|
||
// In click-through mode the hidden bar's input mask covers the full
|
||
// band while the revealed bar's mask covers only the widget sections,
|
||
// so the pointer position is unknowable while it is over a gap. An
|
||
// enter on the hidden bar therefore only reveals once the pointer
|
||
// reaches a thin strip at the screen edge; anything else (including
|
||
// the spurious enter generated by the mask expanding underneath a
|
||
// resting pointer) keeps the bar hidden.
|
||
property bool gapEnterSuppressed: false
|
||
readonly property bool hoverReveal: topBarMouseArea.containsMouse && !gapEnterSuppressed
|
||
readonly property bool ipcReveal: !!SettingsData.barIpcRevealStates[barConfig?.id ?? ""]
|
||
|
||
onRevealChanged: {
|
||
if (reveal && barWindow.clickThroughEnabled)
|
||
revealSettle.restart();
|
||
}
|
||
|
||
// The input mask updates lag reveal transitions, generating spurious
|
||
// enter/leave pairs. Hides are deferred until the transition settles;
|
||
// the timer re-evaluates against the post-transition mask.
|
||
Timer {
|
||
id: revealSettle
|
||
interval: 600
|
||
repeat: false
|
||
onTriggered: topBarCore.evaluateReveal()
|
||
}
|
||
|
||
function inEdgeStrip(x, y) {
|
||
const band = barWindow.isVertical ? topBarMouseArea.width : topBarMouseArea.height;
|
||
const strip = Math.max(8, band * 0.15);
|
||
switch (barPos) {
|
||
case SettingsData.Position.Bottom:
|
||
return y >= band - strip;
|
||
case SettingsData.Position.Left:
|
||
return x <= strip;
|
||
case SettingsData.Position.Right:
|
||
return x >= band - strip;
|
||
default:
|
||
return y <= strip;
|
||
}
|
||
}
|
||
|
||
Timer {
|
||
id: revealHold
|
||
interval: barConfig?.autoHideDelay ?? 250
|
||
repeat: false
|
||
onTriggered: {
|
||
if (!topBarCore.hoverReveal && !topBarCore.popoutPinsReveal)
|
||
topBarCore.revealSticky = false;
|
||
}
|
||
}
|
||
|
||
property bool hasActivePopout: false
|
||
|
||
readonly property bool popoutPinsReveal: !!(hasActivePopout && !(barConfig?.autoHideStrict ?? false))
|
||
|
||
onHasActivePopoutChanged: evaluateReveal()
|
||
|
||
onPopoutPinsRevealChanged: 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();
|
||
}
|
||
|
||
function onPopoutOpening() {
|
||
topBarCore.evaluateReveal();
|
||
}
|
||
}
|
||
|
||
Connections {
|
||
target: TrayMenuManager
|
||
|
||
function onActiveTrayMenusChanged() {
|
||
topBarCore.updateActivePopoutState();
|
||
}
|
||
}
|
||
|
||
property bool reveal: {
|
||
const inOverviewWithShow = CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview;
|
||
if (inOverviewWithShow)
|
||
return true;
|
||
|
||
const showOnWindowsSetting = barConfig?.showOnWindowsOpen ?? false;
|
||
if (showOnWindowsSetting && autoHide && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango)) {
|
||
if (barWindow.shouldHideForWindows)
|
||
return hoverReveal || popoutPinsReveal || revealSticky || ipcReveal;
|
||
return true;
|
||
}
|
||
|
||
if (CompositorService.isNiri && NiriService.inOverview)
|
||
return hoverReveal || popoutPinsReveal || revealSticky || ipcReveal;
|
||
|
||
return (barConfig?.visible ?? true) && (!autoHide || hoverReveal || popoutPinsReveal || revealSticky || ipcReveal);
|
||
}
|
||
|
||
Connections {
|
||
function onBarConfigChanged() {
|
||
topBarCore.autoHide = barConfig?.autoHide ?? false;
|
||
topBarCore.evaluateReveal();
|
||
}
|
||
|
||
target: rootWindow
|
||
}
|
||
|
||
Component.onCompleted: topBarCore.updateActivePopoutState()
|
||
|
||
function evaluateReveal() {
|
||
if (!autoHide)
|
||
return;
|
||
|
||
if (hoverReveal) {
|
||
SettingsData.setBarIpcReveal(barConfig?.id ?? "", false);
|
||
revealSticky = true;
|
||
revealHold.stop();
|
||
return;
|
||
}
|
||
|
||
if (popoutPinsReveal) {
|
||
revealSticky = true;
|
||
revealHold.stop();
|
||
return;
|
||
}
|
||
|
||
if (revealSettle.running)
|
||
return;
|
||
|
||
revealHold.interval = barConfig?.autoHideDelay ?? 250;
|
||
revealHold.restart();
|
||
}
|
||
|
||
Connections {
|
||
target: topBarMouseArea
|
||
function onContainsMouseChanged() {
|
||
if (!topBarMouseArea.containsMouse) {
|
||
topBarCore.gapEnterSuppressed = false;
|
||
} else if (barWindow.clickThroughEnabled && !topBarCore.reveal) {
|
||
topBarCore.gapEnterSuppressed = true;
|
||
}
|
||
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 && barWindow.effectiveOpenOnOverview
|
||
hoverEnabled: (barConfig?.autoHide ?? false) && !inOverview && !topBarCore.popoutPinsReveal
|
||
acceptedButtons: Qt.NoButton
|
||
enabled: (barConfig?.autoHide ?? false) && !inOverview
|
||
onPositionChanged: mouse => {
|
||
if (!topBarCore.gapEnterSuppressed)
|
||
return;
|
||
if (!topBarCore.inEdgeStrip(mouse.x, mouse.y))
|
||
return;
|
||
topBarCore.gapEnterSuppressed = false;
|
||
topBarCore.evaluateReveal();
|
||
}
|
||
|
||
Item {
|
||
id: topBarContainer
|
||
anchors.fill: parent
|
||
|
||
transform: Translate {
|
||
id: topBarSlide
|
||
x: barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (barPos === SettingsData.Position.Right ? barWindow.surfaceImplicitWidth : -barWindow.surfaceImplicitWidth), barWindow._dpr) : 0
|
||
y: !barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (barPos === SettingsData.Position.Bottom ? barWindow.surfaceImplicitHeight : -barWindow.surfaceImplicitHeight), 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.renderBarConfig
|
||
}
|
||
|
||
MouseArea {
|
||
anchors.fill: parent
|
||
z: -2
|
||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||
onClicked: {
|
||
const screenName = barWindow.screen?.name;
|
||
if (!screenName)
|
||
return;
|
||
if (PopoutManager.currentPopoutsByScreen[screenName])
|
||
PopoutManager.closeAllPopouts();
|
||
if (ModalManager.currentModalsByScreen[screenName])
|
||
ModalManager.closeAllModalsExcept(null);
|
||
TrayMenuManager.closeAllMenus();
|
||
}
|
||
}
|
||
|
||
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(barWindow.screenName);
|
||
else
|
||
NiriService.moveColumnLeft(barWindow.screenName);
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function processWheel(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;
|
||
}
|
||
|
||
onWheel: wheel => processWheel(wheel)
|
||
}
|
||
|
||
DankBarContent {
|
||
id: topBarContent
|
||
barWindow: barWindow
|
||
rootWindow: barWindow.rootWindow
|
||
barConfig: barWindow.barConfig
|
||
leftWidgetsModel: barWindow.leftWidgetsModel
|
||
centerWidgetsModel: barWindow.centerWidgetsModel
|
||
rightWidgetsModel: barWindow.rightWidgetsModel
|
||
}
|
||
|
||
// Passive: tracks cursor without intercepting clicks or scroll
|
||
HoverHandler {
|
||
id: hoverPopoutHandler
|
||
enabled: (barConfig?.hoverPopouts ?? false) && !barWindow.clickThroughEnabled
|
||
|
||
property real lastGlobalX: 0
|
||
property real lastGlobalY: 0
|
||
|
||
onPointChanged: {
|
||
const gp = barUnitInset.mapToItem(null, point.position.x, point.position.y);
|
||
lastGlobalX = gp.x;
|
||
lastGlobalY = gp.y;
|
||
topBarContent.queueHoverPopout(gp.x, gp.y);
|
||
}
|
||
|
||
onHoveredChanged: {
|
||
topBarContent.updateHoverBarHovered(hovered);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|