mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
more docs
This commit is contained in:
22
docs/examples/introduction/clock.py
Normal file
22
docs/examples/introduction/clock.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class Clock(Widget):
|
||||
def on_mount(self):
|
||||
self.styles.content_align = ("center", "middle")
|
||||
self.set_interval(1, self.refresh)
|
||||
|
||||
def render(self):
|
||||
return datetime.now().strftime("%c")
|
||||
|
||||
|
||||
class ClockApp(App):
|
||||
def compose(self):
|
||||
yield Clock()
|
||||
|
||||
|
||||
app = ClockApp()
|
||||
app.run()
|
||||
22
docs/examples/introduction/clock01.py
Normal file
22
docs/examples/introduction/clock01.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class Clock(Widget):
|
||||
def on_mount(self):
|
||||
self.styles.content_align = ("center", "middle")
|
||||
self.set_interval(1, self.refresh)
|
||||
|
||||
def render(self):
|
||||
return datetime.now().strftime("%c")
|
||||
|
||||
|
||||
class ClockApp(App):
|
||||
def on_mount(self):
|
||||
self.mount(Clock())
|
||||
|
||||
|
||||
app = ClockApp()
|
||||
app.run()
|
||||
9
docs/examples/introduction/intro01.py
Normal file
9
docs/examples/introduction/intro01.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from textual.app import App
|
||||
|
||||
|
||||
class ExampleApp(App):
|
||||
pass
|
||||
|
||||
|
||||
app = ExampleApp()
|
||||
app.run()
|
||||
29
docs/examples/introduction/intro02.py
Normal file
29
docs/examples/introduction/intro02.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from textual.app import App
|
||||
|
||||
|
||||
class ExampleApp(App):
|
||||
|
||||
COLORS = [
|
||||
"white",
|
||||
"maroon",
|
||||
"red",
|
||||
"purple",
|
||||
"fuchsia",
|
||||
"olive",
|
||||
"yellow",
|
||||
"navy",
|
||||
"teal",
|
||||
"aqua",
|
||||
]
|
||||
|
||||
def on_mount(self):
|
||||
self.styles.background = "darkblue"
|
||||
|
||||
def on_key(self, event):
|
||||
if event.key.isdigit():
|
||||
self.styles.background = self.COLORS[int(event.key)]
|
||||
self.bell()
|
||||
|
||||
|
||||
app = ExampleApp()
|
||||
app.run()
|
||||
17
docs/examples/styles/width.py
Normal file
17
docs/examples/styles/width.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class WidthApp(App):
|
||||
CSS = """
|
||||
Widget {
|
||||
background: blue;
|
||||
width: 50%;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
yield Widget()
|
||||
|
||||
|
||||
app = WidthApp()
|
||||
@@ -1,4 +1,4 @@
|
||||
# Welcome to Textual documentation
|
||||
# Welcome
|
||||
|
||||
Textual is framework for rapidly creating _text user interfaces_ (TUIs from here on) with Python.
|
||||
|
||||
|
||||
145
docs/introduction.md
Normal file
145
docs/introduction.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to the Textual Introduction!
|
||||
|
||||
This is a very gentle introduction to creating Textual applications.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
- Python 3.7 or later. If you have a choice, pick the most recent version.
|
||||
- Installed `textual` from Pypi.
|
||||
- Basic Python skills.
|
||||
|
||||
## A Simple App
|
||||
|
||||
Lets looks at the simplest possible Textual app. It doesn't do much, but will demonstrate the basic steps you will need to create any application.
|
||||
|
||||
If you would like to follow along and run the examples, navigate to the `docs/examples/introduction` directory from the command prompt. We will be looking at `intro01.py`, which you can see here:
|
||||
|
||||
=== "intro01.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/introduction/intro01.py"
|
||||
```
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/introduction/intro01.py"}
|
||||
```
|
||||
|
||||
Enter the following command to run the application:
|
||||
|
||||
```shell
|
||||
python intro01.py
|
||||
```
|
||||
|
||||
The command prompt should disappear and you will see a blank screen. Hit ++ctrl+c++ to exit and return to the command prompt.
|
||||
|
||||
Let's analyze this simple app.
|
||||
|
||||
The first step in all Textual applications is to import the `App` class from `textual.app` and extend it:
|
||||
|
||||
```python
|
||||
from textual.app import App
|
||||
|
||||
class ExampleApp(App):
|
||||
pass
|
||||
```
|
||||
|
||||
There will be a single App object in any Textual application. The App class is responsible for loading data, setting up the screen, managing events etc.
|
||||
|
||||
The following two lines create an instance of the application and calls `run()`:
|
||||
|
||||
```python
|
||||
app = ExampleApp()
|
||||
app.run()
|
||||
```
|
||||
|
||||
The `run` method will put your terminal in to "application mode" which disables the prompt and allows Textual to take over input and output. The `run()` method will return when the application exits.
|
||||
|
||||
## 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, mouse action) and other changes your app needs to know about such as terminal resize, scrolling, timers, etc.
|
||||
|
||||
!!! note
|
||||
|
||||
Although `intro01.py` did not explicitly define any event handlers, Textual still had to respond to the Key event to catch ++ctrl+c++, otherwise you wouldn't be able to exit the app.
|
||||
|
||||
In our next example, we are going to handle two such 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`:
|
||||
|
||||
=== "intro02.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/introduction/intro02.py"
|
||||
```
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/introduction/intro02.py"}
|
||||
```
|
||||
|
||||
When you run this app you should see a blue screen. If you hit any of the number keys ++0++-++9++, the background will change to another color. You may also hear a beep or some other noise when a key is pressed, depending on how your terminal is configured. As before, pressing ++ctrl+c++ will exit the app and return you to your prompt.
|
||||
|
||||
There are two event handlers in this app class. 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.
|
||||
|
||||
Here's the `on_mount` method again:
|
||||
|
||||
```python
|
||||
def on_mount(self):
|
||||
self.styles.background = "darkblue"
|
||||
```
|
||||
|
||||
This method sets the `background` attribute on `self.styles` to `"darkblue"` which makes the application background blue when the app loads. The `styles` object contains a variety of properties which define how your app looks. We will explore what you can do with this object later.
|
||||
|
||||
The second event handler will receive `Key` events whenever you press a key on the keyboard:
|
||||
|
||||
```python
|
||||
def on_key(self, event):
|
||||
if event.key.isdigit():
|
||||
self.styles.background = self.COLORS[int(event.key)]
|
||||
self.bell()
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
## Widgets
|
||||
|
||||
Most Textual applications will also make use of one or more `Widget` classes. A Widget is a self contained component which is 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. More sophisticated user interfaces can be built by combining various widgets.
|
||||
|
||||
Let's look at an app which defines a very simple Widget to show the current time and date. Here is the code for `"clock01.py"` which is n the same directory as the previous examples:
|
||||
|
||||
=== "clock01.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/introduction/clock01.py"
|
||||
```
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/introduction/clock01.py"}
|
||||
```
|
||||
|
||||
This script imports App as before, but also the `Widget` class from `textual.widget`, which is the base class for all Widgets. The `Clock` widget extends `Widget` and adds an `on_mount` handler which is called when the widget is first added to the application.
|
||||
|
||||
Lets have a look at the Clock's Mount event handler:
|
||||
|
||||
```python
|
||||
def on_mount(self):
|
||||
self.styles.content_align = ("center", "middle")
|
||||
self.set_interval(1, self.refresh)
|
||||
```
|
||||
|
||||
The first line in that method sets the `content_align` attribute on the styles object, which defines how text is positioned within the Widget. We're setting it to a tuple of `("center", "middle")` which tells Textual to horizontally center the text, and place it in the middle vertically. If you resize the terminal you should notice that the text is automatically centered.
|
||||
|
||||
The second line calls `self.set_interval` to request that Textual calls `self.refresh` to update the screen once a second. When the screen is refreshed, Textual will call the widget's `render()` method, which we can see here:
|
||||
|
||||
```python
|
||||
def render(self):
|
||||
return datetime.now().strftime("%c")
|
||||
```
|
||||
|
||||
This method uses the datetime module to format the current date and time. It returns a string, but can also return a _Rich renderable_. Don't worry if you aren't familiar with [Rich](https://github.com/Textualize/rich), we will cover that later.
|
||||
1
docs/reference/widget.md
Normal file
1
docs/reference/widget.md
Normal file
@@ -0,0 +1 @@
|
||||
::: textual.widget.Widget
|
||||
37
docs/styles/width.md
Normal file
37
docs/styles/width.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Width
|
||||
|
||||
The `width` property sets a widget's width. By default, it sets the width of the content area, but if `box-sizing` is set to `border-box` it sets the width of the border area.
|
||||
|
||||
## Example
|
||||
|
||||
=== "width.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/styles/width.py"
|
||||
```
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/styles/width.py"}
|
||||
```
|
||||
|
||||
## CSS
|
||||
|
||||
```sass
|
||||
/* Explicit cell width */
|
||||
width: 10;
|
||||
|
||||
/* Percentage width */
|
||||
width: 50%;
|
||||
|
||||
/* Automatic width */
|
||||
width: auto
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
self.styles.width = 10
|
||||
self.styles.width = "50%
|
||||
self.styles.width = "auto"
|
||||
```
|
||||
@@ -53,12 +53,12 @@ lorem = Text.from_markup(
|
||||
|
||||
|
||||
class TweetHeader(Widget):
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text("Lorem Impsum", justify="center")
|
||||
|
||||
|
||||
class TweetBody(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return lorem
|
||||
|
||||
|
||||
@@ -67,22 +67,22 @@ class Tweet(Widget):
|
||||
|
||||
|
||||
class OptionItem(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("Option")
|
||||
|
||||
|
||||
class Error(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is an error message", justify="center")
|
||||
|
||||
|
||||
class Warning(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is a warning message", justify="center")
|
||||
|
||||
|
||||
class Success(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is a success message", justify="center")
|
||||
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ lorem = Text.from_markup(
|
||||
|
||||
|
||||
class Lorem(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Padding(lorem, 1)
|
||||
|
||||
|
||||
class Background(Widget):
|
||||
def render(self, style: Style):
|
||||
def render(self):
|
||||
return VerticalGradient("#212121", "#212121")
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class Numbers(Widget):
|
||||
|
||||
value = Reactive("0")
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
"""Build a Rich renderable to render the calculator display."""
|
||||
return Padding(
|
||||
Align.right(FigletText(self.value), vertical="middle"),
|
||||
|
||||
@@ -45,9 +45,9 @@ plugins:
|
||||
handlers:
|
||||
python:
|
||||
rendering:
|
||||
show_source: false
|
||||
show_source: true
|
||||
selection:
|
||||
filters:
|
||||
filters:
|
||||
- "!^_"
|
||||
- "^__init__$"
|
||||
- "!^can_replace$"
|
||||
|
||||
108
poetry.lock
generated
108
poetry.lock
generated
@@ -35,7 +35,7 @@ frozenlist = ">=1.1.0"
|
||||
name = "astunparse"
|
||||
version = "1.6.3"
|
||||
description = "An AST unparser for Python"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -110,7 +110,7 @@ uvloop = ["uvloop (>=0.15.2)"]
|
||||
name = "cached-property"
|
||||
version = "1.5.2"
|
||||
description = "A decorator for caching properties in classes."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -207,7 +207,7 @@ python-versions = ">=3.7"
|
||||
name = "ghp-import"
|
||||
version = "2.1.0"
|
||||
description = "Copy your docs directly to the gh-pages branch."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -217,6 +217,20 @@ python-dateutil = ">=2.8.1"
|
||||
[package.extras]
|
||||
dev = ["twine", "markdown", "flake8", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "griffe"
|
||||
version = "0.19.2"
|
||||
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 = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
cached_property = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
async = ["aiofiles (>=0.7,<1.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.1"
|
||||
@@ -265,7 +279,7 @@ python-versions = "*"
|
||||
name = "jinja2"
|
||||
version = "3.0.3"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -279,7 +293,7 @@ i18n = ["Babel (>=2.7)"]
|
||||
name = "markdown"
|
||||
version = "3.3.7"
|
||||
description = "Python implementation of Markdown."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -293,7 +307,7 @@ testing = ["coverage", "pyyaml"]
|
||||
name = "markupsafe"
|
||||
version = "2.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@@ -301,7 +315,7 @@ python-versions = ">=3.7"
|
||||
name = "mergedeep"
|
||||
version = "1.3.4"
|
||||
description = "A deep merge function for 🐍."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -309,7 +323,7 @@ python-versions = ">=3.6"
|
||||
name = "mkdocs"
|
||||
version = "1.3.0"
|
||||
description = "Project documentation with Markdown."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -332,7 +346,7 @@ i18n = ["babel (>=2.9.0)"]
|
||||
name = "mkdocs-autorefs"
|
||||
version = "0.4.1"
|
||||
description = "Automatically link across pages in MkDocs."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@@ -366,20 +380,50 @@ python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mkdocstrings"
|
||||
version = "0.17.0"
|
||||
version = "0.18.1"
|
||||
description = "Automatic documentation from sources, for MkDocs."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
Jinja2 = ">=2.11.1"
|
||||
Markdown = ">=3.3"
|
||||
MarkupSafe = ">=1.1"
|
||||
mkdocs = ">=1.2"
|
||||
mkdocs-autorefs = ">=0.1"
|
||||
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"
|
||||
pytkdocs = ">=0.14.0"
|
||||
|
||||
[package.extras]
|
||||
crystal = ["mkdocstrings-crystal (>=0.3.4)"]
|
||||
python = ["mkdocstrings-python (>=0.5.2)"]
|
||||
python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocstrings-python"
|
||||
version = "0.6.6"
|
||||
description = "A Python handler for mkdocstrings."
|
||||
category = "main"
|
||||
optional = false
|
||||
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 = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
mkdocstrings = ">=0.18"
|
||||
pytkdocs = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "msgpack"
|
||||
@@ -436,7 +480,7 @@ python-versions = "*"
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -515,7 +559,7 @@ python-versions = ">=3.6"
|
||||
name = "pymdown-extensions"
|
||||
version = "9.4"
|
||||
description = "Extension pack for Python Markdown."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@@ -526,7 +570,7 @@ markdown = ">=3.2"
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
|
||||
@@ -606,7 +650,7 @@ testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtuale
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
description = "Extensions to the standard Python datetime module"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
|
||||
@@ -617,7 +661,7 @@ six = ">=1.5"
|
||||
name = "pytkdocs"
|
||||
version = "0.16.1"
|
||||
description = "Load Python objects documentation."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@@ -633,7 +677,7 @@ numpy-style = ["docstring_parser (>=0.7)"]
|
||||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -641,7 +685,7 @@ python-versions = ">=3.6"
|
||||
name = "pyyaml-env-tag"
|
||||
version = "0.1"
|
||||
description = "A custom YAML tag for referencing environment variables in YAML files. "
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -668,7 +712,7 @@ jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
@@ -738,7 +782,7 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)",
|
||||
name = "watchdog"
|
||||
version = "2.1.8"
|
||||
description = "Filesystem events monitoring"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -773,7 +817,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "eba121f02e102fd9c551a654bcfab3028ec4fc05fe9b4cf7d5f64002e3586ba0"
|
||||
content-hash = "f32bca17ffa867133de5437f87390b1122fb84ee9c0b6eafab03f8c518fe6c15"
|
||||
|
||||
[metadata.files]
|
||||
aiohttp = [
|
||||
@@ -1039,6 +1083,10 @@ ghp-import = [
|
||||
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
|
||||
{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"},
|
||||
]
|
||||
identify = [
|
||||
{file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"},
|
||||
{file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},
|
||||
@@ -1126,8 +1174,16 @@ mkdocs-material-extensions = [
|
||||
{file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"},
|
||||
]
|
||||
mkdocstrings = [
|
||||
{file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"},
|
||||
{file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"},
|
||||
{file = "mkdocstrings-0.18.1-py3-none-any.whl", hash = "sha256:4053929356df8cd69ed32eef71d8f676a472ef72980c9ffd4f933ead1debcdad"},
|
||||
{file = "mkdocstrings-0.18.1.tar.gz", hash = "sha256:fb7c91ce7e3ab70488d3fa6c073a4f827cdc319042f682ef8ea95459790d64fc"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
msgpack = [
|
||||
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"},
|
||||
|
||||
@@ -28,6 +28,7 @@ click = "8.1.2"
|
||||
importlib-metadata = "^4.11.3"
|
||||
typing-extensions = { version = "^4.0.0", python = "<3.8" }
|
||||
msgpack = "^1.0.3"
|
||||
mkdocstrings = {extras = ["python"], version = "^0.18.1"}
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^6.2.3"
|
||||
@@ -35,7 +36,7 @@ black = "^22.3.0"
|
||||
mypy = "^0.950"
|
||||
pytest-cov = "^2.12.1"
|
||||
mkdocs = "^1.3.0"
|
||||
mkdocstrings = "^0.17.0"
|
||||
mkdocstrings = "^0.18.1"
|
||||
mkdocs-material = "^7.3.6"
|
||||
pre-commit = "^2.13.0"
|
||||
pytest-aiohttp = "^1.0.4"
|
||||
|
||||
1
reference/README.md
Normal file
1
reference/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Contains private docs, mainly for the developers reference
|
||||
@@ -6,7 +6,7 @@ from textual.widgets import Static
|
||||
|
||||
|
||||
class Thing(Widget):
|
||||
def render(self, style: Style):
|
||||
def render(self):
|
||||
return "Hello, 3434 World.\n[b]Lorem impsum."
|
||||
|
||||
|
||||
|
||||
@@ -56,14 +56,14 @@ lorem_long_text = Text.from_markup(lorem * 2)
|
||||
|
||||
|
||||
class TweetHeader(Widget):
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text("Lorem Impsum", justify="center")
|
||||
|
||||
|
||||
class TweetBody(Widget):
|
||||
short_lorem = Reactive[bool](False)
|
||||
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return lorem_short_text if self.short_lorem else lorem_long_text
|
||||
|
||||
|
||||
@@ -72,22 +72,22 @@ class Tweet(Widget):
|
||||
|
||||
|
||||
class OptionItem(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("Option")
|
||||
|
||||
|
||||
class Error(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is an error message", justify="center")
|
||||
|
||||
|
||||
class Warning(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is a warning message", justify="center")
|
||||
|
||||
|
||||
class Success(Widget):
|
||||
def render(self, style: Style) -> Text:
|
||||
def render(self) -> Text:
|
||||
return Text("This is a success message", justify="center")
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class Introduction(Widget):
|
||||
}
|
||||
"""
|
||||
|
||||
def render(self, styles) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text("Here are the color edge types we support.", justify="center")
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class BorderDemo(Widget):
|
||||
def __init__(self, name: str):
|
||||
super().__init__(name=name)
|
||||
|
||||
def render(self, style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text(self.name, style="black on yellow", justify="center")
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from textual.widget import Widget
|
||||
|
||||
|
||||
class PanelWidget(Widget):
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Panel("hello world!", title="Title")
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class Introduction(Widget):
|
||||
}
|
||||
"""
|
||||
|
||||
def render(self, styles) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text(
|
||||
"Press keys 0 to 9 to scroll to the Placeholder with that ID.",
|
||||
justify="center",
|
||||
|
||||
@@ -12,7 +12,7 @@ from textual.widgets.tabs import Tabs, Tab
|
||||
|
||||
|
||||
class Hr(Widget):
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Rule()
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class Info(Widget):
|
||||
super().__init__()
|
||||
self.text = text
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Padding(f"{self.text}", pad=(0, 1))
|
||||
|
||||
|
||||
|
||||
@@ -222,6 +222,9 @@ class Compositor:
|
||||
old_map = self.map.copy()
|
||||
old_widgets = old_map.keys()
|
||||
map, widgets = self._arrange_root(parent)
|
||||
|
||||
# parent.log(map)
|
||||
|
||||
new_widgets = map.keys()
|
||||
|
||||
# Newly visible widgets
|
||||
@@ -552,7 +555,7 @@ class Compositor:
|
||||
]
|
||||
return segment_lines
|
||||
|
||||
def render(self, full: bool = True) -> RenderableType:
|
||||
def render(self, full: bool = False) -> RenderableType:
|
||||
"""Render a layout.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
|
||||
from textual.cli.cli import import_app
|
||||
|
||||
# This module defines our "Custom Fences", powered by SuperFences
|
||||
# @link https://facelessuser.github.io/pymdown-extensions/extensions/superfences/#custom-fences
|
||||
@@ -7,7 +8,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.2"
|
||||
os.environ["COLUMNS"] = attrs.get("columns", "80")
|
||||
os.environ["LINES"] = attrs.get("lines", "24")
|
||||
path = attrs.get("path")
|
||||
@@ -21,6 +22,7 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs):
|
||||
source = python_code.read()
|
||||
app_vars = {}
|
||||
exec(source, app_vars)
|
||||
|
||||
app = app_vars["app"]
|
||||
app.run()
|
||||
svg = app._screenshot
|
||||
|
||||
@@ -33,7 +33,6 @@ from rich.console import Console, RenderableType
|
||||
from rich.measure import Measurement
|
||||
from rich.protocol import is_renderable
|
||||
from rich.segment import Segments
|
||||
from rich.style import Style
|
||||
from rich.traceback import Traceback
|
||||
|
||||
from . import actions
|
||||
@@ -58,6 +57,7 @@ from .geometry import Offset, Region, Size
|
||||
from .layouts.dock import Dock
|
||||
from .message_pump import MessagePump
|
||||
from .reactive import Reactive
|
||||
from .renderables.blank import Blank
|
||||
from .screen import Screen
|
||||
from .widget import Widget
|
||||
|
||||
@@ -106,7 +106,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
"""The base class for Textual Applications"""
|
||||
|
||||
CSS = """
|
||||
|
||||
"""
|
||||
|
||||
CSS_PATH: str | None = None
|
||||
@@ -210,7 +209,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
title: Reactive[str] = Reactive("Textual")
|
||||
sub_title: Reactive[str] = Reactive("")
|
||||
background: Reactive[str] = Reactive("black")
|
||||
dark = Reactive(False)
|
||||
|
||||
@property
|
||||
@@ -525,8 +523,8 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.stylesheet.update(self)
|
||||
self.screen.refresh(layout=True)
|
||||
|
||||
def render(self, styles: Style) -> RenderableType:
|
||||
return ""
|
||||
def render(self) -> RenderableType:
|
||||
return Blank()
|
||||
|
||||
def query(self, selector: str | None = None) -> DOMQuery:
|
||||
"""Get a DOM query in the current screen.
|
||||
@@ -742,6 +740,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
await self.dispatch_message(mount_event)
|
||||
|
||||
self.title = self._title
|
||||
self.stylesheet.update(self)
|
||||
self.refresh()
|
||||
await self.animator.start()
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.console import Console, ConsoleOptions
|
||||
from rich.segment import Segment
|
||||
from rich.style import StyleType
|
||||
|
||||
|
||||
class Blank:
|
||||
"""
|
||||
Render an empty rectangle.
|
||||
|
||||
Args:
|
||||
style (StyleType): Style to apply to the box.
|
||||
width (int, optional): Width of the box in number of cells. Will expand to fit parent if ``None``.
|
||||
height (int, optional): Height of the box in number of cells. Will expand to fit parent if ``None``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, style: StyleType, width: int | None = None, height: int | None = None
|
||||
):
|
||||
self.style = style
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def __rich_console__(self, console: Console, console_options: ConsoleOptions):
|
||||
render_width = self.width or console_options.max_width
|
||||
render_height = (
|
||||
self.height or console_options.height or console_options.max_height
|
||||
)
|
||||
style = console.get_style(self.style)
|
||||
for _ in range(render_height):
|
||||
yield Segment(" " * render_width + "\n", style)
|
||||
@@ -47,7 +47,7 @@ if TYPE_CHECKING:
|
||||
from .._layout import Layout
|
||||
from .styles import DockGroup, Styles, StylesBase
|
||||
|
||||
from .types import EdgeType
|
||||
from .types import EdgeType, AlignHorizontal, AlignVertical
|
||||
|
||||
BorderDefinition = (
|
||||
"Sequence[tuple[EdgeType, str | Color] | None] | tuple[EdgeType, str | Color]"
|
||||
@@ -935,3 +935,23 @@ class FractionalProperty:
|
||||
)
|
||||
if obj.set_rule(name, clamp(float_value, 0, 1)):
|
||||
obj.refresh()
|
||||
|
||||
|
||||
class AlignProperty:
|
||||
def __set_name__(self, owner: StylesBase, name: str) -> None:
|
||||
self.horizontal = f"{name}_horizontal"
|
||||
self.vertical = f"{name}_vertical"
|
||||
|
||||
def __get__(
|
||||
self, obj: StylesBase, type: type[StylesBase]
|
||||
) -> tuple[AlignHorizontal, AlignVertical]:
|
||||
horizontal = getattr(obj, self.horizontal)
|
||||
vertical = getattr(obj, self.vertical)
|
||||
return (horizontal, vertical)
|
||||
|
||||
def __set__(
|
||||
self, obj: StylesBase, value: tuple[AlignHorizontal, AlignVertical]
|
||||
) -> None:
|
||||
horizontal, vertical = value
|
||||
setattr(obj, self.horizontal, horizontal)
|
||||
setattr(obj, self.vertical, vertical)
|
||||
|
||||
@@ -14,6 +14,7 @@ from .._animator import Animation, EasingFunction
|
||||
from ..color import Color
|
||||
from ..geometry import Spacing
|
||||
from ._style_properties import (
|
||||
AlignProperty,
|
||||
BorderProperty,
|
||||
BoxProperty,
|
||||
ColorProperty,
|
||||
@@ -230,9 +231,11 @@ class StylesBase(ABC):
|
||||
|
||||
align_horizontal = StringEnumProperty(VALID_ALIGN_HORIZONTAL, "left")
|
||||
align_vertical = StringEnumProperty(VALID_ALIGN_VERTICAL, "top")
|
||||
align = AlignProperty()
|
||||
|
||||
content_align_horizontal = StringEnumProperty(VALID_ALIGN_HORIZONTAL, "left")
|
||||
content_align_vertical = StringEnumProperty(VALID_ALIGN_VERTICAL, "top")
|
||||
content_align = AlignProperty()
|
||||
|
||||
def __eq__(self, styles: object) -> bool:
|
||||
"""Check that Styles contains the same rules."""
|
||||
@@ -531,7 +534,8 @@ class Styles(StylesBase):
|
||||
) -> Animation | None:
|
||||
from ..widget import Widget
|
||||
|
||||
assert isinstance(self.node, Widget)
|
||||
# node = self.node
|
||||
# assert isinstance(self.node, Widget)
|
||||
if isinstance(value, ScalarOffset):
|
||||
return ScalarAnimation(
|
||||
self.node,
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.console import ConsoleOptions, Console, RenderResult
|
||||
from rich.color import Color
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
|
||||
from ..color import Color
|
||||
|
||||
|
||||
class Blank:
|
||||
"""Draw solid background color."""
|
||||
|
||||
def __init__(self, color: str) -> None:
|
||||
self._style = Style.from_color(None, Color.parse(color))
|
||||
def __init__(self, color: Color | str = "transparent") -> None:
|
||||
background = (
|
||||
color.rich_color
|
||||
if isinstance(color, Color)
|
||||
else Color.parse(color).rich_color
|
||||
)
|
||||
self._style = Style.from_color(None, background)
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
@@ -18,8 +24,11 @@ class Blank:
|
||||
width = options.max_width
|
||||
height = options.height or options.max_height
|
||||
|
||||
segment = Segment(f"{' ' * width}\n", self._style)
|
||||
yield from [segment] * height
|
||||
segment = Segment(f"{' ' * width}", style=self._style)
|
||||
line = Segment.line()
|
||||
for _ in range(height):
|
||||
yield segment
|
||||
yield line
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -29,13 +29,6 @@ class Screen(Widget):
|
||||
|
||||
CSS = """
|
||||
|
||||
Screen {
|
||||
layout: vertical;
|
||||
overflow-y: auto;
|
||||
background: $surface;
|
||||
color: $text-surface;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
dark = Reactive(False)
|
||||
@@ -48,8 +41,8 @@ class Screen(Widget):
|
||||
def watch_dark(self, dark: bool) -> None:
|
||||
pass
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
return self.app.render(style)
|
||||
def render(self) -> RenderableType:
|
||||
return self.app.render()
|
||||
|
||||
def get_offset(self, widget: Widget) -> Offset:
|
||||
"""Get the absolute offset of a given Widget.
|
||||
@@ -174,7 +167,6 @@ class Screen(Widget):
|
||||
|
||||
async def on_resize(self, event: events.Resize) -> None:
|
||||
self.size_updated(event.size, event.virtual_size, event.container_size)
|
||||
self._refresh_layout()
|
||||
event.stop()
|
||||
|
||||
async def _on_mouse_move(self, event: events.MouseMove) -> None:
|
||||
|
||||
@@ -205,7 +205,7 @@ class ScrollBar(Widget):
|
||||
yield "window_size", self.window_size
|
||||
yield "position", self.position
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
styles = self.parent.styles
|
||||
scrollbar_style = Style(
|
||||
bgcolor=(
|
||||
|
||||
@@ -32,6 +32,7 @@ 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
|
||||
|
||||
@@ -232,7 +233,7 @@ class Widget(DOMNode):
|
||||
if self._content_height_cache[0] == cache_key:
|
||||
return self._content_height_cache[1]
|
||||
|
||||
renderable = self.render(self.styles.rich_style)
|
||||
renderable = self.render()
|
||||
options = self.console.options.update_width(width).update(highlight=False)
|
||||
segments = self.console.render(renderable, options)
|
||||
# Cheaper than counting the lines returned from render_lines!
|
||||
@@ -638,11 +639,12 @@ class Widget(DOMNode):
|
||||
Returns:
|
||||
RenderableType: A new renderable.
|
||||
"""
|
||||
renderable = self.render(self.text_style)
|
||||
|
||||
(base_background, base_color), (background, color) = self.colors
|
||||
styles = self.styles
|
||||
|
||||
renderable = self.render()
|
||||
|
||||
content_align = (styles.content_align_horizontal, styles.content_align_vertical)
|
||||
if content_align != ("left", "top"):
|
||||
horizontal, vertical = content_align
|
||||
@@ -852,7 +854,7 @@ class Widget(DOMNode):
|
||||
self._repaint_required = True
|
||||
self.check_idle()
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
"""Get renderable for widget.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -74,7 +74,7 @@ class Button(Widget, can_focus=True):
|
||||
return Text.from_markup(label)
|
||||
return label
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
label = self.label.copy()
|
||||
label.stylize(style)
|
||||
return label
|
||||
|
||||
@@ -59,7 +59,7 @@ class Footer(Widget):
|
||||
text.append_text(key_text)
|
||||
return text
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
if self._key_text is None:
|
||||
self._key_text = self.make_key_text()
|
||||
return self._key_text
|
||||
|
||||
@@ -49,7 +49,7 @@ class Header(Widget):
|
||||
def get_clock(self) -> str:
|
||||
return datetime.now().time().strftime("%X")
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
header_table = Table.grid(padding=(0, 1), expand=True)
|
||||
header_table.style = self.style
|
||||
header_table.add_column(justify="left", ratio=0, width=8)
|
||||
|
||||
@@ -37,7 +37,7 @@ class Placeholder(Widget, can_focus=True):
|
||||
yield "has_focus", self.has_focus, False
|
||||
yield "mouse_over", self.mouse_over, False
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
# Apply colours only inside render_styled
|
||||
# Pass the full RICH style object into `render` - not the `Styles`
|
||||
return Panel(
|
||||
|
||||
@@ -9,7 +9,7 @@ from ..widget import Widget
|
||||
class Static(Widget):
|
||||
def __init__(
|
||||
self,
|
||||
renderable: RenderableType,
|
||||
renderable: RenderableType = "",
|
||||
*,
|
||||
name: str | None = None,
|
||||
id: str | None = None,
|
||||
@@ -18,7 +18,7 @@ class Static(Widget):
|
||||
super().__init__(name=name, id=id, classes=classes)
|
||||
self.renderable = renderable
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return self.renderable
|
||||
|
||||
def update(self, renderable: RenderableType) -> None:
|
||||
|
||||
@@ -248,7 +248,7 @@ class TreeControl(Generic[NodeDataType], Widget):
|
||||
push(iter(node.children))
|
||||
return None
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return self._tree
|
||||
|
||||
def render_node(self, node: TreeNode[NodeDataType]) -> RenderableType:
|
||||
|
||||
@@ -330,7 +330,7 @@ class Tabs(Widget):
|
||||
"""
|
||||
return next((i for i, tab in enumerate(self.tabs) if tab.name == tab_name), 0)
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return TabsRenderable(
|
||||
self.tabs,
|
||||
tab_padding=self.tab_padding,
|
||||
|
||||
@@ -150,7 +150,7 @@ class TextInput(TextWidgetBase, can_focus=True):
|
||||
self.visible_range = (new_visible_range_start, new_visible_range_end)
|
||||
self.refresh()
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
# First render: Cursor at start of text, visible range goes from cursor to content region width
|
||||
if not self.visible_range:
|
||||
self.visible_range = (self._editor.cursor_index, self.content_region.width)
|
||||
@@ -255,7 +255,7 @@ class TextAreaChild(TextWidgetBase, can_focus=True):
|
||||
CSS = "TextAreaChild { height: auto; background: $primary-darken-1; }"
|
||||
STOP_PROPAGATE = {"tab", "shift+tab"}
|
||||
|
||||
def render(self, style: Style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
# We only show the cursor if the widget has focus
|
||||
show_cursor = self.has_focus
|
||||
display_text = Text(self._editor.content, no_wrap=True)
|
||||
|
||||
@@ -202,7 +202,7 @@ async def test_border_edge_types_impact_on_widget_size(
|
||||
expects_visible_char_at_top_left_edge: bool,
|
||||
):
|
||||
class BorderTarget(Widget):
|
||||
def render(self, style) -> RenderableType:
|
||||
def render(self) -> RenderableType:
|
||||
return Text("border target", style="black on yellow", justify="center")
|
||||
|
||||
border_target = BorderTarget()
|
||||
|
||||
@@ -39,7 +39,7 @@ def test_widget_content_width():
|
||||
self.text = text
|
||||
super().__init__(id=id)
|
||||
|
||||
def render(self, style: Style) -> str:
|
||||
def render(self) -> str:
|
||||
return self.text
|
||||
|
||||
widget1 = TextWidget("foo", id="widget1")
|
||||
|
||||
Reference in New Issue
Block a user