Close the popover when 'Add X' opens a dialog or arms map placement

Every menu popover (Units/Spotters/Reference Points/Targets/Scout
Flights) stayed open on top of the modal dialog or map-click placement
its own 'Add <thing>' button just triggered, holding onto focus that
should have gone to the new dialog or the map instead. _add_row() now
takes a close() callback (threaded through from _make_menu_button,
which already owns the Gtk.Popover) and pops the popover down right
before calling on_click().

Verified end to end with a GTK smoke test: opens the real 'Scout
Flights' popover, clicks its real 'Add scout flight' button, confirms
the popover is no longer visible afterward.
This commit is contained in:
Dominik Moritz Roth 2026-08-09 20:32:06 +02:00
parent 77fe6a2948
commit 68d312934b

View File

@ -130,18 +130,23 @@ def _row(
return box return box
def _add_row(label: str, on_click) -> Gtk.Widget: def _add_row(label: str, on_click, close) -> Gtk.Widget:
"""The trailing 'Add <thing>' row in a popover: just one button, not """The trailing 'Add <thing>' row in a popover: just one button, not
the full _row() layout (which always pairs a manual-input action the full _row() layout (which always pairs a manual-input action
with a per-item screenshot action). There's nothing to screenshot with a per-item screenshot action). There's nothing to screenshot
*into* yet for something that doesn't exist, and the universal *into* yet for something that doesn't exist, and the universal
clipboard button in the header already covers 'load everything from clipboard button in the header already covers 'load everything from
a screenshot' for every category, a second, category-specific a screenshot' for every category, a second, category-specific
version of that here was redundant.""" version of that here was redundant.
Closes the popover before calling on_click(): every on_click here
either opens its own modal dialog or arms map-click placement, and
the popover staying open on top of that just holds onto focus the
dialog/map should have instead."""
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6, box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6,
margin_top=4, margin_bottom=4, margin_start=8, margin_end=8) margin_top=4, margin_bottom=4, margin_start=8, margin_end=8)
btn = Gtk.Button(label=label, hexpand=True) btn = Gtk.Button(label=label, hexpand=True)
btn.connect("clicked", lambda _b: on_click()) btn.connect("clicked", lambda _b: (close(), on_click()))
box.append(btn) box.append(btn)
return box return box
@ -341,17 +346,21 @@ class MainWindow(Adw.ApplicationWindow):
# -- generic helpers ----------------------------------------------------- # -- generic helpers -----------------------------------------------------
def _make_menu_button(self, label: str, build_popover) -> Gtk.MenuButton: def _make_menu_button(self, label: str, build_popover) -> Gtk.MenuButton:
"""build_popover(rebuild) returns the popover's content widget; """build_popover(rebuild, close) returns the popover's content
rebuild() lets a row's own callback refresh the popover in place widget; rebuild() lets a row's own callback refresh the popover
(e.g. after toggling hidden, or removing an item) instead of only in place (e.g. after toggling hidden, or removing an item)
refreshing next time it's reopened.""" instead of only refreshing next time it's reopened. close()
closes the popover outright, for an action that opens its own
modal dialog or arms map-click placement, where the popover
staying open on top just holds onto focus that dialog/the map
should have instead (see _add_row's use of it)."""
button = Gtk.MenuButton(label=label) button = Gtk.MenuButton(label=label)
popover = Gtk.Popover() popover = Gtk.Popover()
popover.set_size_request(280, -1) popover.set_size_request(280, -1)
button.set_popover(popover) button.set_popover(popover)
def rebuild(): def rebuild():
popover.set_child(build_popover(rebuild)) popover.set_child(build_popover(rebuild, popover.popdown))
popover.connect("show", lambda _p: rebuild()) popover.connect("show", lambda _p: rebuild())
return button return button
@ -716,7 +725,7 @@ class MainWindow(Adw.ApplicationWindow):
self.cursor_label.set_label(coord.label()) self.cursor_label.set_label(coord.label())
# -- Spotters -------------------------------------------------------------- # -- Spotters --------------------------------------------------------------
def _build_spotters_popover(self, rebuild) -> Gtk.Widget: def _build_spotters_popover(self, rebuild, close) -> Gtk.Widget:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_margin_top(6) box.set_margin_top(6)
box.set_margin_bottom(6) box.set_margin_bottom(6)
@ -742,7 +751,7 @@ class MainWindow(Adw.ApplicationWindow):
on_submit=lambda loc, id_, _t: self._add_spotter(loc, id_), on_submit=lambda loc, id_, _t: self._add_spotter(loc, id_),
show_id=True, show_id=True,
id_placeholder=f"ID (blank = auto, next: {self.board.next_spotter_id()})", id_placeholder=f"ID (blank = auto, next: {self.board.next_spotter_id()})",
))) ), close))
return box return box
def _add_spotter(self, location: Location, id_text: str | None) -> None: def _add_spotter(self, location: Location, id_text: str | None) -> None:
@ -762,7 +771,7 @@ class MainWindow(Adw.ApplicationWindow):
rebuild() rebuild()
# -- Reference Points -------------------------------------------------------- # -- Reference Points --------------------------------------------------------
def _build_rp_popover(self, rebuild) -> Gtk.Widget: def _build_rp_popover(self, rebuild, close) -> Gtk.Widget:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_margin_top(6) box.set_margin_top(6)
box.set_margin_bottom(6) box.set_margin_bottom(6)
@ -787,7 +796,7 @@ class MainWindow(Adw.ApplicationWindow):
box.append(_add_row("Add RP", lambda: self._open_coord_dialog( box.append(_add_row("Add RP", lambda: self._open_coord_dialog(
title="Add reference point", title="Add reference point",
on_submit=lambda loc, _id, _t: self._add_rp(loc), on_submit=lambda loc, _id, _t: self._add_rp(loc),
))) ), close))
return box return box
def _add_rp(self, location: Location) -> None: def _add_rp(self, location: Location) -> None:
@ -815,7 +824,7 @@ class MainWindow(Adw.ApplicationWindow):
self.toast(f"{rp.name} converted to {new_target.name}.") self.toast(f"{rp.name} converted to {new_target.name}.")
# -- Targets ----------------------------------------------------------------- # -- Targets -----------------------------------------------------------------
def _build_targets_popover(self, rebuild) -> Gtk.Widget: def _build_targets_popover(self, rebuild, close) -> Gtk.Widget:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_margin_top(6) box.set_margin_top(6)
box.set_margin_bottom(6) box.set_margin_bottom(6)
@ -844,7 +853,7 @@ class MainWindow(Adw.ApplicationWindow):
on_submit=lambda loc, id_, type_: self._add_target(loc, id_, type_), on_submit=lambda loc, id_, type_: self._add_target(loc, id_, type_),
show_id=True, show_id=True,
show_type=True, show_type=True,
))) ), close))
return box return box
def _add_target(self, location: Location, id_, type_) -> None: def _add_target(self, location: Location, id_, type_) -> None:
@ -866,7 +875,7 @@ class MainWindow(Adw.ApplicationWindow):
self.toast(f"{target.name} converted to {new_rp.name}.") self.toast(f"{target.name} converted to {new_rp.name}.")
# -- Units (Nest + Allies) ------------------------------------------------- # -- Units (Nest + Allies) -------------------------------------------------
def _build_units_popover(self, rebuild) -> Gtk.Widget: def _build_units_popover(self, rebuild, close) -> Gtk.Widget:
"""Your own side of the map: the Nest (always first, there's """Your own side of the map: the Nest (always first, there's
only ever one) plus every friendly contact ('FriendlyTank#1:', only ever one) plus every friendly contact ('FriendlyTank#1:',
tracked entirely separately from Targets, see models.py's tracked entirely separately from Targets, see models.py's
@ -913,7 +922,7 @@ class MainWindow(Adw.ApplicationWindow):
on_submit=lambda loc, id_, type_: self._add_ally(loc, id_, type_), on_submit=lambda loc, id_, type_: self._add_ally(loc, id_, type_),
show_id=True, show_id=True,
show_type=True, show_type=True,
))) ), close))
return box return box
def _add_ally(self, location: Location, id_, type_) -> None: def _add_ally(self, location: Location, id_, type_) -> None:
@ -926,7 +935,7 @@ class MainWindow(Adw.ApplicationWindow):
rebuild() rebuild()
# -- Scout Flights ------------------------------------------------------------ # -- Scout Flights ------------------------------------------------------------
def _build_scout_flights_popover(self, rebuild) -> Gtk.Widget: def _build_scout_flights_popover(self, rebuild, close) -> Gtk.Widget:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_margin_top(6) box.set_margin_top(6)
box.set_margin_bottom(6) box.set_margin_bottom(6)
@ -940,7 +949,7 @@ class MainWindow(Adw.ApplicationWindow):
)) ))
box.append(Gtk.Separator()) box.append(Gtk.Separator())
box.append(_add_row("Add scout flight", self._add_scout_flight)) box.append(_add_row("Add scout flight", self._add_scout_flight, close))
return box return box
def _add_scout_flight(self) -> None: def _add_scout_flight(self) -> None: