Skip to content

Commit 9935b64

Browse files
authored
Merge branch 'main' into constrain-tooltips
2 parents 3794d95 + d7eae41 commit 9935b64

File tree

8 files changed

+264
-30
lines changed

8 files changed

+264
-30
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## Unreleased
99

10+
1011
### Added
1112

1213
- Added support for A-F to Digits widget https://github.com/Textualize/textual/pull/5094
1314
- Added `Region.constrain` https://github.com/Textualize/textual/pull/5097
15+
- Added support for A-F to Digits widget https://github.com/Textualize/textual/pull/5094
1416

1517
### Changed
1618

19+
- `Screen.ALLOW_IN_MAXIMIZED_VIEW` will now default to `App.ALLOW_IN_MAXIMIZED_VIEW` https://github.com/Textualize/textual/pull/5088
20+
- Widgets matching `.-textual-system` will now be included in the maximize view by default https://github.com/Textualize/textual/pull/5088
1721
- Digits are now thin by default, style with text-style: bold to get bold digits https://github.com/Textualize/textual/pull/5094
1822
- Made `Widget.absolute_offset` public https://github.com/Textualize/textual/pull/5097
1923
- Tooltips are now displayed directly below the mouse cursor https://github.com/Textualize/textual/pull/5097
2024
- `Region.inflect` will now assume that margins overlap https://github.com/Textualize/textual/pull/5097
25+
- `Pilot.click` and friends will now accept a widget, in addition to a selector https://github.com/Textualize/textual/pull/5095
2126

2227
## [0.82.0] - 2024-10-03
2328

src/textual/app.py

+3
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,9 @@ class MyApp(App[None]):
461461
COMMAND_PALETTE_DISPLAY: ClassVar[str | None] = None
462462
"""How the command palette key should be displayed in the footer (or `None` for default)."""
463463

464+
ALLOW_IN_MAXIMIZED_VIEW: ClassVar[str] = "Footer"
465+
"""The default value of [Screen.ALLOW_IN_MAXIMIZED_VIEW][textual.screen.Screen.ALLOW_IN_MAXIMIZED_VIEW]."""
466+
464467
BINDINGS: ClassVar[list[BindingType]] = [
465468
Binding("ctrl+c", "quit", "Quit", show=False, priority=True)
466469
]

src/textual/pilot.py

+27-26
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ async def resize_terminal(self, width: int, height: int) -> None:
9898

