feat(astal-menu): Columns count stepper + expanded taskbar is one module
- Columns layout publishes per-workspace column/row counts to columns-state.json and handles a direct "cols +1/-1/N" message. The menu's Columns page now shows a "Columns (−)[N](+)" stepper that reads the live count and drives it. - Expanding the taskbar now fully collapses the strip (header included) via its revealer, so the panel that covers the quads reads as a single module instead of a thin strip plus a detached card below it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SUN7gg6GGfnToghMijLm5Ymain
parent
9d1ec69a11
commit
de666acac7
|
|
@ -25,6 +25,7 @@ from lib.proc import run_json, run_text
|
|||
from paths import CACHE_DIR
|
||||
|
||||
_LAYOUTS_MANIFEST = CACHE_DIR / "layouts.json"
|
||||
_COLUMNS_STATE = CACHE_DIR / "columns-state.json"
|
||||
_LAYOUTS_STATE = CACHE_DIR / "layouts-state.json"
|
||||
|
||||
|
||||
|
|
@ -284,11 +285,46 @@ class Taskbar(Gtk.Box):
|
|||
row.append(sw)
|
||||
page.append(row)
|
||||
self._fit_sws[ly["name"]] = sw
|
||||
if not ly.get("dirs") and not ly.get("fit_method"):
|
||||
if ly.get("stepper"): # Columns: (-)[N](+)
|
||||
page.append(self._cols_stepper())
|
||||
if not ly.get("dirs") and not ly.get("fit_method") and not ly.get("stepper"):
|
||||
page.append(Gtk.Label(label="No adjustable options", xalign=0.0,
|
||||
css_classes=["net-ip"]))
|
||||
return page
|
||||
|
||||
# -- columns count stepper --------------------------------------------
|
||||
def _cols_stepper(self) -> Gtk.Widget:
|
||||
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
||||
row.append(Gtk.Label(label="Columns", xalign=0.0, hexpand=True))
|
||||
minus = Gtk.Button(label="−")
|
||||
minus.add_css_class("quad-action")
|
||||
self._cols_lbl = Gtk.Label(label=str(self._read_cols()))
|
||||
self._cols_lbl.add_css_class("stepper-value")
|
||||
plus = Gtk.Button(label="+")
|
||||
plus.add_css_class("quad-action")
|
||||
minus.connect("clicked", lambda *_a: self._step_cols(-1))
|
||||
plus.connect("clicked", lambda *_a: self._step_cols(1))
|
||||
row.append(minus)
|
||||
row.append(self._cols_lbl)
|
||||
row.append(plus)
|
||||
return row
|
||||
|
||||
def _read_cols(self) -> int:
|
||||
try:
|
||||
data = json.loads(_COLUMNS_STATE.read_text())
|
||||
return int(data.get(str(self._active_ws), {}).get("cols", 2))
|
||||
except (FileNotFoundError, json.JSONDecodeError, ValueError, TypeError):
|
||||
return 2
|
||||
|
||||
def _step_cols(self, delta: int) -> None:
|
||||
try:
|
||||
cur = int(self._cols_lbl.get_label())
|
||||
except ValueError:
|
||||
cur = self._read_cols()
|
||||
new = max(1, cur + delta)
|
||||
self._cols_lbl.set_label(str(new))
|
||||
_eval(f'hl.dispatch(hl.dsp.layout("cols {"+1" if delta > 0 else "-1"}"))')
|
||||
|
||||
# -- sync + handlers ---------------------------------------------------
|
||||
def _sync_layout_controls(self) -> None:
|
||||
# reflect the current per-ws layout / direction / focus_fit in the tabs+options
|
||||
|
|
|
|||
|
|
@ -159,10 +159,13 @@ class MenuWindow(Gtk.ApplicationWindow):
|
|||
|
||||
# -- taskbar panel expansion ------------------------------------------
|
||||
def _on_taskbar_panel(self, expanded: bool) -> None:
|
||||
# Cover the quads with the taskbar's workspace/window panel (the strip itself
|
||||
# collapses inside the Taskbar). Collapse any expanded quad first.
|
||||
# Cover the quads with the taskbar's workspace/window panel. Fully collapse the
|
||||
# taskbar strip (header included) so the expanded panel reads as ONE module
|
||||
# rather than a thin strip on top plus a detached card. Collapse any quad first.
|
||||
if expanded and self.grid.is_expanded:
|
||||
self.grid.request_collapse()
|
||||
self.taskbar_reveal.set_reveal_child(not expanded)
|
||||
self.taskbar_reveal.set_visible(not expanded)
|
||||
self.grid.show_takeover() if expanded else self.grid.hide_takeover()
|
||||
|
||||
# -- appdrawer expansion ----------------------------------------------
|
||||
|
|
|
|||
|
|
@ -19,15 +19,41 @@
|
|||
|
||||
local state = {} -- [wsid] = { ncols, rows, orient, assign={addr=col}, seq={addr=n}, nextseq, pan={col=px} }
|
||||
|
||||
-- Publish per-workspace column/row counts so the astal-menu can show a live stepper.
|
||||
local STATE_FILE = (os.getenv("HOME") or "") .. "/.cache/astal-menu/columns-state.json"
|
||||
local function publish()
|
||||
local parts = {}
|
||||
for wsid, s in pairs(state) do
|
||||
parts[#parts + 1] = string.format('"%s":{"cols":%d,"rows":%d,"orient":"%s"}',
|
||||
tostring(wsid), s.ncols, s.rows, s.orient)
|
||||
end
|
||||
pcall(function()
|
||||
local f = io.open(STATE_FILE, "w")
|
||||
if f then f:write("{" .. table.concat(parts, ",") .. "}\n"); f:close() end
|
||||
end)
|
||||
end
|
||||
|
||||
local function ws_state(wsid)
|
||||
local s = state[wsid]
|
||||
if not s then
|
||||
s = { ncols = 2, rows = 2, orient = "h", assign = {}, seq = {}, nextseq = 1, pan = {} }
|
||||
state[wsid] = s
|
||||
publish()
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- Reflow every window evenly across the current column count, by insertion order.
|
||||
local function reflow(s, targets)
|
||||
local addrs = {}
|
||||
for _, tg in ipairs(targets) do
|
||||
local ok, addr = pcall(function() return tg.window.address end)
|
||||
if ok and addr then addrs[#addrs + 1] = addr end
|
||||
end
|
||||
table.sort(addrs, function(a, b) return (s.seq[a] or 0) < (s.seq[b] or 0) end)
|
||||
for i, addr in ipairs(addrs) do s.assign[addr] = ((i - 1) % s.ncols) + 1 end
|
||||
end
|
||||
|
||||
local function win_ws_id(win)
|
||||
local ok, ws = pcall(function() return win.workspace end)
|
||||
if ok and ws ~= nil then
|
||||
|
|
@ -185,18 +211,18 @@ local function layout_msg(ctx, cmd)
|
|||
elseif arg == less_cols then s.ncols = math.max(1, s.ncols - 1); changed_cols = true
|
||||
elseif arg == more_rows then s.rows = s.rows + 1
|
||||
elseif arg == less_rows then s.rows = math.max(1, s.rows - 1) end
|
||||
if changed_cols then
|
||||
-- reflow all windows evenly across the new column count (by insertion order)
|
||||
local addrs = {}
|
||||
for _, tg in ipairs(t) do
|
||||
local ok, addr = pcall(function() return tg.window.address end)
|
||||
if ok and addr then addrs[#addrs + 1] = addr end
|
||||
end
|
||||
table.sort(addrs, function(a, b) return (s.seq[a] or 0) < (s.seq[b] or 0) end)
|
||||
for i, addr in ipairs(addrs) do s.assign[addr] = ((i - 1) % s.ncols) + 1 end
|
||||
end
|
||||
if changed_cols then reflow(s, t) end
|
||||
publish()
|
||||
elseif verb == "cols" then
|
||||
-- direct column-count change (the menu's (-)[N](+) stepper).
|
||||
if arg == "+" or arg == "+1" then s.ncols = s.ncols + 1
|
||||
elseif arg == "-" or arg == "-1" then s.ncols = math.max(1, s.ncols - 1)
|
||||
else local n = tonumber(arg); if n then s.ncols = math.max(1, math.floor(n)) end end
|
||||
reflow(s, t)
|
||||
publish()
|
||||
elseif verb == "orient" then
|
||||
s.orient = (arg == "v") and "v" or "h"
|
||||
publish()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -216,4 +242,5 @@ return {
|
|||
dir_labels = { "Horizontal", "Vertical" },
|
||||
default_dir = "h",
|
||||
fit_method = false,
|
||||
stepper = true, -- menu shows a "Columns: (-)[N](+)" count stepper
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ function M.write_manifest()
|
|||
',"dirs":[', table.concat(dirs, ","), "]",
|
||||
',"dir_labels":[', table.concat(dlabels, ","), "]",
|
||||
',"default_dir":', jstr(s.default_dir or ""),
|
||||
',"fit_method":', tostring(s.fit_method or false), "}",
|
||||
',"fit_method":', tostring(s.fit_method or false),
|
||||
',"stepper":', tostring(s.stepper or false), "}",
|
||||
})
|
||||
end
|
||||
os.execute("mkdir -p '" .. MANIFEST:match("(.+)/[^/]+$") .. "' 2>/dev/null")
|
||||
|
|
|
|||
Loading…
Reference in New Issue