diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2026-05-06 13:56:12 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-06 13:56:12 +0100 |
| commit | 1681bea42dd2f11ba3fe6df05560d0b231de3c76 (patch) | |
| tree | 899103dbddf675a01758c7cdd379224c9c81a7fb /example | |
| parent | 5f1350f522d3fe3af78cbe30a98311763b3509d5 (diff) | |
config/lua: add simple layout API (#14258)
Adds a simple layout lua api
Diffstat (limited to 'example')
| -rw-r--r-- | example/layouts/columns.lua | 12 | ||||
| -rw-r--r-- | example/layouts/grid.lua | 14 | ||||
| -rw-r--r-- | example/layouts/manual.lua | 144 | ||||
| -rw-r--r-- | example/layouts/spiral.lua | 55 |
4 files changed, 225 insertions, 0 deletions
diff --git a/example/layouts/columns.lua b/example/layouts/columns.lua new file mode 100644 index 00000000..dc907cd4 --- /dev/null +++ b/example/layouts/columns.lua @@ -0,0 +1,12 @@ +hl.layout.register("columns", { + recalculate = function(ctx) + local n = #ctx.targets + if n == 0 then + return + end + + for i, target in ipairs(ctx.targets) do + target:place(ctx:column(i, n)) + end + end, +}) diff --git a/example/layouts/grid.lua b/example/layouts/grid.lua new file mode 100644 index 00000000..10a0cab1 --- /dev/null +++ b/example/layouts/grid.lua @@ -0,0 +1,14 @@ +hl.layout.register("grid", { + recalculate = function(ctx) + local n = #ctx.targets + if n == 0 then + return + end + + local cols = math.ceil(math.sqrt(n)) + + for i, target in ipairs(ctx.targets) do + target:place(ctx:grid_cell(i, cols)) + end + end, +}) diff --git a/example/layouts/manual.lua b/example/layouts/manual.lua new file mode 100644 index 00000000..1ba2ebd0 --- /dev/null +++ b/example/layouts/manual.lua @@ -0,0 +1,144 @@ +local state = { + order = {}, + split = {}, + default_split = "h", +} + +local function target_id(target) + local window = target.window + return window and tostring(window.stable_id) or tostring(target.index) +end + +local function active_id(ctx) + for _, target in ipairs(ctx.targets) do + local window = target.window + if window and window.active then + return target_id(target) + end + end + + return state.order[#state.order] +end + +local function index_of(tbl, value) + for i, v in ipairs(tbl) do + if v == value then + return i + end + end +end + +local function sync_order(ctx) + local present = {} + local targets = {} + + for _, target in ipairs(ctx.targets) do + local id = target_id(target) + present[id] = true + targets[id] = target + end + + local old_order = state.order + state.order = {} + + for _, id in ipairs(old_order) do + if present[id] then + table.insert(state.order, id) + else + state.split[id] = nil + end + end + + local focused = active_id(ctx) + for _, target in ipairs(ctx.targets) do + local id = target_id(target) + if not index_of(state.order, id) then + local after = focused and index_of(state.order, focused) + table.insert(state.order, after and (after + 1) or (#state.order + 1), id) + end + end + + return targets +end + +local function place_chain(ctx, targets, ids, area, i) + if i > #ids then + return + end + + local target = targets[ids[i]] + if not target then + return + end + + if i == #ids then + target:place(area) + return + end + + local split = state.split[ids[i]] or state.default_split + if split == "v" then + target:place(ctx:split(area, "top", 0.5)) + place_chain(ctx, targets, ids, ctx:split(area, "bottom", 0.5), i + 1) + else + target:place(ctx:split(area, "left", 0.5)) + place_chain(ctx, targets, ids, ctx:split(area, "right", 0.5), i + 1) + end +end + +local function move_active(ctx, delta) + local id = active_id(ctx) + local i = id and index_of(state.order, id) + local j = i and (i + delta) + + if not i or j < 1 or j > #state.order then + return + end + + state.order[i], state.order[j] = state.order[j], state.order[i] +end + +hl.layout.register("manual", { + recalculate = function(ctx) + local targets = sync_order(ctx) + place_chain(ctx, targets, state.order, ctx.area, 1) + end, + + layout_msg = function(ctx, msg) + local id = active_id(ctx) + local command = msg:match("^(%S+)") + + if command == "splith" or command == "h" then + if id then + state.split[id] = "h" + end + elseif command == "splitv" or command == "v" then + if id then + state.split[id] = "v" + end + elseif command == "splittoggle" or command == "toggle" then + if id then + state.split[id] = state.split[id] == "v" and "h" or "v" + end + elseif command == "promote" then + local i = id and index_of(state.order, id) + if i then + table.remove(state.order, i) + table.insert(state.order, 1, id) + end + elseif command == "swapnext" then + move_active(ctx, 1) + elseif command == "swapprev" then + move_active(ctx, -1) + elseif command == "rotate" then + for k, v in pairs(state.split) do + state.split[k] = v == "v" and "h" or "v" + end + state.default_split = state.default_split == "v" and "h" or "v" + else + return "manual: expected splith, splitv, splittoggle, promote, swapnext, swapprev, or rotate" + end + + return true + end, +}) diff --git a/example/layouts/spiral.lua b/example/layouts/spiral.lua new file mode 100644 index 00000000..e97f4dbd --- /dev/null +++ b/example/layouts/spiral.lua @@ -0,0 +1,55 @@ +local state = { + ratio = 0.58, + offset = 0, +} + +local sides = { "left", "top", "right", "bottom" } +local opposite = { + left = "right", + right = "left", + top = "bottom", + bottom = "top", +} + +local function clamp(x, min, max) + return math.max(min, math.min(max, x)) +end + +hl.layout.register("spiral", { + recalculate = function(ctx) + local n = #ctx.targets + if n == 0 then + return + end + + local area = ctx.area + + for i, target in ipairs(ctx.targets) do + if i == n then + target:place(area) + else + local side = sides[((i - 1 + state.offset) % #sides) + 1] + target:place(ctx:split(area, side, state.ratio)) + area = ctx:split(area, opposite[side], 1.0 - state.ratio) + end + end + end, + + layout_msg = function(ctx, msg) + local command, arg = msg:match("^(%S+)%s*(.*)$") + + if command == "ratio" then + state.ratio = clamp(tonumber(arg) or state.ratio, 0.1, 0.9) + elseif command == "grow" then + state.ratio = clamp(state.ratio + 0.05, 0.1, 0.9) + elseif command == "shrink" then + state.ratio = clamp(state.ratio - 0.05, 0.1, 0.9) + elseif command == "rotate" then + state.offset = (state.offset + 1) % #sides + else + return "spiral: expected ratio <0.1..0.9>, grow, shrink, or rotate" + end + + return true + end, +}) |