9999
async def mouse_down(
100100
self,
101-
selector: type[Widget] | str | None = None,
101+
widget: Widget | type[Widget] | str | None = None,
102102
offset: tuple[int, int] = (0, 0),
103103
shift: bool = False,
104104
meta: bool = False,
@@ -110,12 +110,12 @@ async def mouse_down(
110110
the offset specified and it must be within the visible area of the screen.
111111
112112
Args:
113-
selector: A selector to specify a widget that should be used as the reference
113+
widget: A widget or selector used as an origin
114114
for the event offset. If this is not specified, the offset is interpreted
115115
relative to the screen. You can use this parameter to try to target a
116116
specific widget. However, if the widget is currently hidden or obscured by
117117
another widget, the event may not land on the widget you specified.
118-
offset: The offset for the event. The offset is relative to the selector
118+
offset: The offset for the event. The offset is relative to the selector / widget
119119
provided or to the screen, if no selector is provided.
120120
shift: Simulate the event with the shift key held down.
121121
meta: Simulate the event with the meta key held down.
@@ -131,7 +131,7 @@ async def mouse_down(
131131
try:
132132
return await self._post_mouse_events(
133133
[MouseDown],
134-
selector=selector,
134+
widget=widget,
135135
offset=offset,
136136
button=1,
137137
shift=shift,
@@ -143,7 +143,7 @@ async def mouse_down(
143143

144144
async def mouse_up(
145145
self,
146-
selector: type[Widget] | str | None = None,
146+
widget: Widget | type[Widget] | str | None = None,
147147
offset: tuple[int, int] = (0, 0),
148148
shift: bool = False,
149149
meta: bool = False,
@@ -155,12 +155,12 @@ async def mouse_up(
155155
the offset specified and it must be within the visible area of the screen.
156156
157157
Args:
158-
selector: A selector to specify a widget that should be used as the reference
158+
widget: A widget or selector used as an origin
159159
for the event offset. If this is not specified, the offset is interpreted
160160
relative to the screen. You can use this parameter to try to target a
161161
specific widget. However, if the widget is currently hidden or obscured by
162162
another widget, the event may not land on the widget you specified.
163-
offset: The offset for the event. The offset is relative to the selector
163+
offset: The offset for the event. The offset is relative to the widget / selector
164164
provided or to the screen, if no selector is provided.
165165
shift: Simulate the event with the shift key held down.
166166
meta: Simulate the event with the meta key held down.
@@ -176,7 +176,7 @@ async def mouse_up(
176176
try:
177177
return await self._post_mouse_events(
178178
[MouseUp],
179-
selector=selector,
179+
widget=widget,
180180
offset=offset,
181181
button=1,
182182
shift=shift,
@@ -188,7 +188,7 @@ async def mouse_up(
188188

189189
async def click(
190190
self,
191-
selector: type[Widget] | str | None = None,
191+
widget: Widget | type[Widget] | str | None = None,
192192
offset: tuple[int, int] = (0, 0),
193193
shift: bool = False,
194194
meta: bool = False,
@@ -207,12 +207,12 @@ async def click(
207207
```
208208
209209
Args:
210-
selector: A selector to specify a widget that should be used as the reference
210+
widget: A widget or selector used as an origin
211211
for the click offset. If this is not specified, the offset is interpreted
212212
relative to the screen. You can use this parameter to try to click on a
213213
specific widget. However, if the widget is currently hidden or obscured by
214214
another widget, the click may not land on the widget you specified.
215-
offset: The offset to click. The offset is relative to the selector provided
215+
offset: The offset to click. The offset is relative to the widget / selector provided
216216
or to the screen, if no selector is provided.
217217
shift: Click with the shift key held down.
218218
meta: Click with the meta key held down.
@@ -228,7 +228,7 @@ async def click(
228228
try:
229229
return await self._post_mouse_events(
230230
[MouseDown, MouseUp, Click],
231-
selector=selector,
231+
widget=widget,
232232
offset=offset,
233233
button=1,
234234
shift=shift,
@@ -240,7 +240,7 @@ async def click(
240240

241241
async def hover(
242242
self,
243-
selector: type[Widget] | str | None | None = None,
243+
widget: Widget | type[Widget] | str | None | None = None,
244244
offset: tuple[int, int] = (0, 0),
245245
) -> bool:
246246
"""Simulate hovering with the mouse cursor at a specified position.
@@ -249,12 +249,12 @@ async def hover(
249249
the offset specified and it must be within the visible area of the screen.
250250
251251
Args:
252-
selector: A selector to specify a widget that should be used as the reference
252+
widget: A widget or selector used as an origin
253253
for the hover offset. If this is not specified, the offset is interpreted
254254
relative to the screen. You can use this parameter to try to hover a
255255
specific widget. However, if the widget is currently hidden or obscured by
256256
another widget, the hover may not land on the widget you specified.
257-
offset: The offset to hover. The offset is relative to the selector provided
257+
offset: The offset to hover. The offset is relative to the widget / selector provided
258258
or to the screen, if no selector is provided.
259259
260260
Raises:
@@ -268,16 +268,14 @@ async def hover(
268268
# "settle" before moving it to the new hover position.
269269
await self.pause()
270270
try:
271-
return await self._post_mouse_events(
272-
[MouseMove], selector, offset, button=0
273-
)
271+
return await self._post_mouse_events([MouseMove], widget, offset, button=0)
274272
except OutOfBounds as error:
275273
raise error from None
276274

277275
async def _post_mouse_events(
278276
self,
279277
events: list[type[MouseEvent]],
280-
selector: type[Widget] | str | None | None = None,
278+
widget: Widget | type[Widget] | str | None | None = None,
281279
offset: tuple[int, int] = (0, 0),
282280
button: int = 0,
283281
shift: bool = False,
@@ -293,12 +291,12 @@ async def _post_mouse_events(
293291
functions that the pilot exposes.
294292
295293
Args:
296-
selector: A selector to specify a widget that should be used as the reference
297-
for the events offset. If this is not specified, the offset is interpreted
294+
widget: A widget or selector used as the origin
295+
for the event's offset. If this is not specified, the offset is interpreted
298296
relative to the screen. You can use this parameter to try to target a
299297
specific widget. However, if the widget is currently hidden or obscured by
300298
another widget, the events may not land on the widget you specified.
301-
offset: The offset for the events. The offset is relative to the selector
299+
offset: The offset for the events. The offset is relative to the widget / selector
302300
provided or to the screen, if no selector is provided.
303301
shift: Simulate the events with the shift key held down.
304302
meta: Simulate the events with the meta key held down.
@@ -313,10 +311,13 @@ async def _post_mouse_events(
313311
"""
314312
app = self.app
315313
screen = app.screen
316-
if selector is not None:
317-
target_widget = app.query_one(selector)
318-
else:
314+
target_widget: Widget
315+
if widget is None:
319316
target_widget = screen
317+
elif isinstance(widget, Widget):
318+
target_widget = widget
319+
else:
320+
target_widget = app.query_one(widget)
320321

321322
message_arguments = _get_mouse_message_arguments(
322323
target_widget,
@@ -351,7 +352,7 @@ async def _post_mouse_events(
351352
app.screen._forward_event(event)
352353
await self.pause()
353354

354-
return selector is None or widget_at is target_widget
355+
return widget is None or widget_at is target_widget
355356

356357
async def _wait_for_screen(self, timeout: float = 30.0) -> bool:
357358
"""Wait for the current screen and its children to have processed all pending events.

src/textual/screen.py

+30-3
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,9 @@ class Screen(Generic[ScreenResultType], Widget):
204204
205205
Should be a set of [`command.Provider`][textual.command.Provider] classes.
206206
"""
207-
ALLOW_IN_MAXIMIZED_VIEW: ClassVar[str] = ".-textual-system,Footer"
208-
"""A selector for the widgets (direct children of Screen) that are allowed in the maximized view (in addition to maximized widget)."""
207+
ALLOW_IN_MAXIMIZED_VIEW: ClassVar[str | None] = None
208+
"""A selector for the widgets (direct children of Screen) that are allowed in the maximized view (in addition to maximized widget). Or
209+
`None` to default to [App.ALLOW_IN_MAXIMIZED_VIEW][textual.app.App.ALLOW_IN_MAXIMIZED_VIEW]"""
209210

210211
ESCAPE_TO_MINIMIZE: ClassVar[bool | None] = None
211212
"""Use escape key to minimize (potentially overriding bindings) or `None` to defer to [`App.ESCAPE_TO_MINIMIZE`][textual.app.App.ESCAPE_TO_MINIMIZE]."""
@@ -434,10 +435,36 @@ def _arrange(self, size: Size) -> DockArrangeResult:
434435
if cached_result is not None:
435436
return cached_result
436437

438+
allow_in_maximized_view = (
439+
self.app.ALLOW_IN_MAXIMIZED_VIEW
440+
if self.ALLOW_IN_MAXIMIZED_VIEW is None
441+
else self.ALLOW_IN_MAXIMIZED_VIEW
442+
)
443+
444+
def get_maximize_widgets(maximized: Widget) -> list[Widget]:
445+
"""Get widgets to display in maximized view.
446+
447+
Returns:
448+
A list of widgets.
449+
450+
"""
451+
# De-duplicate with a set
452+
widgets = {
453+
maximized,
454+
*self.query_children(allow_in_maximized_view),
455+
*self.query_children(".-textual-system"),
456+
}
457+
# Restore order of widgets.
458+
maximize_widgets = [widget for widget in self.children if widget in widgets]
459+
# Add the maximized widget, if its not already included
460+
if maximized not in maximize_widgets:
461+
maximize_widgets.insert(0, maximized)
462+
return maximize_widgets
463+
437464
arrangement = self._arrangement_cache[cache_key] = arrange(
438465
self,
439466
(
440-
[self.maximized, *self.query_children(self.ALLOW_IN_MAXIMIZED_VIEW)]
467+
get_maximize_widgets(self.maximized)
441468
if self.maximized is not None
442469
else self._nodes
443470
),

src/textual/widgets/_radio_button.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class RadioButton(ToggleButton):
1212
A `RadioButton` is best used within a [RadioSet][textual.widgets.RadioSet].
1313
"""
1414

15-
BUTTON_INNER = "\u25CF"
15+
BUTTON_INNER = "\u25cf"
1616
"""The character used for the inside of the button."""
1717

1818
class Changed(ToggleButton.Changed):

0 commit comments

Comments
 (0)