* feat: add mango_layouts plugin * fix: address CI validation errors * refactor: inline apply_layout script for cross-device portability * fix: commit inlined script changes in panel.luau * fix: address official PR review feedback * fix: revert noctalia.translate which caused runtime crash * fix: remove setGlyph empty string to prevent fallback skull icon * feat: restore translations using correct noctalia.tr API * refactor: remove unused label fields from layouts table
175 lines
5.7 KiB
Luau
175 lines
5.7 KiB
Luau
local layouts = {
|
|
{ sym = "T", name = "tile", glyph = "layout-sidebar" },
|
|
{ sym = "S", name = "scroller", glyph = "carousel-horizontal" },
|
|
{ sym = "M", name = "monocle", glyph = "square" },
|
|
{ sym = "G", name = "grid", glyph = "layout-grid" },
|
|
{ sym = "F", name = "fair", glyph = "layout-board-split" },
|
|
{ sym = "D", name = "deck", glyph = "layers-difference" },
|
|
{ sym = "DW", name = "dwindle", glyph = "layout-2" },
|
|
{ sym = "CT", name = "center_tile", glyph = "layout-distribute-vertical" },
|
|
{ sym = "VT", name = "vertical_tile", glyph = "layout-rows" },
|
|
{ sym = "RT", name = "right_tile", glyph = "layout-sidebar-right" },
|
|
{ sym = "VS", name = "vertical_scroller",glyph = "carousel-vertical" },
|
|
{ sym = "VG", name = "vertical_grid", glyph = "grid-dots" },
|
|
{ sym = "VD", name = "vertical_deck", glyph = "chart-funnel" },
|
|
{ sym = "VF", name = "vertical_fair", glyph = "layout-board" },
|
|
}
|
|
|
|
local COLUMNS = 4
|
|
local COLUMN_GAP = 2
|
|
local selectedLayout = ""
|
|
local pickSlots = {}
|
|
|
|
local render
|
|
|
|
local function onPickAt(index)
|
|
local slot = pickSlots[index]
|
|
if slot == nil then return end
|
|
selectedLayout = slot.sym
|
|
noctalia.runAsync("mmsg dispatch setlayout," .. slot.entry.name)
|
|
panel.close()
|
|
end
|
|
|
|
local function makeGrid(children)
|
|
local rows = {}
|
|
local row = {}
|
|
for i, child in ipairs(children) do
|
|
table.insert(row, child)
|
|
if #row >= COLUMNS or i == #children then
|
|
while #row < COLUMNS do
|
|
table.insert(row, ui.spacer({ flexGrow = 1 }))
|
|
end
|
|
table.insert(rows, ui.row({ gap = COLUMN_GAP, align = "stretch" }, row))
|
|
row = {}
|
|
end
|
|
end
|
|
return ui.column({ gap = COLUMN_GAP }, rows)
|
|
end
|
|
|
|
local function getShowConfig(name)
|
|
if name == "tile" then return noctalia.getConfig("show_tile") end
|
|
if name == "scroller" then return noctalia.getConfig("show_scroller") end
|
|
if name == "monocle" then return noctalia.getConfig("show_monocle") end
|
|
if name == "grid" then return noctalia.getConfig("show_grid") end
|
|
if name == "fair" then return noctalia.getConfig("show_fair") end
|
|
if name == "deck" then return noctalia.getConfig("show_deck") end
|
|
if name == "dwindle" then return noctalia.getConfig("show_dwindle") end
|
|
if name == "center_tile" then return noctalia.getConfig("show_center_tile") end
|
|
if name == "vertical_tile" then return noctalia.getConfig("show_vertical_tile") end
|
|
if name == "right_tile" then return noctalia.getConfig("show_right_tile") end
|
|
if name == "vertical_scroller" then return noctalia.getConfig("show_vertical_scroller") end
|
|
if name == "vertical_grid" then return noctalia.getConfig("show_vertical_grid") end
|
|
if name == "vertical_deck" then return noctalia.getConfig("show_vertical_deck") end
|
|
if name == "vertical_fair" then return noctalia.getConfig("show_vertical_fair") end
|
|
return true
|
|
end
|
|
|
|
local function renderButtons(is_list_mode)
|
|
local buttons = {}
|
|
local i = 0
|
|
for _, entry in ipairs(layouts) do
|
|
local raw_val = getShowConfig(entry.name)
|
|
local enabled = (raw_val == true) or (raw_val == "true") or (raw_val == nil)
|
|
|
|
if enabled then
|
|
local slotIndex = i
|
|
local isSelected = selectedLayout == entry.sym
|
|
pickSlots[slotIndex] = { sym = entry.sym, entry = entry }
|
|
|
|
if is_list_mode then
|
|
table.insert(buttons, ui.button({
|
|
key = entry.sym,
|
|
glyph = entry.glyph,
|
|
text = noctalia.tr("name_" .. entry.name),
|
|
variant = if isSelected then "primary" else "ghost",
|
|
onClick = `onPick{slotIndex}`,
|
|
-- If noctalia supports button alignment, this will align it to the left:
|
|
-- (If not, it will just remain centered like an elegant menu)
|
|
}))
|
|
else
|
|
-- Grid Mode
|
|
table.insert(buttons, ui.column({
|
|
key = entry.sym,
|
|
gap = 2,
|
|
align = "center",
|
|
flexGrow = 1,
|
|
}, {
|
|
ui.button({
|
|
glyph = entry.glyph,
|
|
glyphSize = 18,
|
|
variant = if isSelected then "primary" else "ghost",
|
|
onClick = `onPick{slotIndex}`,
|
|
}),
|
|
ui.label({
|
|
text = noctalia.tr("name_" .. entry.name),
|
|
fontSize = 9,
|
|
fontWeight = "bold",
|
|
color = if isSelected then "primary" else "on_surface_variant",
|
|
textAlign = "center",
|
|
maxLines = 1,
|
|
}),
|
|
}))
|
|
end
|
|
i += 1
|
|
end
|
|
end
|
|
return buttons
|
|
end
|
|
|
|
render = function()
|
|
pickSlots = {}
|
|
|
|
local list_mode_raw = noctalia.getConfig("list_mode")
|
|
local is_list_mode = (list_mode_raw == true) or (list_mode_raw == "true")
|
|
|
|
local content
|
|
if is_list_mode then
|
|
content = ui.column({ padding = 8, gap = 4 }, renderButtons(true))
|
|
else
|
|
content = ui.column({ padding = 12, gap = COLUMN_GAP }, {
|
|
makeGrid(renderButtons(false))
|
|
})
|
|
end
|
|
|
|
panel.render(ui.column({ flexGrow = 1 }, {
|
|
ui.scroll({ flexGrow = 1 }, {
|
|
content
|
|
})
|
|
}))
|
|
end
|
|
|
|
function onOpen(context)
|
|
selectedLayout = ""
|
|
render()
|
|
noctalia.runAsync(
|
|
"mmsg get all-monitors | jq -r '.monitors[] | select(.active == true and .last_open_surface != null) | .layout_symbol'",
|
|
function(result)
|
|
if result == nil or result.exitCode ~= 0 then return end
|
|
local sym = result.stdout:gsub("%s+", "")
|
|
|
|
-- Check if sym exists in our ordered list
|
|
local exists = false
|
|
for _, l in ipairs(layouts) do
|
|
if l.sym == sym then exists = true end
|
|
end
|
|
selectedLayout = if exists then sym else ""
|
|
render()
|
|
end
|
|
)
|
|
end
|
|
|
|
function onPick0() onPickAt(0) end
|
|
function onPick1() onPickAt(1) end
|
|
function onPick2() onPickAt(2) end
|
|
function onPick3() onPickAt(3) end
|
|
function onPick4() onPickAt(4) end
|
|
function onPick5() onPickAt(5) end
|
|
function onPick6() onPickAt(6) end
|
|
function onPick7() onPickAt(7) end
|
|
function onPick8() onPickAt(8) end
|
|
function onPick9() onPickAt(9) end
|
|
function onPick10() onPickAt(10) end
|
|
function onPick11() onPickAt(11) end
|
|
function onPick12() onPickAt(12) end
|
|
function onPick13() onPickAt(13) end
|