mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into package-docs
This commit is contained in:
@@ -39,6 +39,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fixed issue with app not processing Paste event https://github.com/Textualize/textual/issues/1666
|
||||
- Fixed glitch with view position with auto width inputs https://github.com/Textualize/textual/issues/1693
|
||||
|
||||
### Removed
|
||||
|
||||
- Methods `MessagePump.emit` and `MessagePump.emit_no_wait` https://github.com/Textualize/textual/pull/1738
|
||||
|
||||
## [0.10.1] - 2023-01-20
|
||||
|
||||
### Added
|
||||
|
||||
@@ -288,7 +288,7 @@ So, thanks to this bit of code in my `Activity` widget...
|
||||
parent.move_child(
|
||||
self, before=parent.children.index( self ) - 1
|
||||
)
|
||||
self.emit_no_wait( self.Moved( self ) )
|
||||
self.post_message_no_wait( self.Moved( self ) )
|
||||
self.scroll_visible( top=True )
|
||||
```
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class ColorButton(Static):
|
||||
self.styles.border = ("tall", self.color)
|
||||
|
||||
async def on_click(self) -> None:
|
||||
# The emit method sends an event to a widget's parent
|
||||
await self.emit(self.Selected(self, self.color))
|
||||
# The post_message method sends an event to be handled in the DOM
|
||||
await self.post_message(self.Selected(self, self.color))
|
||||
|
||||
def render(self) -> str:
|
||||
return str(self.color)
|
||||
|
||||
@@ -110,7 +110,7 @@ The message class is defined within the widget class itself. This is not strictl
|
||||
|
||||
## Sending events
|
||||
|
||||
In the previous example we used [emit()][textual.message_pump.MessagePump.emit] to send an event to its parent. We could also have used [emit_no_wait()][textual.message_pump.MessagePump.emit_no_wait] for non async code. Sending messages in this way allows you to write custom widgets without needing to know in what context they will be used.
|
||||
In the previous example we used [post_message()][textual.message_pump.MessagePump.post_message] to send an event to its parent. We could also have used [post_message_no_wait()][textual.message_pump.MessagePump.post_message_no_wait] for non async code. Sending messages in this way allows you to write custom widgets without needing to know in what context they will be used.
|
||||
|
||||
There are other ways of sending (posting) messages, which you may need to use less frequently.
|
||||
|
||||
|
||||
@@ -580,34 +580,6 @@ class MessagePump(metaclass=MessagePumpMeta):
|
||||
async def on_callback(self, event: events.Callback) -> None:
|
||||
await invoke(event.callback)
|
||||
|
||||
def emit_no_wait(self, message: Message) -> bool:
|
||||
"""Send a message to the _parent_, non async version.
|
||||
|
||||
Args:
|
||||
message: A message object.
|
||||
|
||||
Returns:
|
||||
True if the message was posted successfully.
|
||||
"""
|
||||
if self._parent:
|
||||
return self._parent._post_message_from_child_no_wait(message)
|
||||
else:
|
||||
return False
|
||||
|
||||
async def emit(self, message: Message) -> bool:
|
||||
"""Send a message to the _parent_.
|
||||
|
||||
Args:
|
||||
message: A message object.
|
||||
|
||||
Returns:
|
||||
True if the message was posted successfully.
|
||||
"""
|
||||
if self._parent:
|
||||
return await self._parent._post_message_from_child(message)
|
||||
else:
|
||||
return False
|
||||
|
||||
# TODO: Does dispatch_key belong on message pump?
|
||||
async def dispatch_key(self, event: events.Key) -> bool:
|
||||
"""Dispatch a key event to method.
|
||||
|
||||
@@ -280,10 +280,12 @@ class ScrollBar(Widget):
|
||||
self.mouse_over = False
|
||||
|
||||
async def action_scroll_down(self) -> None:
|
||||
await self.emit(ScrollDown(self) if self.vertical else ScrollRight(self))
|
||||
await self.post_message(
|
||||
ScrollDown(self) if self.vertical else ScrollRight(self)
|
||||
)
|
||||
|
||||
async def action_scroll_up(self) -> None:
|
||||
await self.emit(ScrollUp(self) if self.vertical else ScrollLeft(self))
|
||||
await self.post_message(ScrollUp(self) if self.vertical else ScrollLeft(self))
|
||||
|
||||
def action_grab(self) -> None:
|
||||
self.capture_mouse()
|
||||
@@ -324,7 +326,7 @@ class ScrollBar(Widget):
|
||||
* (self.window_virtual_size / self.window_size)
|
||||
)
|
||||
)
|
||||
await self.emit(ScrollTo(self, x=x, y=y))
|
||||
await self.post_message(ScrollTo(self, x=x, y=y))
|
||||
event.stop()
|
||||
|
||||
async def _on_click(self, event: events.Click) -> None:
|
||||
|
||||
@@ -2465,12 +2465,12 @@ class Widget(DOMNode):
|
||||
def _on_focus(self, event: events.Focus) -> None:
|
||||
self.has_focus = True
|
||||
self.refresh()
|
||||
self.emit_no_wait(events.DescendantFocus(self))
|
||||
self.post_message_no_wait(events.DescendantFocus(self))
|
||||
|
||||
def _on_blur(self, event: events.Blur) -> None:
|
||||
self.has_focus = False
|
||||
self.refresh()
|
||||
self.emit_no_wait(events.DescendantBlur(self))
|
||||
self.post_message_no_wait(events.DescendantBlur(self))
|
||||
|
||||
def _on_descendant_blur(self, event: events.DescendantBlur) -> None:
|
||||
if self._has_focus_within:
|
||||
|
||||
@@ -255,7 +255,7 @@ class Button(Static, can_focus=True):
|
||||
# Manage the "active" effect:
|
||||
self._start_active_affect()
|
||||
# ...and let other components know that we've just been clicked:
|
||||
self.emit_no_wait(Button.Pressed(self))
|
||||
self.post_message_no_wait(Button.Pressed(self))
|
||||
|
||||
def _start_active_affect(self) -> None:
|
||||
"""Start a small animation to show the button was clicked."""
|
||||
@@ -267,7 +267,7 @@ class Button(Static, can_focus=True):
|
||||
async def _on_key(self, event: events.Key) -> None:
|
||||
if event.key == "enter" and not self.disabled:
|
||||
self._start_active_affect()
|
||||
await self.emit(Button.Pressed(self))
|
||||
await self.post_message(Button.Pressed(self))
|
||||
|
||||
@classmethod
|
||||
def success(
|
||||
|
||||
@@ -77,7 +77,7 @@ class Checkbox(Widget, can_focus=True):
|
||||
"""The position of the slider."""
|
||||
|
||||
class Changed(Message, bubble=True):
|
||||
"""Emitted when the status of the checkbox changes.
|
||||
"""Posted when the status of the checkbox changes.
|
||||
|
||||
Can be handled using `on_checkbox_changed` in a subclass of `Checkbox`
|
||||
or in a parent widget in the DOM.
|
||||
@@ -122,7 +122,7 @@ class Checkbox(Widget, can_focus=True):
|
||||
self.animate("slider_pos", target_slider_pos, duration=0.3)
|
||||
else:
|
||||
self.slider_pos = target_slider_pos
|
||||
self.emit_no_wait(self.Changed(self, self.value))
|
||||
self.post_message_no_wait(self.Changed(self, self.value))
|
||||
|
||||
def watch_slider_pos(self, slider_pos: float) -> None:
|
||||
self.set_class(slider_pos == 1, "-on")
|
||||
@@ -151,5 +151,5 @@ class Checkbox(Widget, can_focus=True):
|
||||
|
||||
def toggle(self) -> None:
|
||||
"""Toggle the checkbox value. As a result of the value changing,
|
||||
a Checkbox.Changed message will be emitted."""
|
||||
a Checkbox.Changed message will be posted."""
|
||||
self.value = not self.value
|
||||
|
||||
@@ -190,9 +190,9 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
hover_cell: Reactive[Coordinate] = Reactive(Coordinate(0, 0), repaint=False)
|
||||
|
||||
class CellHighlighted(Message, bubble=True):
|
||||
"""Emitted when the cursor moves to highlight a new cell.
|
||||
"""Posted when the cursor moves to highlight a new cell.
|
||||
It's only relevant when the `cursor_type` is `"cell"`.
|
||||
It's also emitted when the cell cursor is re-enabled (by setting `show_cursor=True`),
|
||||
It's also posted when the cell cursor is re-enabled (by setting `show_cursor=True`),
|
||||
and when the cursor type is changed to `"cell"`. Can be handled using
|
||||
`on_data_table_cell_highlighted` in a subclass of `DataTable` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -215,7 +215,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
yield "coordinate", self.coordinate
|
||||
|
||||
class CellSelected(Message, bubble=True):
|
||||
"""Emitted by the `DataTable` widget when a cell is selected.
|
||||
"""Posted by the `DataTable` widget when a cell is selected.
|
||||
It's only relevant when the `cursor_type` is `"cell"`. Can be handled using
|
||||
`on_data_table_cell_selected` in a subclass of `DataTable` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -238,7 +238,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
yield "coordinate", self.coordinate
|
||||
|
||||
class RowHighlighted(Message, bubble=True):
|
||||
"""Emitted when a row is highlighted. This message is only emitted when the
|
||||
"""Posted when a row is highlighted. This message is only posted when the
|
||||
`cursor_type` is set to `"row"`. Can be handled using `on_data_table_row_highlighted`
|
||||
in a subclass of `DataTable` or in a parent widget in the DOM.
|
||||
|
||||
@@ -255,7 +255,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
yield "cursor_row", self.cursor_row
|
||||
|
||||
class RowSelected(Message, bubble=True):
|
||||
"""Emitted when a row is selected. This message is only emitted when the
|
||||
"""Posted when a row is selected. This message is only posted when the
|
||||
`cursor_type` is set to `"row"`. Can be handled using
|
||||
`on_data_table_row_selected` in a subclass of `DataTable` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -273,7 +273,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
yield "cursor_row", self.cursor_row
|
||||
|
||||
class ColumnHighlighted(Message, bubble=True):
|
||||
"""Emitted when a column is highlighted. This message is only emitted when the
|
||||
"""Posted when a column is highlighted. This message is only posted when the
|
||||
`cursor_type` is set to `"column"`. Can be handled using
|
||||
`on_data_table_column_highlighted` in a subclass of `DataTable` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -291,7 +291,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
yield "cursor_column", self.cursor_column
|
||||
|
||||
class ColumnSelected(Message, bubble=True):
|
||||
"""Emitted when a column is selected. This message is only emitted when the
|
||||
"""Posted when a column is selected. This message is only posted when the
|
||||
`cursor_type` is set to `"column"`. Can be handled using
|
||||
`on_data_table_column_selected` in a subclass of `DataTable` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -405,7 +405,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
self._clear_caches()
|
||||
if show_cursor and self.cursor_type != "none":
|
||||
# When we re-enable the cursor, apply highlighting and
|
||||
# emit the appropriate [Row|Column|Cell]Highlighted event.
|
||||
# post the appropriate [Row|Column|Cell]Highlighted event.
|
||||
self._scroll_cursor_into_view(animate=False)
|
||||
if self.cursor_type == "cell":
|
||||
self._highlight_cell(self.cursor_cell)
|
||||
@@ -431,7 +431,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
self, old_coordinate: Coordinate, new_coordinate: Coordinate
|
||||
) -> None:
|
||||
if old_coordinate != new_coordinate:
|
||||
# Refresh the old and the new cell, and emit the appropriate
|
||||
# Refresh the old and the new cell, and post the appropriate
|
||||
# message to tell users of the newly highlighted row/cell/column.
|
||||
if self.cursor_type == "cell":
|
||||
self.refresh_cell(*old_coordinate)
|
||||
@@ -444,7 +444,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
self._highlight_column(new_coordinate.column)
|
||||
|
||||
def _highlight_cell(self, coordinate: Coordinate) -> None:
|
||||
"""Apply highlighting to the cell at the coordinate, and emit event."""
|
||||
"""Apply highlighting to the cell at the coordinate, and post event."""
|
||||
self.refresh_cell(*coordinate)
|
||||
try:
|
||||
cell_value = self.get_cell_value(coordinate)
|
||||
@@ -453,19 +453,21 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
# In that case, there's nothing for us to do here.
|
||||
return
|
||||
else:
|
||||
self.emit_no_wait(DataTable.CellHighlighted(self, cell_value, coordinate))
|
||||
self.post_message_no_wait(
|
||||
DataTable.CellHighlighted(self, cell_value, coordinate)
|
||||
)
|
||||
|
||||
def _highlight_row(self, row_index: int) -> None:
|
||||
"""Apply highlighting to the row at the given index, and emit event."""
|
||||
"""Apply highlighting to the row at the given index, and post event."""
|
||||
self.refresh_row(row_index)
|
||||
if row_index in self.data:
|
||||
self.emit_no_wait(DataTable.RowHighlighted(self, row_index))
|
||||
self.post_message_no_wait(DataTable.RowHighlighted(self, row_index))
|
||||
|
||||
def _highlight_column(self, column_index: int) -> None:
|
||||
"""Apply highlighting to the column at the given index, and emit event."""
|
||||
"""Apply highlighting to the column at the given index, and post event."""
|
||||
self.refresh_column(column_index)
|
||||
if column_index < len(self.columns):
|
||||
self.emit_no_wait(DataTable.ColumnHighlighted(self, column_index))
|
||||
self.post_message_no_wait(DataTable.ColumnHighlighted(self, column_index))
|
||||
|
||||
def validate_cursor_cell(self, value: Coordinate) -> Coordinate:
|
||||
return self._clamp_cursor_cell(value)
|
||||
@@ -641,7 +643,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
|
||||
# If a position has opened for the cursor to appear, where it previously
|
||||
# could not (e.g. when there's no data in the table), then a highlighted
|
||||
# event is emitted, since there's now a highlighted cell when there wasn't
|
||||
# event is posted, since there's now a highlighted cell when there wasn't
|
||||
# before.
|
||||
cell_now_available = self.row_count == 1 and len(self.columns) > 0
|
||||
visible_cursor = self.show_cursor and self.cursor_type != "none"
|
||||
@@ -1039,8 +1041,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
def on_click(self, event: events.Click) -> None:
|
||||
self._set_hover_cursor(True)
|
||||
if self.show_cursor and self.cursor_type != "none":
|
||||
# Only emit selection events if there is a visible row/col/cell cursor.
|
||||
self._emit_selected_message()
|
||||
# Only post selection events if there is a visible row/col/cell cursor.
|
||||
self._post_message_selected_message()
|
||||
meta = self.get_style_at(event.x, event.y).meta
|
||||
if meta:
|
||||
self.cursor_cell = Coordinate(meta["row"], meta["column"])
|
||||
@@ -1088,14 +1090,14 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
def action_select_cursor(self) -> None:
|
||||
self._set_hover_cursor(False)
|
||||
if self.show_cursor and self.cursor_type != "none":
|
||||
self._emit_selected_message()
|
||||
self._post_selected_message()
|
||||
|
||||
def _emit_selected_message(self):
|
||||
"""Emit the appropriate message for a selection based on the `cursor_type`."""
|
||||
def _post_selected_message(self):
|
||||
"""Post the appropriate message for a selection based on the `cursor_type`."""
|
||||
cursor_cell = self.cursor_cell
|
||||
cursor_type = self.cursor_type
|
||||
if cursor_type == "cell":
|
||||
self.emit_no_wait(
|
||||
self.post_message_no_wait(
|
||||
DataTable.CellSelected(
|
||||
self,
|
||||
self.get_cell_value(cursor_cell),
|
||||
@@ -1104,7 +1106,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
)
|
||||
elif cursor_type == "row":
|
||||
row, _ = cursor_cell
|
||||
self.emit_no_wait(DataTable.RowSelected(self, row))
|
||||
self.post_message_no_wait(DataTable.RowSelected(self, row))
|
||||
elif cursor_type == "column":
|
||||
_, column = cursor_cell
|
||||
self.emit_no_wait(DataTable.ColumnSelected(self, column))
|
||||
self.post_message_no_wait(DataTable.ColumnSelected(self, column))
|
||||
|
||||
@@ -67,7 +67,7 @@ class DirectoryTree(Tree[DirEntry]):
|
||||
"""
|
||||
|
||||
class FileSelected(Message, bubble=True):
|
||||
"""Emitted when a file is selected.
|
||||
"""Posted when a file is selected.
|
||||
|
||||
Can be handled using `on_directory_tree_file_selected` in a subclass of
|
||||
`DirectoryTree` or in a parent widget in the DOM.
|
||||
@@ -173,7 +173,7 @@ class DirectoryTree(Tree[DirEntry]):
|
||||
if not dir_entry.loaded:
|
||||
self.load_directory(event.node)
|
||||
else:
|
||||
self.emit_no_wait(self.FileSelected(self, dir_entry.path))
|
||||
self.post_message_no_wait(self.FileSelected(self, dir_entry.path))
|
||||
|
||||
def on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
|
||||
event.stop()
|
||||
@@ -181,4 +181,4 @@ class DirectoryTree(Tree[DirEntry]):
|
||||
if dir_entry is None:
|
||||
return
|
||||
if not dir_entry.is_dir:
|
||||
self.emit_no_wait(self.FileSelected(self, dir_entry.path))
|
||||
self.post_message_no_wait(self.FileSelected(self, dir_entry.path))
|
||||
|
||||
@@ -139,7 +139,7 @@ class Input(Widget, can_focus=True):
|
||||
max_size: reactive[int | None] = reactive(None)
|
||||
|
||||
class Changed(Message, bubble=True):
|
||||
"""Emitted when the value changes.
|
||||
"""Posted when the value changes.
|
||||
|
||||
Can be handled using `on_input_changed` in a subclass of `Input` or in a parent
|
||||
widget in the DOM.
|
||||
@@ -155,7 +155,7 @@ class Input(Widget, can_focus=True):
|
||||
self.input: Input = sender
|
||||
|
||||
class Submitted(Message, bubble=True):
|
||||
"""Emitted when the enter key is pressed within an `Input`.
|
||||
"""Posted when the enter key is pressed within an `Input`.
|
||||
|
||||
Can be handled using `on_input_submitted` in a subclass of `Input` or in a
|
||||
parent widget in the DOM.
|
||||
@@ -244,7 +244,7 @@ class Input(Widget, can_focus=True):
|
||||
async def watch_value(self, value: str) -> None:
|
||||
if self.styles.auto_dimensions:
|
||||
self.refresh(layout=True)
|
||||
await self.emit(self.Changed(self, value))
|
||||
await self.post_message(self.Changed(self, value))
|
||||
|
||||
@property
|
||||
def cursor_width(self) -> int:
|
||||
@@ -479,4 +479,4 @@ class Input(Widget, can_focus=True):
|
||||
self.cursor_position = 0
|
||||
|
||||
async def action_submit(self) -> None:
|
||||
await self.emit(self.Submitted(self, self.value))
|
||||
await self.post_message(self.Submitted(self, self.value))
|
||||
|
||||
@@ -34,7 +34,7 @@ class ListItem(Widget, can_focus=False):
|
||||
pass
|
||||
|
||||
def on_click(self, event: events.Click) -> None:
|
||||
self.emit_no_wait(self._ChildClicked(self))
|
||||
self.post_message_no_wait(self._ChildClicked(self))
|
||||
|
||||
def watch_highlighted(self, value: bool) -> None:
|
||||
self.set_class(value, "--highlight")
|
||||
|
||||
@@ -35,7 +35,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
||||
index = reactive(0, always_update=True)
|
||||
|
||||
class Highlighted(Message, bubble=True):
|
||||
"""Emitted when the highlighted item changes.
|
||||
"""Posted when the highlighted item changes.
|
||||
|
||||
Highlighted item is controlled using up/down keys.
|
||||
Can be handled using `on_list_view_highlighted` in a subclass of `ListView`
|
||||
@@ -50,7 +50,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
||||
self.item: ListItem | None = item
|
||||
|
||||
class Selected(Message, bubble=True):
|
||||
"""Emitted when a list item is selected, e.g. when you press the enter key on it.
|
||||
"""Posted when a list item is selected, e.g. when you press the enter key on it.
|
||||
|
||||
Can be handled using `on_list_view_selected` in a subclass of `ListView` or in
|
||||
a parent widget in the DOM.
|
||||
@@ -125,7 +125,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
||||
new_child = None
|
||||
|
||||
self._scroll_highlighted_region()
|
||||
self.emit_no_wait(self.Highlighted(self, new_child))
|
||||
self.post_message_no_wait(self.Highlighted(self, new_child))
|
||||
|
||||
def append(self, item: ListItem) -> AwaitMount:
|
||||
"""Append a new ListItem to the end of the ListView.
|
||||
@@ -155,7 +155,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
||||
|
||||
def action_select_cursor(self) -> None:
|
||||
selected_child = self.highlighted_child
|
||||
self.emit_no_wait(self.Selected(self, selected_child))
|
||||
self.post_message_no_wait(self.Selected(self, selected_child))
|
||||
|
||||
def action_cursor_down(self) -> None:
|
||||
self.index += 1
|
||||
@@ -166,7 +166,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
||||
def on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:
|
||||
self.focus()
|
||||
self.index = self.children.index(event.sender)
|
||||
self.emit_no_wait(self.Selected(self, event.sender))
|
||||
self.post_message_no_wait(self.Selected(self, event.sender))
|
||||
|
||||
def _scroll_highlighted_region(self) -> None:
|
||||
"""Used to keep the highlighted index within vision"""
|
||||
|
||||
Reference in New Issue
Block a user