mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-30 00:12:50 -05:00
feat: Dock overflow & New setting options
This commit is contained in:
@@ -443,6 +443,9 @@ Singleton {
|
|||||||
property int dockLauncherLogoSizeOffset: 0
|
property int dockLauncherLogoSizeOffset: 0
|
||||||
property real dockLauncherLogoBrightness: 0.5
|
property real dockLauncherLogoBrightness: 0.5
|
||||||
property real dockLauncherLogoContrast: 1
|
property real dockLauncherLogoContrast: 1
|
||||||
|
property int dockMaxVisibleApps: 10
|
||||||
|
property int dockMaxVisibleRunningApps: 10
|
||||||
|
property bool dockShowOverflowBadge: true
|
||||||
|
|
||||||
property bool notificationOverlayEnabled: false
|
property bool notificationOverlayEnabled: false
|
||||||
property int overviewRows: 2
|
property int overviewRows: 2
|
||||||
|
|||||||
@@ -272,6 +272,9 @@ var SPEC = {
|
|||||||
dockLauncherLogoSizeOffset: { def: 0 },
|
dockLauncherLogoSizeOffset: { def: 0 },
|
||||||
dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
|
dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
|
||||||
dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
|
dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
|
||||||
|
dockMaxVisibleApps: { def: 10 },
|
||||||
|
dockMaxVisibleRunningApps: { def: 10 },
|
||||||
|
dockShowOverflowBadge: { def: true },
|
||||||
|
|
||||||
notificationOverlayEnabled: { def: false },
|
notificationOverlayEnabled: { def: false },
|
||||||
overviewRows: { def: 2, persist: false },
|
overviewRows: { def: 2, persist: false },
|
||||||
|
|||||||
@@ -533,7 +533,6 @@ Variants {
|
|||||||
anchors {
|
anchors {
|
||||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined) : undefined
|
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined) : undefined
|
||||||
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : 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
|
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined) : undefined
|
||||||
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
||||||
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
||||||
@@ -543,11 +542,23 @@ Variants {
|
|||||||
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
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
|
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)
|
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)
|
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2)
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
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
|
layer.enabled: true
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
@@ -625,13 +636,12 @@ Variants {
|
|||||||
|
|
||||||
anchors.top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top ? dockBackground.top : undefined) : undefined
|
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.bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? dockBackground.bottom : undefined) : undefined
|
||||||
anchors.horizontalCenter: !dock.isVertical ? dockBackground.horizontalCenter : undefined
|
anchors.left: !dock.isVertical ? dockBackground.left : (SettingsData.dockPosition === SettingsData.Position.Left ? dockBackground.left : 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.right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? dockBackground.right : undefined) : undefined
|
||||||
anchors.verticalCenter: dock.isVertical ? dockBackground.verticalCenter : undefined
|
anchors.verticalCenter: dock.isVertical ? dockBackground.verticalCenter : undefined
|
||||||
anchors.topMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
anchors.topMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
anchors.bottomMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
anchors.bottomMargin: !dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
anchors.leftMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
anchors.leftMargin: SettingsData.dockSpacing
|
||||||
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0
|
||||||
|
|
||||||
contextMenu: dockVariants.contextMenu
|
contextMenu: dockVariants.contextMenu
|
||||||
@@ -639,6 +649,27 @@ Variants {
|
|||||||
isVertical: dock.isVertical
|
isVertical: dock.isVertical
|
||||||
dockScreen: dock.screen
|
dockScreen: dock.screen
|
||||||
iconSize: dock.widgetHeight
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ Item {
|
|||||||
property bool showTooltip: mouseArea.containsMouse && !dragging
|
property bool showTooltip: mouseArea.containsMouse && !dragging
|
||||||
property var cachedDesktopEntry: null
|
property var cachedDesktopEntry: null
|
||||||
property real actualIconSize: 40
|
property real actualIconSize: 40
|
||||||
|
property bool shouldShowIndicator: {
|
||||||
|
if (!appData)
|
||||||
|
return false;
|
||||||
|
if (appData.type === "window")
|
||||||
|
return true;
|
||||||
|
if (appData.type === "grouped")
|
||||||
|
return appData.windowCount > 0;
|
||||||
|
return appData.isRunning;
|
||||||
|
}
|
||||||
readonly property string coreIconColorOverride: SettingsData.dockLauncherLogoColorOverride
|
readonly property string coreIconColorOverride: SettingsData.dockLauncherLogoColorOverride
|
||||||
readonly property bool coreIconHasCustomColor: coreIconColorOverride !== "" && coreIconColorOverride !== "primary" && coreIconColorOverride !== "surface"
|
readonly property bool coreIconHasCustomColor: coreIconColorOverride !== "" && coreIconColorOverride !== "primary" && coreIconColorOverride !== "surface"
|
||||||
readonly property color effectiveCoreIconColor: {
|
readonly property color effectiveCoreIconColor: {
|
||||||
@@ -206,9 +215,20 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
enabled: true
|
enabled: true
|
||||||
preventStealing: 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)
|
||||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
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 => {
|
onPressed: mouse => {
|
||||||
if (mouse.button === Qt.LeftButton && appData && appData.isPinned) {
|
if (mouse.button === Qt.LeftButton && appData && appData.isPinned) {
|
||||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||||
@@ -221,8 +241,14 @@ Item {
|
|||||||
const wasDragging = dragging;
|
const wasDragging = dragging;
|
||||||
const didReorder = wasDragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps;
|
const didReorder = wasDragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps;
|
||||||
|
|
||||||
if (didReorder)
|
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)
|
||||||
dockApps.movePinnedApp(originalIndex, targetIndex);
|
dockApps.movePinnedApp(originalIndex, targetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
longPressing = false;
|
longPressing = false;
|
||||||
dragging = false;
|
dragging = false;
|
||||||
@@ -295,7 +321,7 @@ Item {
|
|||||||
groupedToplevel.activate();
|
groupedToplevel.activate();
|
||||||
} else if (contextMenu) {
|
} else if (contextMenu) {
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -305,6 +331,7 @@ Item {
|
|||||||
const distance = Math.sqrt(Math.pow(mouse.x - dragStartPos.x, 2) + Math.pow(mouse.y - dragStartPos.y, 2));
|
const distance = Math.sqrt(Math.pow(mouse.x - dragStartPos.x, 2) + Math.pow(mouse.y - dragStartPos.y, 2));
|
||||||
if (distance > 5) {
|
if (distance > 5) {
|
||||||
dragging = true;
|
dragging = true;
|
||||||
|
// Use dock index directly (original behavior)
|
||||||
targetIndex = index;
|
targetIndex = index;
|
||||||
originalIndex = index;
|
originalIndex = index;
|
||||||
if (dockApps) {
|
if (dockApps) {
|
||||||
@@ -323,6 +350,7 @@ Item {
|
|||||||
const spacing = Math.min(8, Math.max(4, actualIconSize * 0.08));
|
const spacing = Math.min(8, Math.max(4, actualIconSize * 0.08));
|
||||||
const itemSize = actualIconSize * 1.2 + spacing;
|
const itemSize = actualIconSize * 1.2 + spacing;
|
||||||
const slotOffset = Math.round(axisOffset / itemSize);
|
const slotOffset = Math.round(axisOffset / itemSize);
|
||||||
|
// Use pinnedAppCount as max (original behavior)
|
||||||
const newTargetIndex = Math.max(0, Math.min(dockApps.pinnedAppCount - 1, originalIndex + slotOffset));
|
const newTargetIndex = Math.max(0, Math.min(dockApps.pinnedAppCount - 1, originalIndex + slotOffset));
|
||||||
|
|
||||||
if (newTargetIndex !== targetIndex) {
|
if (newTargetIndex !== targetIndex) {
|
||||||
@@ -342,7 +370,7 @@ Item {
|
|||||||
case "grouped":
|
case "grouped":
|
||||||
if (contextMenu) {
|
if (contextMenu) {
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -365,7 +393,7 @@ Item {
|
|||||||
if (!contextMenu)
|
if (!contextMenu)
|
||||||
return;
|
return;
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,15 +526,7 @@ Item {
|
|||||||
|
|
||||||
sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator
|
sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator
|
||||||
|
|
||||||
visible: {
|
visible: root.shouldShowIndicator
|
||||||
if (!appData)
|
|
||||||
return false;
|
|
||||||
if (appData.type === "window")
|
|
||||||
return true;
|
|
||||||
if (appData.type === "grouped")
|
|
||||||
return appData.windowCount > 0;
|
|
||||||
return appData.isRunning;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,42 @@ Item {
|
|||||||
property int draggedIndex: -1
|
property int draggedIndex: -1
|
||||||
property int dropTargetIndex: -1
|
property int dropTargetIndex: -1
|
||||||
property bool suppressShiftAnimation: false
|
property bool suppressShiftAnimation: false
|
||||||
|
property int maxVisibleApps: SettingsData.dockMaxVisibleApps
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
return layoutFlow.childrenRect.height > availableScreenHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
clip: false
|
clip: false
|
||||||
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
||||||
@@ -36,34 +72,124 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function movePinnedApp(fromDockIndex, toDockIndex) {
|
function movePinnedApp(fromDockIndex, toDockIndex) {
|
||||||
|
console.warn("movePinnedApp: dock", fromDockIndex, "->", toDockIndex);
|
||||||
|
|
||||||
const fromPinnedIndex = dockIndexToPinnedIndex(fromDockIndex);
|
const fromPinnedIndex = dockIndexToPinnedIndex(fromDockIndex);
|
||||||
const toPinnedIndex = dockIndexToPinnedIndex(toDockIndex);
|
const toPinnedIndex = dockIndexToPinnedIndex(toDockIndex);
|
||||||
|
|
||||||
|
console.warn(" Converted to pinned indices:", fromPinnedIndex, "->", toPinnedIndex);
|
||||||
|
|
||||||
if (fromPinnedIndex === toPinnedIndex) {
|
if (fromPinnedIndex === toPinnedIndex) {
|
||||||
|
console.warn(" Same pinned index, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPinned = [...(SessionData.pinnedApps || [])];
|
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||||
|
console.warn(" Current pinned count:", currentPinned.length);
|
||||||
|
|
||||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= 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);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
||||||
|
console.warn(" Moving app:", movedApp);
|
||||||
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
||||||
|
|
||||||
SessionData.setPinnedApps(currentPinned);
|
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 {
|
Item {
|
||||||
id: appLayout
|
id: appLayout
|
||||||
width: layoutFlow.width
|
width: layoutFlickable.width
|
||||||
height: layoutFlow.height
|
height: layoutFlickable.height
|
||||||
|
|
||||||
|
// Clip when scrolling to prevent apps from overlapping dock bounds
|
||||||
|
clip: root.canScroll
|
||||||
|
|
||||||
|
// Anchoring for proper dock positioning
|
||||||
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
|
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
|
||||||
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
|
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
|
||||||
anchors.left: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : 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.right: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined
|
||||||
anchors.top: root.isVertical ? undefined : parent.top
|
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 {
|
Flow {
|
||||||
id: layoutFlow
|
id: layoutFlow
|
||||||
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
||||||
@@ -141,6 +267,50 @@ Item {
|
|||||||
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||||
const allToplevels = CompositorService.sortedToplevels;
|
const allToplevels = CompositorService.sortedToplevels;
|
||||||
const sortedToplevels = (SettingsData.dockIsolateDisplays && root.dockScreen) ? allToplevels.filter(t => isOnScreen(t, root.dockScreen.name)) : allToplevels;
|
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) {
|
if (root.groupByApp) {
|
||||||
const appGroups = new Map();
|
const appGroups = new Map();
|
||||||
@@ -229,26 +399,40 @@ Item {
|
|||||||
unpinnedGroups.forEach(item => items.push(item));
|
unpinnedGroups.forEach(item => items.push(item));
|
||||||
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||||
} else {
|
} else {
|
||||||
|
const remainingWindowItems = windowItems.slice();
|
||||||
|
|
||||||
pinnedApps.forEach(rawAppId => {
|
pinnedApps.forEach(rawAppId => {
|
||||||
const appId = Paths.moddedAppId(rawAppId);
|
const appId = Paths.moddedAppId(rawAppId);
|
||||||
const coreAppData = getCoreAppData(appId);
|
const coreAppData = getCoreAppData(appId);
|
||||||
|
const matchIndex = remainingWindowItems.findIndex(item => item.appId === appId);
|
||||||
|
|
||||||
|
if (matchIndex !== -1) {
|
||||||
|
const windowItem = remainingWindowItems.splice(matchIndex, 1)[0];
|
||||||
|
windowItem.isPinned = true;
|
||||||
|
if (!windowItem.isCoreApp && coreAppData) {
|
||||||
|
windowItem.isCoreApp = true;
|
||||||
|
windowItem.coreAppData = coreAppData;
|
||||||
|
}
|
||||||
|
items.push(windowItem);
|
||||||
|
} else {
|
||||||
items.push({
|
items.push({
|
||||||
uniqueKey: "pinned_" + appId,
|
uniqueKey: "pinned_" + appId,
|
||||||
type: "pinned",
|
type: "pinned",
|
||||||
appId: appId,
|
appId: appId,
|
||||||
toplevel: null,
|
toplevel: null,
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
isRunning: false,
|
isRunning: runningAppIds.has(appId),
|
||||||
isCoreApp: coreAppData !== null,
|
isCoreApp: coreAppData !== null,
|
||||||
coreAppData: coreAppData
|
coreAppData: coreAppData
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||||
|
|
||||||
insertLauncher(items);
|
insertLauncher(items);
|
||||||
|
|
||||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
if (pinnedApps.length > 0 && remainingWindowItems.length > 0) {
|
||||||
items.push({
|
items.push({
|
||||||
uniqueKey: "separator_ungrouped",
|
uniqueKey: "separator_ungrouped",
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@@ -258,70 +442,205 @@ Item {
|
|||||||
isRunning: false
|
isRunning: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
remainingWindowItems.forEach(item => items.push(item));
|
||||||
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";
|
// Overflow logic
|
||||||
const moddedAppId = Paths.moddedAppId(rawAppId);
|
const countableItems = items.filter(item => (item.type === "pinned" || item.type === "grouped" || item.type === "window") && item.isPinned && item.appId !== "__LAUNCHER__");
|
||||||
|
|
||||||
// Check if this is a core app window (e.g., Settings modal with appId "org.quickshell")
|
const hideRunningItems = root.maxVisibleRunningApps === 0;
|
||||||
let coreAppData = null;
|
let runningItems = [];
|
||||||
let isCoreApp = false;
|
if (!hideRunningItems) {
|
||||||
if (rawAppId === "org.quickshell") {
|
if (root.groupByApp) {
|
||||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
runningItems = items.filter(item => item.type === "grouped" && item.isRunning && !item.isPinned);
|
||||||
if (coreAppData) {
|
} else {
|
||||||
isCoreApp = true;
|
runningItems = items.filter(item => item.type === "window" && item.isRunning && !item.isPinned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalAppId = isCoreApp ? coreAppData.builtInPluginId : moddedAppId;
|
let uniqueRunningItems = runningItems;
|
||||||
const isPinned = pinnedApps.indexOf(finalAppId) !== -1;
|
let duplicateRunningItems = [];
|
||||||
|
|
||||||
items.push({
|
if (!root.groupByApp && runningItems.length > 0) {
|
||||||
uniqueKey: uniqueKey,
|
const pinnedAppIds = new Set(items.filter(item => item.isPinned).map(item => item.appId));
|
||||||
type: "window",
|
const seenRunningIds = new Set();
|
||||||
appId: finalAppId,
|
uniqueRunningItems = [];
|
||||||
toplevel: toplevel,
|
duplicateRunningItems = [];
|
||||||
isPinned: isPinned,
|
|
||||||
|
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",
|
||||||
|
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 = {
|
||||||
|
uniqueKey: item.uniqueKey,
|
||||||
|
type: item.type,
|
||||||
|
appId: item.appId,
|
||||||
|
toplevel: item.toplevel,
|
||||||
|
isPinned: item.isPinned,
|
||||||
|
isRunning: item.isRunning,
|
||||||
|
windowCount: item.windowCount,
|
||||||
|
allWindows: item.allWindows,
|
||||||
|
isCoreApp: item.isCoreApp,
|
||||||
|
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,
|
isRunning: true,
|
||||||
isCoreApp: isCoreApp,
|
windowCount: item.windowCount,
|
||||||
coreAppData: coreAppData
|
allWindows: item.allWindows,
|
||||||
});
|
isCoreApp: item.isCoreApp,
|
||||||
});
|
coreAppData: item.coreAppData,
|
||||||
|
isInOverflow: true
|
||||||
|
};
|
||||||
|
finalItems.push(overflowItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.overflowItemCount = totalOverflowCount;
|
||||||
|
root.pinnedAppCount = countableItems.length;
|
||||||
|
dockItems = finalItems;
|
||||||
|
} else {
|
||||||
|
root.overflowItemCount = 0;
|
||||||
|
root.pinnedAppCount = countableItems.length;
|
||||||
|
|
||||||
|
if (hideRunningItems) {
|
||||||
|
const filteredItems = items.filter(item => !((item.type === "window" || item.type === "grouped") && item.isRunning && !item.isPinned) && item.type !== "separator");
|
||||||
|
dockItems = filteredItems;
|
||||||
|
} else {
|
||||||
dockItems = items;
|
dockItems = items;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
property var dockButton: itemData.type === "launcher" ? launcherButton : button
|
property var dockButton: itemData.type === "launcher" ? launcherButton : button
|
||||||
property var itemData: modelData
|
property var itemData: modelData
|
||||||
|
readonly property bool isOverflowToggle: itemData.type === "overflow-toggle"
|
||||||
|
readonly property bool isInOverflow: itemData.isInOverflow === true
|
||||||
clip: false
|
clip: false
|
||||||
z: (itemData.type === "launcher" ? launcherButton.dragging : button.dragging) ? 100 : 0
|
z: (itemData.type === "launcher" ? launcherButton.dragging : button.dragging) ? 100 : 0
|
||||||
|
|
||||||
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
// Overflow items: hidden when collapsed, visible when expanded
|
||||||
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
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
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: {
|
property real shiftOffset: {
|
||||||
if (root.draggedIndex < 0 || !itemData.isPinned || itemData.type === "separator")
|
if (root.draggedIndex < 0 || !itemData.isPinned || itemData.type === "separator")
|
||||||
return 0;
|
return 0;
|
||||||
if (model.index === root.draggedIndex)
|
const myIdx = root.draggingPinned ? root.pinnedIndexForDockIndex(model.index) : model.index;
|
||||||
|
if (myIdx < 0)
|
||||||
|
return 0;
|
||||||
|
if (myIdx === root.draggedIndex)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const dragIdx = root.draggedIndex;
|
const dragIdx = root.draggedIndex;
|
||||||
const dropIdx = root.dropTargetIndex;
|
const dropIdx = root.dropTargetIndex;
|
||||||
const myIdx = model.index;
|
|
||||||
const shiftAmount = root.iconSize * 1.2 + layoutFlow.spacing;
|
const shiftAmount = root.iconSize * 1.2 + layoutFlow.spacing;
|
||||||
|
|
||||||
if (dropIdx < 0)
|
if (dropIdx < 0)
|
||||||
@@ -363,6 +682,19 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DockOverflowButton {
|
||||||
|
id: overflowButton
|
||||||
|
visible: isOverflowToggle
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: delegateItem.width
|
||||||
|
height: delegateItem.height
|
||||||
|
actualIconSize: root.iconSize
|
||||||
|
overflowCount: itemData.overflowCount || 0
|
||||||
|
overflowExpanded: root.overflowExpanded
|
||||||
|
isVertical: root.isVertical
|
||||||
|
onClicked: root.overflowExpanded = !root.overflowExpanded
|
||||||
|
}
|
||||||
|
|
||||||
DockLauncherButton {
|
DockLauncherButton {
|
||||||
id: launcherButton
|
id: launcherButton
|
||||||
visible: itemData.type === "launcher"
|
visible: itemData.type === "launcher"
|
||||||
@@ -378,7 +710,7 @@ Item {
|
|||||||
|
|
||||||
DockAppButton {
|
DockAppButton {
|
||||||
id: button
|
id: button
|
||||||
visible: itemData.type !== "separator" && itemData.type !== "launcher"
|
visible: !isOverflowToggle && itemData.type !== "separator" && itemData.type !== "launcher"
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
width: delegateItem.width
|
width: delegateItem.width
|
||||||
@@ -401,6 +733,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: CompositorService
|
target: CompositorService
|
||||||
@@ -415,6 +748,7 @@ Item {
|
|||||||
root.suppressShiftAnimation = true;
|
root.suppressShiftAnimation = true;
|
||||||
root.draggedIndex = -1;
|
root.draggedIndex = -1;
|
||||||
root.dropTargetIndex = -1;
|
root.dropTargetIndex = -1;
|
||||||
|
root.draggingPinned = false;
|
||||||
repeater.updateModel();
|
repeater.updateModel();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.suppressShiftAnimation = false;
|
root.suppressShiftAnimation = false;
|
||||||
@@ -423,6 +757,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onGroupByAppChanged: repeater.updateModel()
|
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 {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
@@ -452,4 +788,18 @@ Item {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onDockMaxVisibleAppsChanged() {
|
||||||
|
repeater.updateModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onDockMaxVisibleRunningAppsChanged() {
|
||||||
|
repeater.updateModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ PanelWindow {
|
|||||||
property bool hidePin: false
|
property bool hidePin: false
|
||||||
property var desktopEntry: null
|
property var desktopEntry: null
|
||||||
property bool isDmsWindow: appData?.appId === "org.quickshell"
|
property bool isDmsWindow: appData?.appId === "org.quickshell"
|
||||||
|
property var dockApps: null
|
||||||
|
|
||||||
function showForButton(button, data, dockHeight, hidePinOption, entry, dockScreen) {
|
function showForButton(button, data, dockHeight, hidePinOption, entry, dockScreen, parentDockApps) {
|
||||||
if (dockScreen) {
|
if (dockScreen) {
|
||||||
root.screen = dockScreen
|
root.screen = dockScreen
|
||||||
}
|
}
|
||||||
@@ -30,6 +31,7 @@ PanelWindow {
|
|||||||
dockVisibleHeight = dockHeight || 40
|
dockVisibleHeight = dockHeight || 40
|
||||||
hidePin = hidePinOption || false
|
hidePin = hidePinOption || false
|
||||||
desktopEntry = entry || null
|
desktopEntry = entry || null
|
||||||
|
dockApps = parentDockApps || null
|
||||||
|
|
||||||
visible = true
|
visible = true
|
||||||
}
|
}
|
||||||
@@ -397,6 +399,16 @@ PanelWindow {
|
|||||||
SessionData.removePinnedApp(root.appData.appId)
|
SessionData.removePinnedApp(root.appData.appId)
|
||||||
} else {
|
} else {
|
||||||
SessionData.addPinnedApp(root.appData.appId)
|
SessionData.addPinnedApp(root.appData.appId)
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,9 +115,20 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
enabled: true
|
enabled: true
|
||||||
preventStealing: 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)
|
||||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton
|
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 => {
|
onPressed: mouse => {
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||||
|
|||||||
88
quickshell/Modules/Dock/DockOverflowButton.qml
Normal file
88
quickshell/Modules/Dock/DockOverflowButton.qml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real actualIconSize: 40
|
||||||
|
property int overflowCount: 0
|
||||||
|
property bool overflowExpanded: false
|
||||||
|
property bool isVertical: false
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonBackground
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: actualIconSize
|
||||||
|
height: actualIconSize
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: arrowIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Badge showing overflow count (outside main rectangle to avoid clipping)
|
||||||
|
Rectangle {
|
||||||
|
visible: overflowCount > 0 && !overflowExpanded && SettingsData.dockShowOverflowBadge
|
||||||
|
anchors.right: buttonBackground.right
|
||||||
|
anchors.top: buttonBackground.top
|
||||||
|
anchors.rightMargin: -4
|
||||||
|
anchors.topMargin: -4
|
||||||
|
width: Math.max(18, badgeText.width + 8)
|
||||||
|
height: 18
|
||||||
|
radius: 9
|
||||||
|
color: Theme.primary
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: badgeText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: `+${overflowCount}`
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.onPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -162,6 +162,39 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
settingKey: "dockMaxVisibleApps"
|
||||||
|
tags: ["dock", "overflow", "max", "apps", "limit"]
|
||||||
|
text: I18n.tr("Max Number of Pinned Apps Before Overflow")
|
||||||
|
minimum: 3
|
||||||
|
maximum: 20
|
||||||
|
value: SettingsData.dockMaxVisibleApps
|
||||||
|
defaultValue: 10
|
||||||
|
unit: ""
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleApps", newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
settingKey: "dockMaxVisibleRunningApps"
|
||||||
|
tags: ["dock", "overflow", "max", "running", "apps", "limit"]
|
||||||
|
text: I18n.tr("Max Open Running Apps Before Overflow")
|
||||||
|
minimum: 0
|
||||||
|
maximum: 20
|
||||||
|
value: SettingsData.dockMaxVisibleRunningApps
|
||||||
|
defaultValue: 10
|
||||||
|
unit: ""
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleRunningApps", newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
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")
|
||||||
|
checked: SettingsData.dockShowOverflowBadge
|
||||||
|
onToggled: checked => SettingsData.set("dockShowOverflowBadge", checked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
|
|||||||
Reference in New Issue
Block a user