Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Added `grid_size` property to `GridLayout` https://github.com/Textualize/textual/pull/6210
- Exposed `NoSelection` and `BLANK` via `textual.widgets.select` https://github.com/Textualize/textual/pull/6214
- Added `Widget.FOCUS_ON_CLICK` classvar amd `Widget.focus_on_click` method https://github.com/Textualize/textual/pull/6216

### Changed

Expand Down
8 changes: 4 additions & 4 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2437,8 +2437,8 @@ def mount(
MountError: If there is a problem with the mount request.

Note:
Only one of ``before`` or ``after`` can be provided. If both are
provided a ``MountError`` will be raised.
Only one of `before` or `after` can be provided. If both are
provided a `MountError` will be raised.
"""
return self.screen.mount(*widgets, before=before, after=after)

Expand Down Expand Up @@ -2467,8 +2467,8 @@ def mount_all(
MountError: If there is a problem with the mount request.

Note:
Only one of ``before`` or ``after`` can be provided. If both are
provided a ``MountError`` will be raised.
Only one of `before` or `after` can be provided. If both are
provided a `MountError` will be raised.
"""
return self.mount(*widgets, before=before, after=after)

Expand Down
5 changes: 4 additions & 1 deletion src/textual/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,10 @@ def _forward_event(self, event: events.Event) -> None:
else:
if isinstance(event, events.MouseDown):
focusable_widget = self.get_focusable_widget_at(event.x, event.y)
if focusable_widget:
if (
focusable_widget is not None
and focusable_widget.focus_on_click()
):
self.set_focus(focusable_widget, scroll_visible=False)
event.style = self.get_style_at(event.screen_x, event.screen_y)
if widget.loading:
Expand Down
64 changes: 61 additions & 3 deletions src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,10 @@ class Widget(DOMNode):
"""

ALLOW_SELECT: ClassVar[bool] = True
"""Does this widget support automatic text selection? May be further refined with [Widget.allow_select][textual.widget.Widget.allow_select]"""
"""Does this widget support automatic text selection? May be further refined with [Widget.allow_select][textual.widget.Widget.allow_select]."""

FOCUS_ON_CLICK: ClassVar[bool] = True
"""Should focusable widgets be automatically focused on click? Default return value of [Widget.focus_on_click][textual.widget.Widget.focus_on_click]."""

can_focus: bool = False
"""Widget may receive focus."""
Expand Down Expand Up @@ -679,6 +682,17 @@ def text_selection(self) -> Selection | None:
"""Text selection information, or `None` if no text is selected in this widget."""
return self.screen.selections.get(self, None)

def focus_on_click(self) -> bool:
"""Automatically focus the widget on click?

Implement this if you want to change the default click to focus behavior.
The default will return the classvar `FOCUS_ON_CLICK`.

Returns:
`True` if Textual should set focus automatically on a click, or `False` if it shouldn't.
"""
return self.FOCUS_ON_CLICK

def get_line_filters(self) -> Sequence[LineFilter]:
"""Get the line filters enabled for this widget.

Expand Down Expand Up @@ -1470,14 +1484,58 @@ def mount_all(
MountError: If there is a problem with the mount request.

Note:
Only one of ``before`` or ``after`` can be provided. If both are
provided a ``MountError`` will be raised.
Only one of `before` or `after` can be provided. If both are
provided a `MountError` will be raised.
"""
if self.app._exit:
return AwaitMount(self, [])
await_mount = self.mount(*widgets, before=before, after=after)
return await_mount

def mount_compose(
self,
compose_result: ComposeResult,
*,
before: int | str | Widget | None = None,
after: int | str | Widget | None = None,
) -> AwaitMount:
"""Mount widgets from the result of a compose method.

Example:
```python
def on_key(self, event:events.Key) -> None:

def add_key(key:str) -> ComposeResult:
'''Compose key information widgets'''
with containers.HorizontalGroup():
yield Label("You pressed:")
yield Label(key)

self.mount_compose(add_key(event.key))

```

Args:
compose_result: The result of a compose method.
before: Optional location to mount before. An `int` is the index
of the child to mount before, a `str` is a `query_one` query to
find the widget to mount before.
after: Optional location to mount after. An `int` is the index
of the child to mount after, a `str` is a `query_one` query to
find the widget to mount after.

Returns:
An awaitable object that waits for widgets to be mounted.

Raises:
MountError: If there is a problem with the mount request.

Note:
Only one of `before` or `after` can be provided. If both are
provided a `MountError` will be raised.
"""
return self.mount_all(compose(self, compose_result), before=before, after=after)

if TYPE_CHECKING:

@overload
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading