2 Commits
Author SHA1 Message Date
Myah MitchellandAram Drevekenin 6af3a36eb8 feat(plugin-api): let a plugin give its layout space back while empty (#5590)
* feat(plugin-api): let a plugin give its layout space back while empty

A plugin mounted in a layout as a bar keeps the row the layout reserved
for it even when it has nothing to draw, so an empty bar shows as a blank
line. Nothing in the plugin API could give that row up: HideSelf
suppresses the pane and brings it back through add_tiled_pane, which
re-inserts it by splitting rather than at its layout position, and
CloseSelf does not come back at all.

SetSelfCollapsed takes the pane out of the constraint solve without
taking it out of the layout. The pane keeps its size constraint while it
is out, so a Fixed(1) bar is still Fixed(1) when it rejoins and expanding
restores exactly what the layout asked for, while constrain_spans makes
its neighbors grow over the space in the meantime.

Collapsed panes are tracked separately from panes_to_hide, which
fullscreen overwrites wholesale, so a collapse survives entering and
leaving fullscreen. They are also left out of the viewport offset, since
a pane holding no space must not shrink the viewport, and their state is
dropped when the pane closes so a reused pane id does not arrive already
invisible.

* test(tab): cover collapsing a pane out of the layout and back

Pins down what the collapse is for: the neighbors take the space, the
collapsed pane keeps its size constraint so expanding restores the layout
exactly rather than approximately, and the collapse outlives a fullscreen,
which rebuilds the hidden set from scratch.

Also covers the two ways it should do nothing: a second collapse of an
already collapsed pane, and a pane id that is not in the tab. Closing a
collapsed pane drops the state, so a reused id does not come back
invisible.

* fix(screen): keep one deferred collapse per pane instead of a queue

A plugin sends this whenever its content comes and goes, so a plugin
with no pane in any tab would otherwise leave an entry behind on every
change, all of them replayed the next time a tab is created. Only the
most recent word on a pane means anything, so drop the earlier one.

* fix(tab): keep a collapsed pane's geometry current across a relayout

Being filtered out of the constraint solve is what stops a collapsed pane
from holding space, but it also means nothing writes geometry to it. That
was fine while the tab kept its shape and wrong the moment it did not: the
pane was left describing a display area that no longer existed.

Worse than stale, because the solver reads the layout tree back off where
the panes currently sit. A one-row bar collapsed in a 121x20 tab that then
became 118x23 stayed at y 19 in a tab whose last row was 22, so the next
solve was handed a tree that did not match the screen and gave up. The bar
came back three rows above the bottom, at a width wider than the tab, and
its neighbor never gave the row back.

Let collapsed panes take part in the resize and the relayout, then collapse
them again over the result, which is what fullscreen already does a few
lines above for the same reason.

* docs(changelog): note the plugin collapse command

Links the tracking issue rather than a pull request, since no pull
request is open yet. Wants updating to the pull request URL once there
is one, to match every entry in the released sections.

* fix(tab): give a collapsed pane's row back to the viewport after a relayout

A relayout solves with the collapsed panes taking part, and the layout
offsets the viewport while they are still there. Collapsing them again
afterwards handed their rows back to the panes around them, but not to the
viewport, so a collapsed bar went on holding its row out of it until the
next resize. Fullscreen reads the viewport, so it would not cover that row
either.

Offset the viewport again once the collapsed panes are gone, the same as
resize_whole_tab already does.

* refactor(tiled-panes): keep the hidden pane bookkeeping in PanesToHide

Fullscreen and collapse both hide tiled panes, and each needs to undo only
what it hid. Until now that was done by keeping collapsed panes in a set of
their own and rebuilding panes_to_hide from both sources whenever either
changed, which every caller had to remember to do.

panes_to_hide is now a PanesToHide struct that tracks the two reasons
separately: panes beneath a fullscreen pane, and covered panes such as a
plugin that collapsed itself. set_fullscreen and unset_fullscreen only
touch the first, set_covered and unset_covered only the second, so a
covered pane stays hidden across a fullscreen without any rebuilding, and
the fullscreen pane itself is never hidden.

add_to_hidden_panels and remove_from_hidden_panels had no callers and
wrote straight into the set, so they are gone.

* fix(plugin-api): collapse the slot being rendered for a shared plugin

set_self_collapsed sent env.plugin_id, which for a shared plugin is the
instance and not any of its panes, so a shared plugin such as the bundled
bars could not collapse at all. Resolve the pane through self_pane_id like
the other self commands do, and list SetSelfCollapsed in is_self_command so
a shared plugin can only use it while a slot is current.

* feat(plugin-api): let a shared plugin collapse a slot by id

The other self commands have a slot form that names the pane outright, so
a shared plugin can use them from update or pipe, where no slot is current
and the self form is refused. set_collapsed_slot does the same for
set_self_collapsed, with the ownership check and response the other slot
commands give.

* fix: resurrection, covering selectable panes, mouse clicks, closing, paneinfo

* rebuild assets

---------

Co-authored-by: Aram Drevekenin <[email protected]>
2026-10-06 13:23:29 +02:00
Myah Mitchell b1d228f55a fix(screen): keep sending events to suppressed plugin panes (#5680)
* fix(screen): keep sending events to suppressed plugin panes

A plugin that hides itself (hide_self, hide_pane_with_id or hide_slot)
moves into the tab's suppressed panes. Event delivery only looked at the
tiled and floating panes, so a hidden plugin stopped receiving ModeUpdate,
TabUpdate, PaneUpdate and InputReceived, and could never learn that it
should show itself again.

Include suppressed plugin panes when updating input modes and when picking
the plugins for a broadcast event. Render targeting and the client visible
plugins report still use get_plugin_ids, so a suppressed plugin is not
rendered.

* docs(changelog): note suppressed plugins receiving events
2026-10-05 11:15:45 +02:00