1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
pragma Singleton
import Quickshell
import Quickshell.Hyprland
Singleton {
id: root
function doLayout(windowList, outerWidth, outerHeight, maxSplits) {
var N = windowList.length
if (N === 0) return []
if (maxSplits === undefined) maxSplits = 3
// Standard Gap: 0.8% of screen
var rawGap = outerWidth * 0.008
var gap = Math.max(8, Math.min(24, rawGap))
// Primary Gap: The space between the first Big Window and the rest.
// We make it 3x larger than the standard gap for emphasis.
var primaryGap = gap * 3
// Safe Area (90%)
var contentScale = 0.90
var useW = outerWidth * contentScale
var useH = outerHeight * contentScale
var offX = (outerWidth - useW) / 2
var offY = (outerHeight - useH) / 2
// Move Active Window to start
var activeAddr = Hyprland.activeToplevel?.lastIpcObject?.address
if (activeAddr) {
var activeIdx = windowList.findIndex(it => it.lastIpcObject.address === activeAddr)
if (activeIdx !== -1) {
windowList = [windowList[activeIdx], ...windowList.filter(it => it !== windowList[activeIdx])]
}
}
var result = []
// Working area cursor
var curX = offX
var curY = offY
var curW = useW
var curH = useH
// Items to process in Spiral mode
var spiralCount = Math.min(N - 1, maxSplits)
// Spiral cuts
for (var k = 0; k < spiralCount; k++) {
var sItem = windowList[k]
var sBoxW, sBoxH
var sBoxX = curX
var sBoxY = curY
// Logic change: Use 'primaryGap' only for the very first cut (k=0),
// otherwise use standard 'gap'.
var currentGap = (k === 0) ? primaryGap : gap
if (curW > curH) { // Split Vertical
// Calculate width subtracting the specific gap for this iteration
sBoxW = (curW - currentGap) / 2
sBoxH = curH
// Shift working area for next items by the specific gap
curX += sBoxW + currentGap
curW -= (sBoxW + currentGap)
} else { // Split Horizontal
sBoxW = curW
sBoxH = (curH - currentGap) / 2
// Shift working area for next items by the specific gap
curY += sBoxH + currentGap
curH -= (sBoxH + currentGap)
}
// Aspect Fit
var sw0 = (sItem.width > 0) ? sItem.width : 100
var sh0 = (sItem.height > 0) ? sItem.height : 100
var sScale = Math.min(sBoxW / sw0, sBoxH / sh0)
result.push({
win: sItem.win,
x: sBoxX + (sBoxW - (sw0 * sScale))/2,
y: sBoxY + (sBoxH - (sh0 * sScale))/2,
width: sw0 * sScale,
height: sh0 * sScale,
isSpiral: true,
index: k
})
}
// Overflow grid
var remainingItems = windowList.slice(spiralCount)
var remN = remainingItems.length
if (remN > 0) {
// Standard Grid logic for the remaining box
var bestCols = 1
var bestScale = 0
var TARGET_ASPECT = 16.0/9.0
for (var c = 1; c <= remN; c++) {
var r = Math.ceil(remN / c)
var avW = curW - gap * (c - 1)
var avH = curH - gap * (r - 1)
if (avW <= 0 || avH <= 0) continue
var cW = avW / c
var cH = avH / r
var sc = Math.min(cW / TARGET_ASPECT, cH)
if (sc > bestScale) {
bestScale = sc
bestCols = c
}
}
var remRows = Math.ceil(remN / bestCols)
var finalCellW = (curW - gap * (bestCols - 1)) / bestCols
var finalCellH = (curH - gap * (remRows - 1)) / remRows
var gridContentH = remRows * finalCellH + (remRows - 1) * gap
var gridStartY = curY + (curH - gridContentH) / 2
for (var j = 0; j < remN; j++) {
var rItem = remainingItems[j]
var row = Math.floor(j / bestCols)
var col = j % bestCols
var itemsInRow = Math.min((row + 1) * bestCols, remN) - (row * bestCols)
var rowW = itemsInRow * finalCellW + (itemsInRow - 1) * gap
var rowStartX = curX + (curW - rowW) / 2
var cellX = rowStartX + col * (finalCellW + gap)
var cellY = gridStartY + row * (finalCellH + gap)
var rw0 = (rItem.width > 0) ? rItem.width : 100
var rh0 = (rItem.height > 0) ? rItem.height : 100
var rSc = Math.min(finalCellW / rw0, finalCellH / rh0)
result.push({
win: rItem.win,
x: cellX + (finalCellW - (rw0 * rSc))/2,
y: cellY + (finalCellH - (rh0 * rSc))/2,
width: rw0 * rSc,
height: rh0 * rSc,
isSpiral: false
})
}
}
return result
}
}
|