Merge branch 'main' into fix-1607

This commit is contained in:
Rodrigo Girão Serrão
2023-01-26 16:07:58 +00:00
28 changed files with 283 additions and 137 deletions

View File

@@ -7,11 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.11.0] - Unreleased
### Changed
- Breaking change: `TreeNode` can no longer be imported from `textual.widgets`; it is now available via `from textual.widgets.tree import TreeNode`. https://github.com/Textualize/textual/pull/1637
### Fixed
- Fixed stuck screen https://github.com/Textualize/textual/issues/1632
- Fixed programmatic style changes not refreshing children layouts when parent widget did not change size https://github.com/Textualize/textual/issues/1607
- Fixed relative units in `grid-rows` and `grid-columns` being computed with respect to the wrong dimension https://github.com/Textualize/textual/issues/1406
- Programmatically setting `overflow_x`/`overflow_y` refreshes the layout correctly https://github.com/Textualize/textual/issues/1616
- Fixed double-paste into `Input` https://github.com/Textualize/textual/issues/1657
- Added a workaround for an apparent Windows Terminal paste issue https://github.com/Textualize/textual/issues/1661
## [0.10.1] - 2023-01-20

View File

@@ -1 +1 @@
::: textual.widgets.TreeNode
::: textual.widgets.tree.TreeNode

View File

