Skip to content

Commit 5d9e3c0

Browse files
authored
Merge branch 'main' into fix-get-event-loop-call
2 parents 7e412cf + 2b52252 commit 5d9e3c0

File tree

11 files changed

+145
-64
lines changed

11 files changed

+145
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1414
- Fixed `Button` allowing text selection https://github.com/Textualize/textual/pull/5770
1515
- Fixed running `App.run` after `asyncio.run` https://github.com/Textualize/textual/pull/5799
1616
- Fixed triggering a deprecation warning in py >= 3.10 https://github.com/Textualize/textual/pull/5799
17+
- Fixed `Input` invalid cursor position after updating the value https://github.com/Textualize/textual/issues/5811
1718

1819
## [3.2.0] - 2025-05-02
1920

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
draft: false
3+
date: 2025-05-07
4+
title: "The future of Textualize"
5+
categories:
6+
- News
7+
authors:
8+
- willmcgugan
9+
---
10+
11+
Textual has come a *long* way since I figured why not build an application framework on top of [Rich](https://github.com/Textualize/rich).
12+
13+
Both were initially hobby projects. I mean look how much fun I was having back then:
14+
15+
<!-- more -->
16+
17+
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="en" dir="ltr">Making good progress with Textual CSS. <br><br>Here&#39;s a &quot;basic&quot; app. The <a href="https://twitter.com/hashtag/Python?src=hash&amp;ref_src=twsrc%5Etfw">#Python</a> + CSS in the screenshots generates the layout in the terminal here.<br><br>Separating the layout and design from the runtime logic will make it easy to create gorgeous TUI apps. 🤩 <a href="https://t.co/Rxnwzs4pXd">pic.twitter.com/Rxnwzs4pXd</a></p>&mdash; Will McGugan (@willmcgugan) <a href="https://twitter.com/willmcgugan/status/1463977921891217411?ref_src=twsrc%5Etfw">November 25, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
18+
19+
Working on Textual has been a constant source of delight; figuring out how to make a terminal do things that it shouldn't really be able to do, and to a lesser extent a source of frustration when working around the baffling edge cases in emulators and the terminal protocol.
20+
21+
But work around it we did, and now Textual is an awesome piece of software that has spawned a community of developers building TUIs for [all kinds of things](https://github.com/textualize/transcendent-textual) (not to mention, [web apps](https://github.com/Textualize/textual-serve))!
22+
23+
Additionally, Textual has some of the [best docs](https://textual.textualize.io/guide/screens/) for any Open Source project. Shout out to [@squidfunk](https://x.com/squidfunk) and [@pawamoy](https://x.com/pawamoy) for the tech that makes these beautiful docs possible.
24+
25+
Ultimately though a business needs a product. Textual has always been a solution in search of a problem. And while there are plenty of problems to which Textual is a fantastic solution, we weren't able to find a shared problem or pain-point to build a viable business around. Which is why Textualize, the company, will be wrapping up in the next few weeks.
26+
27+
Textual will live on as an Open Source project. In the near term, nothing much will change. I will be maintaining Textual and Rich as I have always done. Software is never finished, but Textual is mature and battle-tested. I'm confident transitioning from a full-time funded project to a community project won't have a negative impact.
28+
29+
## Thanks!
30+
31+
I'd like to thank the awesome devs I worked with at Textualize, and the many developers that followed along, contributing and building apps. Wether you were an early adopter or you just discovered Textual, you made Textual what it is today.
32+
33+
## Get in touch
34+
35+
If you would like to talk Textual, feel free to find me on our [Discord server](https://github.com/textualize/textual/) or the socials.
36+
37+
I've also started a [blog](https://willmcgugan.github.io/) where I will write a little more on this from a more personal perspective.

docs/guide/CSS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Unlike the `id` attribute, a widget's classes can be changed after the widget wa
297297
- [remove_class()][textual.dom.DOMNode.remove_class] Removes class name(s) from a widget.
298298
- [toggle_class()][textual.dom.DOMNode.toggle_class] Removes a class name if it is present, or adds the name if it's not already present.
299299
- [has_class()][textual.dom.DOMNode.has_class] Checks if one or more classes are set on a widget.
300+
- [set_class()][textual.css.query.DOMQuery.set_class] Sets or removes a class dependant on a boolean.
300301
- [classes][textual.dom.DOMNode.classes] Is a frozen set of the class(es) set on a widget.
301302

302303

src/textual/demo/home.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
6767
```python
6868
# Start building!
69-
from textual import App, ComposeResult
69+
from textual.app import App, ComposeResult
7070
from textual.widgets import Label
7171
7272
class MyApp(App):

src/textual/dom.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,17 @@ def has_class(self, *class_names: str) -> bool:
16651665
def set_class(self, add: bool, *class_names: str, update: bool = True) -> Self:
16661666
"""Add or remove class(es) based on a condition.
16671667
1668+
This can condense the four lines required to implement the equivalent branch into a single line.
1669+
1670+
Example:
1671+
```python
1672+
#if foo:
1673+
# self.add_class("-foo")
1674+
#else:
1675+
# self.remove_class("-foo")
1676+
self.set_class(foo, "-foo")
1677+
```
1678+
16681679
Args:
16691680
add: Add the classes if True, otherwise remove them.
16701681
update: Also update styles.

src/textual/widgets/_input.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ def _watch_value(self, value: str) -> None:
536536
if self._initial_value:
537537
self.cursor_position = len(self.value)
538538
self._initial_value = False
539+
else:
540+
# Force a re-validation of the selection to ensure it accounts for
541+
# the length of the new value
542+
self.selection = self.selection
539543

540544
def _watch_valid_empty(self) -> None:
541545
"""Repeat validation when valid_empty changes."""

src/textual/widgets/_option_list.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ def add_options(self, new_options: Iterable[OptionListContent]) -> Self:
373373
self._id_to_option[option._id] = option
374374
add_option(option)
375375
if self.is_mounted:
376+
self.refresh(layout=self.styles.auto_dimensions)
376377
self._update_lines()
377378
return self
378379

tests/input/test_input_properties.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,16 @@ async def test_input_selection_deleted_programmatically():
9393
input_widget.selection = Selection(4, 0)
9494
input_widget.delete_selection()
9595
assert input_widget.value == "o, world!"
96+
97+
98+
async def test_input_selection_is_valid_after_updating_value():
99+
"""Regression test for https://github.com/Textualize/textual/issues/5811"""
100+
app = InputApp()
101+
async with app.run_test() as pilot:
102+
input_widget = pilot.app.query_one(Input)
103+
# Sanity check (by default focusing the input selects all text)
104+
assert input_widget.selection == (0, len(input_widget.value))
105+
106+
input_widget.value = "foo"
107+
108+
assert input_widget.selection == (0, len(input_widget.value))

0 commit comments

Comments
 (0)