mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-31 00:42:50 -05:00
dock: some cleanup to overflow
This commit is contained in:
@@ -443,8 +443,8 @@ Singleton {
|
||||
property int dockLauncherLogoSizeOffset: 0
|
||||
property real dockLauncherLogoBrightness: 0.5
|
||||
property real dockLauncherLogoContrast: 1
|
||||
property int dockMaxVisibleApps: 10
|
||||
property int dockMaxVisibleRunningApps: 10
|
||||
property int dockMaxVisibleApps: 0
|
||||
property int dockMaxVisibleRunningApps: 0
|
||||
property bool dockShowOverflowBadge: true
|
||||
|
||||
property bool notificationOverlayEnabled: false
|
||||
|
||||
@@ -272,8 +272,8 @@ var SPEC = {
|
||||
dockLauncherLogoSizeOffset: { def: 0 },
|
||||
dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
|
||||
dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
|
||||
dockMaxVisibleApps: { def: 10 },
|
||||
dockMaxVisibleRunningApps: { def: 10 },
|
||||
dockMaxVisibleApps: { def: 0 },
|
||||
dockMaxVisibleRunningApps: { def: 0 },
|
||||
dockShowOverflowBadge: { def: true },
|
||||
|
||||
notificationOverlayEnabled: { def: false },
|
||||
|
||||
@@ -437,21 +437,19 @@ Variants {
|
||||
|
||||
height: {
|
||||
if (dock.isVertical) {
|
||||
const extra = 4 + dock.borderThickness;
|
||||
const hiddenHeight = Math.min(Math.max(dockBackground.implicitHeight + 64, 200), screenHeight * 0.5);
|
||||
return dock.reveal ? Math.max(Math.min(dockBackground.implicitHeight + extra, maxDockHeight), hiddenHeight) : hiddenHeight;
|
||||
} else {
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
if (!dock.reveal)
|
||||
return Math.min(Math.max(dockBackground.height + 64, 200), screenHeight * 0.5);
|
||||
return Math.min(dockBackground.height + 8 + dock.borderThickness, maxDockHeight);
|
||||
}
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
}
|
||||
width: {
|
||||
if (dock.isVertical) {
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
} else {
|
||||
const extra = 4 + dock.borderThickness;
|
||||
const hiddenWidth = Math.min(Math.max(dockBackground.implicitWidth + 64, 200), screenWidth * 0.5);
|
||||
return dock.reveal ? Math.max(Math.min(dockBackground.implicitWidth + extra, maxDockWidth), hiddenWidth) : hiddenWidth;
|
||||
}
|
||||
if (!dock.reveal)
|
||||
return Math.min(Math.max(dockBackground.width + 64, 200), screenWidth * 0.5);
|
||||
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
||||
}
|
||||
anchors {
|
||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined
|
||||
@@ -533,6 +531,7 @@ Variants {
|
||||
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
|
||||
@@ -542,23 +541,11 @@ Variants {
|
||||
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
||||
anchors.rightMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
||||
|
||||
readonly property real baseImplicitWidth: dock.isVertical ? (dockApps.baseImplicitHeight + SettingsData.dockSpacing * 2) : (dockApps.baseImplicitWidth + SettingsData.dockSpacing * 2)
|
||||
readonly property real baseImplicitHeight: dock.isVertical ? (dockApps.baseImplicitWidth + SettingsData.dockSpacing * 2) : (dockApps.baseImplicitHeight + SettingsData.dockSpacing * 2)
|
||||
|
||||
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
|
||||
|
||||
x: {
|
||||
if (dock.isVertical)
|
||||
return 0;
|
||||
|
||||
const targetWidth = (dockApps.overflowExpanded) ? implicitWidth : baseImplicitWidth;
|
||||
const centered = (parent.width - targetWidth) / 2;
|
||||
return Math.max(0, centered);
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
clip: false
|
||||
|
||||
@@ -636,12 +623,13 @@ Variants {
|
||||
|
||||
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.left: !dock.isVertical ? dockBackground.left : (SettingsData.dockPosition === SettingsData.Position.Left ? dockBackground.left : 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: SettingsData.dockSpacing
|
||||
anchors.leftMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
||||
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
||||
|
||||
contextMenu: dockVariants.contextMenu
|
||||
@@ -649,27 +637,6 @@ Variants {
|
||||
isVertical: dock.isVertical
|
||||
dockScreen: dock.screen
|
||||
iconSize: dock.widgetHeight
|
||||
|
||||
maxAvailableLength: {
|
||||
const border = (SettingsData.dockBorderEnabled ? dock.borderThickness * 2 : 0);
|
||||
const internalPadding = SettingsData.dockSpacing * 2;
|
||||
|
||||
if (dock.isVertical) {
|
||||
// Calculate vertical space available for apps
|
||||
const maxH = dockMouseArea.maxDockHeight;
|
||||
const vMargins = (dockBackground.anchors.topMargin || 0) + (dockBackground.anchors.bottomMargin || 0);
|
||||
const result = maxH - vMargins - internalPadding - border;
|
||||
console.warn("Dock: maxAvailableLength (V):", result, "= maxH:", maxH, "- margins:", vMargins, "- padding:", internalPadding, "- border:", border);
|
||||
return Math.max(0, result); // Ensure non-negative
|
||||
} else {
|
||||
// Calculate horizontal space available for apps
|
||||
const maxW = dockMouseArea.maxDockWidth;
|
||||
const hMargins = (dockBackground.anchors.leftMargin || 0) + (dockBackground.anchors.rightMargin || 0);
|
||||
const result = maxW - hMargins - internalPadding - border;
|
||||
console.warn("Dock: maxAvailableLength (H):", result, "= maxW:", maxW, "- margins:", hMargins, "- padding:", internalPadding, "- border:", border);
|
||||
return Math.max(0, result); // Ensure non-negative
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,20 +215,9 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
// Prevent stealing during drag operations
|
||||
// Also prevent stealing when NOT in scroll mode (original behavior)
|
||||
// Only allow Flickable to steal when scrollable AND not dragging
|
||||
preventStealing: dragging || longPressing || !(dockApps && dockApps.canScroll)
|
||||
preventStealing: dragging || longPressing
|
||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onWheel: (wheel) => {
|
||||
// Only handle wheel if we're NOT in scrollable mode
|
||||
if (dockApps && dockApps.canScroll) {
|
||||
wheel.accepted = false // Allow event to propagate to Flickable
|
||||
} else {
|
||||
wheel.accepted = true // Consume event (no scrolling needed)
|
||||
}
|
||||
}
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.LeftButton && appData && appData.isPinned) {
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
@@ -241,14 +230,8 @@ Item {
|
||||
const wasDragging = dragging;
|
||||
const didReorder = wasDragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps;
|
||||
|
||||
console.warn("DockAppButton onReleased:", appData?.appId || "unknown");
|
||||
console.warn(" wasDragging:", wasDragging, "originalIndex:", originalIndex, "targetIndex:", targetIndex);
|
||||
console.warn(" didReorder:", didReorder);
|
||||
|
||||
if (didReorder) {
|
||||
// Use movePinnedApp which takes dock indices (original behavior)
|
||||
if (didReorder)
|
||||
dockApps.movePinnedApp(originalIndex, targetIndex);
|
||||
}
|
||||
|
||||
longPressing = false;
|
||||
dragging = false;
|
||||
@@ -331,7 +314,6 @@ Item {
|
||||
const distance = Math.sqrt(Math.pow(mouse.x - dragStartPos.x, 2) + Math.pow(mouse.y - dragStartPos.y, 2));
|
||||
if (distance > 5) {
|
||||
dragging = true;
|
||||
// Use dock index directly (original behavior)
|
||||
targetIndex = index;
|
||||
originalIndex = index;
|
||||
if (dockApps) {
|
||||
@@ -350,7 +332,6 @@ Item {
|
||||
const spacing = Math.min(8, Math.max(4, actualIconSize * 0.08));
|
||||
const itemSize = actualIconSize * 1.2 + spacing;
|
||||
const slotOffset = Math.round(axisOffset / itemSize);
|
||||
// Use pinnedAppCount as max (original behavior)
|
||||
const newTargetIndex = Math.max(0, Math.min(dockApps.pinnedAppCount - 1, originalIndex + slotOffset));
|
||||
|
||||
if (newTargetIndex !== targetIndex) {
|
||||
|
||||
@@ -21,175 +21,64 @@ Item {
|
||||
property int maxVisibleRunningApps: SettingsData.dockMaxVisibleRunningApps
|
||||
property bool overflowExpanded: false
|
||||
property int overflowItemCount: 0
|
||||
property bool draggingPinned: false
|
||||
property real maxAvailableLength: 0
|
||||
|
||||
// Calculate if scrolling is needed (use childrenRect for accurate measurement)
|
||||
readonly property bool canScroll: {
|
||||
if (!root.isVertical) {
|
||||
return layoutFlow.childrenRect.width > availableScreenWidth;
|
||||
readonly property real baseImplicitWidth: isVertical ? baseAppHeight : baseAppWidth
|
||||
readonly property real baseImplicitHeight: isVertical ? baseAppWidth : baseAppHeight
|
||||
readonly property real baseAppWidth: {
|
||||
let count = 0;
|
||||
const items = repeater.dockItems;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.isInOverflow)
|
||||
continue;
|
||||
if (item.type === "separator") {
|
||||
count += 8 / (iconSize * 1.2);
|
||||
} else {
|
||||
return layoutFlow.childrenRect.height > availableScreenHeight;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Available space calculations - 10% padding for nice margins when scrolling
|
||||
readonly property real availableScreenWidth: {
|
||||
if (!root.isVertical && maxAvailableLength > 0) {
|
||||
return maxAvailableLength * 0.9; // 10% padding on sides
|
||||
}
|
||||
if (dockScreen && dockScreen.geometry && dockScreen.geometry.width) {
|
||||
return dockScreen.geometry.width - 200;
|
||||
}
|
||||
return 1720;
|
||||
}
|
||||
|
||||
readonly property real availableScreenHeight: {
|
||||
if (root.isVertical && maxAvailableLength > 0) {
|
||||
return maxAvailableLength * 0.9; // 10% padding
|
||||
}
|
||||
if (dockScreen && dockScreen.geometry && dockScreen.geometry.height) {
|
||||
return dockScreen.geometry.height - 100;
|
||||
}
|
||||
return 980;
|
||||
return count * (iconSize * 1.2) + Math.max(0, count - 1) * layoutFlow.spacing;
|
||||
}
|
||||
readonly property real baseAppHeight: iconSize
|
||||
|
||||
clip: false
|
||||
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
||||
implicitHeight: isVertical ? appLayout.width : appLayout.height
|
||||
|
||||
function dockIndexToPinnedIndex(dockIndex) {
|
||||
if (!SettingsData.dockLauncherEnabled) {
|
||||
if (!SettingsData.dockLauncherEnabled)
|
||||
return dockIndex;
|
||||
}
|
||||
|
||||
const launcherPos = SessionData.dockLauncherPosition;
|
||||
if (dockIndex < launcherPos) {
|
||||
return dockIndex;
|
||||
} else {
|
||||
return dockIndex - 1;
|
||||
}
|
||||
return dockIndex < launcherPos ? dockIndex : dockIndex - 1;
|
||||
}
|
||||
|
||||
function movePinnedApp(fromDockIndex, toDockIndex) {
|
||||
console.warn("movePinnedApp: dock", fromDockIndex, "->", toDockIndex);
|
||||
|
||||
const fromPinnedIndex = dockIndexToPinnedIndex(fromDockIndex);
|
||||
const toPinnedIndex = dockIndexToPinnedIndex(toDockIndex);
|
||||
|
||||
console.warn(" Converted to pinned indices:", fromPinnedIndex, "->", toPinnedIndex);
|
||||
|
||||
if (fromPinnedIndex === toPinnedIndex) {
|
||||
console.warn(" Same pinned index, skipping");
|
||||
if (fromPinnedIndex === toPinnedIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||
console.warn(" Current pinned count:", currentPinned.length);
|
||||
|
||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length) {
|
||||
console.warn(" Invalid pinned indices! from:", fromPinnedIndex, "to:", toPinnedIndex, "length:", currentPinned.length);
|
||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length)
|
||||
return;
|
||||
}
|
||||
|
||||
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
||||
console.warn(" Moving app:", movedApp);
|
||||
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
||||
|
||||
SessionData.setPinnedApps(currentPinned);
|
||||
console.warn(" Move complete");
|
||||
}
|
||||
|
||||
function movePinnedAppByPinnedIndex(fromPinnedIndex, toPinnedIndex) {
|
||||
console.warn("movePinnedAppByPinnedIndex:", fromPinnedIndex, "->", toPinnedIndex);
|
||||
|
||||
if (fromPinnedIndex === toPinnedIndex) {
|
||||
console.warn(" Same index, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||
console.warn(" Current pinned count:", currentPinned.length);
|
||||
|
||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length) {
|
||||
console.warn(" Invalid indices! from:", fromPinnedIndex, "to:", toPinnedIndex, "length:", currentPinned.length);
|
||||
return;
|
||||
}
|
||||
|
||||
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
||||
console.warn(" Moving app:", movedApp);
|
||||
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
||||
|
||||
SessionData.setPinnedApps(currentPinned);
|
||||
console.warn(" Move complete");
|
||||
}
|
||||
|
||||
function pinnedIndexForDockIndex(dockIndex) {
|
||||
const items = repeater.dockItems || [];
|
||||
let pinnedIndex = 0;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (item.type === "separator" || item.type === "overflow-toggle" || item.type === "launcher") {
|
||||
continue;
|
||||
}
|
||||
if (!(item.type === "pinned" || item.type === "grouped") || !item.isPinned) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i === dockIndex) {
|
||||
return pinnedIndex;
|
||||
}
|
||||
|
||||
pinnedIndex++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: appLayout
|
||||
width: layoutFlickable.width
|
||||
height: layoutFlickable.height
|
||||
|
||||
// Clip when scrolling to prevent apps from overlapping dock bounds
|
||||
clip: root.canScroll
|
||||
|
||||
// Anchoring for proper dock positioning
|
||||
width: layoutFlow.width
|
||||
height: layoutFlow.height
|
||||
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
|
||||
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
|
||||
anchors.left: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined
|
||||
anchors.right: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined
|
||||
anchors.top: root.isVertical ? undefined : parent.top
|
||||
|
||||
Flickable {
|
||||
id: layoutFlickable
|
||||
width: {
|
||||
if (!root.isVertical) {
|
||||
const contentWidth = layoutFlow.childrenRect.width;
|
||||
return Math.min(contentWidth, root.availableScreenWidth);
|
||||
}
|
||||
return layoutFlow.childrenRect.width;
|
||||
}
|
||||
height: {
|
||||
if (root.isVertical) {
|
||||
const contentHeight = layoutFlow.childrenRect.height;
|
||||
return Math.min(contentHeight, root.availableScreenHeight);
|
||||
}
|
||||
return layoutFlow.childrenRect.height;
|
||||
}
|
||||
contentWidth: layoutFlow.childrenRect.width
|
||||
contentHeight: layoutFlow.childrenRect.height
|
||||
// Don't clip - let indicators extend beyond. Parent appLayout clips for scrolling.
|
||||
clip: false
|
||||
flickableDirection: root.isVertical ? Flickable.VerticalFlick : Flickable.HorizontalFlick
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
// Smooth scrolling
|
||||
maximumFlickVelocity: 2500
|
||||
flickDeceleration: 1500
|
||||
|
||||
Flow {
|
||||
id: layoutFlow
|
||||
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
||||
@@ -220,13 +109,10 @@ Item {
|
||||
function getCoreAppData(appId) {
|
||||
if (typeof AppSearchService === "undefined")
|
||||
return null;
|
||||
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
const app = coreApps[i];
|
||||
if (app.builtInPluginId === appId) {
|
||||
return app;
|
||||
}
|
||||
if (coreApps[i].builtInPluginId === appId)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -234,85 +120,28 @@ Item {
|
||||
function getCoreAppDataByTitle(windowTitle) {
|
||||
if (typeof AppSearchService === "undefined" || !windowTitle)
|
||||
return null;
|
||||
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
const app = coreApps[i];
|
||||
if (app.name === windowTitle) {
|
||||
return app;
|
||||
}
|
||||
if (coreApps[i].name === windowTitle)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function insertLauncher(targetArray) {
|
||||
if (!SettingsData.dockLauncherEnabled)
|
||||
return;
|
||||
|
||||
const launcherItem = {
|
||||
uniqueKey: "launcher_button",
|
||||
type: "launcher",
|
||||
appId: "__LAUNCHER__",
|
||||
toplevel: null,
|
||||
isPinned: true,
|
||||
isRunning: false
|
||||
};
|
||||
|
||||
const pos = Math.max(0, Math.min(SessionData.dockLauncherPosition, targetArray.length));
|
||||
targetArray.splice(pos, 0, launcherItem);
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
function buildBaseItems() {
|
||||
const items = [];
|
||||
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||
const allToplevels = CompositorService.sortedToplevels;
|
||||
const sortedToplevels = (SettingsData.dockIsolateDisplays && root.dockScreen) ? allToplevels.filter(t => isOnScreen(t, root.dockScreen.name)) : allToplevels;
|
||||
const runningAppIds = new Set();
|
||||
const windowItems = [];
|
||||
|
||||
if (!root.groupByApp) {
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
let uniqueKey = "window_" + index;
|
||||
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values);
|
||||
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
||||
if (hyprlandToplevels[i].wayland === toplevel) {
|
||||
uniqueKey = "window_" + hyprlandToplevels[i].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
const moddedAppId = Paths.moddedAppId(rawAppId);
|
||||
|
||||
let coreAppData = null;
|
||||
let isCoreApp = false;
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData) {
|
||||
isCoreApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
const finalAppId = isCoreApp ? coreAppData.builtInPluginId : moddedAppId;
|
||||
|
||||
windowItems.push({
|
||||
uniqueKey: uniqueKey,
|
||||
type: "window",
|
||||
appId: finalAppId,
|
||||
toplevel: toplevel,
|
||||
isPinned: false,
|
||||
isRunning: true,
|
||||
isCoreApp: isCoreApp,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
|
||||
runningAppIds.add(finalAppId);
|
||||
});
|
||||
}
|
||||
|
||||
if (root.groupByApp) {
|
||||
return buildGroupedItems(pinnedApps, sortedToplevels);
|
||||
}
|
||||
return buildUngroupedItems(pinnedApps, sortedToplevels);
|
||||
}
|
||||
|
||||
function buildGroupedItems(pinnedApps, sortedToplevels) {
|
||||
const items = [];
|
||||
const appGroups = new Map();
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
@@ -330,14 +159,13 @@ Item {
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
let appId = Paths.moddedAppId(rawAppId);
|
||||
|
||||
let coreAppData = null;
|
||||
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData) {
|
||||
if (coreAppData)
|
||||
appId = coreAppData.builtInPluginId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appGroups.has(appId)) {
|
||||
appGroups.set(appId, {
|
||||
@@ -348,7 +176,6 @@ Item {
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
}
|
||||
|
||||
appGroups.get(appId).windows.push({
|
||||
toplevel: toplevel,
|
||||
index: index
|
||||
@@ -358,9 +185,8 @@ Item {
|
||||
const pinnedGroups = [];
|
||||
const unpinnedGroups = [];
|
||||
|
||||
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||
appGroups.forEach((group, appId) => {
|
||||
const firstWindow = group.windows.length > 0 ? group.windows[0] : null;
|
||||
|
||||
const item = {
|
||||
uniqueKey: "grouped_" + appId,
|
||||
type: "grouped",
|
||||
@@ -373,32 +199,67 @@ Item {
|
||||
isCoreApp: group.isCoreApp || false,
|
||||
coreAppData: group.coreAppData || null
|
||||
};
|
||||
|
||||
if (group.isPinned) {
|
||||
pinnedGroups.push(item);
|
||||
} else {
|
||||
unpinnedGroups.push(item);
|
||||
}
|
||||
(group.isPinned ? pinnedGroups : unpinnedGroups).push(item);
|
||||
});
|
||||
|
||||
pinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
insertLauncher(items);
|
||||
|
||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||
items.push({
|
||||
uniqueKey: "separator_grouped",
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
items.push(createSeparator("separator_grouped"));
|
||||
}
|
||||
unpinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
return {
|
||||
items,
|
||||
pinnedCount: pinnedGroups.length,
|
||||
runningCount: unpinnedGroups.length
|
||||
};
|
||||
}
|
||||
|
||||
unpinnedGroups.forEach(item => items.push(item));
|
||||
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
} else {
|
||||
function buildUngroupedItems(pinnedApps, sortedToplevels) {
|
||||
const items = [];
|
||||
const runningAppIds = new Set();
|
||||
const windowItems = [];
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
let uniqueKey = "window_" + index;
|
||||
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values);
|
||||
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
||||
if (hyprlandToplevels[i].wayland === toplevel) {
|
||||
uniqueKey = "window_" + hyprlandToplevels[i].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
const moddedAppId = Paths.moddedAppId(rawAppId);
|
||||
let coreAppData = null;
|
||||
let isCoreApp = false;
|
||||
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData)
|
||||
isCoreApp = true;
|
||||
}
|
||||
|
||||
const finalAppId = isCoreApp ? coreAppData.builtInPluginId : moddedAppId;
|
||||
windowItems.push({
|
||||
uniqueKey: uniqueKey,
|
||||
type: "window",
|
||||
appId: finalAppId,
|
||||
toplevel: toplevel,
|
||||
isPinned: false,
|
||||
isRunning: true,
|
||||
isCoreApp: isCoreApp,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
runningAppIds.add(finalAppId);
|
||||
});
|
||||
|
||||
const remainingWindowItems = windowItems.slice();
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
@@ -429,117 +290,48 @@ Item {
|
||||
});
|
||||
|
||||
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
|
||||
insertLauncher(items);
|
||||
|
||||
if (pinnedApps.length > 0 && remainingWindowItems.length > 0) {
|
||||
items.push({
|
||||
uniqueKey: "separator_ungrouped",
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
items.push(createSeparator("separator_ungrouped"));
|
||||
}
|
||||
remainingWindowItems.forEach(item => items.push(item));
|
||||
|
||||
return {
|
||||
items,
|
||||
pinnedCount: pinnedApps.length,
|
||||
runningCount: remainingWindowItems.length
|
||||
};
|
||||
}
|
||||
|
||||
// Overflow logic
|
||||
const countableItems = items.filter(item => (item.type === "pinned" || item.type === "grouped" || item.type === "window") && item.isPinned && item.appId !== "__LAUNCHER__");
|
||||
|
||||
const hideRunningItems = root.maxVisibleRunningApps === 0;
|
||||
let runningItems = [];
|
||||
if (!hideRunningItems) {
|
||||
if (root.groupByApp) {
|
||||
runningItems = items.filter(item => item.type === "grouped" && item.isRunning && !item.isPinned);
|
||||
} else {
|
||||
runningItems = items.filter(item => item.type === "window" && item.isRunning && !item.isPinned);
|
||||
}
|
||||
function insertLauncher(targetArray) {
|
||||
if (!SettingsData.dockLauncherEnabled)
|
||||
return;
|
||||
const launcherItem = {
|
||||
uniqueKey: "launcher_button",
|
||||
type: "launcher",
|
||||
appId: "__LAUNCHER__",
|
||||
toplevel: null,
|
||||
isPinned: true,
|
||||
isRunning: false
|
||||
};
|
||||
const pos = Math.max(0, Math.min(SessionData.dockLauncherPosition, targetArray.length));
|
||||
targetArray.splice(pos, 0, launcherItem);
|
||||
}
|
||||
|
||||
let uniqueRunningItems = runningItems;
|
||||
let duplicateRunningItems = [];
|
||||
|
||||
if (!root.groupByApp && runningItems.length > 0) {
|
||||
const pinnedAppIds = new Set(items.filter(item => item.isPinned).map(item => item.appId));
|
||||
const seenRunningIds = new Set();
|
||||
uniqueRunningItems = [];
|
||||
duplicateRunningItems = [];
|
||||
|
||||
for (let i = 0; i < runningItems.length; i++) {
|
||||
const item = runningItems[i];
|
||||
if (pinnedAppIds.has(item.appId) || seenRunningIds.has(item.appId)) {
|
||||
duplicateRunningItems.push(item);
|
||||
continue;
|
||||
}
|
||||
seenRunningIds.add(item.appId);
|
||||
uniqueRunningItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
const pinnedOverflowNeeded = root.maxVisibleApps > 0 && countableItems.length > root.maxVisibleApps;
|
||||
const runningOverflowNeeded = !hideRunningItems && root.maxVisibleRunningApps > 0 && uniqueRunningItems.length > root.maxVisibleRunningApps;
|
||||
const overflowNeeded = pinnedOverflowNeeded || runningOverflowNeeded;
|
||||
|
||||
if (overflowNeeded) {
|
||||
const visibleCountable = pinnedOverflowNeeded ? countableItems.slice(0, root.maxVisibleApps) : countableItems.slice(0, countableItems.length);
|
||||
const overflowCountable = pinnedOverflowNeeded ? countableItems.slice(root.maxVisibleApps) : [];
|
||||
|
||||
const visibleRunning = !hideRunningItems ? uniqueRunningItems.slice(0, root.maxVisibleRunningApps) : [];
|
||||
const overflowRunning = !hideRunningItems ? uniqueRunningItems.slice(root.maxVisibleRunningApps) : [];
|
||||
const combinedOverflowRunning = duplicateRunningItems.concat(overflowRunning);
|
||||
|
||||
const finalItems = [];
|
||||
|
||||
// Add items in order, preserving launcher position
|
||||
let visibleIndex = 0;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (item.type === "launcher") {
|
||||
finalItems.push(item);
|
||||
} else if (visibleIndex < visibleCountable.length && item.uniqueKey === visibleCountable[visibleIndex].uniqueKey) {
|
||||
finalItems.push(item);
|
||||
visibleIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
const totalOverflowCount = overflowCountable.length + combinedOverflowRunning.length;
|
||||
const hasSeparator = items.some(item => item.type === "separator");
|
||||
const shouldShowSeparator = hasSeparator && (visibleRunning.length > 0 || totalOverflowCount > 0);
|
||||
|
||||
if (shouldShowSeparator) {
|
||||
finalItems.push({
|
||||
uniqueKey: "separator",
|
||||
function createSeparator(key) {
|
||||
return {
|
||||
uniqueKey: key,
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleRunning.length; i++) {
|
||||
finalItems.push(visibleRunning[i]);
|
||||
}
|
||||
|
||||
if (totalOverflowCount > 0) {
|
||||
finalItems.push({
|
||||
uniqueKey: "overflow_toggle",
|
||||
type: "overflow-toggle",
|
||||
appId: "__OVERFLOW_TOGGLE__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false,
|
||||
overflowCount: totalOverflowCount
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < overflowCountable.length; i++) {
|
||||
const item = overflowCountable[i];
|
||||
const overflowItem = {
|
||||
function markAsOverflow(item) {
|
||||
return {
|
||||
uniqueKey: item.uniqueKey,
|
||||
type: item.type,
|
||||
appId: item.appId,
|
||||
@@ -552,66 +344,112 @@ Item {
|
||||
coreAppData: item.coreAppData,
|
||||
isInOverflow: true
|
||||
};
|
||||
finalItems.push(overflowItem);
|
||||
}
|
||||
|
||||
for (let i = 0; i < combinedOverflowRunning.length; i++) {
|
||||
const item = combinedOverflowRunning[i];
|
||||
const overflowItem = {
|
||||
uniqueKey: item.uniqueKey,
|
||||
type: item.type,
|
||||
appId: item.appId,
|
||||
toplevel: item.toplevel,
|
||||
isPinned: false,
|
||||
isRunning: true,
|
||||
windowCount: item.windowCount,
|
||||
allWindows: item.allWindows,
|
||||
isCoreApp: item.isCoreApp,
|
||||
coreAppData: item.coreAppData,
|
||||
isInOverflow: true
|
||||
};
|
||||
finalItems.push(overflowItem);
|
||||
}
|
||||
function applyOverflow(baseResult) {
|
||||
const {
|
||||
items
|
||||
} = baseResult;
|
||||
const maxPinned = root.maxVisibleApps;
|
||||
const maxRunning = root.maxVisibleRunningApps;
|
||||
const hideRunning = maxRunning === 0;
|
||||
|
||||
root.overflowItemCount = totalOverflowCount;
|
||||
root.pinnedAppCount = countableItems.length;
|
||||
dockItems = finalItems;
|
||||
} else {
|
||||
const pinnedItems = items.filter(i => (i.type === "pinned" || i.type === "grouped" || i.type === "window") && i.isPinned && i.appId !== "__LAUNCHER__");
|
||||
const runningItems = hideRunning ? [] : items.filter(i => (i.type === "window" || i.type === "grouped") && i.isRunning && !i.isPinned);
|
||||
|
||||
const pinnedOverflow = maxPinned > 0 && pinnedItems.length > maxPinned;
|
||||
const runningOverflow = !hideRunning && maxRunning > 0 && runningItems.length > maxRunning;
|
||||
|
||||
if (!pinnedOverflow && !runningOverflow) {
|
||||
root.overflowItemCount = 0;
|
||||
root.pinnedAppCount = countableItems.length;
|
||||
if (hideRunning) {
|
||||
return items.filter(i => !((i.type === "window" || i.type === "grouped") && i.isRunning && !i.isPinned) && i.type !== "separator");
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
if (hideRunningItems) {
|
||||
const filteredItems = items.filter(item => !((item.type === "window" || item.type === "grouped") && item.isRunning && !item.isPinned) && item.type !== "separator");
|
||||
dockItems = filteredItems;
|
||||
const visiblePinnedKeys = new Set(pinnedOverflow ? pinnedItems.slice(0, maxPinned).map(i => i.uniqueKey) : pinnedItems.map(i => i.uniqueKey));
|
||||
const visibleRunningKeys = new Set(runningOverflow ? runningItems.slice(0, maxRunning).map(i => i.uniqueKey) : runningItems.map(i => i.uniqueKey));
|
||||
|
||||
const overflowPinnedCount = pinnedOverflow ? pinnedItems.length - maxPinned : 0;
|
||||
const overflowRunningCount = runningOverflow ? runningItems.length - maxRunning : 0;
|
||||
const totalOverflow = overflowPinnedCount + overflowRunningCount;
|
||||
root.overflowItemCount = totalOverflow;
|
||||
|
||||
const finalItems = [];
|
||||
let addedSeparator = false;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
switch (item.type) {
|
||||
case "launcher":
|
||||
finalItems.push(item);
|
||||
break;
|
||||
case "separator":
|
||||
break;
|
||||
case "pinned":
|
||||
case "grouped":
|
||||
case "window":
|
||||
if (item.isPinned && item.appId !== "__LAUNCHER__") {
|
||||
if (visiblePinnedKeys.has(item.uniqueKey)) {
|
||||
finalItems.push(item);
|
||||
} else {
|
||||
dockItems = items;
|
||||
finalItems.push(markAsOverflow(item));
|
||||
}
|
||||
} else if (item.isRunning && !item.isPinned) {
|
||||
if (!addedSeparator && finalItems.length > 0) {
|
||||
finalItems.push(createSeparator("separator_overflow"));
|
||||
addedSeparator = true;
|
||||
}
|
||||
if (visibleRunningKeys.has(item.uniqueKey)) {
|
||||
finalItems.push(item);
|
||||
} else if (!hideRunning) {
|
||||
finalItems.push(markAsOverflow(item));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalOverflow > 0) {
|
||||
const toggleIndex = finalItems.findIndex(i => i.type === "separator");
|
||||
const insertPos = toggleIndex >= 0 ? toggleIndex : finalItems.length;
|
||||
finalItems.splice(insertPos, 0, {
|
||||
uniqueKey: "overflow_toggle",
|
||||
type: "overflow-toggle",
|
||||
appId: "__OVERFLOW_TOGGLE__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false,
|
||||
overflowCount: totalOverflow
|
||||
});
|
||||
}
|
||||
|
||||
return finalItems;
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
const baseResult = buildBaseItems();
|
||||
dockItems = applyOverflow(baseResult);
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
|
||||
property var dockButton: itemData.type === "launcher" ? launcherButton : button
|
||||
property var itemData: modelData
|
||||
readonly property bool isOverflowToggle: itemData.type === "overflow-toggle"
|
||||
readonly property bool isInOverflow: itemData.isInOverflow === true
|
||||
|
||||
clip: false
|
||||
z: (itemData.type === "launcher" ? launcherButton.dragging : button.dragging) ? 100 : 0
|
||||
|
||||
// Overflow items: hidden when collapsed, visible when expanded
|
||||
visible: !isInOverflow || root.overflowExpanded
|
||||
|
||||
// Overflow items collapse to 0 size when hidden
|
||||
width: (isInOverflow && !root.overflowExpanded) ? 0 :
|
||||
(itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) :
|
||||
(root.isVertical ? root.iconSize : root.iconSize * 1.2))
|
||||
height: (isInOverflow && !root.overflowExpanded) ? 0 :
|
||||
(itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) :
|
||||
(root.isVertical ? root.iconSize * 1.2 : root.iconSize))
|
||||
|
||||
opacity: (isInOverflow && !root.overflowExpanded) ? 0 : 1
|
||||
scale: (isInOverflow && !root.overflowExpanded) ? 0.8 : 1
|
||||
|
||||
width: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2))
|
||||
height: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize))
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
@@ -626,21 +464,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// No Behavior on width/height - they must change immediately
|
||||
// so Flow layout properly excludes hidden overflow items
|
||||
// Visual smoothness comes from opacity and scale animations
|
||||
|
||||
property real shiftOffset: {
|
||||
if (root.draggedIndex < 0 || !itemData.isPinned || itemData.type === "separator")
|
||||
return 0;
|
||||
const myIdx = root.draggingPinned ? root.pinnedIndexForDockIndex(model.index) : model.index;
|
||||
if (myIdx < 0)
|
||||
return 0;
|
||||
if (myIdx === root.draggedIndex)
|
||||
if (model.index === root.draggedIndex)
|
||||
return 0;
|
||||
|
||||
const dragIdx = root.draggedIndex;
|
||||
const dropIdx = root.dropTargetIndex;
|
||||
const myIdx = model.index;
|
||||
const shiftAmount = root.iconSize * 1.2 + layoutFlow.spacing;
|
||||
|
||||
if (dropIdx < 0)
|
||||
@@ -699,11 +531,9 @@ Item {
|
||||
id: launcherButton
|
||||
visible: itemData.type === "launcher"
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: delegateItem.width
|
||||
height: delegateItem.height
|
||||
actualIconSize: root.iconSize
|
||||
|
||||
dockApps: root
|
||||
index: model.index
|
||||
}
|
||||
@@ -712,17 +542,14 @@ Item {
|
||||
id: button
|
||||
visible: !isOverflowToggle && itemData.type !== "separator" && itemData.type !== "launcher"
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: delegateItem.width
|
||||
height: delegateItem.height
|
||||
actualIconSize: root.iconSize
|
||||
|
||||
appData: itemData
|
||||
contextMenu: root.contextMenu
|
||||
dockApps: root
|
||||
index: model.index
|
||||
parentDockScreen: root.dockScreen
|
||||
|
||||
showWindowTitle: itemData?.type === "window" || itemData?.type === "grouped"
|
||||
windowTitle: {
|
||||
const title = itemData?.toplevel?.title || "(Unnamed)";
|
||||
@@ -733,7 +560,6 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CompositorService
|
||||
@@ -748,7 +574,15 @@ Item {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
root.draggingPinned = false;
|
||||
repeater.updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
function onDockLauncherPositionChanged() {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
repeater.updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
@@ -756,10 +590,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
onGroupByAppChanged: repeater.updateModel()
|
||||
// Don't rebuild model on overflow toggle - delegates react to overflowExpanded via bindings
|
||||
// Model structure doesn't change, only delegate visibility changes
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onDockIsolateDisplaysChanged() {
|
||||
@@ -774,32 +604,13 @@ Item {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onDockLauncherPositionChanged() {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
repeater.updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onDockMaxVisibleAppsChanged() {
|
||||
repeater.updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onDockMaxVisibleRunningAppsChanged() {
|
||||
repeater.updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
onGroupByAppChanged: repeater.updateModel()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -23,20 +22,20 @@ PanelWindow {
|
||||
|
||||
function showForButton(button, data, dockHeight, hidePinOption, entry, dockScreen, parentDockApps) {
|
||||
if (dockScreen) {
|
||||
root.screen = dockScreen
|
||||
root.screen = dockScreen;
|
||||
}
|
||||
|
||||
anchorItem = button
|
||||
appData = data
|
||||
dockVisibleHeight = dockHeight || 40
|
||||
hidePin = hidePinOption || false
|
||||
desktopEntry = entry || null
|
||||
dockApps = parentDockApps || null
|
||||
anchorItem = button;
|
||||
appData = data;
|
||||
dockVisibleHeight = dockHeight || 40;
|
||||
hidePin = hidePinOption || false;
|
||||
desktopEntry = entry || null;
|
||||
dockApps = parentDockApps || null;
|
||||
|
||||
visible = true
|
||||
visible = true;
|
||||
}
|
||||
function close() {
|
||||
visible = false
|
||||
visible = false;
|
||||
}
|
||||
|
||||
screen: null
|
||||
@@ -57,110 +56,110 @@ PanelWindow {
|
||||
onAnchorItemChanged: updatePosition()
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
updatePosition()
|
||||
updatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
if (!anchorItem) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
|
||||
return
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const dockWindow = anchorItem.Window.window
|
||||
const dockWindow = anchorItem.Window.window;
|
||||
if (!dockWindow) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
|
||||
return
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const buttonPosInDock = anchorItem.mapToItem(dockWindow.contentItem, 0, 0)
|
||||
let actualDockHeight = root.dockVisibleHeight
|
||||
const buttonPosInDock = anchorItem.mapToItem(dockWindow.contentItem, 0, 0);
|
||||
let actualDockHeight = root.dockVisibleHeight;
|
||||
|
||||
function findDockBackground(item) {
|
||||
if (item.objectName === "dockBackground") {
|
||||
return item
|
||||
return item;
|
||||
}
|
||||
for (var i = 0; i < item.children.length; i++) {
|
||||
const found = findDockBackground(item.children[i])
|
||||
const found = findDockBackground(item.children[i]);
|
||||
if (found) {
|
||||
return found
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const dockBackground = findDockBackground(dockWindow.contentItem)
|
||||
let actualDockWidth = dockWindow.width
|
||||
const dockBackground = findDockBackground(dockWindow.contentItem);
|
||||
let actualDockWidth = dockWindow.width;
|
||||
if (dockBackground) {
|
||||
actualDockHeight = dockBackground.height
|
||||
actualDockWidth = dockBackground.width
|
||||
actualDockHeight = dockBackground.height;
|
||||
actualDockWidth = dockBackground.width;
|
||||
}
|
||||
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const dockMargin = SettingsData.dockMargin + 16
|
||||
let buttonScreenX, buttonScreenY
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
const dockMargin = SettingsData.dockMargin + 16;
|
||||
let buttonScreenX, buttonScreenY;
|
||||
|
||||
if (isVertical) {
|
||||
const dockContentHeight = dockWindow.height
|
||||
const screenHeight = root.screen.height
|
||||
const dockTopMargin = Math.round((screenHeight - dockContentHeight) / 2)
|
||||
buttonScreenY = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2
|
||||
const dockContentHeight = dockWindow.height;
|
||||
const screenHeight = root.screen.height;
|
||||
const dockTopMargin = Math.round((screenHeight - dockContentHeight) / 2);
|
||||
buttonScreenY = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
||||
|
||||
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
||||
buttonScreenX = root.screen.width - actualDockWidth - dockMargin - 20
|
||||
buttonScreenX = root.screen.width - actualDockWidth - dockMargin - 20;
|
||||
} else {
|
||||
buttonScreenX = actualDockWidth + dockMargin + 20
|
||||
buttonScreenX = actualDockWidth + dockMargin + 20;
|
||||
}
|
||||
} else {
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
||||
|
||||
if (isDockAtBottom) {
|
||||
buttonScreenY = root.screen.height - actualDockHeight - dockMargin - 20
|
||||
buttonScreenY = root.screen.height - actualDockHeight - dockMargin - 20;
|
||||
} else {
|
||||
buttonScreenY = actualDockHeight + dockMargin + 20
|
||||
buttonScreenY = actualDockHeight + dockMargin + 20;
|
||||
}
|
||||
|
||||
const dockContentWidth = dockWindow.width
|
||||
const screenWidth = root.screen.width
|
||||
const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2)
|
||||
buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2
|
||||
const dockContentWidth = dockWindow.width;
|
||||
const screenWidth = root.screen.width;
|
||||
const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2);
|
||||
buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
||||
}
|
||||
|
||||
anchorPos = Qt.point(buttonScreenX, buttonScreenY)
|
||||
anchorPos = Qt.point(buttonScreenX, buttonScreenY);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menuContainer
|
||||
|
||||
x: {
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isVertical) {
|
||||
const isDockAtRight = SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isDockAtRight = SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isDockAtRight) {
|
||||
return Math.max(10, root.anchorPos.x - width + 30)
|
||||
return Math.max(10, root.anchorPos.x - width + 30);
|
||||
} else {
|
||||
return Math.min(root.width - width - 10, root.anchorPos.x - 30)
|
||||
return Math.min(root.width - width - 10, root.anchorPos.x - 30);
|
||||
}
|
||||
} else {
|
||||
const left = 10
|
||||
const right = root.width - width - 10
|
||||
const want = root.anchorPos.x - width / 2
|
||||
return Math.max(left, Math.min(right, want))
|
||||
const left = 10;
|
||||
const right = root.width - width - 10;
|
||||
const want = root.anchorPos.x - width / 2;
|
||||
return Math.max(left, Math.min(right, want));
|
||||
}
|
||||
}
|
||||
y: {
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isVertical) {
|
||||
const top = 10
|
||||
const bottom = root.height - height - 10
|
||||
const want = root.anchorPos.y - height / 2
|
||||
return Math.max(top, Math.min(bottom, want))
|
||||
const top = 10;
|
||||
const bottom = root.height - height - 10;
|
||||
const want = root.anchorPos.y - height / 2;
|
||||
return Math.max(top, Math.min(bottom, want));
|
||||
} else {
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
||||
if (isDockAtBottom) {
|
||||
return Math.max(10, root.anchorPos.y - height + 30)
|
||||
return Math.max(10, root.anchorPos.y - height + 30);
|
||||
} else {
|
||||
return Math.min(root.height - height - 10, root.anchorPos.y - 30)
|
||||
return Math.min(root.height - height - 10, root.anchorPos.y - 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,17 +203,18 @@ PanelWindow {
|
||||
// Window list for grouped apps
|
||||
Repeater {
|
||||
model: {
|
||||
if (!root.appData || root.appData.type !== "grouped") return []
|
||||
if (!root.appData || root.appData.type !== "grouped")
|
||||
return [];
|
||||
|
||||
const toplevels = []
|
||||
const allToplevels = ToplevelManager.toplevels.values
|
||||
const toplevels = [];
|
||||
const allToplevels = ToplevelManager.toplevels.values;
|
||||
for (let i = 0; i < allToplevels.length; i++) {
|
||||
const toplevel = allToplevels[i]
|
||||
const toplevel = allToplevels[i];
|
||||
if (toplevel.appId === root.appData.appId) {
|
||||
toplevels.push(toplevel)
|
||||
toplevels.push(toplevel);
|
||||
}
|
||||
}
|
||||
return toplevels
|
||||
return toplevels;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -261,9 +261,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.close) {
|
||||
modelData.close()
|
||||
modelData.close();
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,9 +276,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.activate) {
|
||||
modelData.activate()
|
||||
modelData.activate();
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,9 +286,11 @@ PanelWindow {
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.appData) return false
|
||||
if (root.appData.type !== "grouped") return false
|
||||
return root.appData.windowCount > 0
|
||||
if (!root.appData)
|
||||
return false;
|
||||
if (root.appData.type !== "grouped")
|
||||
return false;
|
||||
return root.appData.windowCount > 0;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -345,9 +347,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData) {
|
||||
SessionService.launchDesktopAction(root.desktopEntry, modelData)
|
||||
SessionService.launchDesktopAction(root.desktopEntry, modelData);
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,9 +358,9 @@ PanelWindow {
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.desktopEntry?.actions || root.desktopEntry.actions.length === 0) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand)
|
||||
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand);
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -392,36 +394,26 @@ PanelWindow {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (!root.appData) {
|
||||
return
|
||||
}
|
||||
if (root.appData.isPinned) {
|
||||
SessionData.removePinnedApp(root.appData.appId)
|
||||
} else {
|
||||
SessionData.addPinnedApp(root.appData.appId)
|
||||
if (!root.appData)
|
||||
return;
|
||||
|
||||
// Auto-expand overflow if pinning would exceed limit
|
||||
if (root.dockApps) {
|
||||
Qt.callLater(() => {
|
||||
const newPinnedCount = SessionData.pinnedApps.length;
|
||||
if (newPinnedCount > root.dockApps.maxVisibleApps && root.dockApps.overflowItemCount > 0) {
|
||||
root.dockApps.overflowExpanded = true;
|
||||
if (root.appData.isPinned) {
|
||||
SessionData.removePinnedApp(root.appData.appId);
|
||||
} else {
|
||||
SessionData.addPinnedApp(root.appData.appId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand
|
||||
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0))
|
||||
const hasPinOption = !root.hidePin
|
||||
const hasContentAbove = hasPinOption || hasNvidia
|
||||
return hasContentAbove && hasWindow
|
||||
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand;
|
||||
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0));
|
||||
const hasPinOption = !root.hidePin;
|
||||
const hasContentAbove = hasPinOption || hasNvidia;
|
||||
return hasContentAbove && hasWindow;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -456,9 +448,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.desktopEntry) {
|
||||
SessionService.launchDesktopEntry(root.desktopEntry, true)
|
||||
SessionService.launchDesktopEntry(root.desktopEntry, true);
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,9 +470,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (root.appData && root.appData.type === "grouped") {
|
||||
return "Close All Windows"
|
||||
return "Close All Windows";
|
||||
}
|
||||
return "Close Window"
|
||||
return "Close Window";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
@@ -496,11 +488,11 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.appData?.type === "window") {
|
||||
root.appData?.toplevel?.close()
|
||||
root.appData?.toplevel?.close();
|
||||
} else if (root.appData?.type === "grouped") {
|
||||
root.appData?.allWindows?.forEach(window => window.toplevel?.close())
|
||||
root.appData?.allWindows?.forEach(window => window.toplevel?.close());
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,20 +115,9 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
// Prevent stealing during drag operations
|
||||
// Also prevent stealing when NOT in scroll mode (original behavior)
|
||||
// Only allow Flickable to steal when scrollable AND not dragging
|
||||
preventStealing: dragging || longPressing || !(dockApps && dockApps.canScroll)
|
||||
preventStealing: dragging || longPressing
|
||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onWheel: (wheel) => {
|
||||
// Only handle wheel if we're NOT in scrollable mode
|
||||
if (dockApps && dockApps.canScroll) {
|
||||
wheel.accepted = false // Allow event to propagate to Flickable
|
||||
} else {
|
||||
wheel.accepted = true // Consume event (no scrolling needed)
|
||||
}
|
||||
}
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
@@ -11,7 +10,7 @@ Item {
|
||||
property bool overflowExpanded: false
|
||||
property bool isVertical: false
|
||||
|
||||
signal clicked()
|
||||
signal clicked
|
||||
|
||||
Rectangle {
|
||||
id: buttonBackground
|
||||
@@ -22,7 +21,9 @@ Item {
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, mouseArea.containsMouse ? 0.2 : 0.1)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
@@ -31,16 +32,7 @@ Item {
|
||||
size: actualIconSize * 0.6
|
||||
name: "expand_more"
|
||||
color: Theme.surfaceText
|
||||
|
||||
// For horizontal docks, rotate -90° to point right (collapsed), then 90° to point left (expanded)
|
||||
// For vertical docks, keep default (down arrow = 0°), flip 180° to point up (expanded)
|
||||
rotation: {
|
||||
if (isVertical) {
|
||||
return overflowExpanded ? 180 : 0;
|
||||
} else {
|
||||
return overflowExpanded ? 90 : -90;
|
||||
}
|
||||
}
|
||||
rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90)
|
||||
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
@@ -51,7 +43,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Badge showing overflow count (outside main rectangle to avoid clipping)
|
||||
Rectangle {
|
||||
visible: overflowCount > 0 && !overflowExpanded && SettingsData.dockShowOverflowBadge
|
||||
anchors.right: buttonBackground.right
|
||||
@@ -79,10 +70,6 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onWheel: (wheel) => {
|
||||
// Always allow wheel events to propagate to Flickable for scrolling
|
||||
wheel.accepted = false
|
||||
}
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +166,11 @@ Item {
|
||||
SettingsSliderRow {
|
||||
settingKey: "dockMaxVisibleApps"
|
||||
tags: ["dock", "overflow", "max", "apps", "limit"]
|
||||
text: I18n.tr("Max Number of Pinned Apps Before Overflow")
|
||||
minimum: 3
|
||||
maximum: 20
|
||||
text: I18n.tr("Max Pinned Apps (0 = Unlimited)")
|
||||
minimum: 0
|
||||
maximum: 30
|
||||
value: SettingsData.dockMaxVisibleApps
|
||||
defaultValue: 10
|
||||
defaultValue: 0
|
||||
unit: ""
|
||||
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleApps", newValue)
|
||||
}
|
||||
@@ -178,11 +178,11 @@ Item {
|
||||
SettingsSliderRow {
|
||||
settingKey: "dockMaxVisibleRunningApps"
|
||||
tags: ["dock", "overflow", "max", "running", "apps", "limit"]
|
||||
text: I18n.tr("Max Open Running Apps Before Overflow")
|
||||
text: I18n.tr("Max Running Apps (0 = Unlimited)")
|
||||
minimum: 0
|
||||
maximum: 20
|
||||
maximum: 30
|
||||
value: SettingsData.dockMaxVisibleRunningApps
|
||||
defaultValue: 10
|
||||
defaultValue: 0
|
||||
unit: ""
|
||||
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleRunningApps", newValue)
|
||||
}
|
||||
@@ -191,7 +191,7 @@ Item {
|
||||
settingKey: "dockShowOverflowBadge"
|
||||
tags: ["dock", "overflow", "badge", "count", "indicator"]
|
||||
text: I18n.tr("Show Overflow Badge Count")
|
||||
description: I18n.tr("Display a badge with the number of apps in overflow")
|
||||
description: I18n.tr("Displays count when overflow is active")
|
||||
checked: SettingsData.dockShowOverflowBadge
|
||||
onToggled: checked => SettingsData.set("dockShowOverflowBadge", checked)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user