mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 11:38:30 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cc307aff98 | |||
| cee7957e15 | |||
| 602697ef51 |
@@ -2735,6 +2735,35 @@ Singleton {
|
|||||||
return edges;
|
return edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOverlayBarEdgesForScreen(screen) {
|
||||||
|
if (!screen)
|
||||||
|
return [];
|
||||||
|
var edges = [];
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled || !(bc.useOverlayLayer ?? false))
|
||||||
|
continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs))
|
||||||
|
continue;
|
||||||
|
switch (bc.position ?? 0) {
|
||||||
|
case SettingsData.Position.Top:
|
||||||
|
edges.push("top");
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Bottom:
|
||||||
|
edges.push("bottom");
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Left:
|
||||||
|
edges.push("left");
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Right:
|
||||||
|
edges.push("right");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
function frameEdgeInsetForSide(screen, side) {
|
function frameEdgeInsetForSide(screen, side) {
|
||||||
if (!frameEnabled || !screen)
|
if (!frameEnabled || !screen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -314,6 +314,17 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: BarWidgetService
|
||||||
|
property: "dockContextMenu"
|
||||||
|
value: dockContextMenuLoader.item
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: BarWidgetService
|
||||||
|
property: "dockTrashContextMenu"
|
||||||
|
value: dockTrashContextMenuLoader.item
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onDockShowTrashChanged() {
|
function onDockShowTrashChanged() {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ Item {
|
|||||||
signal colorPickerRequested
|
signal colorPickerRequested
|
||||||
signal barReady(var barConfig)
|
signal barReady(var barConfig)
|
||||||
|
|
||||||
|
Component.onCompleted: BarWidgetService.registerDankBarItem(root.barConfig?.id, root)
|
||||||
|
Component.onDestruction: BarWidgetService.unregisterDankBarItem(root.barConfig?.id, root)
|
||||||
|
|
||||||
property alias barVariants: barVariants
|
property alias barVariants: barVariants
|
||||||
property var hyprlandOverviewLoader: null
|
property var hyprlandOverviewLoader: null
|
||||||
property bool systemTrayMenuOpen: false
|
property bool systemTrayMenuOpen: false
|
||||||
@@ -161,14 +164,18 @@ Item {
|
|||||||
id: barVariants
|
id: barVariants
|
||||||
model: {
|
model: {
|
||||||
const prefs = root.barConfig?.screenPreferences || ["all"];
|
const prefs = root.barConfig?.screenPreferences || ["all"];
|
||||||
if (prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all")) {
|
const wantsAll = prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all");
|
||||||
return Quickshell.screens;
|
let base;
|
||||||
|
if (wantsAll) {
|
||||||
|
base = Quickshell.screens;
|
||||||
|
} else {
|
||||||
|
base = Quickshell.screens.filter(screen => SettingsData.isScreenInPreferences(screen, prefs));
|
||||||
|
if (base.length === 0 && root.barConfig?.showOnLastDisplay && Quickshell.screens.length === 1)
|
||||||
|
base = Quickshell.screens;
|
||||||
}
|
}
|
||||||
const filtered = Quickshell.screens.filter(screen => SettingsData.isScreenInPreferences(screen, prefs));
|
// Connected frame mode renders the bar inside the frame surface; skip the standalone window there
|
||||||
if (filtered.length === 0 && root.barConfig?.showOnLastDisplay && Quickshell.screens.length === 1) {
|
// unless this bar wants the overlay layer, which the frame surface cannot provide.
|
||||||
return Quickshell.screens;
|
return base.filter(screen => !CompositorService.frameHostsBarForConfig(screen, root.barConfig));
|
||||||
}
|
|
||||||
return filtered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: DankBarWindow {
|
delegate: DankBarWindow {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -38,37 +38,44 @@ Item {
|
|||||||
readonly property real _barInsetPaddingRaw: SettingsData.barInsetPaddingSyncAll ? SettingsData.barInsetPaddingShared : (barConfig?.barInsetPadding ?? -1)
|
readonly property real _barInsetPaddingRaw: SettingsData.barInsetPaddingSyncAll ? SettingsData.barInsetPaddingShared : (barConfig?.barInsetPadding ?? -1)
|
||||||
readonly property real _barInsetPaddingAuto: _barIsVertical ? Theme.spacingXS : _edgeBaseMargin
|
readonly property real _barInsetPaddingAuto: _barIsVertical ? Theme.spacingXS : _edgeBaseMargin
|
||||||
readonly property real _barInsetPadding: _barInsetPaddingRaw < 0 ? _barInsetPaddingAuto : _barInsetPaddingRaw
|
readonly property real _barInsetPadding: _barInsetPaddingRaw < 0 ? _barInsetPaddingAuto : _barInsetPaddingRaw
|
||||||
// Connected-frame Bar Inset Padding: absolute free-end inset (auto < 0 = frameThickness, 0 = edge-to-edge).
|
// Connected-frame Bar Inset Padding: absolute free-end gap the hosted bar spans full-width into
|
||||||
// FrameExclusions already moves a free bar end inward by frameThickness so use frame inset to mangage the gap
|
// (auto < 0 = frameThickness so widgets align with the interior cutout, 0 = edge-to-edge). The extra
|
||||||
|
// beyond frameThickness is what an adjacent bar end adds on top of its corner alignment.
|
||||||
readonly property real _frameInsetResolved: SettingsData.frameBarInsetPadding < 0 ? SettingsData.frameThickness : SettingsData.frameBarInsetPadding
|
readonly property real _frameInsetResolved: SettingsData.frameBarInsetPadding < 0 ? SettingsData.frameThickness : SettingsData.frameBarInsetPadding
|
||||||
readonly property real _frameInsetExtra: Math.max(0, _frameInsetResolved - SettingsData.frameThickness)
|
readonly property real _frameInsetExtra: Math.max(0, _frameInsetResolved - SettingsData.frameThickness)
|
||||||
|
|
||||||
|
// Horizontal bars span the full width and own the corners; the perpendicular vertical bar
|
||||||
|
// tucks in below/above. Where they meet, inset the corner widget so it centres in the
|
||||||
|
// frameBarSize corner cell, aligning it with the vertical bar's widget column.
|
||||||
|
readonly property real _widgetThicknessValue: _hasBarWindow ? barWindow.widgetThickness : 30
|
||||||
|
readonly property real _cornerAlignInset: Math.max(_frameInsetExtra, (SettingsData.frameBarSize - _widgetThicknessValue) / 2)
|
||||||
|
|
||||||
readonly property real _leftMargin: {
|
readonly property real _leftMargin: {
|
||||||
if (_barIsVertical)
|
if (_barIsVertical)
|
||||||
return _edgeBaseMargin;
|
return _edgeBaseMargin;
|
||||||
if (_usesFrameBarChrome)
|
if (_usesFrameBarChrome)
|
||||||
return hasAdjacentLeftBarLive ? (_edgeBaseMargin + SettingsData.frameBarSize + _frameInsetExtra) : _frameInsetExtra;
|
return hasAdjacentLeftBarLive ? _cornerAlignInset : _frameInsetResolved;
|
||||||
return Math.max(0, _barInsetPadding);
|
return Math.max(0, _barInsetPadding);
|
||||||
}
|
}
|
||||||
readonly property real _rightMargin: {
|
readonly property real _rightMargin: {
|
||||||
if (_barIsVertical)
|
if (_barIsVertical)
|
||||||
return _edgeBaseMargin;
|
return _edgeBaseMargin;
|
||||||
if (_usesFrameBarChrome)
|
if (_usesFrameBarChrome)
|
||||||
return hasAdjacentRightBarLive ? (_edgeBaseMargin + SettingsData.frameBarSize + _frameInsetExtra) : _frameInsetExtra;
|
return hasAdjacentRightBarLive ? _cornerAlignInset : _frameInsetResolved;
|
||||||
return Math.max(0, _barInsetPadding);
|
return Math.max(0, _barInsetPadding);
|
||||||
}
|
}
|
||||||
readonly property real _topMargin: {
|
readonly property real _topMargin: {
|
||||||
if (!_barIsVertical)
|
if (!_barIsVertical)
|
||||||
return 0;
|
return 0;
|
||||||
if (_usesFrameBarChrome)
|
if (_usesFrameBarChrome)
|
||||||
return hasAdjacentTopBarLive ? (outlineThickness + SettingsData.frameThickness + _frameInsetExtra) : _frameInsetExtra;
|
return hasAdjacentTopBarLive ? (_edgeBaseMargin + SettingsData.frameBarSize + _frameInsetExtra) : _frameInsetResolved;
|
||||||
return Math.max(0, _barInsetPadding);
|
return Math.max(0, _barInsetPadding);
|
||||||
}
|
}
|
||||||
readonly property real _bottomMargin: {
|
readonly property real _bottomMargin: {
|
||||||
if (!_barIsVertical)
|
if (!_barIsVertical)
|
||||||
return 0;
|
return 0;
|
||||||
if (_usesFrameBarChrome)
|
if (_usesFrameBarChrome)
|
||||||
return hasAdjacentBottomBarLive ? (outlineThickness + SettingsData.frameThickness + _frameInsetExtra) : _frameInsetExtra;
|
return hasAdjacentBottomBarLive ? (_edgeBaseMargin + SettingsData.frameBarSize + _frameInsetExtra) : _frameInsetResolved;
|
||||||
return Math.max(0, _barInsetPadding);
|
return Math.max(0, _barInsetPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,6 +684,7 @@ Item {
|
|||||||
LeftSection {
|
LeftSection {
|
||||||
id: hLeftSection
|
id: hLeftSection
|
||||||
objectName: "leftSection"
|
objectName: "leftSection"
|
||||||
|
edgeIsScreenEdge: !topBarContent.hasAdjacentLeftBarLive
|
||||||
overrideAxisLayout: true
|
overrideAxisLayout: true
|
||||||
forceVerticalLayout: false
|
forceVerticalLayout: false
|
||||||
anchors {
|
anchors {
|
||||||
@@ -710,6 +718,7 @@ Item {
|
|||||||
RightSection {
|
RightSection {
|
||||||
id: hRightSection
|
id: hRightSection
|
||||||
objectName: "rightSection"
|
objectName: "rightSection"
|
||||||
|
edgeIsScreenEdge: !topBarContent.hasAdjacentRightBarLive
|
||||||
overrideAxisLayout: true
|
overrideAxisLayout: true
|
||||||
forceVerticalLayout: false
|
forceVerticalLayout: false
|
||||||
anchors {
|
anchors {
|
||||||
@@ -782,6 +791,7 @@ Item {
|
|||||||
LeftSection {
|
LeftSection {
|
||||||
id: vLeftSection
|
id: vLeftSection
|
||||||
objectName: "leftSection"
|
objectName: "leftSection"
|
||||||
|
edgeIsScreenEdge: !topBarContent.hasAdjacentTopBarLive
|
||||||
overrideAxisLayout: true
|
overrideAxisLayout: true
|
||||||
forceVerticalLayout: true
|
forceVerticalLayout: true
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -850,6 +860,7 @@ Item {
|
|||||||
RightSection {
|
RightSection {
|
||||||
id: vRightSection
|
id: vRightSection
|
||||||
objectName: "rightSection"
|
objectName: "rightSection"
|
||||||
|
edgeIsScreenEdge: !topBarContent.hasAdjacentBottomBarLive
|
||||||
overrideAxisLayout: true
|
overrideAxisLayout: true
|
||||||
forceVerticalLayout: true
|
forceVerticalLayout: true
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -930,7 +930,8 @@ Item {
|
|||||||
if (_barHovered)
|
if (_barHovered)
|
||||||
return;
|
return;
|
||||||
const excludedBar = _barExitPending ? barWindow : null;
|
const excludedBar = _barExitPending ? barWindow : null;
|
||||||
if (cursorOverHoverChain(gx, gy, excludedBar))
|
const staleOverExitedBar = excludedBar?.containsGlobalPoint?.(gx, gy, 0) ?? false;
|
||||||
|
if (!staleOverExitedBar && cursorOverHoverChain(gx, gy, excludedBar))
|
||||||
return;
|
return;
|
||||||
_barExitPending = false;
|
_barExitPending = false;
|
||||||
closeHoverSurfaces();
|
closeHoverSurfaces();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,9 @@ Item {
|
|||||||
property real sectionAvailablePrimarySize: 0
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool overrideAxisLayout: false
|
property bool overrideAxisLayout: false
|
||||||
property bool forceVerticalLayout: false
|
property bool forceVerticalLayout: false
|
||||||
|
// False when a perpendicular bar occupies this edge, so the first widget's click area
|
||||||
|
// doesn't extend past the bar into the neighbour (matters most in the frame surface).
|
||||||
|
property bool edgeIsScreenEdge: true
|
||||||
|
|
||||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||||
property alias widgetLayoutLoader: layoutLoader
|
property alias widgetLayoutLoader: layoutLoader
|
||||||
@@ -68,7 +71,7 @@ Item {
|
|||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === rowRepeater.count - 1
|
isLast: index === rowRepeater.count - 1
|
||||||
sectionSpacing: parent.rowSpacing
|
sectionSpacing: parent.rowSpacing
|
||||||
isLeftBarEdge: true
|
isLeftBarEdge: root.edgeIsScreenEdge
|
||||||
isRightBarEdge: false
|
isRightBarEdge: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +118,7 @@ Item {
|
|||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === columnRepeater.count - 1
|
isLast: index === columnRepeater.count - 1
|
||||||
sectionSpacing: parent.columnSpacing
|
sectionSpacing: parent.columnSpacing
|
||||||
isTopBarEdge: true
|
isTopBarEdge: root.edgeIsScreenEdge
|
||||||
isBottomBarEdge: false
|
isBottomBarEdge: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ Item {
|
|||||||
property real sectionAvailablePrimarySize: 0
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool overrideAxisLayout: false
|
property bool overrideAxisLayout: false
|
||||||
property bool forceVerticalLayout: false
|
property bool forceVerticalLayout: false
|
||||||
|
// False when a perpendicular bar occupies this edge, so the last widget's click area
|
||||||
|
// doesn't extend past the bar into the neighbour (matters most in the frame surface).
|
||||||
|
property bool edgeIsScreenEdge: true
|
||||||
|
|
||||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||||
property alias widgetLayoutLoader: layoutLoader
|
property alias widgetLayoutLoader: layoutLoader
|
||||||
@@ -71,7 +74,7 @@ Item {
|
|||||||
isLast: index === rowRepeater.count - 1
|
isLast: index === rowRepeater.count - 1
|
||||||
sectionSpacing: parent.rowSpacing
|
sectionSpacing: parent.rowSpacing
|
||||||
isLeftBarEdge: false
|
isLeftBarEdge: false
|
||||||
isRightBarEdge: true
|
isRightBarEdge: root.edgeIsScreenEdge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +121,7 @@ Item {
|
|||||||
isLast: index === columnRepeater.count - 1
|
isLast: index === columnRepeater.count - 1
|
||||||
sectionSpacing: parent.columnSpacing
|
sectionSpacing: parent.columnSpacing
|
||||||
isTopBarEdge: false
|
isTopBarEdge: false
|
||||||
isBottomBarEdge: true
|
isBottomBarEdge: root.edgeIsScreenEdge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Shapes
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -10,7 +8,8 @@ import qs.Widgets
|
|||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
id: dockVariants
|
id: dockVariants
|
||||||
model: SettingsData.getFilteredScreens("dock")
|
// Connected frame mode renders the dock inside the frame surface; skip the standalone window there.
|
||||||
|
model: SettingsData.getFilteredScreens("dock").filter(screen => !CompositorService.frameHostsDockForScreen(screen))
|
||||||
|
|
||||||
property var contextMenu
|
property var contextMenu
|
||||||
property var trashContextMenu
|
property var trashContextMenu
|
||||||
@@ -18,864 +17,74 @@ Variants {
|
|||||||
delegate: PanelWindow {
|
delegate: PanelWindow {
|
||||||
id: dock
|
id: dock
|
||||||
|
|
||||||
WindowBlur {
|
|
||||||
targetWindow: dock
|
|
||||||
blurEnabled: dock.effectiveBlurEnabled && !dock.usesConnectedFrameChrome
|
|
||||||
blurX: dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x + dockSlide.x
|
|
||||||
blurY: dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y + dockSlide.y
|
|
||||||
blurWidth: dock.hasApps && dock.reveal ? dockBackground.width : 0
|
|
||||||
blurHeight: dock.hasApps && dock.reveal ? dockBackground.height : 0
|
|
||||||
blurRadius: dock.usesConnectedFrameChrome ? Theme.connectedCornerRadius : dock.surfaceRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:dock"
|
|
||||||
WlrLayershell.layer: dock.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top
|
|
||||||
|
|
||||||
readonly property bool isVertical: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: !isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top) : true
|
|
||||||
bottom: !isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom) : true
|
|
||||||
left: !isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Left)
|
|
||||||
right: !isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Right)
|
|
||||||
}
|
|
||||||
|
|
||||||
property var modelData: item
|
property var modelData: item
|
||||||
property bool autoHide: SettingsData.dockAutoHide || SettingsData.dockSmartAutoHide
|
|
||||||
property real backgroundTransparency: SettingsData.dockTransparency
|
|
||||||
property bool groupByApp: SettingsData.dockGroupByApp
|
|
||||||
readonly property int borderThickness: SettingsData.dockBorderEnabled ? SettingsData.dockBorderThickness : 0
|
|
||||||
readonly property string connectedBarSide: SettingsData.dockPosition === SettingsData.Position.Top ? "top" : SettingsData.dockPosition === SettingsData.Position.Bottom ? "bottom" : SettingsData.dockPosition === SettingsData.Position.Left ? "left" : "right"
|
|
||||||
readonly property bool frameDockExclusionActive: dockGeometry.frameExclusionActive
|
|
||||||
readonly property bool connectedBarActiveOnEdge: dockGeometry.connectedBarActiveOnEdge
|
|
||||||
readonly property real connectedJoinInset: dockGeometry.connectedJoinInset
|
|
||||||
readonly property real dockFrameInset: dockGeometry.frameInset
|
|
||||||
readonly property real surfaceRadius: usesConnectedFrameChrome ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
|
||||||
readonly property color surfaceColor: usesConnectedFrameChrome ? Theme.connectedSurfaceColor : Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
|
|
||||||
readonly property color surfaceBorderColor: usesConnectedFrameChrome ? Theme.withAlpha(BlurService.borderColor, 0) : BlurService.borderColor
|
|
||||||
readonly property real surfaceBorderWidth: usesConnectedFrameChrome ? 0 : BlurService.borderWidth
|
|
||||||
readonly property real surfaceTopLeftRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
|
||||||
readonly property real surfaceTopRightRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
|
||||||
readonly property real surfaceBottomLeftRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
|
||||||
readonly property real surfaceBottomRightRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
|
||||||
readonly property real horizontalConnectorExtent: usesConnectedFrameChrome && !isVertical ? Theme.connectedCornerRadius : 0
|
|
||||||
readonly property real verticalConnectorExtent: usesConnectedFrameChrome && isVertical ? Theme.connectedCornerRadius : 0
|
|
||||||
|
|
||||||
readonly property int hasApps: dockApps.implicitWidth > 0 || dockApps.implicitHeight > 0
|
|
||||||
|
|
||||||
readonly property real widgetHeight: SettingsData.dockIconSize
|
|
||||||
readonly property real effectiveBarHeight: dockGeometry.visualThickness
|
|
||||||
function getBarHeight(barConfig) {
|
|
||||||
if (!barConfig)
|
|
||||||
return 0;
|
|
||||||
const innerPadding = barConfig.innerPadding ?? 4;
|
|
||||||
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
|
|
||||||
const barThickness = Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
|
|
||||||
const spacing = barConfig.spacing ?? 4;
|
|
||||||
const bottomGap = barConfig.bottomGap ?? 0;
|
|
||||||
return barThickness + spacing + bottomGap;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real barSpacing: {
|
|
||||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
|
||||||
if (!defaultBar)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const barPos = defaultBar.position ?? SettingsData.Position.Top;
|
|
||||||
const barIsHorizontal = (barPos === SettingsData.Position.Top || barPos === SettingsData.Position.Bottom);
|
|
||||||
const barIsVertical = (barPos === SettingsData.Position.Left || barPos === SettingsData.Position.Right);
|
|
||||||
const samePosition = (SettingsData.dockPosition === barPos);
|
|
||||||
const dockIsHorizontal = !isVertical;
|
|
||||||
const dockIsVertical = isVertical;
|
|
||||||
|
|
||||||
if (!(defaultBar.visible ?? true))
|
|
||||||
return 0;
|
|
||||||
const spacing = defaultBar.spacing ?? 4;
|
|
||||||
const bottomGap = defaultBar.bottomGap ?? 0;
|
|
||||||
if (dockIsHorizontal && barIsHorizontal && samePosition) {
|
|
||||||
return spacing + effectiveBarHeight + bottomGap;
|
|
||||||
}
|
|
||||||
if (dockIsVertical && barIsVertical && samePosition) {
|
|
||||||
return spacing + effectiveBarHeight + bottomGap;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real adjacentTopBarHeight: {
|
|
||||||
if (!isVertical || autoHide)
|
|
||||||
return 0;
|
|
||||||
const screenName = dock.modelData?.name ?? "";
|
|
||||||
const topBar = SettingsData.barConfigs.find(bc => {
|
|
||||||
if (!bc.enabled || bc.autoHide || !(bc.visible ?? true))
|
|
||||||
return false;
|
|
||||||
if (bc.position !== SettingsData.Position.Top && bc.position !== 0)
|
|
||||||
return false;
|
|
||||||
const onThisScreen = bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all") || bc.screenPreferences.includes(screenName);
|
|
||||||
return onThisScreen;
|
|
||||||
});
|
|
||||||
return getBarHeight(topBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real adjacentLeftBarWidth: {
|
|
||||||
if (isVertical || autoHide)
|
|
||||||
return 0;
|
|
||||||
const screenName = dock.modelData?.name ?? "";
|
|
||||||
const leftBar = SettingsData.barConfigs.find(bc => {
|
|
||||||
if (!bc.enabled || bc.autoHide || !(bc.visible ?? true))
|
|
||||||
return false;
|
|
||||||
if (bc.position !== SettingsData.Position.Left && bc.position !== 2)
|
|
||||||
return false;
|
|
||||||
const onThisScreen = bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all") || bc.screenPreferences.includes(screenName);
|
|
||||||
return onThisScreen;
|
|
||||||
});
|
|
||||||
return getBarHeight(leftBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real dockMargin: SettingsData.dockMargin
|
|
||||||
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
|
||||||
readonly property real effectiveDockBottomGap: dockGeometry.visualOffset
|
|
||||||
readonly property real effectiveDockMargin: dockGeometry.effectiveMargin
|
|
||||||
readonly property real positionSpacing: barSpacing + effectiveDockBottomGap + effectiveDockMargin
|
|
||||||
readonly property real joinedEdgeMargin: dockGeometry.joinedEdgeMargin
|
|
||||||
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
|
|
||||||
function px(v) {
|
|
||||||
return Math.round(v * _dpr) / _dpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DockGeometry {
|
|
||||||
id: dockGeometry
|
|
||||||
|
|
||||||
screen: dock.screen || dock.modelData
|
|
||||||
edge: dock.connectedBarSide
|
|
||||||
dockVisible: dock.visible
|
|
||||||
autoHide: dock.autoHide
|
|
||||||
iconSize: dock.widgetHeight
|
|
||||||
spacing: SettingsData.dockSpacing
|
|
||||||
borderThickness: dock.borderThickness
|
|
||||||
offset: SettingsData.dockBottomGap
|
|
||||||
margin: SettingsData.dockMargin
|
|
||||||
barSpacing: dock.barSpacing
|
|
||||||
dpr: dock._dpr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dock window origin in screen-relative coordinates (FrameWindow space).
|
|
||||||
function _dockWindowOriginX() {
|
|
||||||
if (!dock.isVertical)
|
|
||||||
return 0;
|
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Right)
|
|
||||||
return (dock.screen ? dock.screen.width : 0) - dock.width;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
function _dockWindowOriginY() {
|
|
||||||
if (dock.isVertical)
|
|
||||||
return 0;
|
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Bottom)
|
|
||||||
return (dock.screen ? dock.screen.height : 0) - dock.height;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property string _dockScreenName: dock.modelData ? dock.modelData.name : (dock.screen ? dock.screen.name : "")
|
|
||||||
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(dock._dockScreenName)
|
|
||||||
readonly property bool usesOverlayLayer: CompositorService.framePeerSurfacesUseOverlayForScreen(dock._dockScreenName) || SettingsData.dockUseOverlayLayer
|
|
||||||
|
|
||||||
function _syncDockChromeState() {
|
|
||||||
if (!dock._dockScreenName)
|
|
||||||
return;
|
|
||||||
if (!dock.usesConnectedFrameChrome) {
|
|
||||||
ConnectedModeState.clearDockState(dock._dockScreenName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const presented = dock.visible && (dock.reveal || slideXAnimation.running || slideYAnimation.running) && dock.hasApps;
|
|
||||||
const phase = !presented ? "hidden" : ((!dock.reveal && (slideXAnimation.running || slideYAnimation.running)) ? "closing" : ((slideXAnimation.running || slideYAnimation.running) ? "opening" : "open"));
|
|
||||||
const bodyX = dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x;
|
|
||||||
const bodyY = dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y;
|
|
||||||
const bodyW = dock.hasApps ? dockBackground.width : 0;
|
|
||||||
const bodyH = dock.hasApps ? dockBackground.height : 0;
|
|
||||||
ConnectedModeState.setDockState(dock._dockScreenName, {
|
|
||||||
"kind": "dock",
|
|
||||||
"screenName": dock._dockScreenName,
|
|
||||||
"phase": phase,
|
|
||||||
"visible": presented,
|
|
||||||
"presented": presented,
|
|
||||||
"reveal": presented,
|
|
||||||
"barSide": dock.connectedBarSide,
|
|
||||||
"bodyRect": {
|
|
||||||
"x": bodyX,
|
|
||||||
"y": bodyY,
|
|
||||||
"width": bodyW,
|
|
||||||
"height": bodyH
|
|
||||||
},
|
|
||||||
"animationOffset": {
|
|
||||||
"x": dockSlide.x,
|
|
||||||
"y": dockSlide.y
|
|
||||||
},
|
|
||||||
"scale": 1,
|
|
||||||
"opacity": Theme.connectedSurfaceColor.a,
|
|
||||||
"bodyX": bodyX,
|
|
||||||
"bodyY": bodyY,
|
|
||||||
"bodyW": bodyW,
|
|
||||||
"bodyH": bodyH,
|
|
||||||
"slideX": dockSlide.x,
|
|
||||||
"slideY": dockSlide.y
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _syncDockSlide() {
|
|
||||||
if (!dock._dockScreenName || !dock.usesConnectedFrameChrome)
|
|
||||||
return;
|
|
||||||
ConnectedModeState.setDockSlide(dock._dockScreenName, dockSlide.x, dockSlide.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeferredAction {
|
|
||||||
id: dockSlideSync
|
|
||||||
enabled: dock.usesConnectedFrameChrome
|
|
||||||
onTriggered: dock._syncDockSlide()
|
|
||||||
}
|
|
||||||
|
|
||||||
function _queueSlideSync() {
|
|
||||||
if (!dock.usesConnectedFrameChrome)
|
|
||||||
return;
|
|
||||||
dockSlideSync.schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeferredAction {
|
|
||||||
id: dockChromeSync
|
|
||||||
onTriggered: dock._syncDockChromeState()
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData)
|
|
||||||
property bool revealSticky: false
|
|
||||||
|
|
||||||
readonly property bool shouldHideForWindows: {
|
|
||||||
if (!SettingsData.dockSmartAutoHide)
|
|
||||||
return false;
|
|
||||||
if (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isMango)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const screenName = dock.modelData?.name ?? "";
|
|
||||||
const dockThickness = dockGeometry.motionThickness;
|
|
||||||
const screenWidth = dock.screen?.width ?? 0;
|
|
||||||
const screenHeight = dock.screen?.height ?? 0;
|
|
||||||
|
|
||||||
if (CompositorService.isNiri) {
|
|
||||||
NiriService.windows;
|
|
||||||
|
|
||||||
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)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < NiriService.windows.length; i++) {
|
|
||||||
const win = NiriService.windows[i];
|
|
||||||
if (win.workspace_id !== currentWorkspaceId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get window position and size from layout data
|
|
||||||
const tilePos = win.layout?.tile_pos_in_workspace_view;
|
|
||||||
const winSize = win.layout?.window_size || win.layout?.tile_size;
|
|
||||||
|
|
||||||
if (tilePos && winSize) {
|
|
||||||
const winX = tilePos[0];
|
|
||||||
const winY = tilePos[1];
|
|
||||||
const winW = winSize[0];
|
|
||||||
const winH = winSize[1];
|
|
||||||
|
|
||||||
switch (SettingsData.dockPosition) {
|
|
||||||
case SettingsData.Position.Top:
|
|
||||||
if (winY < dockThickness)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case SettingsData.Position.Bottom:
|
|
||||||
if (winY + winH > screenHeight - dockThickness)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case SettingsData.Position.Left:
|
|
||||||
if (winX < dockThickness)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case SettingsData.Position.Right:
|
|
||||||
if (winX + winW > screenWidth - dockThickness)
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (!win.is_floating) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CompositorService.isMango) {
|
|
||||||
MangoService.windows;
|
|
||||||
MangoService.outputs;
|
|
||||||
return CompositorService.mangoDockOverlapForSmartAutoHide(screenName, SettingsData.dockPosition, dockThickness, screenWidth, screenHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hyprland implementation (current workspace + visible special workspaces)
|
|
||||||
Hyprland.focusedWorkspace;
|
|
||||||
Hyprland.toplevels;
|
|
||||||
return CompositorService.hyprlandDockOverlapForSmartAutoHide(screenName, SettingsData.dockPosition, dockThickness, screenWidth, screenHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: revealHold
|
|
||||||
interval: 250
|
|
||||||
repeat: false
|
|
||||||
onTriggered: dock.revealSticky = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flip `reveal` false when a modal claims this edge; reuses the slide animation
|
|
||||||
readonly property bool _modalRetractActive: {
|
|
||||||
if (!dock._dockScreenName)
|
|
||||||
return false;
|
|
||||||
return ConnectedModeState.dockRetractActiveForSide(dock._dockScreenName, dock.connectedBarSide);
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool startupRevealDone: false
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: startupRevealTimer
|
|
||||||
interval: 200
|
|
||||||
running: true
|
|
||||||
onTriggered: dock.startupRevealDone = true
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool reveal: {
|
|
||||||
if (!startupRevealDone)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_modalRetractActive)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (CompositorService.isNiri && NiriService.inOverview && SettingsData.dockOpenOnOverview) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smart auto-hide: show dock when no windows overlap, hide when they do
|
|
||||||
if (SettingsData.dockSmartAutoHide) {
|
|
||||||
if (shouldHideForWindows)
|
|
||||||
return dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky;
|
|
||||||
return true; // No overlapping windows - show dock
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular auto-hide: always hide unless hovering
|
|
||||||
return !autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky;
|
|
||||||
}
|
|
||||||
|
|
||||||
onContextMenuOpenChanged: {
|
|
||||||
if (!contextMenuOpen && autoHide && !dockMouseArea.containsMouse) {
|
|
||||||
revealSticky = true;
|
|
||||||
revealHold.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: dockChromeSync.schedule()
|
|
||||||
Component.onDestruction: {
|
|
||||||
dockChromeSync.cancel();
|
|
||||||
dockSlideSync.cancel();
|
|
||||||
ConnectedModeState.clearDockState(dock._dockScreenName);
|
|
||||||
}
|
|
||||||
|
|
||||||
onRevealChanged: dock._syncDockChromeState()
|
|
||||||
onWidthChanged: dock._syncDockChromeState()
|
|
||||||
onHeightChanged: dock._syncDockChromeState()
|
|
||||||
onVisibleChanged: dock._syncDockChromeState()
|
|
||||||
onHasAppsChanged: dock._syncDockChromeState()
|
|
||||||
onConnectedBarSideChanged: dock._syncDockChromeState()
|
|
||||||
onUsesConnectedFrameChromeChanged: dock._syncDockChromeState()
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: SettingsData
|
|
||||||
function onConnectedFrameModeActiveChanged() {
|
|
||||||
dockSlideSync.cancel();
|
|
||||||
dock._syncDockChromeState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: SettingsData
|
|
||||||
function onDockTransparencyChanged() {
|
|
||||||
dock.backgroundTransparency = SettingsData.dockTransparency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
screen: modelData
|
screen: modelData
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:dock"
|
||||||
|
WlrLayershell.layer: body.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: !body.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top) : true
|
||||||
|
bottom: !body.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom) : true
|
||||||
|
left: !body.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Left)
|
||||||
|
right: !body.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
|
}
|
||||||
|
|
||||||
visible: {
|
visible: {
|
||||||
if (CompositorService.isNiri && NiriService.inOverview) {
|
if (CompositorService.isNiri && NiriService.inOverview) {
|
||||||
return SettingsData.dockOpenOnOverview;
|
return SettingsData.dockOpenOnOverview;
|
||||||
}
|
}
|
||||||
return SettingsData.showDock;
|
return SettingsData.showDock;
|
||||||
}
|
}
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
readonly property real dockReserveZone: dockGeometry.reserveZone
|
implicitWidth: body.surfaceImplicitWidth
|
||||||
readonly property bool shouldReserveDockSpace: dockGeometry.shouldReserveSpace
|
implicitHeight: body.surfaceImplicitHeight
|
||||||
|
exclusiveZone: body.surfaceExclusiveZone
|
||||||
|
|
||||||
exclusiveZone: {
|
WindowBlur {
|
||||||
if (!dock.shouldReserveDockSpace)
|
targetWindow: dock
|
||||||
return -1;
|
blurEnabled: body.effectiveBlurEnabled && !body.usesConnectedFrameChrome
|
||||||
if (dock.frameDockExclusionActive)
|
blurX: body.blurX
|
||||||
return -1;
|
blurY: body.blurY
|
||||||
return dock.dockReserveZone;
|
blurWidth: body.blurWidth
|
||||||
}
|
blurHeight: body.blurHeight
|
||||||
|
blurRadius: body.blurRadius
|
||||||
property real animationHeadroom: Math.ceil(SettingsData.dockIconSize * 0.35)
|
|
||||||
|
|
||||||
implicitWidth: isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
|
||||||
implicitHeight: !isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: maskItem
|
|
||||||
parent: dock.contentItem
|
|
||||||
visible: false
|
|
||||||
readonly property bool expanded: dock.reveal
|
|
||||||
x: {
|
|
||||||
const baseX = dockCore.x + dockMouseArea.x;
|
|
||||||
if (isVertical && SettingsData.dockPosition === SettingsData.Position.Right)
|
|
||||||
return baseX - (expanded ? animationHeadroom + borderThickness + dock.horizontalConnectorExtent : 0);
|
|
||||||
return baseX - (expanded ? borderThickness + dock.horizontalConnectorExtent : 0);
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
const baseY = dockCore.y + dockMouseArea.y;
|
|
||||||
if (!isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom)
|
|
||||||
return baseY - (expanded ? animationHeadroom + borderThickness + dock.verticalConnectorExtent : 0);
|
|
||||||
return baseY - (expanded ? borderThickness + dock.verticalConnectorExtent : 0);
|
|
||||||
}
|
|
||||||
width: dockMouseArea.width + (isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.horizontalConnectorExtent * 2 : 0)
|
|
||||||
height: dockMouseArea.height + (!isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.verticalConnectorExtent * 2 : 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: maskItem
|
item: body.inputMaskItem
|
||||||
|
}
|
||||||
|
|
||||||
|
DockBody {
|
||||||
|
id: body
|
||||||
|
anchors.fill: parent
|
||||||
|
hostWindow: dock
|
||||||
|
modelData: dock.modelData
|
||||||
|
contextMenu: dockVariants.contextMenu
|
||||||
|
trashContextMenu: dockVariants.trashContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: dockExclusion
|
id: dockExclusion
|
||||||
|
|
||||||
screen: dock.screen || dock.modelData
|
screen: dock.screen || dock.modelData
|
||||||
visible: dock.frameDockExclusionActive && dock.shouldReserveDockSpace
|
visible: body.frameDockExclusionActive && body.shouldReserveDockSpace
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
mask: Region {}
|
mask: Region {}
|
||||||
implicitWidth: dock.isVertical ? dock.dockReserveZone : 1
|
implicitWidth: body.isVertical ? body.dockReserveZone : 1
|
||||||
implicitHeight: dock.isVertical ? 1 : dock.dockReserveZone
|
implicitHeight: body.isVertical ? 1 : body.dockReserveZone
|
||||||
exclusiveZone: visible ? dock.dockReserveZone : -1
|
exclusiveZone: visible ? body.dockReserveZone : -1
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:dock-exclusion"
|
WlrLayershell.namespace: "dms:dock-exclusion"
|
||||||
WlrLayershell.layer: WlrLayer.Top
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top) : true
|
top: !body.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top) : true
|
||||||
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom) : true
|
bottom: !body.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom) : true
|
||||||
left: !dock.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Left)
|
left: !body.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Left)
|
||||||
right: !dock.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Right)
|
right: !body.isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property var hoveredButton: {
|
|
||||||
if (!dockApps.children[0]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const layoutItem = dockApps.children[0];
|
|
||||||
const flowLayout = layoutItem.children[0];
|
|
||||||
let repeater = null;
|
|
||||||
for (var i = 0; i < flowLayout.children.length; i++) {
|
|
||||||
const child = flowLayout.children[i];
|
|
||||||
if (child && typeof child.count !== "undefined" && typeof child.itemAt === "function") {
|
|
||||||
repeater = child;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!repeater || !repeater.itemAt) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < repeater.count; i++) {
|
|
||||||
const item = repeater.itemAt(i);
|
|
||||||
if (item && item.dockButton && item.dockButton.showTooltip) {
|
|
||||||
return item.dockButton;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DankTooltip {
|
|
||||||
id: dockTooltip
|
|
||||||
targetScreen: dock.screen
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: tooltipRevealDelay
|
|
||||||
interval: 250
|
|
||||||
repeat: false
|
|
||||||
onTriggered: dock.showTooltipForHoveredButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
function showTooltipForHoveredButton() {
|
|
||||||
dockTooltip.hide();
|
|
||||||
if (!dock.hoveredButton || !dock.reveal || slideXAnimation.running || slideYAnimation.running)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const buttonLocalPos = dock.hoveredButton.mapToItem(null, 0, 0);
|
|
||||||
const tooltipText = dock.hoveredButton.tooltipText || "";
|
|
||||||
if (!tooltipText)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const screenHeight = dock.screen ? dock.screen.height : 0;
|
|
||||||
|
|
||||||
const gap = Theme.spacingS;
|
|
||||||
const bgMargin = dockGeometry.bodyEdgeMargin;
|
|
||||||
const btnW = dock.hoveredButton.width;
|
|
||||||
const btnH = dock.hoveredButton.height;
|
|
||||||
|
|
||||||
if (!dock.isVertical) {
|
|
||||||
const isBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
|
||||||
const tooltipX = buttonLocalPos.x + btnW / 2 + adjacentLeftBarWidth;
|
|
||||||
const tooltipHeight = 32;
|
|
||||||
const totalFromEdge = bgMargin + dockBackground.height + dock.borderThickness + gap;
|
|
||||||
const screenRelativeY = isBottom ? (screenHeight - totalFromEdge - tooltipHeight) : totalFromEdge;
|
|
||||||
dockTooltip.show(tooltipText, tooltipX, screenRelativeY, dock.screen, false, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLeft = SettingsData.dockPosition === SettingsData.Position.Left;
|
|
||||||
const screenWidth = dock.screen ? dock.screen.width : 0;
|
|
||||||
const totalFromEdge = bgMargin + dockBackground.width + dock.borderThickness + gap;
|
|
||||||
const tooltipX = isLeft ? totalFromEdge : (screenWidth - totalFromEdge);
|
|
||||||
const screenRelativeY = buttonLocalPos.y + btnH / 2 + adjacentTopBarHeight;
|
|
||||||
dockTooltip.show(tooltipText, tooltipX, screenRelativeY, dock.screen, isLeft, !isLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: dock
|
|
||||||
function onRevealChanged() {
|
|
||||||
if (!dock.reveal) {
|
|
||||||
tooltipRevealDelay.stop();
|
|
||||||
dockTooltip.hide();
|
|
||||||
} else {
|
|
||||||
tooltipRevealDelay.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onHoveredButtonChanged() {
|
|
||||||
dock.showTooltipForHoveredButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: dockCore
|
|
||||||
anchors.fill: parent
|
|
||||||
x: isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? animationHeadroom : 0
|
|
||||||
y: !isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? animationHeadroom : 0
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: dockMouseArea
|
|
||||||
function onContainsMouseChanged() {
|
|
||||||
if (dockMouseArea.containsMouse) {
|
|
||||||
dock.revealSticky = true;
|
|
||||||
revealHold.stop();
|
|
||||||
} else {
|
|
||||||
if (dock.autoHide && !dock.contextMenuOpen) {
|
|
||||||
revealHold.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: dockMouseArea
|
|
||||||
property real currentScreen: modelData ? modelData : dock.screen
|
|
||||||
property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920
|
|
||||||
property real screenHeight: currentScreen ? currentScreen.geometry.height : 1080
|
|
||||||
property real maxDockWidth: screenWidth * 0.98
|
|
||||||
property real maxDockHeight: screenHeight * 0.98
|
|
||||||
|
|
||||||
height: {
|
|
||||||
if (dock.isVertical) {
|
|
||||||
// Keep the taller hit area regardless of the reveal state to prevent shrinking loop
|
|
||||||
return Math.min(Math.max(dockBackground.height + 64, 200), maxDockHeight);
|
|
||||||
}
|
|
||||||
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
|
|
||||||
}
|
|
||||||
width: {
|
|
||||||
if (dock.isVertical) {
|
|
||||||
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
|
|
||||||
}
|
|
||||||
// Keep the wider hit area regardless of the reveal state to prevent shrinking loop
|
|
||||||
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
|
||||||
}
|
|
||||||
anchors {
|
|
||||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined
|
|
||||||
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
|
|
||||||
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
|
|
||||||
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.left) : undefined
|
|
||||||
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
|
||||||
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
|
||||||
}
|
|
||||||
hoverEnabled: true
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: dockContainer
|
|
||||||
anchors.fill: parent
|
|
||||||
clip: false
|
|
||||||
opacity: dock.startupRevealDone ? 1 : 0
|
|
||||||
|
|
||||||
transform: Translate {
|
|
||||||
id: dockSlide
|
|
||||||
x: {
|
|
||||||
if (!dock.isVertical)
|
|
||||||
return 0;
|
|
||||||
if (dock.reveal)
|
|
||||||
return 0;
|
|
||||||
if (dock.usesConnectedFrameChrome) {
|
|
||||||
const retractDist = dockBackground.width + SettingsData.dockSpacing + 10;
|
|
||||||
return SettingsData.dockPosition === SettingsData.Position.Right ? retractDist : -retractDist;
|
|
||||||
}
|
|
||||||
const hideDistance = dockGeometry.motionThickness + 10;
|
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
|
||||||
return hideDistance;
|
|
||||||
} else {
|
|
||||||
return -hideDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
if (dock.isVertical)
|
|
||||||
return 0;
|
|
||||||
if (dock.reveal)
|
|
||||||
return 0;
|
|
||||||
if (dock.usesConnectedFrameChrome) {
|
|
||||||
const retractDist = dockBackground.height + SettingsData.dockSpacing + 10;
|
|
||||||
return SettingsData.dockPosition === SettingsData.Position.Bottom ? retractDist : -retractDist;
|
|
||||||
}
|
|
||||||
const hideDistance = dockGeometry.motionThickness + 10;
|
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
|
|
||||||
return hideDistance;
|
|
||||||
} else {
|
|
||||||
return -hideDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
NumberAnimation {
|
|
||||||
id: slideXAnimation
|
|
||||||
duration: dock.usesConnectedFrameChrome ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
|
||||||
easing.type: dock.usesConnectedFrameChrome ? Easing.BezierSpline : Easing.OutCubic
|
|
||||||
easing.bezierCurve: dock.usesConnectedFrameChrome ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
|
||||||
onRunningChanged: if (!running)
|
|
||||||
dock._syncDockChromeState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on y {
|
|
||||||
NumberAnimation {
|
|
||||||
id: slideYAnimation
|
|
||||||
duration: dock.usesConnectedFrameChrome ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
|
||||||
easing.type: dock.usesConnectedFrameChrome ? Easing.BezierSpline : Easing.OutCubic
|
|
||||||
easing.bezierCurve: dock.usesConnectedFrameChrome ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
|
||||||
onRunningChanged: if (!running)
|
|
||||||
dock._syncDockChromeState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onXChanged: dock._queueSlideSync()
|
|
||||||
onYChanged: dock._queueSlideSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: dockBackground
|
|
||||||
objectName: "dockBackground"
|
|
||||||
visible: dock.hasApps
|
|
||||||
anchors {
|
|
||||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined) : undefined
|
|
||||||
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
|
|
||||||
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
|
|
||||||
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined) : undefined
|
|
||||||
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
|
||||||
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
|
||||||
}
|
|
||||||
anchors.topMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Top ? dockGeometry.bodyEdgeMargin : 0
|
|
||||||
anchors.bottomMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? dockGeometry.bodyEdgeMargin : 0
|
|
||||||
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? dockGeometry.bodyEdgeMargin : 0
|
|
||||||
anchors.rightMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? dockGeometry.bodyEdgeMargin : 0
|
|
||||||
|
|
||||||
implicitWidth: dock.isVertical ? (dockApps.implicitHeight + SettingsData.dockSpacing * 2) : (dockApps.implicitWidth + SettingsData.dockSpacing * 2)
|
|
||||||
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2)
|
|
||||||
width: implicitWidth
|
|
||||||
height: implicitHeight
|
|
||||||
|
|
||||||
// Avoid an offscreen texture seam where the connected dock meets the frame.
|
|
||||||
layer.enabled: !usesConnectedFrameChrome
|
|
||||||
clip: false
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: !usesConnectedFrameChrome && (!FrameTransitionState.effectiveConnectedFrameModeActive || dock.reveal)
|
|
||||||
color: dock.surfaceColor
|
|
||||||
topLeftRadius: dock.surfaceTopLeftRadius
|
|
||||||
topRightRadius: dock.surfaceTopRightRadius
|
|
||||||
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
|
||||||
bottomRightRadius: dock.surfaceBottomRightRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: !usesConnectedFrameChrome && (!FrameTransitionState.effectiveConnectedFrameModeActive || dock.reveal)
|
|
||||||
color: "transparent"
|
|
||||||
topLeftRadius: dock.surfaceTopLeftRadius
|
|
||||||
topRightRadius: dock.surfaceTopRightRadius
|
|
||||||
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
|
||||||
bottomRightRadius: dock.surfaceBottomRightRadius
|
|
||||||
border.color: dock.surfaceBorderColor
|
|
||||||
border.width: dock.surfaceBorderWidth
|
|
||||||
z: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync dockBackground geometry to ConnectedModeState
|
|
||||||
onXChanged: dock._syncDockChromeState()
|
|
||||||
onYChanged: dock._syncDockChromeState()
|
|
||||||
onWidthChanged: dock._syncDockChromeState()
|
|
||||||
onHeightChanged: dock._syncDockChromeState()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: dockConnectedChrome
|
|
||||||
visible: Theme.isConnectedEffect && dock.reveal && dock.hasApps && !FrameTransitionState.effectiveConnectedFrameModeActive
|
|
||||||
readonly property real extraLeft: dock.isVertical ? 0 : Theme.connectedCornerRadius
|
|
||||||
readonly property real extraTop: dock.isVertical ? Theme.connectedCornerRadius : 0
|
|
||||||
readonly property real bodyRadius: dock.surfaceRadius
|
|
||||||
readonly property bool barTop: dock.connectedBarSide === "top"
|
|
||||||
readonly property bool barBottom: dock.connectedBarSide === "bottom"
|
|
||||||
readonly property bool barLeft: dock.connectedBarSide === "left"
|
|
||||||
readonly property bool barRight: dock.connectedBarSide === "right"
|
|
||||||
|
|
||||||
x: dockBackground.x - extraLeft
|
|
||||||
y: dockBackground.y - extraTop
|
|
||||||
width: dockBackground.width + extraLeft * 2
|
|
||||||
height: dockBackground.height + extraTop * 2
|
|
||||||
|
|
||||||
ShaderEffect {
|
|
||||||
anchors.fill: parent
|
|
||||||
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/connected_chrome.frag.qsb")
|
|
||||||
|
|
||||||
property real widthPx: width
|
|
||||||
property real heightPx: height
|
|
||||||
property vector4d surfaceColor: Qt.vector4d(dock.surfaceColor.r, dock.surfaceColor.g, dock.surfaceColor.b, dock.surfaceColor.a)
|
|
||||||
property vector4d shadowColor: Qt.vector4d(0, 0, 0, 0)
|
|
||||||
property vector4d shadowParam: Qt.vector4d(0, 0, 0, 0)
|
|
||||||
property vector4d ambientParam: Qt.vector4d(0, 0, 0, 0)
|
|
||||||
property vector4d bodyRect: Qt.vector4d(dockConnectedChrome.extraLeft, dockConnectedChrome.extraTop, dockBackground.width, dockBackground.height)
|
|
||||||
property vector4d cornerRadius: Qt.vector4d(dockConnectedChrome.barTop || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barTop || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius)
|
|
||||||
property vector4d edgeParam: Qt.vector4d(dockConnectedChrome.barTop ? 0 : (dockConnectedChrome.barBottom ? 1 : (dockConnectedChrome.barLeft ? 2 : 3)), Theme.connectedCornerRadius, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape {
|
|
||||||
id: dockBorderShape
|
|
||||||
x: dockBackground.x - borderThickness
|
|
||||||
y: dockBackground.y - borderThickness
|
|
||||||
width: dockBackground.width + borderThickness * 2
|
|
||||||
height: dockBackground.height + borderThickness * 2
|
|
||||||
visible: SettingsData.dockBorderEnabled && dock.hasApps && !usesConnectedFrameChrome
|
|
||||||
preferredRendererType: Shape.CurveRenderer
|
|
||||||
|
|
||||||
readonly property real borderThickness: Math.max(1, dock.borderThickness)
|
|
||||||
readonly property real i: borderThickness / 2
|
|
||||||
readonly property real cr: dock.surfaceRadius
|
|
||||||
readonly property real w: dockBackground.width
|
|
||||||
readonly property real h: dockBackground.height
|
|
||||||
|
|
||||||
readonly property color borderColor: {
|
|
||||||
const opacity = SettingsData.dockBorderOpacity;
|
|
||||||
switch (SettingsData.dockBorderColor) {
|
|
||||||
case "secondary":
|
|
||||||
return Theme.withAlpha(Theme.secondary, opacity);
|
|
||||||
case "primary":
|
|
||||||
return Theme.withAlpha(Theme.primary, opacity);
|
|
||||||
default:
|
|
||||||
return Theme.withAlpha(Theme.surfaceText, opacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: "transparent"
|
|
||||||
strokeColor: dockBorderShape.borderColor
|
|
||||||
strokeWidth: dockBorderShape.borderThickness
|
|
||||||
joinStyle: ShapePath.RoundJoin
|
|
||||||
capStyle: ShapePath.FlatCap
|
|
||||||
|
|
||||||
PathSvg {
|
|
||||||
path: {
|
|
||||||
const bt = dockBorderShape.borderThickness;
|
|
||||||
const i = dockBorderShape.i;
|
|
||||||
const cr = dockBorderShape.cr + bt - i;
|
|
||||||
const w = dockBorderShape.w;
|
|
||||||
const h = dockBorderShape.h;
|
|
||||||
|
|
||||||
let d = `M ${i + cr} ${i}`;
|
|
||||||
d += ` L ${i + w + 2 * (bt - i) - cr} ${i}`;
|
|
||||||
if (cr > 0)
|
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${i + w + 2 * (bt - i)} ${i + cr}`;
|
|
||||||
d += ` L ${i + w + 2 * (bt - i)} ${i + h + 2 * (bt - i) - cr}`;
|
|
||||||
if (cr > 0)
|
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${i + w + 2 * (bt - i) - cr} ${i + h + 2 * (bt - i)}`;
|
|
||||||
d += ` L ${i + cr} ${i + h + 2 * (bt - i)}`;
|
|
||||||
if (cr > 0)
|
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${i} ${i + h + 2 * (bt - i) - cr}`;
|
|
||||||
d += ` L ${i} ${i + cr}`;
|
|
||||||
if (cr > 0)
|
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${i + cr} ${i}`;
|
|
||||||
d += " Z";
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DockApps {
|
|
||||||
id: dockApps
|
|
||||||
|
|
||||||
anchors.top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? dockBackground.top : undefined) : undefined
|
|
||||||
anchors.bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? dockBackground.bottom : undefined) : undefined
|
|
||||||
anchors.horizontalCenter: !dock.isVertical ? dockBackground.horizontalCenter : undefined
|
|
||||||
anchors.left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Left ? dockBackground.left : undefined) : undefined
|
|
||||||
anchors.right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? dockBackground.right : undefined) : undefined
|
|
||||||
anchors.verticalCenter: dock.isVertical ? dockBackground.verticalCenter : undefined
|
|
||||||
anchors.topMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
|
||||||
anchors.bottomMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
|
||||||
anchors.leftMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
|
||||||
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
|
||||||
|
|
||||||
contextMenu: dockVariants.contextMenu
|
|
||||||
trashContextMenu: dockVariants.trashContextMenu
|
|
||||||
groupByApp: dock.groupByApp
|
|
||||||
isVertical: dock.isVertical
|
|
||||||
dockScreen: dock.screen
|
|
||||||
iconSize: dock.widgetHeight
|
|
||||||
usesOverlayLayer: dock.usesOverlayLayer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,829 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dock
|
||||||
|
|
||||||
|
required property var hostWindow
|
||||||
|
required property var modelData
|
||||||
|
readonly property var screen: modelData
|
||||||
|
property var contextMenu
|
||||||
|
property var trashContextMenu
|
||||||
|
|
||||||
|
readonly property bool isVertical: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||||
|
|
||||||
|
property bool autoHide: SettingsData.dockAutoHide || SettingsData.dockSmartAutoHide
|
||||||
|
property real backgroundTransparency: SettingsData.dockTransparency
|
||||||
|
property bool groupByApp: SettingsData.dockGroupByApp
|
||||||
|
readonly property int borderThickness: SettingsData.dockBorderEnabled ? SettingsData.dockBorderThickness : 0
|
||||||
|
readonly property string connectedBarSide: SettingsData.dockPosition === SettingsData.Position.Top ? "top" : SettingsData.dockPosition === SettingsData.Position.Bottom ? "bottom" : SettingsData.dockPosition === SettingsData.Position.Left ? "left" : "right"
|
||||||
|
readonly property bool frameDockExclusionActive: dockGeometry.frameExclusionActive
|
||||||
|
readonly property bool connectedBarActiveOnEdge: dockGeometry.connectedBarActiveOnEdge
|
||||||
|
readonly property real connectedJoinInset: dockGeometry.connectedJoinInset
|
||||||
|
readonly property real dockFrameInset: dockGeometry.frameInset
|
||||||
|
readonly property real surfaceRadius: usesConnectedFrameChrome ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
|
readonly property color surfaceColor: usesConnectedFrameChrome ? Theme.connectedSurfaceColor : Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
|
||||||
|
readonly property color surfaceBorderColor: usesConnectedFrameChrome ? Theme.withAlpha(BlurService.borderColor, 0) : BlurService.borderColor
|
||||||
|
readonly property real surfaceBorderWidth: usesConnectedFrameChrome ? 0 : BlurService.borderWidth
|
||||||
|
readonly property real surfaceTopLeftRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceTopRightRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceBottomLeftRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceBottomRightRadius: usesConnectedFrameChrome && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
||||||
|
readonly property real horizontalConnectorExtent: usesConnectedFrameChrome && !isVertical ? Theme.connectedCornerRadius : 0
|
||||||
|
readonly property real verticalConnectorExtent: usesConnectedFrameChrome && isVertical ? Theme.connectedCornerRadius : 0
|
||||||
|
|
||||||
|
readonly property int hasApps: dockApps.implicitWidth > 0 || dockApps.implicitHeight > 0
|
||||||
|
|
||||||
|
readonly property real widgetHeight: SettingsData.dockIconSize
|
||||||
|
readonly property real effectiveBarHeight: dockGeometry.visualThickness
|
||||||
|
function getBarHeight(barConfig) {
|
||||||
|
if (!barConfig)
|
||||||
|
return 0;
|
||||||
|
const innerPadding = barConfig.innerPadding ?? 4;
|
||||||
|
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
|
||||||
|
const barThickness = Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
|
||||||
|
const spacing = barConfig.spacing ?? 4;
|
||||||
|
const bottomGap = barConfig.bottomGap ?? 0;
|
||||||
|
return barThickness + spacing + bottomGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real barSpacing: {
|
||||||
|
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||||
|
if (!defaultBar)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const barPos = defaultBar.position ?? SettingsData.Position.Top;
|
||||||
|
const barIsHorizontal = (barPos === SettingsData.Position.Top || barPos === SettingsData.Position.Bottom);
|
||||||
|
const barIsVertical = (barPos === SettingsData.Position.Left || barPos === SettingsData.Position.Right);
|
||||||
|
const samePosition = (SettingsData.dockPosition === barPos);
|
||||||
|
const dockIsHorizontal = !isVertical;
|
||||||
|
const dockIsVertical = isVertical;
|
||||||
|
|
||||||
|
if (!(defaultBar.visible ?? true))
|
||||||
|
return 0;
|
||||||
|
const spacing = defaultBar.spacing ?? 4;
|
||||||
|
const bottomGap = defaultBar.bottomGap ?? 0;
|
||||||
|
if (dockIsHorizontal && barIsHorizontal && samePosition) {
|
||||||
|
return spacing + effectiveBarHeight + bottomGap;
|
||||||
|
}
|
||||||
|
if (dockIsVertical && barIsVertical && samePosition) {
|
||||||
|
return spacing + effectiveBarHeight + bottomGap;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real adjacentTopBarHeight: {
|
||||||
|
if (!isVertical || autoHide)
|
||||||
|
return 0;
|
||||||
|
const screenName = dock.modelData?.name ?? "";
|
||||||
|
const topBar = SettingsData.barConfigs.find(bc => {
|
||||||
|
if (!bc.enabled || bc.autoHide || !(bc.visible ?? true))
|
||||||
|
return false;
|
||||||
|
if (bc.position !== SettingsData.Position.Top && bc.position !== 0)
|
||||||
|
return false;
|
||||||
|
const onThisScreen = bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all") || bc.screenPreferences.includes(screenName);
|
||||||
|
return onThisScreen;
|
||||||
|
});
|
||||||
|
return getBarHeight(topBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real adjacentLeftBarWidth: {
|
||||||
|
if (isVertical || autoHide)
|
||||||
|
return 0;
|
||||||
|
const screenName = dock.modelData?.name ?? "";
|
||||||
|
const leftBar = SettingsData.barConfigs.find(bc => {
|
||||||
|
if (!bc.enabled || bc.autoHide || !(bc.visible ?? true))
|
||||||
|
return false;
|
||||||
|
if (bc.position !== SettingsData.Position.Left && bc.position !== 2)
|
||||||
|
return false;
|
||||||
|
const onThisScreen = bc.screenPreferences.length === 0 || bc.screenPreferences.includes("all") || bc.screenPreferences.includes(screenName);
|
||||||
|
return onThisScreen;
|
||||||
|
});
|
||||||
|
return getBarHeight(leftBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real dockMargin: SettingsData.dockMargin
|
||||||
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
|
readonly property real effectiveDockBottomGap: dockGeometry.visualOffset
|
||||||
|
readonly property real effectiveDockMargin: dockGeometry.effectiveMargin
|
||||||
|
readonly property real positionSpacing: barSpacing + effectiveDockBottomGap + effectiveDockMargin
|
||||||
|
readonly property real joinedEdgeMargin: dockGeometry.joinedEdgeMargin
|
||||||
|
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
|
||||||
|
function px(v) {
|
||||||
|
return Math.round(v * _dpr) / _dpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockGeometry {
|
||||||
|
id: dockGeometry
|
||||||
|
|
||||||
|
screen: dock.screen || dock.modelData
|
||||||
|
edge: dock.connectedBarSide
|
||||||
|
dockVisible: dock.visible
|
||||||
|
autoHide: dock.autoHide
|
||||||
|
iconSize: dock.widgetHeight
|
||||||
|
spacing: SettingsData.dockSpacing
|
||||||
|
borderThickness: dock.borderThickness
|
||||||
|
offset: SettingsData.dockBottomGap
|
||||||
|
margin: SettingsData.dockMargin
|
||||||
|
barSpacing: dock.barSpacing
|
||||||
|
dpr: dock._dpr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dock window origin in screen-relative coordinates (FrameWindow space).
|
||||||
|
function _dockWindowOriginX() {
|
||||||
|
if (!dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
|
return (dock.screen ? dock.screen.width : 0) - dock.width;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
function _dockWindowOriginY() {
|
||||||
|
if (dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
|
return (dock.screen ? dock.screen.height : 0) - dock.height;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string _dockScreenName: dock.modelData ? dock.modelData.name : (dock.screen ? dock.screen.name : "")
|
||||||
|
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(dock._dockScreenName)
|
||||||
|
readonly property bool usesOverlayLayer: CompositorService.framePeerSurfacesUseOverlayForScreen(dock._dockScreenName) || SettingsData.dockUseOverlayLayer
|
||||||
|
|
||||||
|
function _syncDockChromeState() {
|
||||||
|
if (!dock._dockScreenName)
|
||||||
|
return;
|
||||||
|
if (!dock.usesConnectedFrameChrome) {
|
||||||
|
ConnectedModeState.clearDockState(dock._dockScreenName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const presented = dock.visible && (dock.reveal || slideXAnimation.running || slideYAnimation.running) && dock.hasApps;
|
||||||
|
const phase = !presented ? "hidden" : ((!dock.reveal && (slideXAnimation.running || slideYAnimation.running)) ? "closing" : ((slideXAnimation.running || slideYAnimation.running) ? "opening" : "open"));
|
||||||
|
const bodyX = dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x;
|
||||||
|
const bodyY = dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y;
|
||||||
|
const bodyW = dock.hasApps ? dockBackground.width : 0;
|
||||||
|
const bodyH = dock.hasApps ? dockBackground.height : 0;
|
||||||
|
ConnectedModeState.setDockState(dock._dockScreenName, {
|
||||||
|
"kind": "dock",
|
||||||
|
"screenName": dock._dockScreenName,
|
||||||
|
"phase": phase,
|
||||||
|
"visible": presented,
|
||||||
|
"presented": presented,
|
||||||
|
"reveal": presented,
|
||||||
|
"barSide": dock.connectedBarSide,
|
||||||
|
"bodyRect": {
|
||||||
|
"x": bodyX,
|
||||||
|
"y": bodyY,
|
||||||
|
"width": bodyW,
|
||||||
|
"height": bodyH
|
||||||
|
},
|
||||||
|
"animationOffset": {
|
||||||
|
"x": dockSlide.x,
|
||||||
|
"y": dockSlide.y
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"opacity": Theme.connectedSurfaceColor.a,
|
||||||
|
"bodyX": bodyX,
|
||||||
|
"bodyY": bodyY,
|
||||||
|
"bodyW": bodyW,
|
||||||
|
"bodyH": bodyH,
|
||||||
|
"slideX": dockSlide.x,
|
||||||
|
"slideY": dockSlide.y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _syncDockSlide() {
|
||||||
|
if (!dock._dockScreenName || !dock.usesConnectedFrameChrome)
|
||||||
|
return;
|
||||||
|
ConnectedModeState.setDockSlide(dock._dockScreenName, dockSlide.x, dockSlide.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredAction {
|
||||||
|
id: dockSlideSync
|
||||||
|
enabled: dock.usesConnectedFrameChrome
|
||||||
|
onTriggered: dock._syncDockSlide()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _queueSlideSync() {
|
||||||
|
if (!dock.usesConnectedFrameChrome)
|
||||||
|
return;
|
||||||
|
dockSlideSync.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredAction {
|
||||||
|
id: dockChromeSync
|
||||||
|
onTriggered: dock._syncDockChromeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool contextMenuOpen: (dock.contextMenu && dock.contextMenu.visible && dock.contextMenu.screen === modelData)
|
||||||
|
property bool revealSticky: false
|
||||||
|
|
||||||
|
readonly property bool shouldHideForWindows: {
|
||||||
|
if (!SettingsData.dockSmartAutoHide)
|
||||||
|
return false;
|
||||||
|
if (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isMango)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const screenName = dock.modelData?.name ?? "";
|
||||||
|
const dockThickness = dockGeometry.motionThickness;
|
||||||
|
const screenWidth = dock.screen?.width ?? 0;
|
||||||
|
const screenHeight = dock.screen?.height ?? 0;
|
||||||
|
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
NiriService.windows;
|
||||||
|
|
||||||
|
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)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (let i = 0; i < NiriService.windows.length; i++) {
|
||||||
|
const win = NiriService.windows[i];
|
||||||
|
if (win.workspace_id !== currentWorkspaceId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get window position and size from layout data
|
||||||
|
const tilePos = win.layout?.tile_pos_in_workspace_view;
|
||||||
|
const winSize = win.layout?.window_size || win.layout?.tile_size;
|
||||||
|
|
||||||
|
if (tilePos && winSize) {
|
||||||
|
const winX = tilePos[0];
|
||||||
|
const winY = tilePos[1];
|
||||||
|
const winW = winSize[0];
|
||||||
|
const winH = winSize[1];
|
||||||
|
|
||||||
|
switch (SettingsData.dockPosition) {
|
||||||
|
case SettingsData.Position.Top:
|
||||||
|
if (winY < dockThickness)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Bottom:
|
||||||
|
if (winY + winH > screenHeight - dockThickness)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Left:
|
||||||
|
if (winX < dockThickness)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Right:
|
||||||
|
if (winX + winW > screenWidth - dockThickness)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (!win.is_floating) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CompositorService.isMango) {
|
||||||
|
MangoService.windows;
|
||||||
|
MangoService.outputs;
|
||||||
|
return CompositorService.mangoDockOverlapForSmartAutoHide(screenName, SettingsData.dockPosition, dockThickness, screenWidth, screenHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hyprland implementation (current workspace + visible special workspaces)
|
||||||
|
Hyprland.focusedWorkspace;
|
||||||
|
Hyprland.toplevels;
|
||||||
|
return CompositorService.hyprlandDockOverlapForSmartAutoHide(screenName, SettingsData.dockPosition, dockThickness, screenWidth, screenHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: revealHold
|
||||||
|
interval: 250
|
||||||
|
repeat: false
|
||||||
|
onTriggered: dock.revealSticky = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip `reveal` false when a modal claims this edge; reuses the slide animation
|
||||||
|
readonly property bool _modalRetractActive: {
|
||||||
|
if (!dock._dockScreenName)
|
||||||
|
return false;
|
||||||
|
return ConnectedModeState.dockRetractActiveForSide(dock._dockScreenName, dock.connectedBarSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool startupRevealDone: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: startupRevealTimer
|
||||||
|
interval: 200
|
||||||
|
running: true
|
||||||
|
onTriggered: dock.startupRevealDone = true
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool reveal: {
|
||||||
|
if (!startupRevealDone)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_modalRetractActive)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CompositorService.isNiri && NiriService.inOverview && SettingsData.dockOpenOnOverview) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smart auto-hide: show dock when no windows overlap, hide when they do
|
||||||
|
if (SettingsData.dockSmartAutoHide) {
|
||||||
|
if (shouldHideForWindows)
|
||||||
|
return dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky;
|
||||||
|
return true; // No overlapping windows - show dock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular auto-hide: always hide unless hovering
|
||||||
|
return !autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky;
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenuOpenChanged: {
|
||||||
|
if (!contextMenuOpen && autoHide && !dockMouseArea.containsMouse) {
|
||||||
|
revealSticky = true;
|
||||||
|
revealHold.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: dockChromeSync.schedule()
|
||||||
|
Component.onDestruction: {
|
||||||
|
dockChromeSync.cancel();
|
||||||
|
dockSlideSync.cancel();
|
||||||
|
ConnectedModeState.clearDockState(dock._dockScreenName);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRevealChanged: dock._syncDockChromeState()
|
||||||
|
onWidthChanged: dock._syncDockChromeState()
|
||||||
|
onHeightChanged: dock._syncDockChromeState()
|
||||||
|
onVisibleChanged: dock._syncDockChromeState()
|
||||||
|
onHasAppsChanged: dock._syncDockChromeState()
|
||||||
|
onConnectedBarSideChanged: dock._syncDockChromeState()
|
||||||
|
onUsesConnectedFrameChromeChanged: dock._syncDockChromeState()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onConnectedFrameModeActiveChanged() {
|
||||||
|
dockSlideSync.cancel();
|
||||||
|
dock._syncDockChromeState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onDockTransparencyChanged() {
|
||||||
|
dock.backgroundTransparency = SettingsData.dockTransparency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real dockReserveZone: dockGeometry.reserveZone
|
||||||
|
readonly property bool shouldReserveDockSpace: dockGeometry.shouldReserveSpace
|
||||||
|
|
||||||
|
readonly property real surfaceExclusiveZone: {
|
||||||
|
if (!dock.shouldReserveDockSpace)
|
||||||
|
return -1;
|
||||||
|
if (dock.frameDockExclusionActive)
|
||||||
|
return -1;
|
||||||
|
return dock.dockReserveZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
property real animationHeadroom: Math.ceil(SettingsData.dockIconSize * 0.35)
|
||||||
|
|
||||||
|
readonly property real surfaceImplicitWidth: isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
||||||
|
readonly property real surfaceImplicitHeight: !isVertical ? (px(dockGeometry.surfaceThickness + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
||||||
|
|
||||||
|
readonly property real blurX: dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x + dockSlide.x
|
||||||
|
readonly property real blurY: dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y + dockSlide.y
|
||||||
|
readonly property real blurWidth: dock.hasApps && dock.reveal ? dockBackground.width : 0
|
||||||
|
readonly property real blurHeight: dock.hasApps && dock.reveal ? dockBackground.height : 0
|
||||||
|
readonly property real blurRadius: dock.usesConnectedFrameChrome ? Theme.connectedCornerRadius : dock.surfaceRadius
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: maskItem
|
||||||
|
visible: false
|
||||||
|
readonly property bool expanded: dock.reveal
|
||||||
|
x: {
|
||||||
|
const baseX = dockCore.x + dockMouseArea.x;
|
||||||
|
if (isVertical && SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
|
return baseX - (expanded ? animationHeadroom + borderThickness + dock.horizontalConnectorExtent : 0);
|
||||||
|
return baseX - (expanded ? borderThickness + dock.horizontalConnectorExtent : 0);
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
const baseY = dockCore.y + dockMouseArea.y;
|
||||||
|
if (!isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
|
return baseY - (expanded ? animationHeadroom + borderThickness + dock.verticalConnectorExtent : 0);
|
||||||
|
return baseY - (expanded ? borderThickness + dock.verticalConnectorExtent : 0);
|
||||||
|
}
|
||||||
|
width: dockMouseArea.width + (isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.horizontalConnectorExtent * 2 : 0)
|
||||||
|
height: dockMouseArea.height + (!isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.verticalConnectorExtent * 2 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property alias inputMaskItem: maskItem
|
||||||
|
|
||||||
|
property var hoveredButton: {
|
||||||
|
if (!dockApps.children[0]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const layoutItem = dockApps.children[0];
|
||||||
|
const flowLayout = layoutItem.children[0];
|
||||||
|
let repeater = null;
|
||||||
|
for (var i = 0; i < flowLayout.children.length; i++) {
|
||||||
|
const child = flowLayout.children[i];
|
||||||
|
if (child && typeof child.count !== "undefined" && typeof child.itemAt === "function") {
|
||||||
|
repeater = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!repeater || !repeater.itemAt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < repeater.count; i++) {
|
||||||
|
const item = repeater.itemAt(i);
|
||||||
|
if (item && item.dockButton && item.dockButton.showTooltip) {
|
||||||
|
return item.dockButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTooltip {
|
||||||
|
id: dockTooltip
|
||||||
|
targetScreen: dock.screen
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: tooltipRevealDelay
|
||||||
|
interval: 250
|
||||||
|
repeat: false
|
||||||
|
onTriggered: dock.showTooltipForHoveredButton()
|
||||||
|
}
|
||||||
|
|
||||||
|
function showTooltipForHoveredButton() {
|
||||||
|
dockTooltip.hide();
|
||||||
|
if (!dock.hoveredButton || !dock.reveal || slideXAnimation.running || slideYAnimation.running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const buttonLocalPos = dock.hoveredButton.mapToItem(null, 0, 0);
|
||||||
|
const tooltipText = dock.hoveredButton.tooltipText || "";
|
||||||
|
if (!tooltipText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const screenHeight = dock.screen ? dock.screen.height : 0;
|
||||||
|
|
||||||
|
const gap = Theme.spacingS;
|
||||||
|
const bgMargin = dockGeometry.bodyEdgeMargin;
|
||||||
|
const btnW = dock.hoveredButton.width;
|
||||||
|
const btnH = dock.hoveredButton.height;
|
||||||
|
|
||||||
|
if (!dock.isVertical) {
|
||||||
|
const isBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
||||||
|
const tooltipX = buttonLocalPos.x + btnW / 2 + adjacentLeftBarWidth;
|
||||||
|
const tooltipHeight = 32;
|
||||||
|
const totalFromEdge = bgMargin + dockBackground.height + dock.borderThickness + gap;
|
||||||
|
const screenRelativeY = isBottom ? (screenHeight - totalFromEdge - tooltipHeight) : totalFromEdge;
|
||||||
|
dockTooltip.show(tooltipText, tooltipX, screenRelativeY, dock.screen, false, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLeft = SettingsData.dockPosition === SettingsData.Position.Left;
|
||||||
|
const screenWidth = dock.screen ? dock.screen.width : 0;
|
||||||
|
const totalFromEdge = bgMargin + dockBackground.width + dock.borderThickness + gap;
|
||||||
|
const tooltipX = isLeft ? totalFromEdge : (screenWidth - totalFromEdge);
|
||||||
|
const screenRelativeY = buttonLocalPos.y + btnH / 2 + adjacentTopBarHeight;
|
||||||
|
dockTooltip.show(tooltipText, tooltipX, screenRelativeY, dock.screen, isLeft, !isLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: dock
|
||||||
|
function onRevealChanged() {
|
||||||
|
if (!dock.reveal) {
|
||||||
|
tooltipRevealDelay.stop();
|
||||||
|
dockTooltip.hide();
|
||||||
|
} else {
|
||||||
|
tooltipRevealDelay.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHoveredButtonChanged() {
|
||||||
|
dock.showTooltipForHoveredButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dockCore
|
||||||
|
anchors.fill: parent
|
||||||
|
x: isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? animationHeadroom : 0
|
||||||
|
y: !isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? animationHeadroom : 0
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: dockMouseArea
|
||||||
|
function onContainsMouseChanged() {
|
||||||
|
if (dockMouseArea.containsMouse) {
|
||||||
|
dock.revealSticky = true;
|
||||||
|
revealHold.stop();
|
||||||
|
} else {
|
||||||
|
if (dock.autoHide && !dock.contextMenuOpen) {
|
||||||
|
revealHold.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dockMouseArea
|
||||||
|
property real currentScreen: modelData ? modelData : dock.screen
|
||||||
|
property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920
|
||||||
|
property real screenHeight: currentScreen ? currentScreen.geometry.height : 1080
|
||||||
|
property real maxDockWidth: screenWidth * 0.98
|
||||||
|
property real maxDockHeight: screenHeight * 0.98
|
||||||
|
|
||||||
|
height: {
|
||||||
|
if (dock.isVertical) {
|
||||||
|
// Keep the taller hit area regardless of the reveal state to prevent shrinking loop
|
||||||
|
return Math.min(Math.max(dockBackground.height + 64, 200), maxDockHeight);
|
||||||
|
}
|
||||||
|
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
|
||||||
|
}
|
||||||
|
width: {
|
||||||
|
if (dock.isVertical) {
|
||||||
|
return dock.reveal ? px(dockGeometry.motionThickness) : 1;
|
||||||
|
}
|
||||||
|
// Keep the wider hit area regardless of the reveal state to prevent shrinking loop
|
||||||
|
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
||||||
|
}
|
||||||
|
anchors {
|
||||||
|
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined
|
||||||
|
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
|
||||||
|
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
|
||||||
|
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.left) : undefined
|
||||||
|
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
||||||
|
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
||||||
|
}
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dockContainer
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: false
|
||||||
|
opacity: dock.startupRevealDone ? 1 : 0
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
id: dockSlide
|
||||||
|
x: {
|
||||||
|
if (!dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (dock.reveal)
|
||||||
|
return 0;
|
||||||
|
if (dock.usesConnectedFrameChrome) {
|
||||||
|
const retractDist = dockBackground.width + SettingsData.dockSpacing + 10;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Right ? retractDist : -retractDist;
|
||||||
|
}
|
||||||
|
const hideDistance = dockGeometry.motionThickness + 10;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
||||||
|
return hideDistance;
|
||||||
|
} else {
|
||||||
|
return -hideDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
if (dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (dock.reveal)
|
||||||
|
return 0;
|
||||||
|
if (dock.usesConnectedFrameChrome) {
|
||||||
|
const retractDist = dockBackground.height + SettingsData.dockSpacing + 10;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Bottom ? retractDist : -retractDist;
|
||||||
|
}
|
||||||
|
const hideDistance = dockGeometry.motionThickness + 10;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
|
||||||
|
return hideDistance;
|
||||||
|
} else {
|
||||||
|
return -hideDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
id: slideXAnimation
|
||||||
|
duration: dock.usesConnectedFrameChrome ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
||||||
|
easing.type: dock.usesConnectedFrameChrome ? Easing.BezierSpline : Easing.OutCubic
|
||||||
|
easing.bezierCurve: dock.usesConnectedFrameChrome ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
||||||
|
onRunningChanged: if (!running)
|
||||||
|
dock._syncDockChromeState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
id: slideYAnimation
|
||||||
|
duration: dock.usesConnectedFrameChrome ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
||||||
|
easing.type: dock.usesConnectedFrameChrome ? Easing.BezierSpline : Easing.OutCubic
|
||||||
|
easing.bezierCurve: dock.usesConnectedFrameChrome ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
||||||
|
onRunningChanged: if (!running)
|
||||||
|
dock._syncDockChromeState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onXChanged: dock._queueSlideSync()
|
||||||
|
onYChanged: dock._queueSlideSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dockBackground
|
||||||
|
objectName: "dockBackground"
|
||||||
|
visible: dock.hasApps
|
||||||
|
anchors {
|
||||||
|
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined) : undefined
|
||||||
|
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
|
||||||
|
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
|
||||||
|
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined) : undefined
|
||||||
|
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
||||||
|
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
||||||
|
}
|
||||||
|
anchors.topMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Top ? dockGeometry.bodyEdgeMargin : 0
|
||||||
|
anchors.bottomMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? dockGeometry.bodyEdgeMargin : 0
|
||||||
|
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? dockGeometry.bodyEdgeMargin : 0
|
||||||
|
anchors.rightMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? dockGeometry.bodyEdgeMargin : 0
|
||||||
|
|
||||||
|
implicitWidth: dock.isVertical ? (dockApps.implicitHeight + SettingsData.dockSpacing * 2) : (dockApps.implicitWidth + SettingsData.dockSpacing * 2)
|
||||||
|
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2)
|
||||||
|
width: implicitWidth
|
||||||
|
height: implicitHeight
|
||||||
|
|
||||||
|
// Avoid an offscreen texture seam where the connected dock meets the frame.
|
||||||
|
layer.enabled: !usesConnectedFrameChrome
|
||||||
|
clip: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: !usesConnectedFrameChrome && (!FrameTransitionState.effectiveConnectedFrameModeActive || dock.reveal)
|
||||||
|
color: dock.surfaceColor
|
||||||
|
topLeftRadius: dock.surfaceTopLeftRadius
|
||||||
|
topRightRadius: dock.surfaceTopRightRadius
|
||||||
|
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
||||||
|
bottomRightRadius: dock.surfaceBottomRightRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: !usesConnectedFrameChrome && (!FrameTransitionState.effectiveConnectedFrameModeActive || dock.reveal)
|
||||||
|
color: "transparent"
|
||||||
|
topLeftRadius: dock.surfaceTopLeftRadius
|
||||||
|
topRightRadius: dock.surfaceTopRightRadius
|
||||||
|
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
||||||
|
bottomRightRadius: dock.surfaceBottomRightRadius
|
||||||
|
border.color: dock.surfaceBorderColor
|
||||||
|
border.width: dock.surfaceBorderWidth
|
||||||
|
z: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync dockBackground geometry to ConnectedModeState
|
||||||
|
onXChanged: dock._syncDockChromeState()
|
||||||
|
onYChanged: dock._syncDockChromeState()
|
||||||
|
onWidthChanged: dock._syncDockChromeState()
|
||||||
|
onHeightChanged: dock._syncDockChromeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dockConnectedChrome
|
||||||
|
visible: Theme.isConnectedEffect && dock.reveal && dock.hasApps && !FrameTransitionState.effectiveConnectedFrameModeActive
|
||||||
|
readonly property real extraLeft: dock.isVertical ? 0 : Theme.connectedCornerRadius
|
||||||
|
readonly property real extraTop: dock.isVertical ? Theme.connectedCornerRadius : 0
|
||||||
|
readonly property real bodyRadius: dock.surfaceRadius
|
||||||
|
readonly property bool barTop: dock.connectedBarSide === "top"
|
||||||
|
readonly property bool barBottom: dock.connectedBarSide === "bottom"
|
||||||
|
readonly property bool barLeft: dock.connectedBarSide === "left"
|
||||||
|
readonly property bool barRight: dock.connectedBarSide === "right"
|
||||||
|
|
||||||
|
x: dockBackground.x - extraLeft
|
||||||
|
y: dockBackground.y - extraTop
|
||||||
|
width: dockBackground.width + extraLeft * 2
|
||||||
|
height: dockBackground.height + extraTop * 2
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/connected_chrome.frag.qsb")
|
||||||
|
|
||||||
|
property real widthPx: width
|
||||||
|
property real heightPx: height
|
||||||
|
property vector4d surfaceColor: Qt.vector4d(dock.surfaceColor.r, dock.surfaceColor.g, dock.surfaceColor.b, dock.surfaceColor.a)
|
||||||
|
property vector4d shadowColor: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d shadowParam: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d ambientParam: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d bodyRect: Qt.vector4d(dockConnectedChrome.extraLeft, dockConnectedChrome.extraTop, dockBackground.width, dockBackground.height)
|
||||||
|
property vector4d cornerRadius: Qt.vector4d(dockConnectedChrome.barTop || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barTop || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius)
|
||||||
|
property vector4d edgeParam: Qt.vector4d(dockConnectedChrome.barTop ? 0 : (dockConnectedChrome.barBottom ? 1 : (dockConnectedChrome.barLeft ? 2 : 3)), Theme.connectedCornerRadius, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shape {
|
||||||
|
id: dockBorderShape
|
||||||
|
x: dockBackground.x - borderThickness
|
||||||
|
y: dockBackground.y - borderThickness
|
||||||
|
width: dockBackground.width + borderThickness * 2
|
||||||
|
height: dockBackground.height + borderThickness * 2
|
||||||
|
visible: SettingsData.dockBorderEnabled && dock.hasApps && !usesConnectedFrameChrome
|
||||||
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
|
||||||
|
readonly property real borderThickness: Math.max(1, dock.borderThickness)
|
||||||
|
readonly property real i: borderThickness / 2
|
||||||
|
readonly property real cr: dock.surfaceRadius
|
||||||
|
readonly property real w: dockBackground.width
|
||||||
|
readonly property real h: dockBackground.height
|
||||||
|
|
||||||
|
readonly property color borderColor: {
|
||||||
|
const opacity = SettingsData.dockBorderOpacity;
|
||||||
|
switch (SettingsData.dockBorderColor) {
|
||||||
|
case "secondary":
|
||||||
|
return Theme.withAlpha(Theme.secondary, opacity);
|
||||||
|
case "primary":
|
||||||
|
return Theme.withAlpha(Theme.primary, opacity);
|
||||||
|
default:
|
||||||
|
return Theme.withAlpha(Theme.surfaceText, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
fillColor: "transparent"
|
||||||
|
strokeColor: dockBorderShape.borderColor
|
||||||
|
strokeWidth: dockBorderShape.borderThickness
|
||||||
|
joinStyle: ShapePath.RoundJoin
|
||||||
|
capStyle: ShapePath.FlatCap
|
||||||
|
|
||||||
|
PathSvg {
|
||||||
|
path: {
|
||||||
|
const bt = dockBorderShape.borderThickness;
|
||||||
|
const i = dockBorderShape.i;
|
||||||
|
const cr = dockBorderShape.cr + bt - i;
|
||||||
|
const w = dockBorderShape.w;
|
||||||
|
const h = dockBorderShape.h;
|
||||||
|
|
||||||
|
let d = `M ${i + cr} ${i}`;
|
||||||
|
d += ` L ${i + w + 2 * (bt - i) - cr} ${i}`;
|
||||||
|
if (cr > 0)
|
||||||
|
d += ` A ${cr} ${cr} 0 0 1 ${i + w + 2 * (bt - i)} ${i + cr}`;
|
||||||
|
d += ` L ${i + w + 2 * (bt - i)} ${i + h + 2 * (bt - i) - cr}`;
|
||||||
|
if (cr > 0)
|
||||||
|
d += ` A ${cr} ${cr} 0 0 1 ${i + w + 2 * (bt - i) - cr} ${i + h + 2 * (bt - i)}`;
|
||||||
|
d += ` L ${i + cr} ${i + h + 2 * (bt - i)}`;
|
||||||
|
if (cr > 0)
|
||||||
|
d += ` A ${cr} ${cr} 0 0 1 ${i} ${i + h + 2 * (bt - i) - cr}`;
|
||||||
|
d += ` L ${i} ${i + cr}`;
|
||||||
|
if (cr > 0)
|
||||||
|
d += ` A ${cr} ${cr} 0 0 1 ${i + cr} ${i}`;
|
||||||
|
d += " Z";
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockApps {
|
||||||
|
id: dockApps
|
||||||
|
|
||||||
|
anchors.top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? dockBackground.top : undefined) : undefined
|
||||||
|
anchors.bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? dockBackground.bottom : undefined) : undefined
|
||||||
|
anchors.horizontalCenter: !dock.isVertical ? dockBackground.horizontalCenter : undefined
|
||||||
|
anchors.left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Left ? dockBackground.left : undefined) : undefined
|
||||||
|
anchors.right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? dockBackground.right : undefined) : undefined
|
||||||
|
anchors.verticalCenter: dock.isVertical ? dockBackground.verticalCenter : undefined
|
||||||
|
anchors.topMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
|
anchors.bottomMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
|
anchors.leftMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
|
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
|
|
||||||
|
contextMenu: dock.contextMenu
|
||||||
|
trashContextMenu: dock.trashContextMenu
|
||||||
|
groupByApp: dock.groupByApp
|
||||||
|
isVertical: dock.isVertical
|
||||||
|
dockScreen: dock.screen
|
||||||
|
iconSize: dock.widgetHeight
|
||||||
|
usesOverlayLayer: dock.usesOverlayLayer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,6 +61,11 @@ PanelWindow {
|
|||||||
const actualDockHeight = dockBackground ? dockBackground.height : root.dockVisibleHeight;
|
const actualDockHeight = dockBackground ? dockBackground.height : root.dockVisibleHeight;
|
||||||
const actualDockWidth = dockBackground ? dockBackground.width : dockWindow.width;
|
const actualDockWidth = dockBackground ? dockBackground.width : dockWindow.width;
|
||||||
const dockMargin = SettingsData.dockMargin + 16;
|
const dockMargin = SettingsData.dockMargin + 16;
|
||||||
|
|
||||||
|
// Connected mode hosts the dock inside the fullscreen frame surface, inset from the
|
||||||
|
// screen edge. Anchor off the dock body's real position instead of the screen edge.
|
||||||
|
const dockBgPos = dockBackground ? dockBackground.mapToItem(dockWindow.contentItem, 0, 0) : null;
|
||||||
|
const frameHosted = dockBgPos && dockWindow.width >= screen.width - 1 && dockWindow.height >= screen.height - 1;
|
||||||
let x = 0;
|
let x = 0;
|
||||||
let y = 0;
|
let y = 0;
|
||||||
|
|
||||||
@@ -68,14 +73,14 @@ PanelWindow {
|
|||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
{
|
{
|
||||||
const dockTopMargin = Math.round((screen.height - dockWindow.height) / 2);
|
const dockTopMargin = Math.round((screen.height - dockWindow.height) / 2);
|
||||||
x = actualDockWidth + dockMargin + 20;
|
x = frameHosted ? (dockBgPos.x + actualDockWidth + dockMargin + 20) : (actualDockWidth + dockMargin + 20);
|
||||||
y = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
y = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
{
|
{
|
||||||
const dockTopMargin = Math.round((screen.height - dockWindow.height) / 2);
|
const dockTopMargin = Math.round((screen.height - dockWindow.height) / 2);
|
||||||
x = screen.width - actualDockWidth - dockMargin - 20;
|
x = frameHosted ? (dockBgPos.x - dockMargin - 20) : (screen.width - actualDockWidth - dockMargin - 20);
|
||||||
y = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
y = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -83,7 +88,7 @@ PanelWindow {
|
|||||||
{
|
{
|
||||||
const dockLeftMargin = Math.round((screen.width - dockWindow.width) / 2);
|
const dockLeftMargin = Math.round((screen.width - dockWindow.width) / 2);
|
||||||
x = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
x = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
||||||
y = actualDockHeight + dockMargin + 20;
|
y = frameHosted ? (dockBgPos.y + actualDockHeight + dockMargin + 20) : (actualDockHeight + dockMargin + 20);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
@@ -91,7 +96,7 @@ PanelWindow {
|
|||||||
{
|
{
|
||||||
const dockLeftMargin = Math.round((screen.width - dockWindow.width) / 2);
|
const dockLeftMargin = Math.round((screen.width - dockWindow.width) / 2);
|
||||||
x = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
x = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
||||||
y = screen.height - actualDockHeight - dockMargin - 20;
|
y = frameHosted ? (dockBgPos.y - dockMargin - 20) : (screen.height - actualDockHeight - dockMargin - 20);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.DankBar
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
// Renders the bar(s) inside the frame surface in connected mode: one DankBarBody per
|
||||||
|
// active bar edge, positioned in the frame's cutout band. Reuses the existing DankBar
|
||||||
|
// item (per bar config) as rootWindow so colour-picker, overview loader and widget
|
||||||
|
// models are shared with the standalone path.
|
||||||
|
Item {
|
||||||
|
id: host
|
||||||
|
|
||||||
|
required property var frameWindow
|
||||||
|
required property var targetScreen
|
||||||
|
|
||||||
|
readonly property string screenName: targetScreen ? targetScreen.name : ""
|
||||||
|
|
||||||
|
readonly property var barSlots: {
|
||||||
|
SettingsData.barConfigs;
|
||||||
|
const out = [];
|
||||||
|
const configs = SettingsData.barConfigs || [];
|
||||||
|
for (let i = 0; i < configs.length; i++) {
|
||||||
|
const bc = configs[i];
|
||||||
|
if (!bc.enabled || (bc.useOverlayLayer ?? false))
|
||||||
|
continue;
|
||||||
|
const prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !SettingsData.isScreenInPreferences(host.targetScreen, prefs))
|
||||||
|
continue;
|
||||||
|
let edge = "top";
|
||||||
|
switch (bc.position ?? 0) {
|
||||||
|
case SettingsData.Position.Bottom:
|
||||||
|
edge = "bottom";
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Left:
|
||||||
|
edge = "left";
|
||||||
|
break;
|
||||||
|
case SettingsData.Position.Right:
|
||||||
|
edge = "right";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out.push({
|
||||||
|
"barId": bc.id,
|
||||||
|
"edge": edge
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Horizontal bars own the corners, so render them last (on top) — their edge widget
|
||||||
|
// must receive the corner click over the vertical bar's hover area beneath it.
|
||||||
|
out.sort((a, b) => {
|
||||||
|
const rank = e => (e === "left" || e === "right") ? 0 : 1;
|
||||||
|
return rank(a.edge) - rank(b.edge);
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: host.barSlots
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: slot
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
readonly property string edge: modelData.edge
|
||||||
|
readonly property var dankBarItem: BarWidgetService.dankBarItems[modelData.barId] ?? null
|
||||||
|
readonly property var slotBarConfig: dankBarItem?.barConfig ?? SettingsData.getBarConfig(modelData.barId)
|
||||||
|
|
||||||
|
// Each bar spans its full edge (matching the standalone window extent) so
|
||||||
|
// DankBarContent's own adjacency margins inset it, rather than double-insetting
|
||||||
|
// against a pre-carved strip.
|
||||||
|
x: edge === "right" ? host.frameWindow._windowRegionWidth - host.frameWindow.cutoutRightInset : 0
|
||||||
|
y: edge === "bottom" ? host.frameWindow._windowRegionHeight - host.frameWindow.cutoutBottomInset : 0
|
||||||
|
width: {
|
||||||
|
switch (edge) {
|
||||||
|
case "left":
|
||||||
|
return host.frameWindow.cutoutLeftInset;
|
||||||
|
case "right":
|
||||||
|
return host.frameWindow.cutoutRightInset;
|
||||||
|
default:
|
||||||
|
return host.frameWindow._windowRegionWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
height: {
|
||||||
|
switch (edge) {
|
||||||
|
case "top":
|
||||||
|
return host.frameWindow.cutoutTopInset;
|
||||||
|
case "bottom":
|
||||||
|
return host.frameWindow.cutoutBottomInset;
|
||||||
|
default:
|
||||||
|
return host.frameWindow._windowRegionHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: slot.dankBarItem !== null && slot.slotBarConfig !== null
|
||||||
|
|
||||||
|
sourceComponent: DankBarBody {
|
||||||
|
hostWindow: host.frameWindow
|
||||||
|
modelData: host.targetScreen
|
||||||
|
rootWindow: slot.dankBarItem
|
||||||
|
barConfig: slot.slotBarConfig
|
||||||
|
leftWidgetsModel: slot.dankBarItem?.leftWidgetsModel ?? null
|
||||||
|
centerWidgetsModel: slot.dankBarItem?.centerWidgetsModel ?? null
|
||||||
|
rightWidgetsModel: slot.dankBarItem?.rightWidgetsModel ?? null
|
||||||
|
|
||||||
|
Component.onCompleted: BarWidgetService.registerFrameBar(host.screenName, this)
|
||||||
|
Component.onDestruction: BarWidgetService.unregisterFrameBar(host.screenName, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Dock
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
// Renders the dock inside the frame surface in connected mode: a single DockBody
|
||||||
|
// positioned by SettingsData.dockPosition. It fills the frame and positions its own
|
||||||
|
// content via its internal anchors/geometry using hostWindow.
|
||||||
|
Item {
|
||||||
|
id: host
|
||||||
|
|
||||||
|
required property var frameWindow
|
||||||
|
required property var targetScreen
|
||||||
|
|
||||||
|
readonly property string screenName: targetScreen ? targetScreen.name : ""
|
||||||
|
|
||||||
|
// Exposed so FrameWindow can add the dock's hover strip to its input region.
|
||||||
|
readonly property alias dockMaskItem: dockBody.inputMaskItem
|
||||||
|
|
||||||
|
DockBody {
|
||||||
|
id: dockBody
|
||||||
|
anchors.fill: parent
|
||||||
|
hostWindow: host.frameWindow
|
||||||
|
modelData: host.targetScreen
|
||||||
|
contextMenu: BarWidgetService.dockContextMenu
|
||||||
|
trashContextMenu: BarWidgetService.dockTrashContextMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,15 +16,33 @@ Scope {
|
|||||||
return SettingsData.getActiveBarEdgesForScreen(screen);
|
return SettingsData.getActiveBarEdgesForScreen(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// One thin invisible PanelWindow per edge.
|
// Overlay-layer bars stay standalone even in connected mode and reserve their own edge.
|
||||||
// Skips any edge where a bar already provides its own exclusiveZone.
|
readonly property var overlayBarEdges: {
|
||||||
|
SettingsData.barConfigs;
|
||||||
|
return SettingsData.getOverlayBarEdgesForScreen(root.screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// One thin invisible PanelWindow per edge. A standalone bar reserves its own edge, so
|
||||||
|
// that edge is skipped — unless the frame hosts the bar (connected mode), where no bar
|
||||||
|
// window exists and the exclusion must reserve the full bar thickness itself.
|
||||||
|
|
||||||
readonly property bool screenEnabled: CompositorService.frameWindowVisibleForScreen(root.screen)
|
readonly property bool screenEnabled: CompositorService.frameWindowVisibleForScreen(root.screen)
|
||||||
|
readonly property bool hostsBar: CompositorService.frameHostsSurfacesForScreen(root.screen)
|
||||||
|
|
||||||
|
function hostsBandForEdge(edge) {
|
||||||
|
return root.hostsBar && !root.overlayBarEdges.includes(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exclusionSizeForEdge(edge) {
|
||||||
|
return root.barEdges.includes(edge) ? SettingsData.frameBarSize : SettingsData.frameThickness;
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: root.screenEnabled && !root.barEdges.includes("top")
|
readonly property bool isBarEdge: root.barEdges.includes("top")
|
||||||
|
active: root.screenEnabled && (!isBarEdge || root.hostsBandForEdge("top"))
|
||||||
sourceComponent: EdgeExclusion {
|
sourceComponent: EdgeExclusion {
|
||||||
targetScreen: root.screen
|
targetScreen: root.screen
|
||||||
|
exclusionSize: root.exclusionSizeForEdge("top")
|
||||||
anchorTop: true
|
anchorTop: true
|
||||||
anchorLeft: true
|
anchorLeft: true
|
||||||
anchorRight: true
|
anchorRight: true
|
||||||
@@ -32,9 +50,11 @@ Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: root.screenEnabled && !root.barEdges.includes("bottom")
|
readonly property bool isBarEdge: root.barEdges.includes("bottom")
|
||||||
|
active: root.screenEnabled && (!isBarEdge || root.hostsBandForEdge("bottom"))
|
||||||
sourceComponent: EdgeExclusion {
|
sourceComponent: EdgeExclusion {
|
||||||
targetScreen: root.screen
|
targetScreen: root.screen
|
||||||
|
exclusionSize: root.exclusionSizeForEdge("bottom")
|
||||||
anchorBottom: true
|
anchorBottom: true
|
||||||
anchorLeft: true
|
anchorLeft: true
|
||||||
anchorRight: true
|
anchorRight: true
|
||||||
@@ -42,9 +62,11 @@ Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: root.screenEnabled && !root.barEdges.includes("left")
|
readonly property bool isBarEdge: root.barEdges.includes("left")
|
||||||
|
active: root.screenEnabled && (!isBarEdge || root.hostsBandForEdge("left"))
|
||||||
sourceComponent: EdgeExclusion {
|
sourceComponent: EdgeExclusion {
|
||||||
targetScreen: root.screen
|
targetScreen: root.screen
|
||||||
|
exclusionSize: root.exclusionSizeForEdge("left")
|
||||||
anchorLeft: true
|
anchorLeft: true
|
||||||
anchorTop: true
|
anchorTop: true
|
||||||
anchorBottom: true
|
anchorBottom: true
|
||||||
@@ -52,9 +74,11 @@ Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: root.screenEnabled && !root.barEdges.includes("right")
|
readonly property bool isBarEdge: root.barEdges.includes("right")
|
||||||
|
active: root.screenEnabled && (!isBarEdge || root.hostsBandForEdge("right"))
|
||||||
sourceComponent: EdgeExclusion {
|
sourceComponent: EdgeExclusion {
|
||||||
targetScreen: root.screen
|
targetScreen: root.screen
|
||||||
|
exclusionSize: root.exclusionSizeForEdge("right")
|
||||||
anchorRight: true
|
anchorRight: true
|
||||||
anchorTop: true
|
anchorTop: true
|
||||||
anchorBottom: true
|
anchorBottom: true
|
||||||
@@ -63,6 +87,7 @@ Scope {
|
|||||||
|
|
||||||
component EdgeExclusion: PanelWindow {
|
component EdgeExclusion: PanelWindow {
|
||||||
required property var targetScreen
|
required property var targetScreen
|
||||||
|
property real exclusionSize: SettingsData.frameThickness
|
||||||
|
|
||||||
screen: targetScreen
|
screen: targetScreen
|
||||||
property bool anchorTop: false
|
property bool anchorTop: false
|
||||||
@@ -72,7 +97,7 @@ Scope {
|
|||||||
|
|
||||||
WlrLayershell.namespace: "dms:frame-exclusion"
|
WlrLayershell.namespace: "dms:frame-exclusion"
|
||||||
WlrLayershell.layer: WlrLayer.Top
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
exclusiveZone: SettingsData.frameThickness
|
exclusiveZone: exclusionSize
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
mask: Region {}
|
mask: Region {}
|
||||||
implicitWidth: 1
|
implicitWidth: 1
|
||||||
|
|||||||
@@ -32,7 +32,41 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
mask: Region {}
|
// Click-through everywhere except the bar strips it hosts in connected mode.
|
||||||
|
mask: Region {
|
||||||
|
Region {
|
||||||
|
readonly property bool present: win._connectedActive && win.barEdges.includes("top")
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: present ? win._windowRegionWidth : 0
|
||||||
|
height: present ? win.cutoutTopInset : 0
|
||||||
|
}
|
||||||
|
Region {
|
||||||
|
readonly property bool present: win._connectedActive && win.barEdges.includes("bottom")
|
||||||
|
x: 0
|
||||||
|
y: present ? win._windowRegionHeight - win.cutoutBottomInset : 0
|
||||||
|
width: present ? win._windowRegionWidth : 0
|
||||||
|
height: present ? win.cutoutBottomInset : 0
|
||||||
|
}
|
||||||
|
Region {
|
||||||
|
readonly property bool present: win._connectedActive && win.barEdges.includes("left")
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: present ? win.cutoutLeftInset : 0
|
||||||
|
height: present ? win._windowRegionHeight : 0
|
||||||
|
}
|
||||||
|
Region {
|
||||||
|
readonly property bool present: win._connectedActive && win.barEdges.includes("right")
|
||||||
|
x: present ? win._windowRegionWidth - win.cutoutRightInset : 0
|
||||||
|
y: 0
|
||||||
|
width: present ? win.cutoutRightInset : 0
|
||||||
|
height: present ? win._windowRegionHeight : 0
|
||||||
|
}
|
||||||
|
// The frame-hosted dock's hover strip, so hover-reveal works through the frame surface.
|
||||||
|
Region {
|
||||||
|
item: frameDockHostLoader.item ? frameDockHostLoader.item.dockMaskItem : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
readonly property var barEdges: {
|
readonly property var barEdges: {
|
||||||
SettingsData.barConfigs;
|
SettingsData.barConfigs;
|
||||||
@@ -51,6 +85,16 @@ PanelWindow {
|
|||||||
readonly property var _modalDescriptor: ConnectedModeState.surfaceDescriptor(win._screenName, "modal")
|
readonly property var _modalDescriptor: ConnectedModeState.surfaceDescriptor(win._screenName, "modal")
|
||||||
|
|
||||||
readonly property bool _connectedActive: CompositorService.usesConnectedFrameChromeForScreen(win.targetScreen)
|
readonly property bool _connectedActive: CompositorService.usesConnectedFrameChromeForScreen(win.targetScreen)
|
||||||
|
readonly property bool _dockHostedHere: {
|
||||||
|
if (!win._connectedActive || !SettingsData.showDock || SettingsData.dockUseOverlayLayer)
|
||||||
|
return false;
|
||||||
|
const screens = SettingsData.getFilteredScreens("dock");
|
||||||
|
for (let i = 0; i < screens.length; i++) {
|
||||||
|
if (screens[i] && screens[i].name === win._screenName)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
readonly property string _barSide: {
|
readonly property string _barSide: {
|
||||||
const edges = win.barEdges;
|
const edges = win.barEdges;
|
||||||
if (edges.includes("top"))
|
if (edges.includes("top"))
|
||||||
@@ -191,6 +235,33 @@ PanelWindow {
|
|||||||
return Math.max(0, Math.round(Theme.px(value, win._dpr)));
|
return Math.max(0, Math.round(Theme.px(value, win._dpr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _pointInBand(lx, ly, bx, by, bw, bh, pad) {
|
||||||
|
return lx >= bx - pad && lx < bx + bw + pad && ly >= by - pad && ly < by + bh + pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsGlobalPoint(gx, gy, padding) {
|
||||||
|
if (!win._connectedActive || !win.contentItem)
|
||||||
|
return false;
|
||||||
|
const origin = win.contentItem.mapToItem(null, 0, 0);
|
||||||
|
if (!origin)
|
||||||
|
return false;
|
||||||
|
const pad = padding !== undefined ? padding : 16;
|
||||||
|
const lx = gx - origin.x;
|
||||||
|
const ly = gy - origin.y;
|
||||||
|
const w = win._windowRegionWidth;
|
||||||
|
const h = win._windowRegionHeight;
|
||||||
|
const edges = win.barEdges;
|
||||||
|
if (edges.includes("top") && win._pointInBand(lx, ly, 0, 0, w, win.cutoutTopInset, pad))
|
||||||
|
return true;
|
||||||
|
if (edges.includes("bottom") && win._pointInBand(lx, ly, 0, h - win.cutoutBottomInset, w, win.cutoutBottomInset, pad))
|
||||||
|
return true;
|
||||||
|
if (edges.includes("left") && win._pointInBand(lx, ly, 0, 0, win.cutoutLeftInset, h, pad))
|
||||||
|
return true;
|
||||||
|
if (edges.includes("right") && win._pointInBand(lx, ly, w - win.cutoutRightInset, 0, win.cutoutRightInset, h, pad))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
readonly property int cutoutTopInset: win._regionInt(barEdges.includes("top") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
readonly property int cutoutTopInset: win._regionInt(barEdges.includes("top") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
||||||
readonly property int cutoutBottomInset: win._regionInt(barEdges.includes("bottom") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
readonly property int cutoutBottomInset: win._regionInt(barEdges.includes("bottom") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
||||||
readonly property int cutoutLeftInset: win._regionInt(barEdges.includes("left") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
readonly property int cutoutLeftInset: win._regionInt(barEdges.includes("left") ? SettingsData.frameBarSize : SettingsData.frameThickness)
|
||||||
@@ -1049,10 +1120,12 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
KeyboardFocus.registerBarWindow(win);
|
||||||
win._scheduleBlurRebuild();
|
win._scheduleBlurRebuild();
|
||||||
win._scheduleSurfaceRefresh();
|
win._scheduleSurfaceRefresh();
|
||||||
}
|
}
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
|
KeyboardFocus.unregisterBarWindow(win);
|
||||||
blurRebuildAction.cancel();
|
blurRebuildAction.cancel();
|
||||||
surfaceRefreshAction.cancel();
|
surfaceRefreshAction.cancel();
|
||||||
win._teardownBlur();
|
win._teardownBlur();
|
||||||
@@ -1101,4 +1174,25 @@ PanelWindow {
|
|||||||
property vector4d chromeK3: win._sdfSlots[3].k
|
property vector4d chromeK3: win._sdfSlots[3].k
|
||||||
property vector4d chromeParam3: win._sdfSlots[3].param
|
property vector4d chromeParam3: win._sdfSlots[3].param
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 1
|
||||||
|
active: win._connectedActive
|
||||||
|
sourceComponent: FrameBarHost {
|
||||||
|
frameWindow: win
|
||||||
|
targetScreen: win.targetScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: frameDockHostLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 1
|
||||||
|
active: win._dockHostedHere
|
||||||
|
sourceComponent: FrameDockHost {
|
||||||
|
frameWindow: win
|
||||||
|
targetScreen: win.targetScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1256,6 +1256,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggleCard {
|
SettingsToggleCard {
|
||||||
|
settingKey: "hoverPopouts"
|
||||||
|
tags: ["bar", "hover", "popout", "reveal", "widget", "delay"]
|
||||||
iconName: "touch_app"
|
iconName: "touch_app"
|
||||||
title: I18n.tr("Hover Popouts")
|
title: I18n.tr("Hover Popouts")
|
||||||
description: I18n.tr("Open widget popouts by hovering over the bar. Moving to another widget switches the popout.")
|
description: I18n.tr("Open widget popouts by hovering over the bar. Moving to another widget switches the popout.")
|
||||||
|
|||||||
@@ -12,6 +12,50 @@ Singleton {
|
|||||||
property var widgetRegistry: ({})
|
property var widgetRegistry: ({})
|
||||||
property var dankBarRepeater: null
|
property var dankBarRepeater: null
|
||||||
|
|
||||||
|
// Bars rendered inside the frame surface (connected mode) have no DankBarWindow in the
|
||||||
|
// repeater, so they self-register here (screenName -> DankBarBody) for trigger routing.
|
||||||
|
property var frameHostedBars: ({})
|
||||||
|
|
||||||
|
// Shared dock context-menu windows (created in DMSShell) so a frame-hosted dock can reach them.
|
||||||
|
property var dockContextMenu: null
|
||||||
|
property var dockTrashContextMenu: null
|
||||||
|
|
||||||
|
function registerFrameBar(screenName, body) {
|
||||||
|
if (!screenName || !body)
|
||||||
|
return;
|
||||||
|
const next = Object.assign({}, frameHostedBars);
|
||||||
|
next[screenName] = body;
|
||||||
|
frameHostedBars = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterFrameBar(screenName, body) {
|
||||||
|
if (!screenName || frameHostedBars[screenName] !== body)
|
||||||
|
return;
|
||||||
|
const next = Object.assign({}, frameHostedBars);
|
||||||
|
delete next[screenName];
|
||||||
|
frameHostedBars = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DankBar items self-register here (barConfig id -> DankBar) so frame-hosted bars can
|
||||||
|
// resolve their models/rootWindow reactively, independent of repeater load ordering.
|
||||||
|
property var dankBarItems: ({})
|
||||||
|
|
||||||
|
function registerDankBarItem(barId, item) {
|
||||||
|
if (!barId || !item)
|
||||||
|
return;
|
||||||
|
const next = Object.assign({}, dankBarItems);
|
||||||
|
next[barId] = item;
|
||||||
|
dankBarItems = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterDankBarItem(barId, item) {
|
||||||
|
if (!barId || dankBarItems[barId] !== item)
|
||||||
|
return;
|
||||||
|
const next = Object.assign({}, dankBarItems);
|
||||||
|
delete next[barId];
|
||||||
|
dankBarItems = next;
|
||||||
|
}
|
||||||
|
|
||||||
signal widgetRegistered(string widgetId, string screenName)
|
signal widgetRegistered(string widgetId, string screenName)
|
||||||
signal widgetUnregistered(string widgetId, string screenName)
|
signal widgetUnregistered(string widgetId, string screenName)
|
||||||
|
|
||||||
@@ -141,6 +185,9 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getBarWindowForScreen(screenName) {
|
function getBarWindowForScreen(screenName) {
|
||||||
|
if (frameHostedBars[screenName])
|
||||||
|
return frameHostedBars[screenName];
|
||||||
|
|
||||||
if (!dankBarRepeater)
|
if (!dankBarRepeater)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -170,17 +217,18 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFirstBarWindow() {
|
function getFirstBarWindow() {
|
||||||
if (!dankBarRepeater || dankBarRepeater.count === 0)
|
if (dankBarRepeater) {
|
||||||
return null;
|
for (var i = 0; i < dankBarRepeater.count; i++) {
|
||||||
|
const barItem = dankBarRepeater.itemAt(i)?.item;
|
||||||
|
if (barItem?.barVariants?.instances?.length > 0)
|
||||||
|
return barItem.barVariants.instances[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loader = dankBarRepeater.itemAt(0);
|
for (const screenName in frameHostedBars) {
|
||||||
if (!loader?.item)
|
if (frameHostedBars[screenName])
|
||||||
return null;
|
return frameHostedBars[screenName];
|
||||||
|
}
|
||||||
const barItem = loader.item;
|
return null;
|
||||||
if (!barItem.barVariants?.instances || barItem.barVariants.instances.length === 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return barItem.barVariants.instances[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -533,43 +533,6 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasFullscreenToplevelOnScreen(screenOrName) {
|
|
||||||
const screenName = _screenName(screenOrName);
|
|
||||||
if (!screenName)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (isNiri) {
|
|
||||||
const active = ToplevelManager.activeToplevel;
|
|
||||||
if (active?.fullscreen && active?.activated && _toplevelOnScreen(active, screenName))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const filtered = filterCurrentWorkspace(sortedToplevels, screenName);
|
|
||||||
for (let i = 0; i < filtered.length; i++) {
|
|
||||||
if (filtered[i]?.fullscreen)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isHyprland) {
|
|
||||||
const filtered = filterCurrentWorkspace(sortedToplevels, screenName);
|
|
||||||
for (let i = 0; i < filtered.length; i++) {
|
|
||||||
if (filtered[i]?.fullscreen)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ToplevelManager.toplevels?.values)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (const toplevel of ToplevelManager.toplevels.values) {
|
|
||||||
if (toplevel?.fullscreen && _toplevelOnScreen(toplevel, screenName))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _hyprlandToplevelMapped(hyprToplevel) {
|
function _hyprlandToplevelMapped(hyprToplevel) {
|
||||||
if (!hyprToplevel)
|
if (!hyprToplevel)
|
||||||
return false;
|
return false;
|
||||||
@@ -630,12 +593,6 @@ Singleton {
|
|||||||
// Per-screen cache for connectedFrameBlockedOnScreen to avoid recomputing on every consumer binding.
|
// Per-screen cache for connectedFrameBlockedOnScreen to avoid recomputing on every consumer binding.
|
||||||
property var frameBlockedByScreen: ({})
|
property var frameBlockedByScreen: ({})
|
||||||
|
|
||||||
function _computeConnectedFrameBlocked(screenName) {
|
|
||||||
if (hasFullscreenToplevelOnScreen(screenName))
|
|
||||||
return true;
|
|
||||||
return hyprlandSpecialWorkspaceBlocksConnectedFrame(screenName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _recomputeFrameBlocked() {
|
function _recomputeFrameBlocked() {
|
||||||
const screens = Quickshell.screens || [];
|
const screens = Quickshell.screens || [];
|
||||||
const next = {};
|
const next = {};
|
||||||
@@ -644,7 +601,7 @@ Singleton {
|
|||||||
const name = screens[i]?.name;
|
const name = screens[i]?.name;
|
||||||
if (!name)
|
if (!name)
|
||||||
continue;
|
continue;
|
||||||
const blocked = _computeConnectedFrameBlocked(name);
|
const blocked = hyprlandSpecialWorkspaceBlocksConnectedFrame(name);
|
||||||
next[name] = blocked;
|
next[name] = blocked;
|
||||||
if (frameBlockedByScreen[name] !== blocked)
|
if (frameBlockedByScreen[name] !== blocked)
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -668,7 +625,7 @@ Singleton {
|
|||||||
const cached = frameBlockedByScreen[screenName];
|
const cached = frameBlockedByScreen[screenName];
|
||||||
if (cached !== undefined)
|
if (cached !== undefined)
|
||||||
return cached;
|
return cached;
|
||||||
return _computeConnectedFrameBlocked(screenName);
|
return hyprlandSpecialWorkspaceBlocksConnectedFrame(screenName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -730,6 +687,23 @@ Singleton {
|
|||||||
return FrameTransitionState.effectiveConnectedFrameModeActive && frameWindowVisibleForScreen(screenOrName);
|
return FrameTransitionState.effectiveConnectedFrameModeActive && frameWindowVisibleForScreen(screenOrName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connected mode renders the bar inside the frame surface. True whenever connected
|
||||||
|
// chrome is configured for the screen, independent of the fullscreen block, so the
|
||||||
|
// standalone bar surface stays suppressed while the frame-hosted bar hides with the frame.
|
||||||
|
function frameHostsSurfacesForScreen(screenOrName) {
|
||||||
|
return FrameTransitionState.effectiveConnectedFrameModeActive && frameConfiguredForScreen(screenOrName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A surface set to the overlay layer cannot live inside the frame's single top-layer window,
|
||||||
|
// so it stays a standalone window (which resolves itself onto the overlay layer) even in connected mode.
|
||||||
|
function frameHostsBarForConfig(screenOrName, barConfig) {
|
||||||
|
return frameHostsSurfacesForScreen(screenOrName) && !(barConfig?.useOverlayLayer ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function frameHostsDockForScreen(screenOrName) {
|
||||||
|
return frameHostsSurfacesForScreen(screenOrName) && !SettingsData.dockUseOverlayLayer;
|
||||||
|
}
|
||||||
|
|
||||||
function framePeerSurfacesUseOverlayForScreen(screenOrName) {
|
function framePeerSurfacesUseOverlayForScreen(screenOrName) {
|
||||||
return frameWindowVisibleForScreen(screenOrName);
|
return frameWindowVisibleForScreen(screenOrName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ Item {
|
|||||||
// keep the bg window in active-update mode.
|
// keep the bg window in active-update mode.
|
||||||
property bool _bgCommitWindow: false
|
property bool _bgCommitWindow: false
|
||||||
|
|
||||||
|
// True only while the overlay is actually on screen, not merely declared.
|
||||||
|
readonly property bool _overlayActive: overlayLoader.item?.visible ?? false
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: bgCommitSettleTimer
|
id: bgCommitSettleTimer
|
||||||
interval: 250
|
interval: 250
|
||||||
@@ -236,6 +239,9 @@ Item {
|
|||||||
return;
|
return;
|
||||||
_blurCommitSuppress = true;
|
_blurCommitSuppress = true;
|
||||||
blurCommitPulseTimer.restart();
|
blurCommitPulseTimer.restart();
|
||||||
|
// Land the off->on toggle on a commit even after _overlayActive has dropped.
|
||||||
|
_bgCommitWindow = true;
|
||||||
|
bgCommitSettleTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -563,10 +569,9 @@ Item {
|
|||||||
screen: root.surfaceScreen
|
screen: root.surfaceScreen
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
// Skip buffer updates when there's nothing to render. Briefly flipped
|
// Idle full-screen surface skips buffer updates, else it damages the whole screen
|
||||||
// true via _bgCommitWindow when _surfaceBodyW/H changes so the
|
// every frame. Live while an overlay shows or _bgCommitWindow flushes a mask change.
|
||||||
// contentHoleRect mask carve-out actually commits to the compositor.
|
updatesEnabled: !root._blurCommitSuppress && (root._overlayActive || root._bgCommitWindow)
|
||||||
updatesEnabled: !root._blurCommitSuppress && (root.overlayContent !== null || root._bgCommitWindow)
|
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace + ":background"
|
WlrLayershell.namespace: root.layerNamespace + ":background"
|
||||||
WlrLayershell.layer: root.effectivePopoutLayer
|
WlrLayershell.layer: root.effectivePopoutLayer
|
||||||
|
|||||||
@@ -772,6 +772,34 @@
|
|||||||
],
|
],
|
||||||
"icon": "display_settings"
|
"icon": "display_settings"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "hoverPopouts",
|
||||||
|
"label": "Hover Popouts",
|
||||||
|
"tabIndex": 3,
|
||||||
|
"category": "Dank Bar",
|
||||||
|
"keywords": [
|
||||||
|
"another",
|
||||||
|
"bar",
|
||||||
|
"dank",
|
||||||
|
"delay",
|
||||||
|
"hover",
|
||||||
|
"hovering",
|
||||||
|
"moving",
|
||||||
|
"open",
|
||||||
|
"over",
|
||||||
|
"panel",
|
||||||
|
"popout",
|
||||||
|
"popouts",
|
||||||
|
"reveal",
|
||||||
|
"statusbar",
|
||||||
|
"switches",
|
||||||
|
"taskbar",
|
||||||
|
"topbar",
|
||||||
|
"widget"
|
||||||
|
],
|
||||||
|
"icon": "touch_app",
|
||||||
|
"description": "Open widget popouts by hovering over the bar. Moving to another widget switches the popout."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "barPosition",
|
"section": "barPosition",
|
||||||
"label": "Position",
|
"label": "Position",
|
||||||
|
|||||||
Reference in New Issue
Block a user