Skip to content

fix(configurator): reopen open edits only once their row can hold them - #437

Merged
devmobasa merged 2 commits into
mainfrom
fix/configurator-popovers-after-rebuild
Nov 4, 2026
Merged

devmobasa merged 2 commits into
mainfrom
fix/configurator-popovers-after-rebuild

Conversation

@devmobasa

Copy link
Copy Markdown
Owner

Summary

On newer GTK, changing the interface language while a shortcut is being
recorded or text-edited cancels the edit. The configurator is meant to carry
open edits across the language rebuild. That works on GTK 4.14, which CI
uses, but not on GTK 4.24.

app::component::tests::language_changes_rebuild_the_window_in_place shows
this with GTK 4.24 and Weston 15.

Cause

GTK 4.24 closes a popover it cannot place. gtk_popover_native_layout calls
gtk_popover_popdown when the compositor gives the popup less than its
minimum size, which is what happens once the anchor row is outside the view.
GTK 4.14 does not have that check. field_canceled then reports the close
as the user canceling the edit.

A language change hit this path in two ways:

  1. Reopening too early. The first refresh after
    rebuild_if_language_changed reopened the edit's popover on the new row.
    At that moment the new page has no layout and is at scroll 0, so the row
    is off screen. The saved scroll is restored only after the first
    allocation.
  2. The restored scroll is not enough. Translated rows above the edited
    row can be taller. In the test, the Undo row ends up 560 px down a 446 px
    view at the restored offset after switching to Chinese.

The test also started recording on a row far below the visible part of the
page. A real recording starts from a click on its row, so that row is always
on screen.

Changes

  • pages/mod.rs: a settle ticket per rebuild. A newer rebuild makes an older
    ticket stale.

  • component/rebuild.rs: the rebuild takes a ticket before its first
    refresh. A new settle step:

    1. waits for the visible page to be laid out
    2. restores the scroll
    3. waits one more frame
    4. ends settling and sends Message::PagesLaidOut

    Focus that was inside a popover that has not reopened yet is restored, with
    its caret and selection, right after the popover opens. If the edit ends
    before the popover reopens, the saved focus is dropped. A rebuild that
    starts before the previous one settled carries over that rebuild's scroll
    for the same page, unless the page has a scroll of its own.

  • pages/keybindings/widgets.rs: PopoverPresenter opens the recorder and
    text-editor popovers only when all of these hold:

    • the pages have settled
    • no Leave confirmation is pending, because the popover's focus would
      select its row and that withdraws the confirmation
    • the row is mapped and laid out

    If the row is outside the view, it scrolls the row into view and opens the
    popover after the next layout. A row that is not showing, on another page
    or in a hidden category, opens once it is shown and laid out.

  • messages.rs and update/mod.rs: PagesLaidOut changes no state. It is
    allowed while a document replacement is in flight and does not withdraw a
    pending Leave confirmation.

Tests

The GTK scenario, with existing assertions unchanged:

  • Recording now starts on a row that is on screen. After every language
    switch the test waits for the rebuilt page to reopen the open edit, and
    asserts that each popover opened with its row on screen. The listeners
    attach when the rebuilt page becomes visible, before its first refresh.
    Without the production changes this check fails with [false] on GTK 4.24.
  • The reopened text editor keeps its selection.
  • A cancel made while the pages settle stays canceled, and nothing reopens.
  • If a text edit is canceled before its editor reopens, the next edit opened
    on that row does not get the old selection.
  • A Reload Leave confirmation asked while the pages settle survives. The
    recorder reopens once, on screen, after the confirmation is canceled.
  • Two language changes before the text editor reopens keep its selection.
  • A recording whose category is hidden while the pages settle stays active
    and closed, and reopens when the category is shown again.
  • The reload scenario asserts that the recording is active before Reload.

The Leave, old-selection, two-change and hidden-category checks each fail
when the production change they cover is removed.

A unit test covers the settle ticket.

Validation

  • ./tools/test-gtk-widgets.sh and ./tools/lint-and-test.sh pass with
    GTK 4.24.1 and Weston 15.
  • cargo clippy with --all-features and with --no-default-features, and
    cargo fmt --check, pass.
  • CI runs the same scenario on Ubuntu 24.04.

Not changed

  • The compact-mode shortcut editor is still not reopened after a rebuild. The
    model does not track it, which was already the case before this change.
  • Focus is still restored by widget path rather than by shortcut field.
  • The GTK widget job pin to ubuntu-24.04 (ci: build the Nix flake and check the minimum Rust version #436) can be removed once this
    scenario passes on a newer runner image.

GTK 4.24 closes a popover it cannot place, and the configurator reports
that close as the user canceling the edit. After a language change the
rebuilt page reopened a recording or text edit before the page had a
layout or its restored scroll, so the row was off screen and the edit
ended.

Rebuilt pages now settle first: the scroll is restored after the first
layout, and a PagesLaidOut message ends settling. Editor popovers open
only when the pages have settled, no Leave confirmation is pending, and
the row is shown and laid out, scrolling the row into view when a
translation moved it. Focus and selection inside a popover that has not
reopened yet are restored when it opens.
The Leave scenario queued the language change and Reload in one main
loop turn. On GTK 4.14 focus stays on the edited row while its recorder
is open, so the rebuild puts focus back on the new row, and that
selection ran after Reload and withdrew the confirmation. A real click
on Reload always comes after the rebuild's refresh, so the test now runs
the queued messages first and asserts that the pages are still settling.
@devmobasa
devmobasa merged commit cbaf37d into main Nov 4, 2026
6 checks passed
@devmobasa
devmobasa deleted the fix/configurator-popovers-after-rebuild branch November 4, 2026 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant