Skip to content

Commit e7a0b73

Browse files
authored
Merge branch 'main' into bug/5436/fix-help-alignment
2 parents 1525e18 + 18bcbd6 commit e7a0b73

File tree

422 files changed

+24677
-23083
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

422 files changed

+24677
-23083
lines changed

Diff for: CHANGELOG.md

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

88
## Unreleased
99

10+
### Changed
1011

1112
### Fixed
1213

1314
- Fixed `Pilot.click` not working with `times` parameter https://github.com/Textualize/textual/pull/5398
1415
- Fixed select refocusing itself too late https://github.com/Textualize/textual/pull/5420
1516
- Fixed layout of the keys in the help panel when a key has a tooltip but no description https://github.com/Textualize/textual/issues/5436
17+
- Footer can now be scrolled horizontally without holding `shift` https://github.com/Textualize/textual/pull/5404
18+
- The content of an `Input` will now only be automatically selected when the widget is focused by the user, not when the app itself has regained focus (similar to web browsers). https://github.com/Textualize/textual/pull/5379
19+
- `Pilot.mouse_down` and `Pilot.mouse_up` now issue a prior `MouseMove` event, to more closely reflect real mouse actions. https://github.com/Textualize/textual/pull/5409
20+
- Snapshots tests now discard meta, which should reduce test breaking with no visual differences https://github.com/Textualize/textual/pull/5409
1621

1722
### Added
1823

24+
- Added `Select.type_to_search` which allows you to type to move the cursor to a matching option https://github.com/Textualize/textual/pull/5403
25+
- Updated `TextArea` and `Input` behavior when there is a selection and the user presses left or right https://github.com/Textualize/textual/pull/5400
1926
- Added `from_app_focus` to `Focus` event to indicate if a widget is being focused because the app itself has regained focus or not https://github.com/Textualize/textual/pull/5379
20-
- - Added `Select.type_to_search` which allows you to type to move the cursor to a matching option https://github.com/Textualize/textual/pull/5403
27+
- Added `Blurred` message to `Input` widget (matching `Submitted` and `Changed`) to make it easier to synchronize with `validate_on` parameter when set to 'blur'.
28+
- Added `Offset.transpose` https://github.com/Textualize/textual/pull/5409
29+
- Added `screen--selection` component class to define style for selection https://github.com/Textualize/textual/pull/5409
30+
- Added `Widget.select_container` property https://github.com/Textualize/textual/pull/5409
31+
- Added `Widget.select_all` https://github.com/Textualize/textual/pull/5409
32+
- Added `Region.bottom_right_inclusive` https://github.com/Textualize/textual/pull/5409
33+
- Added double click to select, triple click to select all in container https://github.com/Textualize/textual/pull/5409
34+
- Added arbitrary text selection https://github.com/Textualize/textual/pull/5409
35+
- Added Widget.ALLOW_SELECT classvar for a per-widget switch to disable text selection https://github.com/Textualize/textual/pull/5409
36+
- Added Widget.allow_select method for programmatic control of text selection https://github.com/Textualize/textual/pull/5409
37+
- Added App.ALLOW_SELECT for a global switch to disable text selection https://github.com/Textualize/textual/pull/5409
38+
- Added `DOMNode.query_ancestor` https://github.com/Textualize/textual/pull/5409
2139

22-
### Changed
40+
### Fixed
2341

2442
- The content of an `Input` will now only be automatically selected when the widget is focused by the user, not when the app itself has regained focus (similar to web browsers). https://github.com/Textualize/textual/pull/5379
2543
- Updated `TextArea` and `Input` behavior when there is a selection and the user presses left or right https://github.com/Textualize/textual/pull/5400
2644
- Footer can now be scrolled horizontally without holding `shift` https://github.com/Textualize/textual/pull/5404
45+
- Modified _on_blur method in `Input` to post a `Blurred` message
46+
- Fixed `Pilot.click` not working with `times` parameter https://github.com/Textualize/textual/pull/5398
47+
- Fixed select refocusing itself too late https://github.com/Textualize/textual/pull/5420
48+
- Fixed Log widget not refreshing on resize https://github.com/Textualize/textual/pull/5460
2749

2850

2951
## [1.0.0] - 2024-12-12

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ In addition to serving your apps locally, you can serve apps with [Textual Web](
199199

200200
Textual Web's firewall-busting technology can serve an unlimited number of applications.
201201

202-
Since Textual apps have low system requirements, you can install them anywhere Python also runs. Turning any device in to a connected device.
202+
Since Textual apps have low system requirements, you can install them anywhere Python also runs. Turning any device into a connected device.
203203
No desktop required!
204204

205205

Diff for: docs/examples/guide/actions/actions05.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
TEXT = """
55
[b]Set your background[/b]
6-
[@click=app.set_background('cyan')]Cyan[/]
7-
[@click=app.set_background('magenta')]Magenta[/]
8-
[@click=app.set_background('yellow')]Yellow[/]
6+
[@click=set_background('cyan')]Cyan[/]
7+
[@click=set_background('magenta')]Magenta[/]
8+
[@click=set_background('yellow')]Yellow[/]
99
"""
1010

1111

Diff for: docs/examples/guide/widgets/checker04.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def get_square_region(square_offset: Offset) -> Region:
5252
"""Get region relative to widget from square coordinate."""
5353
x, y = square_offset
5454
region = Region(x * 8, y * 4, 8, 4)
55-
# Move the region in to the widgets frame of reference
55+
# Move the region into the widgets frame of reference
5656
region = region.translate(-self.scroll_offset)
5757
return region
5858

Diff for: docs/guide/app.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# App Basics
22

3-
In this chapter we will cover how to use Textual's App class to create an application. Just enough to get you up to speed. We will go in to more detail in the following chapters.
3+
In this chapter we will cover how to use Textual's App class to create an application. Just enough to get you up to speed. We will go into more detail in the following chapters.
44

55
## The App class
66

@@ -30,7 +30,7 @@ If we run this app with `python simple02.py` you will see a blank terminal, some
3030
```{.textual path="docs/examples/app/simple02.py"}
3131
```
3232

33-
When you call [App.run()][textual.app.App.run] Textual puts the terminal in to a special state called *application mode*. When in application mode the terminal will no longer echo what you type. Textual will take over responding to user input (keyboard and mouse) and will update the visible portion of the terminal (i.e. the *screen*).
33+
When you call [App.run()][textual.app.App.run] Textual puts the terminal into a special state called *application mode*. When in application mode the terminal will no longer echo what you type. Textual will take over responding to user input (keyboard and mouse) and will update the visible portion of the terminal (i.e. the *screen*).
3434

3535
If you hit ++ctrl+q++ Textual will exit application mode and return you to the command prompt. Any content you had in the terminal prior to application mode will be restored.
3636

@@ -42,7 +42,7 @@ If you hit ++ctrl+q++ Textual will exit application mode and return you to the c
4242

4343
!!! tip "Added in version 0.55.0"
4444

45-
You can also run apps in _inline_ mode, which will cause the app to appear beneath the prompt (and won't go in to application mode).
45+
You can also run apps in _inline_ mode, which will cause the app to appear beneath the prompt (and won't go into application mode).
4646
Inline apps are useful for tools that integrate closely with the typical workflow of a terminal.
4747

4848
To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. See [Style Inline Apps](../how-to/style-inline-apps.md) for how to apply additional styles to inline apps.

Diff for: docs/guide/devtools.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ textual run -c textual colors
6262
## Serve
6363

6464
The devtools can also serve your application in a browser.
65-
Effectively turning your terminal app in to a web application!
65+
Effectively turning your terminal app into a web application!
6666

6767
The `serve` sub-command is similar to `run`. Here's how you can serve an app launched from a Python file:
6868

@@ -144,7 +144,7 @@ textual console -v
144144

145145
### Decreasing verbosity
146146

147-
Log messages are classififed in to groups, and the `-x` flag can be used to **exclude** all message from a group. The groups are: `EVENT`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `PRINT`, `SYSTEM`, `LOGGING` and `WORKER`. The group a message belongs to is printed after its timestamp.
147+
Log messages are classififed into groups, and the `-x` flag can be used to **exclude** all message from a group. The groups are: `EVENT`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `PRINT`, `SYSTEM`, `LOGGING` and `WORKER`. The group a message belongs to is printed after its timestamp.
148148

149149
Multiple groups may be excluded, for example to exclude everything except warning, errors, and `print` statements:
150150

Diff for: docs/guide/events.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This processing of messages is done within an asyncio Task which is started when
2020

2121
The FastAPI docs have an [excellent introduction](https://fastapi.tiangolo.com/async/) to Python async programming.
2222

23-
By way of an example, let's consider what happens if you were to type "Text" in to a `Input` widget. When you hit the ++t++ key, Textual creates a [key][textual.events.Key] event and sends it to the widget's message queue. Ditto for ++e++, ++x++, and ++t++.
23+
By way of an example, let's consider what happens if you were to type "Text" into a `Input` widget. When you hit the ++t++ key, Textual creates a [key][textual.events.Key] event and sends it to the widget's message queue. Ditto for ++e++, ++x++, and ++t++.
2424

2525
The widget's task will pick the first message from the queue (a key event for the ++t++ key) and call the `on_key` method with the event as the first argument. In other words it will call `Input.on_key(event)`, which updates the display to show the new letter.
2626

@@ -334,4 +334,4 @@ Let's look at an example which looks up word definitions from an [api](https://d
334334
```{.textual path="docs/examples/events/dictionary.py"}
335335
```
336336

337-
Note the highlighted line in the above code which calls `asyncio.create_task` to run a coroutine in the background. Without this you would find typing in to the text box to be unresponsive.
337+
Note the highlighted line in the above code which calls `asyncio.create_task` to run a coroutine in the background. Without this you would find typing into the text box to be unresponsive.

Diff for: docs/guide/input.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ The following example shows how focus works in practice.
105105
```{.textual path="docs/examples/guide/input/key03.py", press="H,e,l,l,o,tab,W,o,r,l,d,!"}
106106
```
107107

108-
The app splits the screen in to quarters, with a `RichLog` widget in each quarter. If you click any of the text logs, you should see that it is highlighted to show that the widget has focus. Key events will be sent to the focused widget only.
108+
The app splits the screen into quarters, with a `RichLog` widget in each quarter. If you click any of the text logs, you should see that it is highlighted to show that the widget has focus. Key events will be sent to the focused widget only.
109109

110110
!!! tip
111111

@@ -255,4 +255,4 @@ Most mice have a scroll wheel which you can use to scroll the window underneath
255255

256256
!!! information
257257

258-
Terminal emulators will typically convert trackpad gestures in to scroll events.
258+
Terminal emulators will typically convert trackpad gestures into scroll events.

Diff for: docs/guide/reactivity.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ The following example modifies "refresh01.py" so that the greeting has an automa
134134
```{.textual path="docs/examples/guide/reactivity/refresh02.py" press="n,a,m,e"}
135135
```
136136

137-
If you type in to the input now, the greeting will expand to fit the content. If you were to set `layout=False` on the reactive attribute, you should see that the box remains the same size when you type.
137+
If you type into the input now, the greeting will expand to fit the content. If you were to set `layout=False` on the reactive attribute, you should see that the box remains the same size when you type.
138138

139139
## Validation
140140

@@ -171,7 +171,7 @@ Watch method names begin with `watch_` followed by the name of the attribute, an
171171
If the method accepts a single argument, it will be called with the new assigned value.
172172
If the method accepts *two* positional arguments, it will be called with both the *old* value and the *new* value.
173173

174-
The following app will display any color you type in to the input. Try it with a valid color in Textual CSS. For example `"darkorchid"` or `"#52de44"`.
174+
The following app will display any color you type into the input. Try it with a valid color in Textual CSS. For example `"darkorchid"` or `"#52de44"`.
175175

176176
=== "watch01.py"
177177

@@ -311,15 +311,15 @@ Compute methods are the final superpower offered by the `reactive` descriptor. T
311311

312312
You could be forgiven in thinking this sounds a lot like Python's property decorator. The difference is that Textual will cache the value of compute methods, and update them when any other reactive attribute changes.
313313

314-
The following example uses a computed attribute. It displays three inputs for each color component (red, green, and blue). If you enter numbers in to these inputs, the background color of another widget changes.
314+
The following example uses a computed attribute. It displays three inputs for each color component (red, green, and blue). If you enter numbers into these inputs, the background color of another widget changes.
315315

316316
=== "computed01.py"
317317

318318
```python hl_lines="25-26 28-29"
319319
--8<-- "docs/examples/guide/reactivity/computed01.py"
320320
```
321321

322-
1. Combines color components in to a Color object.
322+
1. Combines color components into a Color object.
323323
2. The watch method is called when the _result_ of `compute_color` changes.
324324

325325
=== "computed01.tcss"

Diff for: docs/guide/testing.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Knowing which test has failed will help you quickly track down where your code w
9191
## Simulating key presses
9292

9393
We've seen how the [`press`][textual.pilot.Pilot] method simulates keys.
94-
You can also supply multiple keys to simulate the user typing in to the app.
94+
You can also supply multiple keys to simulate the user typing into the app.
9595
Here's an example of simulating the user typing the word "hello".
9696

9797
```python

Diff for: docs/guide/widgets.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ Textual will call this method as required to get content for every row of charac
407407
--8<-- "docs/images/render_line.excalidraw.svg"
408408
</div>
409409

410-
Let's look at an example before we go in to the details. The following Textual app implements a widget with the line API that renders a checkerboard pattern. This might form the basis of a chess / checkers game. Here's the code:
410+
Let's look at an example before we go into the details. The following Textual app implements a widget with the line API that renders a checkerboard pattern. This might form the basis of a chess / checkers game. Here's the code:
411411

412412
=== "checker01.py"
413413

Diff for: docs/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Click the tabs to see the code behind the example.
196196

197197
=== "Calculator example"
198198

199-
```{.textual path="examples/calculator.py" columns=100 lines=41 press="3,.,1,4,5,9,2,wait:400"}
199+
```{.textual path="examples/calculator.py" columns=100 lines=41 press="6,.,2,8,3,1,8,5,3,0,7,1,wait:400"}
200200
```
201201

202202
=== "calculator.py"

Diff for: docs/tutorial.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,14 @@ Here's what the above app defines:
157157
--8<-- "docs/examples/tutorial/stopwatch01.py"
158158
```
159159

160-
The final three lines create an instance of the app and calls the [run()][textual.app.App.run] method which puts your terminal in to *application mode* and runs the app until you exit with ++ctrl+q++. This happens within a `__name__ == "__main__"` block so we could run the app with `python stopwatch01.py` or import it as part of a larger project.
160+
The final three lines create an instance of the app and calls the [run()][textual.app.App.run] method which puts your terminal into *application mode* and runs the app until you exit with ++ctrl+q++. This happens within a `__name__ == "__main__"` block so we could run the app with `python stopwatch01.py` or import it as part of a larger project.
161161

162162
## Designing a UI with widgets
163163

164164
Textual has a large number of [builtin widgets](./widget_gallery.md).
165165
For our app we will need new widgets, which we can create by extending and combining the builtin widgets.
166166

167-
Before we dive in to building widgets, let's first sketch a design for the app &mdash; so we know what we're aiming for.
167+
Before we dive into building widgets, let's first sketch a design for the app &mdash; so we know what we're aiming for.
168168

169169

170170
<div class="excalidraw">
@@ -188,7 +188,7 @@ Just a skeleton for now, we will add the rest of the features as we go.
188188
```
189189

190190
We've imported two new widgets in this code: [`Button`](widgets/button.md) for the buttons and [`Digits`](widgets/digits.md) for the time display.
191-
Additionally, we've imported [`Horizontal`][textual.containers.Horizontal] and [`VerticalScroll`][textual.containers.VerticalScroll] from `textual.containers` (as the name of the module suggests, *containers* are widgets which contain other widgets).
191+
Additionally, we've imported [`HorizontalGroup`][textual.containers.HorizontalGroup] and [`VerticalScroll`][textual.containers.VerticalScroll] from `textual.containers` (as the name of the module suggests, *containers* are widgets which contain other widgets).
192192
We will use these container widgets to define the general layout of our interface.
193193

194194
The `TimeDisplay` is currently very simple, all it does is extend `Digits` without adding any new features. We will flesh this out later.
@@ -205,7 +205,7 @@ The Button constructor takes a label to be displayed in the button (`"Start"`, `
205205

206206
### Composing the widgets
207207

208-
The new line in `StopwatchApp.compose()` yields a single `VerticalScroll` which will scroll if the contents don't quite fit. This widget also takes care of key bindings required for scrolling, like ++up++, ++down++, ++pgdn++, ++pgup++, ++home++, ++end++, etc.
208+
The new line in `StopwatchApp.compose()` yields a single `VerticalScroll` which will scroll if the contents don't quite fit. This widget also takes care of key bindings required for scrolling, like ++up++, ++down++, ++page-down++, ++page-up++, ++home++, ++end++, etc.
209209

210210
When widgets contain other widgets (like `VerticalScroll`) they will typically accept their child widgets as positional arguments.
211211
So the line `yield VerticalScroll(Stopwatch(), Stopwatch(), Stopwatch())` creates a `VerticalScroll` containing three `Stopwatch` widgets.

Diff for: docs/widgets/select.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A Select widget is a compact control to allow the user to select between a numbe
99
- [ ] Container
1010

1111

12-
The options in a select control may be passed in to the constructor or set later with [set_options][textual.widgets.Select.set_options].
12+
The options in a select control may be passed into the constructor or set later with [set_options][textual.widgets.Select.set_options].
1313
Options should be given as a sequence of tuples consisting of two values: the first is the string (or [Rich Renderable](https://rich.readthedocs.io/en/latest/protocol.html)) to display in the control and list of options, the second is the value of option.
1414

1515
The value of the currently selected option is stored in the `value` attribute of the widget, and the `value` attribute of the [Changed][textual.widgets.Select.Changed] message.

Diff for: examples/dictionary.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ async def lookup_word(self, word: str) -> None:
4848
self.query_one("#results", Markdown).update(markdown)
4949

5050
def make_word_markdown(self, results: object) -> str:
51-
"""Convert the results in to markdown."""
51+
"""Convert the results into markdown."""
5252
lines = []
5353
if isinstance(results, dict):
5454
lines.append(f"# {results['title']}")

Diff for: examples/example.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This is H3 Content
1212

1313
#### This is H4
1414

15-
Header level 4 content. Drilling down in to finer headings.
15+
Header level 4 content. Drilling down into finer headings.
1616

1717
##### This is H5
1818

@@ -85,7 +85,7 @@ In the future I think we could add controls to export the code, copy to the clip
8585
```python
8686
@lru_cache(maxsize=1024)
8787
def split(self, cut_x: int, cut_y: int) -> tuple[Region, Region, Region, Region]:
88-
"""Split a region in to 4 from given x and y offsets (cuts).
88+
"""Split a region into 4 from given x and y offsets (cuts).
8989
9090
```
9191
cut_x ↓

0 commit comments

Comments
 (0)