This commit is contained in:
Will McGugan
2022-06-04 09:19:11 +01:00
26 changed files with 350 additions and 259 deletions

View File

@@ -6,3 +6,7 @@ format:
black src
format-check:
black --check src
docs-serve:
mkdocs serve
docs-build:
mkdocs build

View File

@@ -17,7 +17,3 @@ The `Resize` event is sent to a widget when its size changes and when it is firs
`event.container_size`
: The size of the widget's container.
## Code
::: textual.events.Resize

View File

@@ -0,0 +1,20 @@
from textual.app import App
from textual.widgets import Button
class ButtonApp(App):
CSS = """
Button {
width: 100%;
}
"""
def compose(self):
yield Button("Lights off")
def handle_pressed(self, event):
self.dark = not self.dark
event.button.label = "Lights ON" if self.dark else "Lights OFF"

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -34,13 +34,15 @@ The command prompt should disappear and you will see a blank screen. It will loo
Hit ++ctrl+c++ to exit and return to the command prompt.
### The code
The first step in all Textual applications is to import the `App` class from `textual.app` and extend it:
```python hl_lines="1 2 3 4 5" title="intro01.py"
--8<-- "docs/examples/introduction/intro01.py"
```
There will be a single App class in any Textual application. The App class is responsible for loading data, setting up the screen, managing events etc. In a real app most of the core logic of your application will be contained within methods on the this class.
This App class is responsible for loading data, setting up the screen, managing events etc. In a real app most of the core logic of your application will be contained within methods on the this class.
The last two lines create an instance of the application and calls `run()`:
@@ -52,13 +54,15 @@ The `run` method will put your terminal in to "application mode" which disables
## Handling Events
In the previously example our app did next to nothing. Most applications will contain event handler methods, which are called in response to user actions such as key presses and mouse movements in addition to other changes your app needs to know about such as terminal resize, scrolling, timers, etc.
Most real-world applications will want to interact with the user in some way. To do this we can make use of _event handler_ methods, which are called in response to things the user does such as pressing a key(s), moving the mouse, resizing the terminal, etc.
Each event type is represented by an event object, which is an instance of a class containing information you may need to respond the the event. For instance the `Key` event contains the key the user pressed and a `Mouse` event will contain the coordinates of the mouse cursor.
!!! note
Although `intro01.py` did not explicitly define any event handlers, Textual still had to respond to events to catch ++ctrl+c++, otherwise you wouldn't be able to exit the app.
In our next example, we are going to handle two events; `Mount` and `Key`. The `Mount` event is sent when the app is first run, and a `Key` event is sent when the user presses a key on the keyboard. Try running `intro02.py` in the `docs/examples/introduction`:
The next example demonstrates handling events. Try running `intro02.py` in the `docs/examples/introduction`:
```python title="intro02.py"
--8<-- "docs/examples/introduction/intro02.py"
@@ -78,17 +82,17 @@ If you hit any of the number keys ++0++-++9++, the background will change color
There are two event handlers in this app. Event handlers start with the text `on_` followed by the name of the event in lower case. Hence `on_mount` is called for the `Mount` event, and `on_key` is called for the `Key` event.
The first event handler to run is `on_mount`:
The first event handler to run is `on_mount`. The `Mount` is sent to your application immediately after entering application mode.
```python hl_lines="19 20" title="intro02.py"
--8<-- "docs/examples/introduction/intro02.py"
```
This `on_mount` method sets the `background` attribute of `self.styles` to `"darkblue"` which updates the background color. There are a lot of other properties on the Styles object, which define how your app looks. We will explore what you can do with this object later.
This `on_mount` method sets the `background` attribute of `self.styles` to `"darkblue"` which makes the background blue when the application starts. There are a lot of other properties on the Styles object, which define how your app looks. We will explore what you can do with this object later.
!!! note
You may have noticed there was no function call to repaint the screen in this examples. Textual will detect when a refresh is required, and do it automatically.
You may have noticed there is no function call to repaint the screen in this example. Textual is generally quite smart in detecting when a refresh is required, and updating the screen automatically.
The second event handler will receive `Key` events whenever you press a key on the keyboard:
@@ -96,15 +100,15 @@ The second event handler will receive `Key` events whenever you press a key on t
--8<-- "docs/examples/introduction/intro02.py"
```
This method has an `event` positional argument which contains information regarding the key that was pressed. The body of the method sets the background to a corresponding color int the `COLORS` list when you press one of the digit keys. It also calls `bell()` which is a method on App that plays your terminal's bell.
This method has an `event` positional argument which will receive the event object; in this case the `Key` event. The body of the method sets the background to a corresponding color in the `COLORS` list when you press one of the digit keys. It also calls `bell()` which is a method on App that plays your terminal's bell.
!!! note
Every event has a corresponding `Event` object, but Textual knows to only call the event handler with the event object if you have it in the argument list. It does this by inspecting the handler method prior to calling it.
Every event has a corresponding `Event` object, but Textual knows to only call the event handler with the event object if you have it in the argument list. It does this by inspecting the handler method prior to calling it. So if you don't need the event object, you may leave it out.
## Widgets
Most Textual applications will also make use of one or more `Widget` classes. A Widget is a self contained component responsible for defining how a given part of the screen should look. Widgets respond to events in much the same way as the App does.
Most Textual applications will make use of one or more `Widget` classes. A Widget is a self contained component responsible for defining how a given part of the screen should look. Widgets respond to events in much the same way as the App does.
Let's look at an app with a simple Widget to show the current time and date. Here is the code for `"clock01.py"` which is in the same directory as the previous examples:
@@ -124,7 +128,7 @@ This script imports App as before, but also the `Widget` class from `textual.wid
--8<-- "docs/examples/introduction/clock01.py"
```
Widgets support many of the same events as the Application itself, and can be thought of as mini-applications in their own right. The Clock widget also responds to a Mount event which is the first event received when a widget is _mounted_ (added to the App). The code in `Clock.on_mount` sets `styles.content_align` to tuple of `("center", "middle")` which tells Textual to display the Widget's content aligned to the horizontal center, and in the middle vertically. If you resize the terminal, you should find the time remains in the center.
Widgets support many of the same events as the Application itself, and can be thought of as mini-applications in their own right. The Clock widget responds to a Mount event which is the first event received when a widget is _mounted_ (added to the App). The code in `Clock.on_mount` sets `styles.content_align` to tuple of `("center", "middle")` which tells Textual to display the Widget's content aligned to the horizontal center, and in the middle vertically. If you resize the terminal, you should find the time remains in the center.
The second line in `on_mount` calls `self.set_interval` which tells Textual to invoke the `self.refresh` function once a second to refresh the Clock widget.

View File

@@ -0,0 +1 @@
::: textual.geometry

View File

@@ -16,6 +16,7 @@ nav:
- Reference:
- "reference/app.md"
- "reference/events.md"
- "reference/geometry.md"
- "reference/widget.md"
markdown_extensions:
@@ -70,7 +71,7 @@ plugins:
handlers:
python:
rendering:
show_source: true
show_source: false
selection:
filters:
- "!^_"

260
poetry.lock generated
View File

@@ -31,17 +31,6 @@ python-versions = ">=3.6"
[package.dependencies]
frozenlist = ">=1.1.0"
[[package]]
name = "astunparse"
version = "1.6.3"
description = "An AST unparser for Python"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
six = ">=1.6.1,<2.0"
[[package]]
name = "async-timeout"
version = "4.0.2"
@@ -166,7 +155,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
[[package]]
name = "coverage"
version = "6.4"
version = "6.4.1"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -185,7 +174,7 @@ python-versions = "*"
[[package]]
name = "filelock"
version = "3.7.0"
version = "3.7.1"
description = "A platform independent file lock."
category = "dev"
optional = false
@@ -219,7 +208,7 @@ dev = ["twine", "markdown", "flake8", "wheel"]
[[package]]
name = "griffe"
version = "0.19.2"
version = "0.19.3"
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
category = "dev"
optional = false
@@ -356,7 +345,7 @@ mkdocs = ">=1.1"
[[package]]
name = "mkdocs-material"
version = "8.2.15"
version = "8.3.0"
description = "Documentation that simply works"
category = "dev"
optional = false
@@ -380,7 +369,7 @@ python-versions = ">=3.6"
[[package]]
name = "mkdocstrings"
version = "0.18.1"
version = "0.19.0"
description = "Automatic documentation from sources, for MkDocs."
category = "dev"
optional = false
@@ -393,7 +382,6 @@ MarkupSafe = ">=1.1"
mkdocs = ">=1.2"
mkdocs-autorefs = ">=0.3.1"
mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""}
mkdocstrings-python-legacy = ">=0.2"
pymdown-extensions = ">=6.3"
[package.extras]
@@ -403,7 +391,7 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
[[package]]
name = "mkdocstrings-python"
version = "0.6.6"
version = "0.7.0"
description = "A Python handler for mkdocstrings."
category = "dev"
optional = false
@@ -411,24 +399,12 @@ python-versions = ">=3.7"
[package.dependencies]
griffe = ">=0.11.1"
mkdocstrings = ">=0.18"
[[package]]
name = "mkdocstrings-python-legacy"
version = "0.2.2"
description = "A legacy Python handler for mkdocstrings."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
mkdocstrings = ">=0.18"
pytkdocs = ">=0.14"
mkdocstrings = ">=0.19"
[[package]]
name = "msgpack"
version = "1.0.3"
description = "MessagePack (de)serializer."
version = "1.0.4"
description = "MessagePack serializer"
category = "main"
optional = false
python-versions = "*"
@@ -657,22 +633,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
[package.dependencies]
six = ">=1.5"
[[package]]
name = "pytkdocs"
version = "0.16.1"
description = "Load Python objects documentation."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
astunparse = {version = ">=1.6", markers = "python_version < \"3.9\""}
cached-property = {version = ">=1.5", markers = "python_version < \"3.8\""}
typing-extensions = {version = ">=3.7", markers = "python_version < \"3.8\""}
[package.extras]
numpy-style = ["docstring_parser (>=0.7)"]
[[package]]
name = "pyyaml"
version = "6.0"
@@ -817,7 +777,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "bfa71851a0d29adf2bf6de97054967e2122355bdefe859439bca5ec4377c9992"
content-hash = "378a60041202d505cba26ea7084886fe1b01090a0035253b100593b559aed090"
[metadata.files]
aiohttp = [
@@ -898,10 +858,6 @@ aiosignal = [
{file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
{file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
]
astunparse = [
{file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"},
{file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"},
]
async-timeout = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
@@ -968,55 +924,55 @@ commonmark = [
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
]
coverage = [
{file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"},
{file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"},
{file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"},
{file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"},
{file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"},
{file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"},
{file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"},
{file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"},
{file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"},
{file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"},
{file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"},
{file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"},
{file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"},
{file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"},
{file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"},
{file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"},
{file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"},
{file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
{file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"},
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"},
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"},
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"},
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"},
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"},
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"},
{file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"},
{file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"},
{file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"},
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"},
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"},
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"},
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"},
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"},
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"},
{file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"},
{file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"},
{file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"},
{file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"},
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"},
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"},
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"},
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"},
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"},
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"},
{file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"},
{file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"},
{file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"},
{file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"},
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"},
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"},
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"},
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"},
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"},
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"},
{file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"},
{file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"},
{file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"},
{file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
]
distlib = [
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
]
filelock = [
{file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"},
{file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"},
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
]
frozenlist = [
{file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
@@ -1084,8 +1040,8 @@ ghp-import = [
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
]
griffe = [
{file = "griffe-0.19.2-py3-none-any.whl", hash = "sha256:5855368feffaabca51be721ca4110220320e3eb361b6c068f2273a6bf7d0bd05"},
{file = "griffe-0.19.2.tar.gz", hash = "sha256:edf925b6ff3101930a97ce48661e34d547b9347fd11e87d49e8cee4c59d30f90"},
{file = "griffe-0.19.3-py3-none-any.whl", hash = "sha256:348422f0e303ff3b76d6b35a96fa9c90dddcbe175506171fda78e8e5cf8c4246"},
{file = "griffe-0.19.3.tar.gz", hash = "sha256:3a51e53bc58c45d38d6d7b4509b41b153b506de3df99767e6f891c75d91706a5"},
]
identify = [
{file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"},
@@ -1166,60 +1122,74 @@ mkdocs-autorefs = [
{file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"},
]
mkdocs-material = [
{file = "mkdocs-material-8.2.15.tar.gz", hash = "sha256:93b57e53733051431cc83216446e774bdf08bf516a6251ff2f24974f45f98149"},
{file = "mkdocs_material-8.2.15-py2.py3-none-any.whl", hash = "sha256:9d6c4ca1ceecc00b2e38c214665ed7605d275321dcaa22f38b9d1175edc58955"},
{file = "mkdocs-material-8.3.0.tar.gz", hash = "sha256:4cad98c47c49a86591ed16a790c07d59402901fc5345f575a3b7329403f5c66e"},
{file = "mkdocs_material-8.3.0-py2.py3-none-any.whl", hash = "sha256:e37e7da1c5923c5b9f0bb8c4ad90bc694fee8f966b43b302a7ff61cb2c35c2e0"},
]
mkdocs-material-extensions = [
{file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"},
{file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"},
]
mkdocstrings = [
{file = "mkdocstrings-0.18.1-py3-none-any.whl", hash = "sha256:4053929356df8cd69ed32eef71d8f676a472ef72980c9ffd4f933ead1debcdad"},
{file = "mkdocstrings-0.18.1.tar.gz", hash = "sha256:fb7c91ce7e3ab70488d3fa6c073a4f827cdc319042f682ef8ea95459790d64fc"},
{file = "mkdocstrings-0.19.0-py3-none-any.whl", hash = "sha256:3217d510d385c961f69385a670b2677e68e07b5fea4a504d86bf54c006c87c7d"},
{file = "mkdocstrings-0.19.0.tar.gz", hash = "sha256:efa34a67bad11229d532d89f6836a8a215937548623b64f3698a1df62e01cc3e"},
]
mkdocstrings-python = [
{file = "mkdocstrings-python-0.6.6.tar.gz", hash = "sha256:37281696b9f199624ae420e0625b6659b7fdfbea736618bce7fd978682dea3b1"},
{file = "mkdocstrings_python-0.6.6-py3-none-any.whl", hash = "sha256:c118438d3cb4b14c492a51d109f4e5b27ab06ba19b099d624430dfd904926152"},
]
mkdocstrings-python-legacy = [
{file = "mkdocstrings-python-legacy-0.2.2.tar.gz", hash = "sha256:f0e7ec6a19750581b752acb38f6b32fcd1efe006f14f6703125d2c2c9a5c6f02"},
{file = "mkdocstrings_python_legacy-0.2.2-py3-none-any.whl", hash = "sha256:379107a3a5b8db9b462efc4493c122efe21e825e3702425dbd404621302a563a"},
{file = "mkdocstrings-python-0.7.0.tar.gz", hash = "sha256:e54c67890e8bb7dc4604360c8ef5dd214b23b6924de7706f461e3c998d4ea061"},
{file = "mkdocstrings_python-0.7.0-py3-none-any.whl", hash = "sha256:6964bd92f106766e771ac6cd5bc02643a960602b4d921b95362e31d491e9a6db"},
]
msgpack = [
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"},
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"},
{file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"},
{file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"},
{file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"},
{file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"},
{file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"},
{file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"},
{file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"},
{file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"},
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"},
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"},
{file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"},
{file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"},
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"},
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"},
{file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"},
{file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"},
{file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"},
{file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"},
{file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"},
{file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"},
{file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"},
{file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"},
{file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"},
{file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"},
{file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"},
{file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"},
{file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"},
{file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"},
{file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"},
{file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"},
{file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"},
{file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"},
{file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"},
{file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"},
{file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"},
{file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"},
{file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"},
{file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"},
{file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"},
{file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"},
{file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"},
{file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"},
{file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"},
{file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"},
{file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"},
{file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"},
{file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"},
{file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"},
{file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"},
{file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"},
{file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"},
{file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"},
{file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"},
{file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"},
{file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"},
{file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"},
{file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"},
{file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"},
{file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"},
{file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"},
{file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"},
{file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"},
{file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"},
{file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"},
{file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"},
{file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"},
{file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"},
{file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"},
{file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"},
]
multidict = [
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
@@ -1372,10 +1342,6 @@ python-dateutil = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
pytkdocs = [
{file = "pytkdocs-0.16.1-py3-none-any.whl", hash = "sha256:a8c3f46ecef0b92864cc598e9101e9c4cf832ebbf228f50c84aa5dd850aac379"},
{file = "pytkdocs-0.16.1.tar.gz", hash = "sha256:e2ccf6dfe9dbbceb09818673f040f1a7c32ed0bffb2d709b06be6453c4026045"},
]
pyyaml = [
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},

View File

@@ -35,7 +35,7 @@ black = "^22.3.0"
mypy = "^0.950"
pytest-cov = "^2.12.1"
mkdocs = "^1.3.0"
mkdocstrings = {extras = ["python"], version = "^0.18.1"}
mkdocstrings = {extras = ["python"], version = "^0.19.0"}
mkdocs-material = "^8.2.15"
pre-commit = "^2.13.0"
pytest-aiohttp = "^1.0.4"

View File

@@ -11,19 +11,21 @@
scrollbar-background-hover: $panel-darken-3;
scrollbar-color: $system;
scrollbar-color-active: $accent-darken-1;
scrollbar-size-horizontal: 1;
scrollbar-size-vertical: 2;
}
App > Screen {
layout: dock;
docks: side=left/1;
background: $surface;
color: $text-surface;
color: $text-surface;
}
#sidebar {
color: $text-primary;
background: $primary;
background: $primary-background;
dock: side;
width: 30;
offset-x: -100%;
@@ -37,7 +39,7 @@ App > Screen {
#sidebar .title {
height: 3;
background: $primary-darken-2;
background: $primary-background-darken-2;
color: $text-primary-darken-2 ;
border-right: outer $primary-darken-3;
content-align: center middle;
@@ -45,16 +47,16 @@ App > Screen {
#sidebar .user {
height: 8;
background: $primary-darken-1;
background: $primary-background-darken-1;
color: $text-primary-darken-1;
border-right: outer $primary-darken-3;
border-right: outer $primary-background-darken-3;
content-align: center middle;
}
#sidebar .content {
background: $primary;
color: $text-primary;
border-right: outer $primary-darken-3;
background: $primary-background;
color: $text-primary-background;
border-right: outer $primary-background-darken-3;
content-align: center middle;
}
@@ -90,14 +92,6 @@ Tweet {
box-sizing: border-box;
}
Tweet.scrollbar-size-custom {
scrollbar-size-vertical: 2;
}
Tweet.scroll-horizontal {
scrollbar-size-horizontal: 2;
}
.scrollable {
width: 80;
@@ -178,8 +172,8 @@ Tweet.scroll-horizontal TweetBody {
OptionItem {
height: 3;
background: $primary;
border-right: outer $primary-darken-2;
background: $primary-background;
border-right: outer $primary-background-darken-2;
border-left: blank;
content-align: center middle;
}
@@ -187,7 +181,7 @@ OptionItem {
OptionItem:hover {
height: 3;
color: $accent;
background: $primary-darken-1;
background: $primary-background-darken-1;
/* border-top: hkey $accent2-darken-3;
border-bottom: hkey $accent2-darken-3; */
text-style: bold;

View File

@@ -17,8 +17,6 @@ class Hr(Widget):
class Info(Widget):
DEFAULT_STYLES = "height: 2;"
def __init__(self, text: str) -> None:
super().__init__()
self.text = text

View File

@@ -7,7 +7,7 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs):
"""A superfences formatter to insert a SVG screenshot."""
os.environ["TEXTUAL"] = "headless"
os.environ["TEXTUAL_SCREENSHOT"] = "0.1"
os.environ["TEXTUAL_SCREENSHOT"] = "0.15"
os.environ["COLUMNS"] = attrs.get("columns", "80")
os.environ["LINES"] = attrs.get("lines", "24")
path = attrs.get("path")

View File

@@ -47,6 +47,7 @@ from ._context import active_app
from ._event_broker import extract_handler_actions, NoHandler
from .binding import Bindings, NoBinding
from .css.stylesheet import Stylesheet
from .css.styles import RenderStyles
from .css.query import NoMatchingNodesError
from .design import ColorSystem
from .devtools.client import DevtoolsClient, DevtoolsConnectionError, DevtoolsLog
@@ -60,6 +61,8 @@ from .layouts.dock import Dock
from .message_pump import MessagePump
from .reactive import Reactive
from .renderables.blank import Blank
from ._profile import timer
from .screen import Screen
from .widget import Widget
@@ -108,6 +111,10 @@ class App(Generic[ReturnType], DOMNode):
"""The base class for Textual Applications"""
CSS = """
App {
background: $surface;
color: $text-surface;
}
"""
CSS_PATH: str | None = None
@@ -139,6 +146,7 @@ class App(Generic[ReturnType], DOMNode):
# this will create some first references to an asyncio loop.
_init_uvloop()
super().__init__()
self.features: frozenset[FeatureFlag] = parse_features(os.getenv("TEXTUAL", ""))
self.console = Console(
@@ -201,10 +209,10 @@ class App(Generic[ReturnType], DOMNode):
else None
)
super().__init__()
def __init_subclass__(cls, css_path: str | None = None) -> None:
super().__init_subclass__()
def __init_subclass__(
cls, css_path: str | None = None, inherit_css: bool = True
) -> None:
super().__init_subclass__(inherit_css=inherit_css)
cls.CSS_PATH = css_path
title: Reactive[str] = Reactive("Textual")
@@ -335,7 +343,15 @@ class App(Generic[ReturnType], DOMNode):
def watch_dark(self, dark: bool) -> None:
"""Watches the dark bool."""
self.screen.dark = dark
if dark:
self.add_class("-dark-mode")
self.remove_class("-light-mode")
else:
self.remove_class("-dark-mode")
self.add_class("-light-mode")
self.refresh_css()
def get_driver_class(self) -> Type[Driver]:
@@ -359,6 +375,18 @@ class App(Generic[ReturnType], DOMNode):
def __rich_repr__(self) -> rich.repr.Result:
yield "title", self.title
yield "id", self.id, None
if self.name:
yield "name", self.name
if self.classes:
yield "classes", set(self.classes)
pseudo_classes = self.pseudo_classes
if pseudo_classes:
yield "pseudo_classes", set(pseudo_classes)
@property
def is_transparent(self) -> bool:
return True
@property
def animator(self) -> Animator:
@@ -368,10 +396,6 @@ class App(Generic[ReturnType], DOMNode):
def screen(self) -> Screen:
return self._screen_stack[-1]
@property
def css_type(self) -> str:
return "app"
@property
def size(self) -> Size:
return Size(*self.console.size)
@@ -716,10 +740,8 @@ class App(Generic[ReturnType], DOMNode):
try:
if self.css_path is not None:
self.stylesheet.read(self.css_path)
if self.CSS is not None:
self.stylesheet.add_source(
self.CSS, path=f"<{self.__class__.__name__}>"
)
for path, css in self.css:
self.stylesheet.add_source(css, path=path)
except Exception as error:
self.on_exception(error)
self._print_error_renderables()
@@ -736,8 +758,8 @@ class App(Generic[ReturnType], DOMNode):
driver = self._driver = self.driver_class(self.console, self)
driver.start_application_mode()
with redirect_stdout(StdoutRedirector(self.devtools, self._log_file)): # type: ignore
try:
try:
with redirect_stdout(StdoutRedirector(self.devtools, self._log_file)): # type: ignore
mount_event = events.Mount(sender=self)
await self.dispatch_message(mount_event)
@@ -749,8 +771,8 @@ class App(Generic[ReturnType], DOMNode):
await super().process_messages()
await self.animator.stop()
await self.close_all()
finally:
driver.stop_application_mode()
finally:
driver.stop_application_mode()
except Exception as error:
self.on_exception(error)
finally:
@@ -871,6 +893,7 @@ class App(Generic[ReturnType], DOMNode):
def refresh(self, *, repaint: bool = True, layout: bool = False) -> None:
self.screen.refresh(repaint=repaint, layout=layout)
self.check_idle()
def _paint(self):
"""Perform a "paint" (draw the screen)."""
@@ -886,7 +909,7 @@ class App(Generic[ReturnType], DOMNode):
stylesheet.set_variables(self.get_css_variables())
stylesheet.reparse()
stylesheet.update(self.app, animate=animate)
self.refresh(layout=True)
self.screen._refresh_layout(self.size, full=True)
def _display(self, renderable: RenderableType | None) -> None:
"""Display a renderable within a sync.

View File

@@ -86,7 +86,7 @@ class Selector:
return node.has_pseudo_class(*self.pseudo_classes)
def _check_type(self, node: DOMNode) -> bool:
if node.css_type != self._name_lower:
if self._name_lower not in node._css_type_names:
return False
if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes):
return False

View File

@@ -115,7 +115,7 @@ class StylesheetErrors:
)
@rich.repr.auto
@rich.repr.auto(angular=True)
class Stylesheet:
def __init__(self, *, variables: dict[str, str] | None = None) -> None:
self._rules: list[RuleSet] = []
@@ -124,7 +124,7 @@ class Stylesheet:
self._require_parse = False
def __rich_repr__(self) -> rich.repr.Result:
yield self.rules
yield list(self.source.keys())
@property
def rules(self) -> list[RuleSet]:

View File

@@ -188,6 +188,8 @@ class ColorSystem:
COLORS = [
("primary", primary),
("secondary", secondary),
("primary-background", primary),
("secondary-background", secondary),
("background", background),
("panel", panel),
("surface", surface),
@@ -199,7 +201,7 @@ class ColorSystem:
]
# Colors names that have a dark variant
DARK_SHADES = {"primary", "secondary"}
DARK_SHADES = {"primary-background", "secondary-background"}
for name, color in COLORS:
is_dark_shade = dark and name in DARK_SHADES

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import Iterable, Iterator, TYPE_CHECKING
from inspect import getfile
from typing import ClassVar, Iterable, Iterator, Type, TYPE_CHECKING
import rich.repr
from rich.highlighter import ReprHighlighter
@@ -38,8 +39,13 @@ class DOMNode(MessagePump):
"""
DEFAULT_STYLES = ""
INLINE_STYLES = ""
# Custom CSS
CSS: ClassVar[str] = ""
# True if this node inherits the CSS from the base class.
_inherit_css: ClassVar[bool] = True
# List of names of base class (lower cased) that inherit CSS
_css_type_names: ClassVar[frozenset[str]] = frozenset()
def __init__(
self,
@@ -53,14 +59,51 @@ class DOMNode(MessagePump):
self._classes: set[str] = set() if classes is None else set(classes.split())
self.children = NodeList()
self._css_styles: Styles = Styles(self)
self._inline_styles: Styles = Styles.parse(
self.INLINE_STYLES, repr(self), node=self
)
self._inline_styles: Styles = Styles(self)
self.styles = RenderStyles(self, self._css_styles, self._inline_styles)
self._default_styles = Styles()
self._default_rules = self._default_styles.extract_rules((0, 0, 0))
super().__init__()
def __init_subclass__(cls, inherit_css: bool = True) -> None:
super().__init_subclass__()
cls._inherit_css = inherit_css
css_type_names: set[str] = set()
for base in cls._css_bases(cls):
css_type_names.add(base.__name__.lower())
cls._css_type_names = frozenset(css_type_names)
@property
def _node_bases(self) -> Iterator[Type[DOMNode]]:
"""Get the DOMNode bases classes (including self.__class__)
Returns:
Iterator[Type[DOMNode]]: An iterable of DOMNode classes.
"""
return self._css_bases(self.__class__)
@classmethod
def _css_bases(cls, base: Type[DOMNode]) -> Iterator[Type[DOMNode]]:
"""Get the DOMNode base classes, which inherit CSS.
Args:
base (Type[DOMNode]): A DOMNode class
Returns:
Iterator[Type[DOMNode]]: An iterable of DOMNode classes.
"""
_class = base
while True:
yield _class
if not _class._inherit_css:
break
for _base in _class.__bases__:
if issubclass(_base, DOMNode):
_class = _base
break
else:
break
def on_register(self, app: App) -> None:
"""Called when the widget is registered
@@ -74,12 +117,37 @@ class DOMNode(MessagePump):
if self._classes:
yield "classes", " ".join(self._classes)
@property
def css(self) -> list[tuple[str, str]]:
"""Gets the CSS for this class and inherited from bases.
Returns:
list[tuple[str, str]]: a list of tuples containing (PATH, SOURCE) for this
and inherited from base classes.
"""
css_stack: list[tuple[str, str]] = []
def get_path(base: Type[DOMNode]) -> str:
"""Get a path to the DOM Node"""
try:
return f"{getfile(base)}:{base.__name__}"
except TypeError:
return f"{base.__name__}"
for base in self._node_bases:
css = base.CSS.strip()
if css:
css_stack.append((get_path(base), css))
return css_stack
@property
def parent(self) -> DOMNode | None:
"""Get the parent node.
Returns:
DOMNode: The node which is the direct parent of this node.
DOMNode | None: The node which is the direct parent of this node.
"""
return self._parent
@@ -158,15 +226,6 @@ class DOMNode(MessagePump):
pseudo_classes = frozenset({*self.get_pseudo_classes()})
return pseudo_classes
@property
def css_type(self) -> str:
"""Gets the CSS type, used by the CSS.
Returns:
str: A type used in CSS (lower cased class name).
"""
return self.__class__.__name__.lower()
@property
def css_path_nodes(self) -> list[DOMNode]:
"""A list of nodes from the root to this node, forming a "path".
@@ -238,20 +297,29 @@ class DOMNode(MessagePump):
from rich.console import Group
from rich.panel import Panel
highlighter = ReprHighlighter()
tree = Tree(highlighter(repr(self)))
from .widget import Widget
def add_children(tree, node):
for child in node.children:
def render_info(node: DOMNode) -> Columns:
if isinstance(node, Widget):
info = Columns(
[
Pretty(child),
highlighter(f"region={child.region!r}"),
Pretty(node),
highlighter(f"region={node.region!r}"),
highlighter(
f"virtual_size={child.virtual_size!r}",
f"virtual_size={node.virtual_size!r}",
),
]
)
else:
info = Columns([Pretty(node)])
return info
highlighter = ReprHighlighter()
tree = Tree(render_info(self))
def add_children(tree, node):
for child in node.children:
info = render_info(child)
css = child.styles.css
if css:
info = Group(
@@ -464,7 +532,6 @@ class DOMNode(MessagePump):
self._classes.update(class_names)
try:
self.app.stylesheet.update(self.app, animate=True)
self.refresh()
except NoActiveAppError:
pass
@@ -478,7 +545,6 @@ class DOMNode(MessagePump):
self._classes.difference_update(class_names)
try:
self.app.stylesheet.update(self.app, animate=True)
self.refresh()
except NoActiveAppError:
pass
@@ -492,7 +558,6 @@ class DOMNode(MessagePump):
self._classes.symmetric_difference_update(class_names)
try:
self.app.stylesheet.update(self.app, animate=True)
self.refresh()
except NoActiveAppError:
pass

View File

@@ -160,10 +160,10 @@ class LinuxDriver(Driver):
if not self.exit_event.is_set():
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
self._disable_mouse_support()
termios.tcflush(self.fileno, termios.TCIFLUSH)
self.exit_event.set()
if self._key_thread is not None:
self._key_thread.join()
termios.tcflush(self.fileno, termios.TCIFLUSH)
except Exception as error:
# TODO: log this
pass
@@ -207,7 +207,6 @@ class LinuxDriver(Driver):
utf8_decoder = getincrementaldecoder("utf-8")().decode
decode = utf8_decoder
read = os.read
EVENT_READ = selectors.EVENT_READ
try:

View File

@@ -303,7 +303,7 @@ class Region(NamedTuple):
"""Get the maxima and minima of region.
Returns:
tuple[int, int, int, int]: A tuple of (<min x>, <max x>, <min y>, <max y>)
tuple[int, int, int, int]: A tuple of `(<min x>, <max x>, <min y>, <max y>)`
"""
x, y, width, height = self
return x, y, x + width, y + height
@@ -527,6 +527,7 @@ class Region(NamedTuple):
def split(self, cut_x: int, cut_y: int) -> tuple[Region, Region, Region, Region]:
"""Split a region in to 4 from given x and y offsets (cuts).
```
cut_x ↓
┌────────┐┌───┐
│ ││ │
@@ -536,6 +537,7 @@ class Region(NamedTuple):
┌────────┐┌───┐
│ 2 ││ 3 │
└────────┘└───┘
```
Args:
cut_x (int): Offset from self.x where the cut should be made. If negative, the cut
@@ -564,11 +566,13 @@ class Region(NamedTuple):
def split_vertical(self, cut: int) -> tuple[Region, Region]:
"""Split a region in to two, from a given x offset.
```
cut ↓
┌────────┐┌───┐
│ 0 ││ 1 │
│ ││ │
└────────┘└───┘
```
Args:
cut (int): An offset from self.x where the cut should be made. If cut is negative,
@@ -590,6 +594,7 @@ class Region(NamedTuple):
def split_horizontal(self, cut: int) -> tuple[Region, Region]:
"""Split a region in to two, from a given x offset.
```
┌─────────┐
│ 0 │
│ │
@@ -597,6 +602,7 @@ class Region(NamedTuple):
┌─────────┐
│ 1 │
└─────────┘
```
Args:
cut (int): An offset from self.x where the cut should be made. May be negative,

View File

@@ -5,7 +5,7 @@ import inspect
from asyncio import CancelledError
from asyncio import PriorityQueue, QueueEmpty, Task
from functools import partial, total_ordering
from typing import TYPE_CHECKING, Awaitable, Iterable, Callable, NamedTuple
from typing import TYPE_CHECKING, Awaitable, Iterable, Callable
from weakref import WeakSet
from . import events
@@ -268,6 +268,7 @@ class MessagePump:
self.app.on_exception(error)
break
finally:
self._message_queue.task_done()
if self._message_queue.empty():
if not self._closed:
event = events.Idle(self)

View File

@@ -30,7 +30,7 @@ class Screen(Widget):
"""A widget for the root of the app."""
CSS = """
Screen {
Screen {
layout: vertical;
overflow-y: auto;
}
@@ -112,20 +112,19 @@ class Screen(Widget):
def on_idle(self, event: events.Idle) -> None:
# Check for any widgets marked as 'dirty' (needs a repaint)
event.prevent_default()
if self._layout_required:
self._refresh_layout()
self._layout_required = False
self._dirty_widgets.clear()
elif self._dirty_widgets:
elif self._dirty_widgets or self._dirty_regions:
self.update_timer.resume()
def _on_update(self) -> None:
"""Called by the _update_timer."""
# Render widgets together
if self._dirty_widgets:
if self._dirty_widgets or self._dirty_regions:
self._compositor.update_widgets(self._dirty_widgets)
self._compositor.add_dirty_regions(self._dirty_regions)
self.app._display(self._compositor.render())
self._dirty_widgets.clear()
self.update_timer.pause()

View File

@@ -22,7 +22,6 @@ from . import errors
from . import events
from ._animator import BoundAnimator
from ._border import Border
from ._profile import timer
from .box_model import BoxModel, get_box_model
from ._context import active_app
from ._types import Lines
@@ -34,7 +33,6 @@ from .message import Message
from . import messages
from ._layout import Layout
from .reactive import Reactive, watch
from .renderables.blank import Blank
from .renderables.opacity import Opacity
from .renderables.tint import Tint
@@ -69,9 +67,6 @@ class RenderCache(NamedTuple):
@rich.repr.auto
class Widget(DOMNode):
CSS = """
"""
can_focus: bool = False
can_focus_children: bool = True
@@ -179,10 +174,9 @@ class Widget(DOMNode):
Args:
app (App): App instance.
"""
# Parser the Widget's CSS
self.app.stylesheet.add_source(
self.CSS, f"{__file__}:<{self.__class__.__name__}>"
)
# Parse the Widget's CSS
for path, css in self.css:
self.app.stylesheet.add_source(css, path=path)
def get_box_model(
self, container: Size, viewport: Size, fraction_unit: Fraction
@@ -539,12 +533,16 @@ class Widget(DOMNode):
def scroll_page_left(self, *, animate: bool = True) -> bool:
return self.scroll_to(
x=self.scroll_target_x - self.container_size.width, animate=animate
x=self.scroll_target_x - self.container_size.width,
animate=animate,
duration=0.3,
)
def scroll_page_right(self, *, animate: bool = True) -> bool:
return self.scroll_to(
x=self.scroll_target_x + self.container_size.width, animate=animate
x=self.scroll_target_x + self.container_size.width,
animate=animate,
duration=0.3,
)
def scroll_to_widget(self, widget: Widget, *, animate: bool = True) -> bool:
@@ -592,9 +590,12 @@ class Widget(DOMNode):
)
def __init_subclass__(
cls, can_focus: bool = True, can_focus_children: bool = True
cls,
can_focus: bool = True,
can_focus_children: bool = True,
inherit_css: bool = True,
) -> None:
super().__init_subclass__()
super().__init_subclass__(inherit_css=inherit_css)
cls.can_focus = can_focus
cls.can_focus_children = can_focus_children
@@ -1038,8 +1039,8 @@ class Widget(DOMNode):
event.stop()
def handle_scroll_to(self, message: ScrollTo) -> None:
if self.is_scrollable:
self.scroll_to(message.x, message.y, animate=message.animate)
if self.is_container:
self.scroll_to(message.x, message.y, animate=message.animate, duration=0.1)
message.stop()
def handle_scroll_up(self, event: ScrollUp) -> None:

View File

@@ -17,18 +17,31 @@ class Button(Widget, can_focus=True):
CSS = """
Button {
width: auto;
height: 3;
padding: 0 2;
background: $primary;
color: $text-primary;
content-align: center middle;
border: tall $primary-lighten-3;
margin: 1 0;
align: center middle;
text-style: bold;
}
.-dark-mode Button {
border: tall white $primary-lighten-2;
color: $primary-lighten-2;
background: $background;
}
.-dark-mode Button:hover {
background: $surface;
}
Button:hover {
background:$primary-darken-2;
color: $text-primary-darken-2;

View File

@@ -191,8 +191,6 @@ class Tabs(Widget):
that character.
"""
DEFAULT_STYLES = "height: 2;"
_active_tab_name: Reactive[str | None] = Reactive("")
_bar_offset: Reactive[float] = Reactive(0.0)