mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into fix-1607
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
::: textual.widgets.TreeNode
|
||||
::: textual.widgets.tree.TreeNode
|
||||
|
||||
@@ -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__
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
124
poetry.lock
generated
@@ -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"},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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."""
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from ._tree import TreeNode as TreeNode
|
||||
5
src/textual/widgets/data_table.py
Normal file
5
src/textual/widgets/data_table.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Make non-widget DataTable support classes available."""
|
||||
|
||||
from ._data_table import Column, Row
|
||||
|
||||
__all__ = ["Column", "Row"]
|
||||
5
src/textual/widgets/tree.py
Normal file
5
src/textual/widgets/tree.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Make non-widget Tree support classes available."""
|
||||
|
||||
from ._tree import TreeNode
|
||||
|
||||
__all__ = ["TreeNode"]
|
||||
37
tests/test_overflow_change.py
Normal file
37
tests/test_overflow_change.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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]):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from textual.widgets import TreeNode, Tree
|
||||
from textual.widgets import Tree
|
||||
|
||||
|
||||
def test_tree_node_parent() -> None:
|
||||
|
||||
Reference in New Issue
Block a user