Skip to content

Commit

Permalink
Dont prevent SelectionToggled messages in bulk SelectionList methods.…
Browse files Browse the repository at this point in the history
… Its important for callers to know on a per-option basis if one was toggled, and is a reasonable expectation. Also update docs to reflect this..
  • Loading branch information
darrenburns committed Jul 16, 2024
1 parent 53adedf commit bd804bb
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 4 deletions.
25 changes: 21 additions & 4 deletions src/textual/widgets/_selection_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,16 @@ class SelectionHighlighted(SelectionMessage[MessageSelectionType]):
class SelectionToggled(SelectionMessage[MessageSelectionType]):
"""Message sent when a selection is toggled.
This is only sent when the value is *explicitly* toggled e.g.
via `toggle` or `toggle_all`, or via user interaction.
If you programmatically set a value to be selected, this message will
not be sent, even if it happens to be the opposite of what was
originally selected (i.e. setting a True to a False or vice-versa).
Since this message indicates a toggle occurring at a per-option level,
a message will be sent for each option that is toggled, even when a
bulk action is performed (e.g. via `toggle_all`).
Can be handled using `on_selection_list_selection_toggled` in a subclass of
[`SelectionList`][textual.widgets.SelectionList] or in a parent node in the DOM.
"""
Expand All @@ -210,6 +220,13 @@ class SelectionToggled(SelectionMessage[MessageSelectionType]):
class SelectedChanged(Generic[MessageSelectionType], Message):
"""Message sent when the collection of selected values changes.
This is sent regardless of whether the change occurred via user interaction
or programmatically the the `SelectionList` API.
When a bulk change occurs, such as through `select_all` or `deselect_all`,
only a single `SelectedChanged` message will be sent (rather than one per
option).
Can be handled using `on_selection_list_selected_changed` in a subclass of
[`SelectionList`][textual.widgets.SelectionList] or in a parent node in the DOM.
"""
Expand Down Expand Up @@ -316,10 +333,10 @@ def _apply_to_all(self, state_change: Callable[[SelectionType], bool]) -> Self:
# Keep track of if anything changed.
changed = False

# Next we run through everything and apply the change, preventing
# the toggled and changed messages because the caller really isn't
# going to be expecting a message storm from this.
with self.prevent(self.SelectedChanged, self.SelectionToggled):
# Apply the state change function to all options.
# We don't send a SelectedChanged for each option, and instead
# send a single SelectedChanged afterwards if any values change.
with self.prevent(self.SelectedChanged):
for selection in self._options:
changed = (
state_change(cast(Selection[SelectionType], selection).value)
Expand Down
10 changes: 10 additions & 0 deletions tests/selection_list/test_selection_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ async def test_toggle_all() -> None:
await pilot.pause()
assert pilot.app.messages == [
("SelectionHighlighted", 0),
("SelectionToggled", 0),
("SelectionToggled", 1),
("SelectionToggled", 2),
("SelectionToggled", 3),
("SelectionToggled", 4),
("SelectionToggled", 5),
("SelectionToggled", 6),
("SelectionToggled", 7),
("SelectionToggled", 8),
("SelectionToggled", 9),
("SelectedChanged", None),
]

Expand Down

0 comments on commit bd804bb

Please sign in to comment.