Skip to content

Commit b60fe1b

Browse files
authored
Merge pull request #6216 from Textualize/focus-on-click
Focus on click and mount_compose
2 parents 3080464 + 35e6118 commit b60fe1b

File tree

7 files changed

+414
-8
lines changed

7 files changed

+414
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717

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

2122
### Changed
2223

src/textual/app.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,8 +2437,8 @@ def mount(
24372437
MountError: If there is a problem with the mount request.
24382438
24392439
Note:
2440-
Only one of ``before`` or ``after`` can be provided. If both are
2441-
provided a ``MountError`` will be raised.
2440+
Only one of `before` or `after` can be provided. If both are
2441+
provided a `MountError` will be raised.
24422442
"""
24432443
return self.screen.mount(*widgets, before=before, after=after)
24442444

@@ -2467,8 +2467,8 @@ def mount_all(
24672467
MountError: If there is a problem with the mount request.
24682468
24692469
Note:
2470-
Only one of ``before`` or ``after`` can be provided. If both are
2471-
provided a ``MountError`` will be raised.
2470+
Only one of `before` or `after` can be provided. If both are
2471+
provided a `MountError` will be raised.
24722472
"""
24732473
return self.mount(*widgets, before=before, after=after)
24742474

src/textual/screen.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,10 @@ def _forward_event(self, event: events.Event) -> None:
17221722
else:
17231723
if isinstance(event, events.MouseDown):
17241724
focusable_widget = self.get_focusable_widget_at(event.x, event.y)
1725-
if focusable_widget:
1725+
if (
1726+
focusable_widget is not None
1727+
and focusable_widget.focus_on_click()
1728+
):
17261729
self.set_focus(focusable_widget, scroll_visible=False)
17271730
event.style = self.get_style_at(event.screen_x, event.screen_y)
17281731
if widget.loading:

src/textual/widget.py

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,10 @@ class Widget(DOMNode):
324324
"""
325325

326326
ALLOW_SELECT: ClassVar[bool] = True
327-
"""Does this widget support automatic text selection? May be further refined with [Widget.allow_select][textual.widget.Widget.allow_select]"""
327+
"""Does this widget support automatic text selection? May be further refined with [Widget.allow_select][textual.widget.Widget.allow_select]."""
328+
329+
FOCUS_ON_CLICK: ClassVar[bool] = True
330+
"""Should focusable widgets be automatically focused on click? Default return value of [Widget.focus_on_click][textual.widget.Widget.focus_on_click]."""
328331

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

685+
def focus_on_click(self) -> bool:
686+
"""Automatically focus the widget on click?
687+
688+
Implement this if you want to change the default click to focus behavior.
689+
The default will return the classvar `FOCUS_ON_CLICK`.
690+
691+
Returns:
692+
`True` if Textual should set focus automatically on a click, or `False` if it shouldn't.
693+
"""
694+
return self.FOCUS_ON_CLICK
695+
682696
def get_line_filters(self) -> Sequence[LineFilter]:
683697
"""Get the line filters enabled for this widget.
684698
@@ -1470,14 +1484,58 @@ def mount_all(
14701484
MountError: If there is a problem with the mount request.
14711485
14721486
Note:
1473-
Only one of ``before`` or ``after`` can be provided. If both are
1474-
provided a ``MountError`` will be raised.
1487+
Only one of `before` or `after` can be provided. If both are
1488+
provided a `MountError` will be raised.
14751489
"""
14761490
if self.app._exit:
14771491
return AwaitMount(self, [])
14781492
await_mount = self.mount(*widgets, before=before, after=after)
14791493
return await_mount
14801494

1495+
def mount_compose(
1496+
self,
1497+
compose_result: ComposeResult,
1498+
*,
1499+
before: int | str | Widget | None = None,
1500+
after: int | str | Widget | None = None,
1501+
) -> AwaitMount:
1502+
"""Mount widgets from the result of a compose method.
1503+
1504+
Example:
1505+
```python
1506+
def on_key(self, event:events.Key) -> None:
1507+
1508+
def add_key(key:str) -> ComposeResult:
1509+
'''Compose key information widgets'''
1510+
with containers.HorizontalGroup():
1511+
yield Label("You pressed:")
1512+
yield Label(key)
1513+
1514+
self.mount_compose(add_key(event.key))
1515+
1516+
```
1517+
1518+
Args:
1519+
compose_result: The result of a compose method.
1520+
before: Optional location to mount before. An `int` is the index
1521+
of the child to mount before, a `str` is a `query_one` query to
1522+
find the widget to mount before.
1523+
after: Optional location to mount after. An `int` is the index
1524+
of the child to mount after, a `str` is a `query_one` query to
1525+
find the widget to mount after.
1526+
1527+
Returns:
1528+
An awaitable object that waits for widgets to be mounted.
1529+
1530+
Raises:
1531+
MountError: If there is a problem with the mount request.
1532+
1533+
Note:
1534+
Only one of `before` or `after` can be provided. If both are
1535+
provided a `MountError` will be raised.
1536+
"""
1537+
return self.mount_all(compose(self, compose_result), before=before, after=after)
1538+
14811539
if TYPE_CHECKING:
14821540

14831541
@overload
Lines changed: 152 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)