@@ -1,17 +1,24 @@
# Introduction
Welcome to the [Textual](https://github.com/Textualize/textual) framework documentation. Built with ❤️ by [Textualize.io](https://www.textualize.io)
Welcome to the [Textual](https://github.com/Textualize/textual) framework documentation.
!!! tip
See the navigation links in the header or side-bars. Click the :octicons-three-bars-16: button (top left) on mobile.
## In a hurry?
[Get started](./getting_started.md){ .md-button .md-button--primary } or go straight to the [Tutorial](./tutorial.md)
See the navigation links in the header or side-bars. Click the :octicons-three-bars-16: button (top left) on mobile.
[Get started](./getting_started.md){ .md-button .md-button--primary } or [Tutorial](./tutorial.md){ .md-button .md-button--secondary }
## What is Textual?
Textual is a framework for building applications that run within your terminal. Text User Interfaces (TUIs) have a number of advantages over web and desktop apps.
Textual is a *Rapid Application Development* framework for Python, built by [Textualize.io](https://www.textualize.io).
Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and (*coming soon*) a web browser.
<div class="grid cards" markdown>
@@ -26,7 +33,7 @@ Textual is a framework for building applications that run within your terminal.
---
Low system requirements. Run Textual on a single board computer if you want to.
Run Textual on a single board computer if you want to.
@@ -53,7 +60,7 @@ Textual is a framework for building applications that run within your terminal.
- :material-scale-balance:{ .lg .middle } __Open Source, MIT__
- :material-scale-balance:{ .lg .middle } __Open Source__
---

View File

@@ -64,21 +64,6 @@ or by clicking on it.
| `item` | `ListItem` | The item that was selected. |
### ChildrenUpdated
The `ListView.ChildrenUpdated` message is emitted when the elements in the `ListView`
are changed (e.g. a child is added, or the list is cleared).
- [x] Bubbles
#### Attributes
| attribute | type | purpose |
| ---------- | ---------------- | ------------------------- |
| `children` | `list[ListItem]` | The new ListView children |
## See Also
* [ListView](../api/list_view.md) code reference

View File

@@ -21,7 +21,7 @@ The example below creates a simple tree.
--8<-- "docs/examples/widgets/tree.py"
```
A each tree widget has a "root" attribute which is an instance of a [TreeNode][textual.widgets.TreeNode]. Call [add()][textual.widgets.TreeNode.add] or [add_leaf()][textual.widgets.TreeNode.add_leaf] to add new nodes underneath the root. Both these methods return a TreeNode for the child, so you can add more levels.
Tree widgets have a "root" attribute which is an instance of a [TreeNode][textual.widgets.tree.TreeNode]. Call [add()][textual.widgets.tree.TreeNode.add] or [add_leaf()][textual.widgets.tree,TreeNode.add_leaf] to add new nodes underneath the root. Both these methods return a TreeNode for the child which you can use to add additional levels.
## Reactive Attributes
@@ -44,8 +44,8 @@ The `Tree.NodeSelected` message is sent when the user selects a tree node.
#### Attributes
| attribute | type | purpose |
| --------- | ------------------------------------ | -------------- |
| `node` | [TreeNode][textual.widgets.TreeNode] | Selected node. |
| --------- | ----------------------------------------- | -------------- |
| `node` | [TreeNode][textual.widgets.tree.TreeNode] | Selected node. |
### NodeExpanded
@@ -55,8 +55,8 @@ The `Tree.NodeExpanded` message is sent when the user expands a node in the tree
#### Attributes
| attribute | type | purpose |
| --------- | ------------------------------------ | -------------- |
| `node` | [TreeNode][textual.widgets.TreeNode] | Expanded node. |
| --------- | ----------------------------------------- | -------------- |
| `node` | [TreeNode][textual.widgets.tree.TreeNode] | Expanded node. |
### NodeCollapsed
@@ -68,8 +68,8 @@ The `Tree.NodeCollapsed` message is sent when the user expands a node in the tre
#### Attributes
| attribute | type | purpose |
| --------- | ------------------------------------ | --------------- |
| `node` | [TreeNode][textual.widgets.TreeNode] | Collapsed node. |
| --------- | ----------------------------------------- | --------------- |
| `node` | [TreeNode][textual.widgets.tree.TreeNode] | Collapsed node. |
@@ -77,4 +77,4 @@ The `Tree.NodeCollapsed` message is sent when the user expands a node in the tre
## See Also
* [Tree][textual.widgets.Tree] code reference
* [TreeNode][textual.widgets.TreeNode] code reference
* [TreeNode][textual.widgets.tree.TreeNode] code reference

View File

@@ -4,7 +4,8 @@ from pathlib import Path
from rich.text import Text
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Tree, TreeNode
from textual.widgets import Header, Footer, Tree
from textual.widgets.tree import TreeNode
class TreeApp(App):

View File

@@ -221,6 +221,7 @@ theme:
- navigation.tabs
- navigation.indexes
- navigation.tabs.sticky
- navigation.footer
- content.code.annotate
- content.code.copy
palette:

124
poetry.lock generated
View File

@@ -171,7 +171,7 @@ python-versions = "*"
[[package]]
name = "coverage"
version = "7.0.5"
version = "7.1.0"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -322,7 +322,7 @@ socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "identify"
version = "2.5.13"
version = "2.5.15"
description = "File identification library for Python"
category = "dev"
optional = false
@@ -521,7 +521,7 @@ doc = ["mkdocs-bootswatch (>=1,<2)", "mkdocs-minify-plugin (>=0.5.0,<0.6.0)", "p
[[package]]
name = "mkdocstrings"
version = "0.19.1"
version = "0.20.0"
description = "Automatic documentation from sources, for MkDocs."
category = "dev"
optional = false
@@ -626,7 +626,7 @@ python-versions = ">=3.7"
[[package]]
name = "pathspec"
version = "0.10.3"
version = "0.11.0"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
@@ -1028,7 +1028,7 @@ dev = ["aiohttp", "click", "msgpack"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "0e3bcf48b37c16096a3c2b2f7d3f548494f9a22ebdee2e2c5d8ac74b80ab344e"
content-hash = "d76445ef1521cd4068907433b09d59fc1ed56f03e61063c5ad7376bb9823a8e7"
[metadata.files]
aiohttp = [
@@ -1193,57 +1193,57 @@ colored = [
{file = "colored-1.4.4.tar.gz", hash = "sha256:04ff4d4dd514274fe3b99a21bb52fb96f2688c01e93fba7bef37221e7cb56ce0"},
]
coverage = [
{file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"},
{file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"},
{file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"},
{file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"},
{file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"},
{file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"},
{file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"},
{file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"},
{file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"},
{file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"},
{file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"},
{file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"},
{file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"},
{file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"},
{file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"},
{file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"},
{file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"},
{file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"},
{file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"},
{file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"},
{file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"},
{file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"},
{file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"},
{file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"},
{file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"},
{file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"},
{file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"},
{file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"},
{file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"},
{file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"},
{file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"},
{file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"},
{file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"},
{file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"},
{file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"},
{file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"},
{file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"},
{file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"},
{file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"},
{file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"},
{file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"},
{file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"},
]
distlib = [
{file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
@@ -1362,8 +1362,8 @@ httpx = [
{file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"},
]
identify = [
{file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"},
{file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"},
{file = "identify-2.5.15-py2.py3-none-any.whl", hash = "sha256:1f4b36c5f50f3f950864b2a047308743f064eaa6f6645da5e5c780d1c7125487"},
{file = "identify-2.5.15.tar.gz", hash = "sha256:c22aa206f47cc40486ecf585d27ad5f40adbfc494a3fa41dc3ed0499a23b123f"},
]
idna = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
@@ -1470,8 +1470,8 @@ mkdocs-rss-plugin = [
{file = "mkdocs_rss_plugin-1.5.0-py2.py3-none-any.whl", hash = "sha256:2ab14c20bf6b7983acbe50181e7e4a0778731d9c2d5c38107ca7047a7abd2165"},
]
mkdocstrings = [
{file = "mkdocstrings-0.19.1-py3-none-any.whl", hash = "sha256:32a38d88f67f65b264184ea71290f9332db750d189dea4200cbbe408d304c261"},
{file = "mkdocstrings-0.19.1.tar.gz", hash = "sha256:d1037cacb4b522c1e8c164ed5d00d724a82e49dcee0af80db8fb67b384faeef9"},
{file = "mkdocstrings-0.20.0-py3-none-any.whl", hash = "sha256:f17fc2c4f760ec302b069075ef9e31045aa6372ca91d2f35ded3adba8e25a472"},
{file = "mkdocstrings-0.20.0.tar.gz", hash = "sha256:c757f4f646d4f939491d6bc9256bfe33e36c5f8026392f49eaa351d241c838e5"},
]
mkdocstrings-python = [
{file = "mkdocstrings-python-0.8.3.tar.gz", hash = "sha256:9ae473f6dc599339b09eee17e4d2b05d6ac0ec29860f3fc9b7512d940fc61adf"},
@@ -1656,8 +1656,8 @@ packaging = [
{file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
]
pathspec = [
{file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"},
{file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"},
{file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"},
{file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
]
platformdirs = [
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},

View File

@@ -50,7 +50,7 @@ black = "^22.3.0,<22.10.0" # macos wheel issue on 22.10
mypy = "^0.990"
pytest-cov = "^2.12.1"
mkdocs = "^1.3.0"
mkdocstrings = {extras = ["python"], version = "^0.19.0"}
mkdocstrings = {extras = ["python"], version = "^0.20.0"}
mkdocs-material = "^8.2.15"
pre-commit = "^2.13.0"
pytest-aiohttp = "^1.0.4"

View File

@@ -4,6 +4,7 @@ from contextvars import ContextVar
if TYPE_CHECKING:
from .app import App
from .message_pump import MessagePump
class NoActiveAppError(RuntimeError):
@@ -11,3 +12,4 @@ class NoActiveAppError(RuntimeError):
active_app: ContextVar["App"] = ContextVar("active_app")
active_message_pump: ContextVar["MessagePump"] = ContextVar("active_message_pump")

View File

@@ -118,7 +118,10 @@ class XTermParser(Parser[events.Event]):
# ESC from the closing bracket, since at that point we didn't know what
# the full escape code was.
pasted_text = "".join(paste_buffer[:-1])
on_token(events.Paste(self.sender, text=pasted_text))
# Note the removal of NUL characters: https://github.com/Textualize/textual/issues/1661
on_token(
events.Paste(self.sender, text=pasted_text.replace("\x00", ""))
)
paste_buffer.clear()
character = ESC if use_prior_escape else (yield read1())

View File

@@ -46,7 +46,7 @@ from ._animator import DEFAULT_EASING, Animatable, Animator, EasingFunction
from ._ansi_sequences import SYNC_END, SYNC_START
from ._asyncio import create_task
from ._callback import invoke
from ._context import active_app
from ._context import active_app, active_message_pump
from ._event_broker import NoHandler, extract_handler_actions
from ._filter import LineFilter, Monochrome
from ._path import _make_path_object_relative
@@ -1095,8 +1095,12 @@ class App(Generic[ReturnType], DOMNode):
Args:
*widgets: The widget(s) to mount.
before: Optional location to mount before.
after: Optional location to mount after.
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.
@@ -1113,6 +1117,7 @@ class App(Generic[ReturnType], DOMNode):
def mount_all(
self,
widgets: Iterable[Widget],
*,
before: int | str | Widget | None = None,
after: int | str | Widget | None = None,
) -> AwaitMount:
@@ -1120,8 +1125,12 @@ class App(Generic[ReturnType], DOMNode):
Args:
widgets: An iterable of widgets.
before: Optional location to mount before.
after: Optional location to mount after.
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.
@@ -2103,7 +2112,7 @@ class App(Generic[ReturnType], DOMNode):
"""Remove nodes from DOM, and return an awaitable that awaits cleanup.
Args:
widgets: List of nodes to remvoe.
widgets: List of nodes to remove.
Returns:
Awaitable that returns when the nodes have been fully removed.
@@ -2127,17 +2136,19 @@ class App(Generic[ReturnType], DOMNode):
removed_widgets = self._detach_from_dom(widgets)
finished_event = asyncio.Event()
create_task(
remove_task = create_task(
prune_widgets_task(removed_widgets, finished_event), name="prune nodes"
)
return AwaitRemove(finished_event)
await_remove = AwaitRemove(finished_event, remove_task)
self.call_next(await_remove)
return await_remove
async def _prune_nodes(self, widgets: list[Widget]) -> None:
"""Remove nodes and children.
Args:
widgets: _description_
widgets: Widgets to remove.
"""
async with self._dom_lock:
for widget in widgets:

View File

@@ -1,19 +1,24 @@
"""Provides the type of an awaitable remove."""
from asyncio import Event
from asyncio import Event, Task
from typing import Generator
class AwaitRemove:
"""An awaitable returned by App.remove and DOMQuery.remove."""
def __init__(self, finished_flag: Event) -> None:
def __init__(self, finished_flag: Event, task: Task) -> None:
"""Initialise the instance of ``AwaitRemove``.
Args:
finished_flag: The asyncio event to wait on.
task: The task which does the remove (required to keep a reference).
"""
self.finished_flag = finished_flag
self._task = task
async def __call__(self) -> None:
return await self
def __await__(self) -> Generator[None, None, None]:
async def await_prune() -> None:

View File

@@ -736,6 +736,9 @@ class StringEnumProperty:
"""
return obj.get_rule(self.name, self._default)
def _before_refresh(self, obj: StylesBase, value: str | None) -> None:
"""Do any housekeeping before asking for a layout refresh after a value change."""
def __set__(self, obj: StylesBase, value: str | None = None):
"""Set the string property and ensure it is in the set of allowed values.
@@ -749,6 +752,7 @@ class StringEnumProperty:
_rich_traceback_omit = True
if value is None:
if obj.clear_rule(self.name):
self._before_refresh(obj, value)
obj.refresh(layout=self._layout, children=self._children)
else:
if value not in self._valid_values:
@@ -761,9 +765,20 @@ class StringEnumProperty:
),
)
if obj.set_rule(self.name, value):
self._before_refresh(obj, value)
obj.refresh(layout=self._layout, children=self._children)
class OverflowProperty(StringEnumProperty):
"""Descriptor for overflow styles that forces widgets to refresh scrollbars."""
def _before_refresh(self, obj: StylesBase, value: str | None) -> None:
from ..widget import Widget # Avoid circular import
if isinstance(obj.node, Widget):
obj.node._refresh_scrollbars()
class NameProperty:
"""Descriptor for getting and setting name properties."""

View File

@@ -26,6 +26,7 @@ from ._style_properties import (
NameListProperty,
NameProperty,
OffsetProperty,
OverflowProperty,
ScalarListProperty,
ScalarProperty,
SpacingProperty,
@@ -246,12 +247,8 @@ class StylesBase(ABC):
dock = DockProperty()
overflow_x = StringEnumProperty(
VALID_OVERFLOW, "hidden", layout=True, children=True
)
overflow_y = StringEnumProperty(
VALID_OVERFLOW, "hidden", layout=True, children=True
)
overflow_x = OverflowProperty(VALID_OVERFLOW, "hidden", layout=True, children=True)
overflow_y = OverflowProperty(VALID_OVERFLOW, "hidden", layout=True, children=True)
layer = NameProperty()
layers = NameListProperty()

View File

@@ -145,7 +145,12 @@ class LinuxDriver(Driver):
self._request_terminal_sync_mode_support()
self._enable_bracketed_paste()
def _request_terminal_sync_mode_support(self):
def _request_terminal_sync_mode_support(self) -> None:
"""Writes an escape sequence to query the terminal support for the sync protocol."""
# Terminals should ignore this sequence if not supported.
# Apple terminal doesn't, and writes a single 'p' in to the terminal,
# so we will make a special case for Apple terminal (which doesn't support sync anyway).
if self.console._environ.get("TERM_PROGRAM", "") != "Apple_Terminal":
self.console.file.write("\033[?2026$p")
self.console.file.flush()

View File

@@ -17,7 +17,7 @@ from weakref import WeakSet
from . import Logger, events, log, messages
from ._asyncio import create_task
from ._callback import invoke
from ._context import NoActiveAppError, active_app
from ._context import NoActiveAppError, active_app, active_message_pump
from ._time import time
from .case import camel_to_snake
from .errors import DuplicateKeyHandlers
@@ -313,12 +313,18 @@ class MessagePump(metaclass=MessagePumpMeta):
Reactive._reset_object(self)
await self._message_queue.put(None)
if wait and self._task is not None and asyncio.current_task() != self._task:
# Ensure everything is closed before returning
try:
running_widget = active_message_pump.get()
except LookupError:
running_widget = None
if running_widget is None or running_widget is not self:
await self._task
def _start_messages(self) -> None:
"""Start messages task."""
if self.app._running:
active_message_pump.set(self)
self._task = create_task(
self._process_messages(), name=f"message pump {self}"
)
@@ -357,7 +363,6 @@ class MessagePump(metaclass=MessagePumpMeta):
async def _process_messages_loop(self) -> None:
"""Process messages until the queue is closed."""
_rich_traceback_guard = True
while not self._closed:
try:
message = await self._get_message()

View File

@@ -1,7 +1,6 @@
from __future__ import annotations
from asyncio import Lock, wait
from asyncio import Lock, create_task, wait
from collections import Counter
from fractions import Fraction
from itertools import islice
@@ -618,6 +617,37 @@ class Widget(DOMNode):
self.call_next(await_mount)
return await_mount
def mount_all(
self,
widgets: Iterable[Widget],
*,
before: int | str | Widget | None = None,
after: int | str | Widget | None = None,
) -> AwaitMount:
"""Mount widgets from an iterable.
Args:
widgets: An iterable of widgets.
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.
"""
await_mount = self.mount(*widgets, before=before, after=after)
return await_mount
def move_child(
self,
child: int | Widget,
@@ -922,7 +952,7 @@ class Widget(DOMNode):
show_horizontal = self.show_horizontal_scrollbar
if overflow_x == "hidden":
show_horizontal = False
if overflow_x == "scroll":
elif overflow_x == "scroll":
show_horizontal = True
elif overflow_x == "auto":
show_horizontal = self.virtual_size.width > width
@@ -1974,13 +2004,14 @@ class Widget(DOMNode):
"""
show_vertical_scrollbar, show_horizontal_scrollbar = self.scrollbars_enabled
scrollbar_size_horizontal = self.styles.scrollbar_size_horizontal
scrollbar_size_vertical = self.styles.scrollbar_size_vertical
styles = self.styles
scrollbar_size_horizontal = styles.scrollbar_size_horizontal
scrollbar_size_vertical = styles.scrollbar_size_vertical
if self.styles.scrollbar_gutter == "stable":
if styles.scrollbar_gutter == "stable":
# Let's _always_ reserve some space, whether the scrollbar is actually displayed or not:
show_vertical_scrollbar = True
scrollbar_size_vertical = self.styles.scrollbar_size_vertical
scrollbar_size_vertical = styles.scrollbar_size_vertical
if show_horizontal_scrollbar and show_vertical_scrollbar:
(region, _, _, _) = region.split(

View File

@@ -23,7 +23,6 @@ if typing.TYPE_CHECKING:
from ._static import Static
from ._text_log import TextLog
from ._tree import Tree
from ._tree_node import TreeNode
from ._welcome import Welcome
from ..widget import Widget
@@ -44,7 +43,6 @@ __all__ = [
"Static",
"TextLog",
"Tree",
"TreeNode",
"Welcome",
]

View File

@@ -256,6 +256,7 @@ class Input(Widget, can_focus=True):
def on_paste(self, event: events.Paste) -> None:
line = event.text.splitlines()[0]
self.insert_text_at_cursor(line)
event.stop()
def on_click(self, event: events.Click) -> None:
offset = event.get_content_offset(self)

View File

@@ -1 +0,0 @@
from ._tree import TreeNode as TreeNode

View File

@@ -0,0 +1,5 @@
"""Make non-widget DataTable support classes available."""
from ._data_table import Column, Row
__all__ = ["Column", "Row"]

View File

@@ -0,0 +1,5 @@
"""Make non-widget Tree support classes available."""
from ._tree import TreeNode
__all__ = ["TreeNode"]

View File

@@ -0,0 +1,37 @@
"""Regression test for #1616 https://github.com/Textualize/textual/issues/1616"""
import pytest
from textual.app import App
from textual.containers import Vertical
async def test_overflow_change_updates_virtual_size_appropriately():
class MyApp(App):
def compose(self):
yield Vertical()
app = MyApp()
async with app.run_test() as pilot:
vertical = app.query_one(Vertical)
height = vertical.virtual_size.height
vertical.styles.overflow_x = "scroll"
await pilot.pause() # Let changes propagate.
assert vertical.virtual_size.height < height
vertical.styles.overflow_x = "hidden"
await pilot.pause()
assert vertical.virtual_size.height == height
width = vertical.virtual_size.width
vertical.styles.overflow_y = "scroll"
await pilot.pause()
assert vertical.virtual_size.width < width
vertical.styles.overflow_y = "hidden"
await pilot.pause()
assert vertical.virtual_size.width == width

View File

@@ -1,13 +1,12 @@
import pytest
import rich
from textual._node_list import DuplicateIds
from textual.app import App, ComposeResult
from textual.css.errors import StyleValueError
from textual.css.query import NoMatches
from textual.dom import DOMNode
from textual.geometry import Size
from textual.widget import Widget, MountError
from textual.widgets import Label
@pytest.mark.parametrize(
@@ -157,3 +156,28 @@ def test_widget_mount_ids_must_be_unique_mounting_multiple_calls(parent):
parent.mount(widget1)
with pytest.raises(DuplicateIds):
parent.mount(widget2)
# Regression test for https://github.com/Textualize/textual/issues/1634
async def test_remove():
class RemoveMeLabel(Label):
async def on_mount(self) -> None:
await self.action("app.remove_all")
class Container(Widget):
async def clear(self) -> None:
await self.query("*").remove()
class RemoveApp(App):
def compose(self) -> ComposeResult:
yield Container(RemoveMeLabel())
async def action_remove_all(self) -> None:
await self.query_one(Container).clear()
self.exit(123)
app = RemoveApp()
async with app.run_test() as pilot:
await pilot.press("r")
await pilot.pause()
assert app.return_value == 123

View File

@@ -1,5 +1,6 @@
import pytest
from textual.widgets import Tree, TreeNode
from textual.widgets import Tree
from textual.widgets.tree import TreeNode
def label_of(node: TreeNode[None]):

View File

@@ -1,4 +1,5 @@
from textual.widgets import Tree, TreeNode
from textual.widgets import Tree
from textual.widgets.tree import TreeNode
from rich.text import Text

View File

@@ -1,4 +1,4 @@
from textual.widgets import TreeNode, Tree
from textual.widgets import Tree
def test_tree_node_parent() -> None: