-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.lua
More file actions
163 lines (148 loc) · 5.03 KB
/
Copy pathinit.lua
File metadata and controls
163 lines (148 loc) · 5.03 KB
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
156
157
158
159
160
161
162
163
---@diagnostic disable: undefined-global
-- hyprview: float-grid workspace overview for Hyprland 0.55+ (Lua config).
-- Plugin-free workaround for the 0.55 plugin-dispatcher gap (`hyprexpo:expo`
-- and friends aren't invokable from the Lua bridge). On toggle: every tiled
-- window on the active workspace is floated into a grid; on second toggle,
-- they're unfloated in original column order so the scrolling layout puts
-- them back where they were.
local M = {}
local _state = { active = false, addrs = {} }
local _defaults = {
bind = "SUPER + Tab",
padding = 30,
-- Grid sizing. cols(n) returns the number of columns for n windows.
-- Default: roughly square (ceil(sqrt(n))).
cols = function(n)
return math.ceil(math.sqrt(n))
end,
-- On exit, center the focused column in the scrolling viewport. Useful
-- with narrow `column_width` setups where the focused column would
-- otherwise be left/right of center. Implemented by briefly swapping
-- `scrolling.focus_fit_method` from 1 (fit) to 0 (center), re-focusing
-- the window, then restoring via timer.
center_on_exit = false,
-- focus_fit_method to restore to after re-centering. Set to your normal
-- value (Hyprland default is 1 = fit). Ignored unless center_on_exit.
restore_focus_fit_method = 1,
-- Delay before restoring focus_fit_method (ms). Short enough not to
-- affect subsequent focus changes, long enough for the centering pass
-- to land.
restore_delay_ms = 80,
}
local function shallow_merge(base, over)
local out = {}
for k, v in pairs(base) do
out[k] = v
end
if over then
for k, v in pairs(over) do
out[k] = v
end
end
return out
end
local _opts = _defaults
local function enter()
local mon = hl.get_active_monitor()
local ws = hl.get_active_workspace()
if not mon or not ws then
return
end
-- Hyprland reports unrotated w/h even for rotated monitors; swap on
-- 90°/270° transforms (1, 3, 5, 7 are the odd values).
local mw, mh = mon.width, mon.height
if (mon.transform or 0) % 2 == 1 then
mw, mh = mh, mw
end
local tiled = {}
for _, w in ipairs(hl.get_workspace_windows(ws.id) or {}) do
if not w.floating then
table.insert(tiled, w)
end
end
local n = #tiled
if n == 0 then
return
end
local cols = _opts.cols(n)
if cols < 1 then
cols = 1
end
local rows = math.ceil(n / cols)
local pad = _opts.padding
local cw = math.floor((mw - (cols + 1) * pad) / cols)
local ch = math.floor((mh - (rows + 1) * pad) / rows)
_state.addrs = {}
for i, w in ipairs(tiled) do
local row = math.floor((i - 1) / cols)
local col = (i - 1) % cols
local x = mon.position.x + pad + col * (cw + pad)
local y = mon.position.y + pad + row * (ch + pad)
table.insert(_state.addrs, w.address)
hl.dispatch(hl.dsp.window.float({ action = "set", window = w, value = true }))
hl.dispatch(hl.dsp.window.resize({ window = w, x = cw, y = ch }))
hl.dispatch(hl.dsp.window.move({ window = w, x = x, y = y }))
end
_state.active = true
end
local function exit()
-- Remember whichever window the user had focused. Could be the one
-- they were originally on, or one they clicked while in overview.
local focused = hl.get_active_window()
-- Unfloat in original column order, anchoring each insertion next to
-- the previously-unfloated window so the scrolling layout puts them
-- back in left-to-right (or top-to-bottom on rotated) order.
local by_addr = {}
for _, w in ipairs(hl.get_windows() or {}) do
by_addr[w.address] = w
end
local anchor
for _, addr in ipairs(_state.addrs) do
local w = by_addr[addr]
if w and w.floating then
if anchor then
hl.dispatch(hl.dsp.focus({ window = anchor }))
end
hl.dispatch(hl.dsp.window.float({ action = "toggle", window = w }))
anchor = w
end
end
-- Restore focus to whatever the user was looking at, optionally
-- centering it in the scrolling viewport.
if focused then
if _opts.center_on_exit then
hl.config({ scrolling = { focus_fit_method = 0 } })
hl.dispatch(hl.dsp.focus({ window = focused }))
hl.timer(function()
hl.config({ scrolling = { focus_fit_method = _opts.restore_focus_fit_method } })
end, { timeout = _opts.restore_delay_ms, type = "oneshot" })
else
hl.dispatch(hl.dsp.focus({ window = focused }))
end
end
_state.active = false
_state.addrs = {}
end
function M.toggle()
if _state.active then
exit()
else
enter()
end
end
function M.enter()
enter()
end
function M.exit()
exit()
end
function M.is_active()
return _state.active
end
function M.setup(opts)
_opts = shallow_merge(_defaults, opts)
if _opts.bind then
hl.bind(_opts.bind, M.toggle)
end
end
return M