mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' of github.com:Textualize/textual into datatable-cell-keys
This commit is contained in:
@@ -1,15 +1,22 @@
|
|||||||
# See https://pre-commit.com for more information
|
# See https://pre-commit.com for more information
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.3.0
|
rev: v4.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
args: ['--unsafe']
|
args: [ '--unsafe' ]
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/pycqa/isort
|
||||||
rev: 22.10.0
|
rev: 5.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: isort
|
||||||
|
name: isort (python)
|
||||||
|
language_version: '3.11'
|
||||||
|
args: ["--profile", "black", "--filter-files"]
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 23.1.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
exclude: ^tests/snapshot_tests
|
exclude: ^tests/snapshot_tests
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added Shift+scroll wheel and ctrl+scroll wheel to scroll horizontally
|
- Added Shift+scroll wheel and ctrl+scroll wheel to scroll horizontally
|
||||||
- Added `Tree.action_toggle_node` to toggle a node without selecting, and bound it to <kbd>Space</kbd> https://github.com/Textualize/textual/issues/1433
|
- Added `Tree.action_toggle_node` to toggle a node without selecting, and bound it to <kbd>Space</kbd> https://github.com/Textualize/textual/issues/1433
|
||||||
- Added `Tree.reset` to fully reset a `Tree` https://github.com/Textualize/textual/issues/1437
|
- Added `Tree.reset` to fully reset a `Tree` https://github.com/Textualize/textual/issues/1437
|
||||||
|
- Added DOMNode.watch and DOMNode.is_attached methods https://github.com/Textualize/textual/pull/1750
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Breaking change: `TreeNode` can no longer be imported from `textual.widgets`; it is now available via `from textual.widgets.tree import TreeNode`. https://github.com/Textualize/textual/pull/1637
|
- Breaking change: `TreeNode` can no longer be imported from `textual.widgets`; it is now available via `from textual.widgets.tree import TreeNode`. https://github.com/Textualize/textual/pull/1637
|
||||||
- `Tree` now shows a (subdued) cursor for a highlighted node when focus has moved elsewhere https://github.com/Textualize/textual/issues/1471
|
- `Tree` now shows a (subdued) cursor for a highlighted node when focus has moved elsewhere https://github.com/Textualize/textual/issues/1471
|
||||||
|
- Breaking change: renamed `Checkbox` to `Switch`.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Methods `MessagePump.emit` and `MessagePump.emit_no_wait` https://github.com/Textualize/textual/pull/1738
|
- Methods `MessagePump.emit` and `MessagePump.emit_no_wait` https://github.com/Textualize/textual/pull/1738
|
||||||
|
- Removed `reactive.watch` in favor of DOMNode.watch.
|
||||||
|
|
||||||
## [0.10.1] - 2023-01-20
|
## [0.10.1] - 2023-01-20
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
::: textual.widgets.Checkbox
|
|
||||||
1
docs/api/switch.md
Normal file
1
docs/api/switch.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
::: textual.widgets.Switch
|
||||||
@@ -7,7 +7,7 @@ Screen {
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox {
|
Switch {
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ Checkbox {
|
|||||||
background: darkslategrey;
|
background: darkslategrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
#custom-design > .checkbox--switch {
|
#custom-design > .switch--switch {
|
||||||
color: dodgerblue;
|
color: dodgerblue;
|
||||||
background: darkslateblue;
|
background: darkslateblue;
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,35 @@
|
|||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.containers import Horizontal
|
from textual.containers import Horizontal
|
||||||
from textual.widgets import Checkbox, Static
|
from textual.widgets import Switch, Static
|
||||||
|
|
||||||
|
|
||||||
class CheckboxApp(App):
|
class SwitchApp(App):
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Static("[b]Example checkboxes\n", classes="label")
|
yield Static("[b]Example switches\n", classes="label")
|
||||||
yield Horizontal(
|
yield Horizontal(
|
||||||
Static("off: ", classes="label"),
|
Static("off: ", classes="label"),
|
||||||
Checkbox(animate=False),
|
Switch(animate=False),
|
||||||
classes="container",
|
classes="container",
|
||||||
)
|
)
|
||||||
yield Horizontal(
|
yield Horizontal(
|
||||||
Static("on: ", classes="label"),
|
Static("on: ", classes="label"),
|
||||||
Checkbox(value=True),
|
Switch(value=True),
|
||||||
classes="container",
|
classes="container",
|
||||||
)
|
)
|
||||||
|
|
||||||
focused_checkbox = Checkbox()
|
focused_switch = Switch()
|
||||||
focused_checkbox.focus()
|
focused_switch.focus()
|
||||||
yield Horizontal(
|
yield Horizontal(
|
||||||
Static("focused: ", classes="label"), focused_checkbox, classes="container"
|
Static("focused: ", classes="label"), focused_switch, classes="container"
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Horizontal(
|
yield Horizontal(
|
||||||
Static("custom: ", classes="label"),
|
Static("custom: ", classes="label"),
|
||||||
Checkbox(id="custom-design"),
|
Switch(id="custom-design"),
|
||||||
classes="container",
|
classes="container",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
app = CheckboxApp(css_path="checkbox.css")
|
app = SwitchApp(css_path="switch.css")
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run()
|
||||||
@@ -40,7 +40,7 @@ Widgets are key to making user-friendly interfaces. The builtin widgets should c
|
|||||||
- [x] Buttons
|
- [x] Buttons
|
||||||
* [x] Error / warning variants
|
* [x] Error / warning variants
|
||||||
- [ ] Color picker
|
- [ ] Color picker
|
||||||
- [x] Checkbox
|
- [ ] Checkbox
|
||||||
- [ ] Content switcher
|
- [ ] Content switcher
|
||||||
- [x] DataTable
|
- [x] DataTable
|
||||||
* [x] Cell select
|
* [x] Cell select
|
||||||
@@ -70,6 +70,7 @@ Widgets are key to making user-friendly interfaces. The builtin widgets should c
|
|||||||
* [ ] Style variants (solid, thin etc)
|
* [ ] Style variants (solid, thin etc)
|
||||||
- [ ] Radio boxes
|
- [ ] Radio boxes
|
||||||
- [ ] Spark-lines
|
- [ ] Spark-lines
|
||||||
|
- [X] Switch
|
||||||
- [ ] Tabs
|
- [ ] Tabs
|
||||||
- [ ] TextArea (multi-line input)
|
- [ ] TextArea (multi-line input)
|
||||||
* [ ] Basic controls
|
* [ ] Basic controls
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
# Checkbox
|
|
||||||
|
|
||||||
A simple checkbox widget which stores a boolean value.
|
|
||||||
|
|
||||||
- [x] Focusable
|
|
||||||
- [ ] Container
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
The example below shows checkboxes in various states.
|
|
||||||
|
|
||||||
=== "Output"
|
|
||||||
|
|
||||||
```{.textual path="docs/examples/widgets/checkbox.py"}
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "checkbox.py"
|
|
||||||
|
|
||||||
```python
|
|
||||||
--8<-- "docs/examples/widgets/checkbox.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "checkbox.css"
|
|
||||||
|
|
||||||
```sass
|
|
||||||
--8<-- "docs/examples/widgets/checkbox.css"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reactive Attributes
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| ------- | ------ | ------- | ---------------------------------- |
|
|
||||||
| `value` | `bool` | `False` | The default value of the checkbox. |
|
|
||||||
|
|
||||||
## Bindings
|
|
||||||
|
|
||||||
The checkbox widget defines directly the following bindings:
|
|
||||||
|
|
||||||
::: textual.widgets.Checkbox.BINDINGS
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
show_root_toc_entry: false
|
|
||||||
|
|
||||||
## Component Classes
|
|
||||||
|
|
||||||
The checkbox widget provides the following component classes:
|
|
||||||
|
|
||||||
::: textual.widgets.Checkbox.COMPONENT_CLASSES
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
show_root_toc_entry: false
|
|
||||||
|
|
||||||
## Messages
|
|
||||||
|
|
||||||
### ::: textual.widgets.Checkbox.Changed
|
|
||||||
|
|
||||||
## Additional Notes
|
|
||||||
|
|
||||||
- To remove the spacing around a checkbox, set `border: none;` and `padding: 0;`.
|
|
||||||
|
|
||||||
## See Also
|
|
||||||
|
|
||||||
- [Checkbox](../api/checkbox.md) code reference
|
|
||||||
63
docs/widgets/switch.md
Normal file
63
docs/widgets/switch.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Switch
|
||||||
|
|
||||||
|
A simple switch widget which stores a boolean value.
|
||||||
|
|
||||||
|
- [x] Focusable
|
||||||
|
- [ ] Container
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The example below shows switches in various states.
|
||||||
|
|
||||||
|
=== "Output"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/switch.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "switch.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/widgets/switch.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "switch.css"
|
||||||
|
|
||||||
|
```sass
|
||||||
|
--8<-- "docs/examples/widgets/switch.css"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reactive Attributes
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|---------|--------|---------|----------------------------------|
|
||||||
|
| `value` | `bool` | `False` | The default value of the switch. |
|
||||||
|
|
||||||
|
## Bindings
|
||||||
|
|
||||||
|
The switch widget defines directly the following bindings:
|
||||||
|
|
||||||
|
::: textual.widgets.Switch.BINDINGS
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
show_root_toc_entry: false
|
||||||
|
|
||||||
|
## Component Classes
|
||||||
|
|
||||||
|
The switch widget provides the following component classes:
|
||||||
|
|
||||||
|
::: textual.widgets.Switch.COMPONENT_CLASSES
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
show_root_toc_entry: false
|
||||||
|
|
||||||
|
## Messages
|
||||||
|
|
||||||
|
### ::: textual.widgets.Switch.Changed
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
- To remove the spacing around a `Switch`, set `border: none;` and `padding: 0;`.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- [Switch](../api/switch.md) code reference
|
||||||
@@ -126,7 +126,6 @@ nav:
|
|||||||
- "styles/width.md"
|
- "styles/width.md"
|
||||||
- Widgets:
|
- Widgets:
|
||||||
- "widgets/button.md"
|
- "widgets/button.md"
|
||||||
- "widgets/checkbox.md"
|
|
||||||
- "widgets/data_table.md"
|
- "widgets/data_table.md"
|
||||||
- "widgets/directory_tree.md"
|
- "widgets/directory_tree.md"
|
||||||
- "widgets/footer.md"
|
- "widgets/footer.md"
|
||||||
@@ -138,6 +137,7 @@ nav:
|
|||||||
- "widgets/list_view.md"
|
- "widgets/list_view.md"
|
||||||
- "widgets/placeholder.md"
|
- "widgets/placeholder.md"
|
||||||
- "widgets/static.md"
|
- "widgets/static.md"
|
||||||
|
- "widgets/switch.md"
|
||||||
- "widgets/text_log.md"
|
- "widgets/text_log.md"
|
||||||
- "widgets/tree.md"
|
- "widgets/tree.md"
|
||||||
- API:
|
- API:
|
||||||
@@ -145,7 +145,6 @@ nav:
|
|||||||
- "api/app.md"
|
- "api/app.md"
|
||||||
- "api/binding.md"
|
- "api/binding.md"
|
||||||
- "api/button.md"
|
- "api/button.md"
|
||||||
- "api/checkbox.md"
|
|
||||||
- "api/color.md"
|
- "api/color.md"
|
||||||
- "api/containers.md"
|
- "api/containers.md"
|
||||||
- "api/coordinate.md"
|
- "api/coordinate.md"
|
||||||
@@ -170,6 +169,7 @@ nav:
|
|||||||
- "api/scroll_view.md"
|
- "api/scroll_view.md"
|
||||||
- "api/static.md"
|
- "api/static.md"
|
||||||
- "api/strip.md"
|
- "api/strip.md"
|
||||||
|
- "api/switch.md"
|
||||||
- "api/text_log.md"
|
- "api/text_log.md"
|
||||||
- "api/timer.md"
|
- "api/timer.md"
|
||||||
- "api/tree.md"
|
- "api/tree.md"
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
from typing import Callable, TYPE_CHECKING
|
|
||||||
|
|
||||||
from ._context import active_app
|
from ._context import active_app
|
||||||
from ._log import LogGroup, LogVerbosity
|
from ._log import LogGroup, LogVerbosity
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from .demo import DemoApp
|
from .demo import DemoApp
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = DemoApp()
|
app = DemoApp()
|
||||||
app.run()
|
app.run()
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ from __future__ import annotations
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Sequence, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Sequence
|
||||||
|
|
||||||
from .geometry import Region, Size, Spacing
|
|
||||||
from ._layout import DockArrangeResult, WidgetPlacement
|
from ._layout import DockArrangeResult, WidgetPlacement
|
||||||
from ._partition import partition
|
from ._partition import partition
|
||||||
|
from .geometry import Region, Size, Spacing
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ Compatibility layer for asyncio.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
__all__ = ["create_task"]
|
__all__ = ["create_task"]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import cast, Tuple, Union
|
from typing import TYPE_CHECKING, Tuple, Union, cast
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from inspect import signature, isawaitable
|
from inspect import isawaitable, signature
|
||||||
from typing import Any, Callable, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Any, Callable
|
||||||
|
|
||||||
from . import active_app
|
from . import active_app
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import asyncio
|
|||||||
|
|
||||||
from ._time import time
|
from ._time import time
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A module that serves as the single source of truth for everything time-related in a Textual app.
|
A module that serves as the single source of truth for everything time-related in a Textual app.
|
||||||
Having this logic centralised makes it easier to simulate time in integration tests,
|
Having this logic centralised makes it easier to simulate time in integration tests,
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ from rich.style import Style
|
|||||||
from . import errors
|
from . import errors
|
||||||
from ._cells import cell_len
|
from ._cells import cell_len
|
||||||
from ._loop import loop_last
|
from ._loop import loop_last
|
||||||
from .strip import Strip
|
|
||||||
from .geometry import NULL_OFFSET, Offset, Region, Size
|
from .geometry import NULL_OFFSET, Offset, Region, Size
|
||||||
|
from .strip import Strip
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .widget import Widget
|
|
||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
|
from .widget import Widget
|
||||||
|
|
||||||
|
|
||||||
class ReflowResult(NamedTuple):
|
class ReflowResult(NamedTuple):
|
||||||
"""The result of a reflow operation. Describes the chances to widgets."""
|
"""The result of a reflow operation. Describes the chances to widgets."""
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .app import App
|
from .app import App
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import shlex
|
import shlex
|
||||||
|
from pathlib import Path
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
from textual._import_app import import_app
|
||||||
from textual.app import App
|
from textual.app import App
|
||||||
from textual.pilot import Pilot
|
from textual.pilot import Pilot
|
||||||
from textual._import_app import import_app
|
|
||||||
|
|
||||||
|
|
||||||
SCREENSHOT_CACHE = ".screenshot_cache"
|
SCREENSHOT_CACHE = ".screenshot_cache"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Define a series of easing functions for more natural-looking animations.
|
|||||||
Taken from https://easings.net/ and translated from JavaScript.
|
Taken from https://easings.net/ and translated from JavaScript.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from math import pi, cos, sin, sqrt
|
from math import cos, pi, sin, sqrt
|
||||||
|
|
||||||
|
|
||||||
def _in_out_expo(x: float) -> float:
|
def _in_out_expo(x: float) -> float:
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"""Provides an immutable sequence view class."""
|
"""Provides an immutable sequence view class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from sys import maxsize
|
from sys import maxsize
|
||||||
from typing import Generic, TypeVar, Iterator, overload, Sequence
|
from typing import Generic, Iterator, Sequence, TypeVar, overload
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import runpy
|
import runpy
|
||||||
import shlex
|
import shlex
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import cast, TYPE_CHECKING
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from textual.app import App
|
from textual.app import App
|
||||||
@@ -46,10 +45,10 @@ def import_app(import_name: str) -> App:
|
|||||||
A Textual application
|
A Textual application
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import inspect
|
|
||||||
import importlib
|
import importlib
|
||||||
|
import inspect
|
||||||
|
|
||||||
from textual.app import App, WINDOWS
|
from textual.app import WINDOWS, App
|
||||||
|
|
||||||
import_name, *argv = shlex.split(import_name, posix=not WINDOWS)
|
import_name, *argv = shlex.split(import_name, posix=not WINDOWS)
|
||||||
drive, import_name = os.path.splitdrive(import_name)
|
drive, import_name = os.path.splitdrive(import_name)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import ClassVar, NamedTuple, TYPE_CHECKING
|
from typing import TYPE_CHECKING, ClassVar, NamedTuple
|
||||||
|
|
||||||
from .geometry import Region, Size, Spacing
|
from .geometry import Region, Size, Spacing
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|
||||||
ArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget]]"
|
ArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget]]"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import sys
|
|
||||||
|
|
||||||
|
import sys
|
||||||
from typing import TYPE_CHECKING, Any, Iterator, Sequence, overload
|
from typing import TYPE_CHECKING, Any, Iterator, Sequence, overload
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections import deque
|
|
||||||
import io
|
import io
|
||||||
from typing import (
|
from collections import deque
|
||||||
Callable,
|
from typing import Callable, Deque, Generator, Generic, Iterable, TypeVar, Union
|
||||||
Deque,
|
|
||||||
Generator,
|
|
||||||
TypeVar,
|
|
||||||
Generic,
|
|
||||||
Union,
|
|
||||||
Iterable,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ParseError(Exception):
|
class ParseError(Exception):
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Callable, Iterable, TypeVar
|
from typing import Callable, Iterable, TypeVar
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Timer context manager, only used in debug.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
from typing import Generator
|
|
||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
from . import log
|
from . import log
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from itertools import accumulate
|
from itertools import accumulate
|
||||||
from typing import Sequence, cast, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Sequence, cast
|
||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from asyncio import Future, get_running_loop
|
||||||
from time import sleep, perf_counter
|
from threading import Event, Thread
|
||||||
from asyncio import get_running_loop, Future
|
from time import perf_counter, sleep
|
||||||
from threading import Thread, Event
|
|
||||||
|
|
||||||
|
|
||||||
class Sleeper(Thread):
|
class Sleeper(Thread):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from asyncio import sleep
|
from asyncio import sleep
|
||||||
from time import process_time, monotonic
|
from time import monotonic, process_time
|
||||||
|
|
||||||
SLEEP_GRANULARITY: float = 1 / 50
|
SLEEP_GRANULARITY: float = 1 / 50
|
||||||
SLEEP_IDLE: float = SLEEP_GRANULARITY / 2.0
|
SLEEP_IDLE: float = SLEEP_GRANULARITY / 2.0
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ This should only be imported on Windows.
|
|||||||
|
|
||||||
from time import sleep as time_sleep
|
from time import sleep as time_sleep
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["sleep"]
|
__all__ = ["sleep"]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import unicodedata
|
|
||||||
import re
|
import re
|
||||||
|
import unicodedata
|
||||||
from typing import Any, Callable, Generator, Iterable
|
from typing import Any, Callable, Generator, Iterable
|
||||||
|
|
||||||
from . import events
|
from . import events, messages
|
||||||
from . import messages
|
|
||||||
from ._ansi_sequences import ANSI_SEQUENCES_KEYS
|
from ._ansi_sequences import ANSI_SEQUENCES_KEYS
|
||||||
from ._parser import Awaitable, Parser, TokenCallback
|
from ._parser import Awaitable, Parser, TokenCallback
|
||||||
from ._types import MessageTarget
|
from ._types import MessageTarget
|
||||||
from .keys import KEY_NAME_REPLACEMENTS
|
from .keys import KEY_NAME_REPLACEMENTS
|
||||||
|
|
||||||
|
|
||||||
# When trying to determine whether the current sequence is a supported/valid
|
# When trying to determine whether the current sequence is a supported/valid
|
||||||
# escape sequence, at which length should we give up and consider our search
|
# escape sequence, at which length should we give up and consider our search
|
||||||
# to be unsuccessful?
|
# to be unsuccessful?
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ from ._asyncio import create_task
|
|||||||
from ._callback import invoke
|
from ._callback import invoke
|
||||||
from ._context import active_app
|
from ._context import active_app
|
||||||
from ._event_broker import NoHandler, extract_handler_actions
|
from ._event_broker import NoHandler, extract_handler_actions
|
||||||
from .filter import LineFilter, Monochrome
|
|
||||||
from ._path import _make_path_object_relative
|
from ._path import _make_path_object_relative
|
||||||
|
|
||||||
from ._wait import wait_for_idle
|
from ._wait import wait_for_idle
|
||||||
from .actions import SkipAction
|
from .actions import SkipAction
|
||||||
from .await_remove import AwaitRemove
|
from .await_remove import AwaitRemove
|
||||||
@@ -63,6 +61,7 @@ from .driver import Driver
|
|||||||
from .drivers.headless_driver import HeadlessDriver
|
from .drivers.headless_driver import HeadlessDriver
|
||||||
from .features import FeatureFlag, parse_features
|
from .features import FeatureFlag, parse_features
|
||||||
from .file_monitor import FileMonitor
|
from .file_monitor import FileMonitor
|
||||||
|
from .filter import LineFilter, Monochrome
|
||||||
from .geometry import Offset, Region, Size
|
from .geometry import Offset, Region, Size
|
||||||
from .keys import REPLACED_KEYS, _get_key_display
|
from .keys import REPLACED_KEYS, _get_key_display
|
||||||
from .messages import CallbackType
|
from .messages import CallbackType
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from typing import Match, Pattern
|
from typing import Match, Pattern
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ except ImportError:
|
|||||||
|
|
||||||
from importlib_metadata import version
|
from importlib_metadata import version
|
||||||
|
|
||||||
|
from textual._import_app import AppFail, import_app
|
||||||
from textual.pilot import Pilot
|
from textual.pilot import Pilot
|
||||||
from textual._import_app import import_app, AppFail
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@@ -26,6 +26,7 @@ def run():
|
|||||||
def console(verbose: bool, exclude: list[str]) -> None:
|
def console(verbose: bool, exclude: list[str]) -> None:
|
||||||
"""Launch the textual console."""
|
"""Launch the textual console."""
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
from textual.devtools.server import _run_devtools
|
from textual.devtools.server import _run_devtools
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.constants import BORDERS
|
from textual.constants import BORDERS
|
||||||
from textual.widgets import Button, Label
|
|
||||||
from textual.containers import Vertical
|
from textual.containers import Vertical
|
||||||
|
from textual.widgets import Button, Label
|
||||||
|
|
||||||
TEXT = """I must not fear.
|
TEXT = """I must not fear.
|
||||||
Fear is the mind-killer.
|
Fear is the mind-killer.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from textual.app import App, ComposeResult
|
|||||||
from textual.containers import Horizontal, Vertical
|
from textual.containers import Horizontal, Vertical
|
||||||
from textual.design import ColorSystem
|
from textual.design import ColorSystem
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
from textual.widgets import Button, Footer, Static, Label
|
from textual.widgets import Button, Footer, Label, Static
|
||||||
|
|
||||||
|
|
||||||
class ColorButtons(Vertical):
|
class ColorButtons(Vertical):
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
|
|
||||||
from textual._easing import EASING
|
from textual._easing import EASING
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.cli.previews.borders import TEXT
|
from textual.cli.previews.borders import TEXT
|
||||||
from textual.containers import Container, Horizontal, Vertical
|
from textual.containers import Container, Horizontal, Vertical
|
||||||
from textual.reactive import Reactive
|
from textual.reactive import reactive, var
|
||||||
from textual.scrollbar import ScrollBarRender
|
from textual.scrollbar import ScrollBarRender
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
from textual.widgets import Button, Footer, Label, Input
|
from textual.widgets import Button, Footer, Input, Label
|
||||||
|
|
||||||
VIRTUAL_SIZE = 100
|
VIRTUAL_SIZE = 100
|
||||||
WINDOW_SIZE = 10
|
WINDOW_SIZE = 10
|
||||||
@@ -23,8 +24,8 @@ class EasingButtons(Widget):
|
|||||||
|
|
||||||
|
|
||||||
class Bar(Widget):
|
class Bar(Widget):
|
||||||
position = Reactive.init(START_POSITION)
|
position = reactive(START_POSITION)
|
||||||
animation_running = Reactive(False)
|
animation_running = reactive(False)
|
||||||
|
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
|
|
||||||
@@ -53,8 +54,8 @@ class Bar(Widget):
|
|||||||
|
|
||||||
|
|
||||||
class EasingApp(App):
|
class EasingApp(App):
|
||||||
position = Reactive.init(START_POSITION)
|
position = reactive(START_POSITION)
|
||||||
duration = Reactive.var(1.0)
|
duration = var(1.0)
|
||||||
|
|
||||||
def on_load(self):
|
def on_load(self):
|
||||||
self.bind(
|
self.bind(
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
|
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from textual.app import App, ComposeResult
|
|
||||||
from textual.reactive import var, Reactive
|
|
||||||
from textual import events
|
from textual import events
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
from textual.containers import Horizontal
|
from textual.containers import Horizontal
|
||||||
|
from textual.reactive import Reactive, var
|
||||||
from textual.widgets import Button, Header, TextLog
|
from textual.widgets import Button, Header, TextLog
|
||||||
|
|
||||||
|
|
||||||
INSTRUCTIONS = """\
|
INSTRUCTIONS = """\
|
||||||
[u]Press some keys![/]
|
[u]Press some keys![/]
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
"""Textual CLI command code to print diagnostic information."""
|
"""Textual CLI command code to print diagnostic information."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import platform
|
import platform
|
||||||
from typing import Any
|
import sys
|
||||||
from functools import singledispatch
|
from functools import singledispatch
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from importlib_metadata import version
|
from importlib_metadata import version
|
||||||
from rich.console import Console, ConsoleDimensions
|
from rich.console import Console, ConsoleDimensions
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ from typing import Iterable, Sequence
|
|||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from textual.css._error_tools import friendly_list
|
||||||
|
from textual.css.scalar import SYMBOL_UNIT
|
||||||
|
|
||||||
from ..color import ColorParseError
|
from ..color import ColorParseError
|
||||||
from ._help_renderables import Example, Bullet, HelpText
|
from ._help_renderables import Bullet, Example, HelpText
|
||||||
from .constants import (
|
from .constants import (
|
||||||
VALID_BORDER,
|
|
||||||
VALID_LAYOUT,
|
|
||||||
VALID_ALIGN_HORIZONTAL,
|
VALID_ALIGN_HORIZONTAL,
|
||||||
VALID_ALIGN_VERTICAL,
|
VALID_ALIGN_VERTICAL,
|
||||||
|
VALID_BORDER,
|
||||||
|
VALID_LAYOUT,
|
||||||
VALID_STYLE_FLAGS,
|
VALID_STYLE_FLAGS,
|
||||||
VALID_TEXT_ALIGN,
|
VALID_TEXT_ALIGN,
|
||||||
)
|
)
|
||||||
|
|
||||||
from textual.css._error_tools import friendly_list
|
|
||||||
from textual.css.scalar import SYMBOL_UNIT
|
|
||||||
|
|
||||||
StylingContext = Literal["inline", "css"]
|
StylingContext = Literal["inline", "css"]
|
||||||
"""The type of styling the user was using when the error was encountered.
|
"""The type of styling the user was using when the error was encountered.
|
||||||
Used to give help text specific to the context i.e. we give CSS help if the
|
Used to give help text specific to the context i.e. we give CSS help if the
|
||||||
|
|||||||
@@ -10,14 +10,7 @@ when setting and getting.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import (
|
from typing import TYPE_CHECKING, Generic, Iterable, NamedTuple, TypeVar, cast
|
||||||
TYPE_CHECKING,
|
|
||||||
Generic,
|
|
||||||
Iterable,
|
|
||||||
NamedTuple,
|
|
||||||
TypeVar,
|
|
||||||
cast,
|
|
||||||
)
|
|
||||||
|
|
||||||
import rich.errors
|
import rich.errors
|
||||||
import rich.repr
|
import rich.repr
|
||||||
@@ -611,11 +604,8 @@ class LayoutProperty:
|
|||||||
or a ``Layout`` object.
|
or a ``Layout`` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..layouts.factory import (
|
from ..layouts.factory import Layout # Prevents circular import
|
||||||
Layout, # Prevents circular import
|
from ..layouts.factory import MissingLayout, get_layout
|
||||||
MissingLayout,
|
|
||||||
get_layout,
|
|
||||||
)
|
|
||||||
|
|
||||||
_rich_traceback_omit = True
|
_rich_traceback_omit = True
|
||||||
if layout is None:
|
if layout is None:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from ..geometry import Spacing
|
from ..geometry import Spacing
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderResult
|
||||||
from rich.traceback import Traceback
|
from rich.traceback import Traceback
|
||||||
|
|
||||||
from ._help_renderables import HelpText
|
from ._help_renderables import HelpText
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Iterable
|
||||||
from .model import CombinatorType, Selector, SelectorSet
|
|
||||||
|
|
||||||
|
from .model import CombinatorType, Selector, SelectorSet
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..dom import DOMNode
|
from ..dom import DOMNode
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
import rich.repr
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Iterable, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Iterable
|
||||||
|
|
||||||
|
import rich.repr
|
||||||
|
|
||||||
from .styles import Styles
|
from .styles import Styles
|
||||||
from .tokenize import Token
|
from .tokenize import Token
|
||||||
|
|||||||
@@ -2,23 +2,23 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
from typing import Iterator, Iterable, NoReturn
|
from typing import Iterable, Iterator, NoReturn
|
||||||
|
|
||||||
|
from ..suggestions import get_suggestion
|
||||||
|
from ._styles_builder import DeclarationError, StylesBuilder
|
||||||
from .errors import UnresolvedVariableError
|
from .errors import UnresolvedVariableError
|
||||||
from .types import Specificity3
|
|
||||||
from ._styles_builder import StylesBuilder, DeclarationError
|
|
||||||
from .model import (
|
from .model import (
|
||||||
|
CombinatorType,
|
||||||
Declaration,
|
Declaration,
|
||||||
RuleSet,
|
RuleSet,
|
||||||
Selector,
|
Selector,
|
||||||
CombinatorType,
|
|
||||||
SelectorSet,
|
SelectorSet,
|
||||||
SelectorType,
|
SelectorType,
|
||||||
)
|
)
|
||||||
from .styles import Styles
|
from .styles import Styles
|
||||||
from ..suggestions import get_suggestion
|
from .tokenize import Token, tokenize, tokenize_declarations, tokenize_values
|
||||||
from .tokenize import tokenize, tokenize_declarations, Token, tokenize_values
|
|
||||||
from .tokenizer import EOFError, ReferencedBy
|
from .tokenizer import EOFError, ReferencedBy
|
||||||
|
from .types import Specificity3
|
||||||
|
|
||||||
SELECTOR_MAP: dict[str, tuple[SelectorType, Specificity3]] = {
|
SELECTOR_MAP: dict[str, tuple[SelectorType, Specificity3]] = {
|
||||||
"selector": (SelectorType.TYPE, (0, 0, 1)),
|
"selector": (SelectorType.TYPE, (0, 0, 1)),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
from enum import Enum, unique
|
from enum import Enum, unique
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
import re
|
|
||||||
from typing import Iterable, NamedTuple
|
from typing import Iterable, NamedTuple
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from .scalar import ScalarOffset, Scalar
|
from .._animator import Animation, EasingFunction
|
||||||
from .._animator import Animation
|
|
||||||
from .._animator import EasingFunction
|
|
||||||
from .._types import CallbackType
|
from .._types import CallbackType
|
||||||
|
from .scalar import Scalar, ScalarOffset
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..dom import DOMNode
|
from ..dom import DOMNode
|
||||||
|
|
||||||
from .styles import StylesBase
|
from .styles import StylesBase
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ from abc import ABC, abstractmethod
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Iterable, cast
|
from typing import TYPE_CHECKING, Any, Iterable, NamedTuple, cast
|
||||||
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import re
|
|||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from textual.css.tokenizer import Expect, Tokenizer, Token
|
from textual.css.tokenizer import Expect, Token, Tokenizer
|
||||||
|
|
||||||
PERCENT = r"-?\d+\.?\d*%"
|
PERCENT = r"-?\d+\.?\d*%"
|
||||||
DECIMAL = r"-?\d+\.?\d*"
|
DECIMAL = r"-?\d+\.?\d*"
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import re
|
|||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
import rich.repr
|
||||||
from rich.console import Group, RenderableType
|
from rich.console import Group, RenderableType
|
||||||
from rich.highlighter import ReprHighlighter
|
from rich.highlighter import ReprHighlighter
|
||||||
from rich.padding import Padding
|
from rich.padding import Padding
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
import rich.repr
|
|
||||||
from rich.syntax import Syntax
|
from rich.syntax import Syntax
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from ..color import Color
|
from ..color import Color
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ DarkSwitch .label {
|
|||||||
color: $text-muted;
|
color: $text-muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
DarkSwitch Checkbox {
|
DarkSwitch Switch {
|
||||||
background: $boost;
|
background: $boost;
|
||||||
dock: left;
|
dock: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from importlib_metadata import version
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from importlib_metadata import version
|
||||||
from rich import box
|
from rich import box
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
from rich.json import JSON
|
from rich.json import JSON
|
||||||
@@ -15,15 +15,15 @@ from rich.text import Text
|
|||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.binding import Binding
|
from textual.binding import Binding
|
||||||
from textual.containers import Container, Horizontal
|
from textual.containers import Container, Horizontal
|
||||||
from textual.reactive import reactive, watch
|
from textual.reactive import reactive
|
||||||
from textual.widgets import (
|
from textual.widgets import (
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
|
||||||
DataTable,
|
DataTable,
|
||||||
Footer,
|
Footer,
|
||||||
Header,
|
Header,
|
||||||
Input,
|
Input,
|
||||||
Static,
|
Static,
|
||||||
|
Switch,
|
||||||
TextLog,
|
TextLog,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ Build your own or use the builtin widgets.
|
|||||||
|
|
||||||
- **Input** Text / Password input.
|
- **Input** Text / Password input.
|
||||||
- **Button** Clickable button with a number of styles.
|
- **Button** Clickable button with a number of styles.
|
||||||
- **Checkbox** A checkbox to toggle between states.
|
- **Switch** A switch to toggle between states.
|
||||||
- **DataTable** A spreadsheet-like widget for navigating data. Cells may contain text or Rich renderables.
|
- **DataTable** A spreadsheet-like widget for navigating data. Cells may contain text or Rich renderables.
|
||||||
- **Tree** An generic tree with expandable nodes.
|
- **Tree** An generic tree with expandable nodes.
|
||||||
- **DirectoryTree** A tree of file and folders.
|
- **DirectoryTree** A tree of file and folders.
|
||||||
@@ -199,16 +199,16 @@ class Title(Static):
|
|||||||
|
|
||||||
class DarkSwitch(Horizontal):
|
class DarkSwitch(Horizontal):
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Checkbox(value=self.app.dark)
|
yield Switch(value=self.app.dark)
|
||||||
yield Static("Dark mode toggle", classes="label")
|
yield Static("Dark mode toggle", classes="label")
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
watch(self.app, "dark", self.on_dark_change, init=False)
|
self.watch(self.app, "dark", self.on_dark_change, init=False)
|
||||||
|
|
||||||
def on_dark_change(self, dark: bool) -> None:
|
def on_dark_change(self, dark: bool) -> None:
|
||||||
self.query_one(Checkbox).value = self.app.dark
|
self.query_one(Switch).value = self.app.dark
|
||||||
|
|
||||||
def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
|
def on_switch_changed(self, event: Switch.Changed) -> None:
|
||||||
self.app.dark = event.value
|
self.app.dark = event.value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from rich.padding import Padding
|
|||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from .color import Color, WHITE
|
from .color import WHITE, Color
|
||||||
|
|
||||||
|
|
||||||
NUMBER_OF_SHADES = 3
|
NUMBER_OF_SHADES = 3
|
||||||
|
|
||||||
|
|||||||
@@ -9,21 +9,14 @@ from io import StringIO
|
|||||||
from time import time
|
from time import time
|
||||||
from typing import Any, NamedTuple, Type
|
from typing import Any, NamedTuple, Type
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
import msgpack
|
||||||
|
from aiohttp import ClientConnectorError, ClientResponseError, ClientWebSocketResponse
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
|
|
||||||
from .._log import LogGroup, LogVerbosity
|
from .._log import LogGroup, LogVerbosity
|
||||||
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import msgpack
|
|
||||||
from aiohttp import (
|
|
||||||
ClientConnectorError,
|
|
||||||
ClientResponseError,
|
|
||||||
ClientWebSocketResponse,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
DEVTOOLS_PORT = 8081
|
DEVTOOLS_PORT = 8081
|
||||||
WEBSOCKET_CONNECT_TIMEOUT = 3
|
WEBSOCKET_CONNECT_TIMEOUT = 3
|
||||||
LOG_QUEUE_MAXSIZE = 512
|
LOG_QUEUE_MAXSIZE = 512
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
from .client import DevtoolsLog
|
|
||||||
from .._log import LogGroup, LogVerbosity
|
from .._log import LogGroup, LogVerbosity
|
||||||
|
from .client import DevtoolsLog
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .client import DevtoolsClient
|
from .client import DevtoolsClient
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from aiohttp.web import run_app
|
from aiohttp.web import run_app
|
||||||
from aiohttp.web_app import Application
|
from aiohttp.web_app import Application
|
||||||
from aiohttp.web_request import Request
|
from aiohttp.web_request import Request
|
||||||
|
|||||||
@@ -7,19 +7,19 @@ import pickle
|
|||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
|
import msgpack
|
||||||
from aiohttp import WSMessage, WSMsgType
|
from aiohttp import WSMessage, WSMsgType
|
||||||
from aiohttp.abc import Request
|
from aiohttp.abc import Request
|
||||||
from aiohttp.web_ws import WebSocketResponse
|
from aiohttp.web_ws import WebSocketResponse
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markup import escape
|
from rich.markup import escape
|
||||||
import msgpack
|
|
||||||
|
|
||||||
from textual._log import LogGroup
|
from textual._log import LogGroup
|
||||||
from textual._time import time
|
from textual._time import time
|
||||||
from textual.devtools.renderables import (
|
from textual.devtools.renderables import (
|
||||||
|
DevConsoleHeader,
|
||||||
DevConsoleLog,
|
DevConsoleLog,
|
||||||
DevConsoleNotice,
|
DevConsoleNotice,
|
||||||
DevConsoleHeader,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
QUEUEABLE_TYPES = {"client_log", "client_spillover"}
|
QUEUEABLE_TYPES = {"client_log", "client_spillover"}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from rich.tree import Tree
|
|||||||
|
|
||||||
from ._context import NoActiveAppError
|
from ._context import NoActiveAppError
|
||||||
from ._node_list import NodeList
|
from ._node_list import NodeList
|
||||||
|
from ._types import CallbackType
|
||||||
from .binding import Bindings, BindingType
|
from .binding import Bindings, BindingType
|
||||||
from .color import BLACK, WHITE, Color
|
from .color import BLACK, WHITE, Color
|
||||||
from .css._error_tools import friendly_list
|
from .css._error_tools import friendly_list
|
||||||
@@ -31,7 +32,7 @@ from .css.parse import parse_declarations
|
|||||||
from .css.styles import RenderStyles, Styles
|
from .css.styles import RenderStyles, Styles
|
||||||
from .css.tokenize import IDENTIFIER
|
from .css.tokenize import IDENTIFIER
|
||||||
from .message_pump import MessagePump
|
from .message_pump import MessagePump
|
||||||
from .reactive import Reactive
|
from .reactive import Reactive, _watch
|
||||||
from .timer import Timer
|
from .timer import Timer
|
||||||
from .walk import walk_breadth_first, walk_depth_first
|
from .walk import walk_breadth_first, walk_depth_first
|
||||||
|
|
||||||
@@ -210,6 +211,10 @@ class DOMNode(MessagePump):
|
|||||||
styles = self._component_styles[name]
|
styles = self._component_styles[name]
|
||||||
return styles
|
return styles
|
||||||
|
|
||||||
|
def _post_mount(self):
|
||||||
|
"""Called after the object has been mounted."""
|
||||||
|
Reactive._initialize_object(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _node_bases(self) -> Iterator[Type[DOMNode]]:
|
def _node_bases(self) -> Iterator[Type[DOMNode]]:
|
||||||
"""Iterator[Type[DOMNode]]: The DOMNode bases classes (including self.__class__)"""
|
"""Iterator[Type[DOMNode]]: The DOMNode bases classes (including self.__class__)"""
|
||||||
@@ -643,6 +648,23 @@ class DOMNode(MessagePump):
|
|||||||
"""
|
"""
|
||||||
return [child for child in self.children if child.display]
|
return [child for child in self.children if child.display]
|
||||||
|
|
||||||
|
def watch(
|
||||||
|
self,
|
||||||
|
obj: DOMNode,
|
||||||
|
attribute_name: str,
|
||||||
|
callback: CallbackType,
|
||||||
|
init: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""Watches for modifications to reactive attributes on another object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj: Object containing attribute to watch.
|
||||||
|
attribute_name: Attribute to watch.
|
||||||
|
callback: A callback to run when attribute changes.
|
||||||
|
init: Check watchers on first call.
|
||||||
|
"""
|
||||||
|
_watch(self, obj, attribute_name, callback, init=init)
|
||||||
|
|
||||||
def get_pseudo_classes(self) -> Iterable[str]:
|
def get_pseudo_classes(self) -> Iterable[str]:
|
||||||
"""Get any pseudo classes applicable to this Node, e.g. hover, focus.
|
"""Get any pseudo classes applicable to this Node, e.g. hover, focus.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from .. import events
|
||||||
from ..driver import Driver
|
from ..driver import Driver
|
||||||
from ..geometry import Size
|
from ..geometry import Size
|
||||||
from .. import events
|
|
||||||
|
|
||||||
|
|
||||||
class HeadlessDriver(Driver):
|
class HeadlessDriver(Driver):
|
||||||
|
|||||||
@@ -2,27 +2,26 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from codecs import getincrementaldecoder
|
|
||||||
import selectors
|
import selectors
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import termios
|
import termios
|
||||||
import tty
|
import tty
|
||||||
from typing import Any, TYPE_CHECKING
|
from codecs import getincrementaldecoder
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|
||||||
from .. import log
|
from .. import events, log
|
||||||
from ..driver import Driver
|
from .._profile import timer
|
||||||
from ..geometry import Size
|
|
||||||
from .._types import MessageTarget
|
from .._types import MessageTarget
|
||||||
from .._xterm_parser import XTermParser
|
from .._xterm_parser import XTermParser
|
||||||
from .._profile import timer
|
from ..driver import Driver
|
||||||
from .. import events
|
from ..geometry import Size
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter, itemgetter
|
||||||
from typing import (
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
Collection,
|
Collection,
|
||||||
NamedTuple,
|
NamedTuple,
|
||||||
@@ -16,7 +17,6 @@ from typing import (
|
|||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
Union,
|
||||||
cast,
|
cast,
|
||||||
TYPE_CHECKING,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from .._layout import Layout
|
from .._layout import Layout
|
||||||
from .horizontal import HorizontalLayout
|
|
||||||
from .grid import GridLayout
|
from .grid import GridLayout
|
||||||
|
from .horizontal import HorizontalLayout
|
||||||
from .vertical import VerticalLayout
|
from .vertical import VerticalLayout
|
||||||
|
|
||||||
LAYOUT_MAP: dict[str, type[Layout]] = {
|
LAYOUT_MAP: dict[str, type[Layout]] = {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
|
||||||
from .._resolve import resolve_box_models
|
|
||||||
from ..geometry import Size, Region
|
|
||||||
from .._layout import ArrangeResult, Layout, WidgetPlacement
|
from .._layout import ArrangeResult, Layout, WidgetPlacement
|
||||||
|
from .._resolve import resolve_box_models
|
||||||
|
from ..geometry import Region, Size
|
||||||
from ..widget import Widget
|
from ..widget import Widget
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from .._layout import ArrangeResult, Layout, WidgetPlacement
|
||||||
from .._resolve import resolve_box_models
|
from .._resolve import resolve_box_models
|
||||||
from ..geometry import Region, Size
|
from ..geometry import Region, Size
|
||||||
from .._layout import ArrangeResult, Layout, WidgetPlacement
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..widget import Widget
|
from ..widget import Widget
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import ClassVar, TYPE_CHECKING
|
from typing import TYPE_CHECKING, ClassVar
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|
||||||
from . import _clock
|
from . import _clock
|
||||||
from .case import camel_to_snake
|
|
||||||
from ._types import MessageTarget as MessageTarget
|
from ._types import MessageTarget as MessageTarget
|
||||||
|
from .case import camel_to_snake
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .message_pump import MessagePump
|
from .message_pump import MessagePump
|
||||||
|
from .widget import Widget
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
|
|||||||
@@ -123,6 +123,19 @@ class MessagePump(metaclass=MessagePumpMeta):
|
|||||||
"""
|
"""
|
||||||
return self.app._logger
|
return self.app._logger
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_attached(self) -> bool:
|
||||||
|
"""Is the node is attached to the app via the DOM."""
|
||||||
|
from .app import App
|
||||||
|
|
||||||
|
node = self
|
||||||
|
|
||||||
|
while not isinstance(node, App):
|
||||||
|
if node._parent is None:
|
||||||
|
return False
|
||||||
|
node = node._parent
|
||||||
|
return True
|
||||||
|
|
||||||
def _attach(self, parent: MessagePump) -> None:
|
def _attach(self, parent: MessagePump) -> None:
|
||||||
"""Set the parent, and therefore attach this node to the tree.
|
"""Set the parent, and therefore attach this node to the tree.
|
||||||
|
|
||||||
@@ -358,7 +371,10 @@ class MessagePump(metaclass=MessagePumpMeta):
|
|||||||
finally:
|
finally:
|
||||||
# This is critical, mount may be waiting
|
# This is critical, mount may be waiting
|
||||||
self._mounted_event.set()
|
self._mounted_event.set()
|
||||||
Reactive._initialize_object(self)
|
self._post_mount()
|
||||||
|
|
||||||
|
def _post_mount(self):
|
||||||
|
"""Called after the object has been mounted."""
|
||||||
|
|
||||||
async def _process_messages_loop(self) -> None:
|
async def _process_messages_loop(self) -> None:
|
||||||
"""Process messages until the queue is closed."""
|
"""Process messages until the queue is closed."""
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|
||||||
from .geometry import Region
|
|
||||||
from ._types import CallbackType
|
from ._types import CallbackType
|
||||||
|
from .geometry import Region
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .message_pump import MessagePump
|
from .message_pump import MessagePump
|
||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import rich.repr
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Generic
|
from typing import Generic
|
||||||
|
|
||||||
from .app import App, ReturnType
|
import rich.repr
|
||||||
|
|
||||||
from ._wait import wait_for_idle
|
from ._wait import wait_for_idle
|
||||||
|
from .app import App, ReturnType
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto(angular=True)
|
@rich.repr.auto(angular=True)
|
||||||
@@ -53,6 +53,7 @@ class Pilot(Generic[ReturnType]):
|
|||||||
async def wait_for_scheduled_animations(self) -> None:
|
async def wait_for_scheduled_animations(self) -> None:
|
||||||
"""Wait for any current and scheduled animations to complete."""
|
"""Wait for any current and scheduled animations to complete."""
|
||||||
await self._app.animator.wait_until_complete()
|
await self._app.animator.wait_until_complete()
|
||||||
|
await wait_for_idle(0)
|
||||||
|
|
||||||
async def exit(self, result: ReturnType) -> None:
|
async def exit(self, result: ReturnType) -> None:
|
||||||
"""Exit the app with the given result.
|
"""Exit the app with the given result.
|
||||||
|
|||||||
@@ -7,36 +7,26 @@ from typing import (
|
|||||||
Any,
|
Any,
|
||||||
Awaitable,
|
Awaitable,
|
||||||
Callable,
|
Callable,
|
||||||
|
ClassVar,
|
||||||
Generic,
|
Generic,
|
||||||
Type,
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
|
|
||||||
from . import events
|
from . import events
|
||||||
from ._callback import count_parameters, invoke
|
from ._callback import count_parameters
|
||||||
from ._types import MessageTarget
|
from ._types import CallbackType, MessageTarget
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .app import App
|
from .dom import DOMNode
|
||||||
from .widget import Widget
|
|
||||||
|
|
||||||
Reactable = Union[Widget, App]
|
Reactable = DOMNode
|
||||||
|
|
||||||
ReactiveType = TypeVar("ReactiveType")
|
ReactiveType = TypeVar("ReactiveType")
|
||||||
|
|
||||||
|
|
||||||
class _NotSet:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
_NOT_SET = _NotSet()
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
class Reactive(Generic[ReactiveType]):
|
class Reactive(Generic[ReactiveType]):
|
||||||
"""Reactive descriptor.
|
"""Reactive descriptor.
|
||||||
@@ -50,7 +40,7 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
compute: Run compute methods when attribute is changed. Defaults to True.
|
compute: Run compute methods when attribute is changed. Defaults to True.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_reactives: TypeVar[dict[str, object]] = {}
|
_reactives: ClassVar[dict[str, object]] = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -77,37 +67,6 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
yield "always_update", self._always_update
|
yield "always_update", self._always_update
|
||||||
yield "compute", self._run_compute
|
yield "compute", self._run_compute
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def init(
|
|
||||||
cls,
|
|
||||||
default: ReactiveType | Callable[[], ReactiveType],
|
|
||||||
*,
|
|
||||||
layout: bool = False,
|
|
||||||
repaint: bool = True,
|
|
||||||
always_update: bool = False,
|
|
||||||
compute: bool = True,
|
|
||||||
) -> Reactive:
|
|
||||||
"""A reactive variable that calls watchers and compute on initialize (post mount).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
default: A default value or callable that returns a default.
|
|
||||||
layout: Perform a layout on change. Defaults to False.
|
|
||||||
repaint: Perform a repaint on change. Defaults to True.
|
|
||||||
always_update: Call watchers even when the new value equals the old value. Defaults to False.
|
|
||||||
compute: Run compute methods when attribute is changed. Defaults to True.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A Reactive instance which calls watchers or initialize.
|
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
default,
|
|
||||||
layout=layout,
|
|
||||||
repaint=repaint,
|
|
||||||
init=True,
|
|
||||||
always_update=always_update,
|
|
||||||
compute=compute,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def var(
|
def var(
|
||||||
cls,
|
cls,
|
||||||
@@ -254,7 +213,7 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
|
|
||||||
def invoke_watcher(
|
def invoke_watcher(
|
||||||
watch_function: Callable, old_value: object, value: object
|
watch_function: Callable, old_value: object, value: object
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""Invoke a watch function.
|
"""Invoke a watch function.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -262,8 +221,6 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
old_value: The old value of the attribute.
|
old_value: The old value of the attribute.
|
||||||
value: The new value of the attribute.
|
value: The new value of the attribute.
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if the watcher was run, or False if it was posted.
|
|
||||||
"""
|
"""
|
||||||
_rich_traceback_omit = True
|
_rich_traceback_omit = True
|
||||||
param_count = count_parameters(watch_function)
|
param_count = count_parameters(watch_function)
|
||||||
@@ -280,17 +237,23 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
sender=obj, callback=partial(await_watcher, watch_result)
|
sender=obj, callback=partial(await_watcher, watch_result)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
watch_function = getattr(obj, f"watch_{name}", None)
|
watch_function = getattr(obj, f"watch_{name}", None)
|
||||||
if callable(watch_function):
|
if callable(watch_function):
|
||||||
invoke_watcher(watch_function, old_value, value)
|
invoke_watcher(watch_function, old_value, value)
|
||||||
|
|
||||||
watchers: list[Callable] = getattr(obj, "__watchers", {}).get(name, [])
|
# Process "global" watchers
|
||||||
for watcher in watchers:
|
watchers: list[tuple[Reactable, Callable]]
|
||||||
invoke_watcher(watcher, old_value, value)
|
watchers = getattr(obj, "__watchers", {}).get(name, [])
|
||||||
|
# Remove any watchers for reactables that have since closed
|
||||||
|
if watchers:
|
||||||
|
watchers[:] = [
|
||||||
|
(reactable, callback)
|
||||||
|
for reactable, callback in watchers
|
||||||
|
if reactable.is_attached and not reactable._closing
|
||||||
|
]
|
||||||
|
for _, callback in watchers:
|
||||||
|
invoke_watcher(callback, old_value, value)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _compute(cls, obj: Reactable) -> None:
|
def _compute(cls, obj: Reactable) -> None:
|
||||||
@@ -362,10 +325,12 @@ class var(Reactive[ReactiveType]):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def watch(
|
def _watch(
|
||||||
|
node: DOMNode,
|
||||||
obj: Reactable,
|
obj: Reactable,
|
||||||
attribute_name: str,
|
attribute_name: str,
|
||||||
callback: Callable[[Any], object],
|
callback: CallbackType,
|
||||||
|
*,
|
||||||
init: bool = True,
|
init: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Watch a reactive variable on an object.
|
"""Watch a reactive variable on an object.
|
||||||
@@ -379,11 +344,11 @@ def watch(
|
|||||||
|
|
||||||
if not hasattr(obj, "__watchers"):
|
if not hasattr(obj, "__watchers"):
|
||||||
setattr(obj, "__watchers", {})
|
setattr(obj, "__watchers", {})
|
||||||
watchers: dict[str, list[Callable]] = getattr(obj, "__watchers")
|
watchers: dict[str, list[tuple[Reactable, Callable]]] = getattr(obj, "__watchers")
|
||||||
watcher_list = watchers.setdefault(attribute_name, [])
|
watcher_list = watchers.setdefault(attribute_name, [])
|
||||||
if callback in watcher_list:
|
if callback in watcher_list:
|
||||||
return
|
return
|
||||||
watcher_list.append(callback)
|
watcher_list.append((node, callback))
|
||||||
if init:
|
if init:
|
||||||
current_value = getattr(obj, attribute_name, None)
|
current_value = getattr(obj, attribute_name, None)
|
||||||
Reactive._check_watchers(obj, attribute_name, current_value)
|
Reactive._check_watchers(obj, attribute_name, current_value)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderResult
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderResult
|
||||||
|
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import statistics
|
import statistics
|
||||||
from typing import Generic, Sequence, Iterable, Callable, TypeVar
|
from typing import Callable, Generic, Iterable, Sequence, TypeVar
|
||||||
|
|
||||||
from rich.color import Color
|
from rich.color import Color
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderResult
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Iterable, Tuple, cast
|
|||||||
|
|
||||||
from rich.cells import cell_len
|
from rich.cells import cell_len
|
||||||
from rich.color import Color
|
from rich.color import Color
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult, RenderableType
|
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult, RenderableType
|
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import ConsoleOptions, Console, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderResult
|
||||||
from rich.style import StyleType
|
from rich.style import StyleType
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
@@ -99,6 +99,7 @@ class UnderlineBar:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import random
|
import random
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from rich.color import ANSI_COLOR_NAMES
|
from rich.color import ANSI_COLOR_NAMES
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Iterator, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Iterable, Iterator
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
@@ -9,14 +9,14 @@ from rich.style import Style
|
|||||||
from . import errors, events, messages
|
from . import errors, events, messages
|
||||||
from ._callback import invoke
|
from ._callback import invoke
|
||||||
from ._compositor import Compositor, MapGeometry
|
from ._compositor import Compositor, MapGeometry
|
||||||
|
from ._types import CallbackType
|
||||||
from .css.match import match
|
from .css.match import match
|
||||||
from .css.parse import parse_selectors
|
from .css.parse import parse_selectors
|
||||||
from .dom import DOMNode
|
from .dom import DOMNode
|
||||||
from .timer import Timer
|
|
||||||
from ._types import CallbackType
|
|
||||||
from .geometry import Offset, Region, Size
|
from .geometry import Offset, Region, Size
|
||||||
from .reactive import Reactive
|
from .reactive import Reactive
|
||||||
from .renderables.blank import Blank
|
from .renderables.blank import Blank
|
||||||
|
from .timer import Timer
|
||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import Iterable, Iterator, TypeVar, overload, TYPE_CHECKING
|
from typing import TYPE_CHECKING, Iterable, Iterator, TypeVar, overload
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from textual.dom import DOMNode
|
from textual.dom import DOMNode
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ from rich.text import Text
|
|||||||
from rich.traceback import Traceback
|
from rich.traceback import Traceback
|
||||||
|
|
||||||
from . import errors, events, messages
|
from . import errors, events, messages
|
||||||
from ._asyncio import create_task
|
|
||||||
from ._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction
|
from ._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction
|
||||||
from ._arrange import DockArrangeResult, arrange
|
from ._arrange import DockArrangeResult, arrange
|
||||||
|
from ._asyncio import create_task
|
||||||
from ._context import active_app
|
from ._context import active_app
|
||||||
from ._easing import DEFAULT_SCROLL_EASING
|
from ._easing import DEFAULT_SCROLL_EASING
|
||||||
from ._layout import Layout
|
from ._layout import Layout
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from importlib import import_module
|
|
||||||
import typing
|
import typing
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
from ..case import camel_to_snake
|
from ..case import camel_to_snake
|
||||||
|
|
||||||
@@ -8,8 +9,8 @@ from ..case import camel_to_snake
|
|||||||
# but also to the `__init__.pyi` file in this same folder - otherwise text editors and type checkers won't
|
# but also to the `__init__.pyi` file in this same folder - otherwise text editors and type checkers won't
|
||||||
# be able to "see" them.
|
# be able to "see" them.
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
|
from ..widget import Widget
|
||||||
from ._button import Button
|
from ._button import Button
|
||||||
from ._checkbox import Checkbox
|
|
||||||
from ._data_table import DataTable
|
from ._data_table import DataTable
|
||||||
from ._directory_tree import DirectoryTree
|
from ._directory_tree import DirectoryTree
|
||||||
from ._footer import Footer
|
from ._footer import Footer
|
||||||
@@ -21,15 +22,15 @@ if typing.TYPE_CHECKING:
|
|||||||
from ._placeholder import Placeholder
|
from ._placeholder import Placeholder
|
||||||
from ._pretty import Pretty
|
from ._pretty import Pretty
|
||||||
from ._static import Static
|
from ._static import Static
|
||||||
|
from ._switch import Switch
|
||||||
from ._text_log import TextLog
|
from ._text_log import TextLog
|
||||||
from ._tree import Tree
|
from ._tree import Tree
|
||||||
from ._welcome import Welcome
|
from ._welcome import Welcome
|
||||||
from ..widget import Widget
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Button",
|
"Button",
|
||||||
"Checkbox",
|
"Switch",
|
||||||
"DataTable",
|
"DataTable",
|
||||||
"DirectoryTree",
|
"DirectoryTree",
|
||||||
"Footer",
|
"Footer",
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
|
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
|
||||||
from ._button import Button as Button
|
from ._button import Button as Button
|
||||||
from ._data_table import DataTable as DataTable
|
from ._data_table import DataTable as DataTable
|
||||||
from ._checkbox import Checkbox as Checkbox
|
|
||||||
from ._directory_tree import DirectoryTree as DirectoryTree
|
from ._directory_tree import DirectoryTree as DirectoryTree
|
||||||
from ._footer import Footer as Footer
|
from ._footer import Footer as Footer
|
||||||
from ._header import Header as Header
|
from ._header import Header as Header
|
||||||
|
from ._input import Input as Input
|
||||||
from ._label import Label as Label
|
from ._label import Label as Label
|
||||||
from ._list_view import ListView as ListView
|
|
||||||
from ._list_item import ListItem as ListItem
|
from ._list_item import ListItem as ListItem
|
||||||
|
from ._list_view import ListView as ListView
|
||||||
from ._placeholder import Placeholder as Placeholder
|
from ._placeholder import Placeholder as Placeholder
|
||||||
from ._pretty import Pretty as Pretty
|
from ._pretty import Pretty as Pretty
|
||||||
from ._static import Static as Static
|
from ._static import Static as Static
|
||||||
from ._input import Input as Input
|
from ._switch import Switch as Switch
|
||||||
from ._text_log import TextLog as TextLog
|
from ._text_log import TextLog as TextLog
|
||||||
from ._tree import Tree as Tree
|
from ._tree import Tree as Tree
|
||||||
from ._tree_node import TreeNode as TreeNode
|
|
||||||
from ._welcome import Welcome as Welcome
|
from ._welcome import Welcome as Welcome
|
||||||
|
|||||||
@@ -2,19 +2,18 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing_extensions import Literal
|
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
from rich.text import Text, TextType
|
from rich.text import Text, TextType
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from .. import events
|
from .. import events
|
||||||
from ..css._error_tools import friendly_list
|
from ..css._error_tools import friendly_list
|
||||||
from ..message import Message
|
from ..message import Message
|
||||||
from ..reactive import Reactive
|
from ..reactive import reactive
|
||||||
from ..widgets import Static
|
from ..widgets import Static
|
||||||
|
|
||||||
|
|
||||||
ButtonVariant = Literal["default", "primary", "success", "warning", "error"]
|
ButtonVariant = Literal["default", "primary", "success", "warning", "error"]
|
||||||
_VALID_BUTTON_VARIANTS = {"default", "primary", "success", "warning", "error"}
|
_VALID_BUTTON_VARIANTS = {"default", "primary", "success", "warning", "error"}
|
||||||
|
|
||||||
@@ -151,13 +150,13 @@ class Button(Static, can_focus=True):
|
|||||||
ACTIVE_EFFECT_DURATION = 0.3
|
ACTIVE_EFFECT_DURATION = 0.3
|
||||||
"""When buttons are clicked they get the `-active` class for this duration (in seconds)"""
|
"""When buttons are clicked they get the `-active` class for this duration (in seconds)"""
|
||||||
|
|
||||||
label: Reactive[RenderableType] = Reactive("")
|
label: reactive[RenderableType] = reactive("")
|
||||||
"""The text label that appears within the button."""
|
"""The text label that appears within the button."""
|
||||||
|
|
||||||
variant = Reactive.init("default")
|
variant = reactive("default")
|
||||||
"""The variant name for the button."""
|
"""The variant name for the button."""
|
||||||
|
|
||||||
disabled = Reactive(False)
|
disabled = reactive(False)
|
||||||
"""The disabled state of the button; `True` if disabled, `False` if not."""
|
"""The disabled state of the button; `True` if disabled, `False` if not."""
|
||||||
|
|
||||||
class Pressed(Message, bubble=True):
|
class Pressed(Message, bubble=True):
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from rich.style import Style
|
|||||||
from rich.text import Text, TextType
|
from rich.text import Text, TextType
|
||||||
from typing_extensions import Literal, TypeAlias
|
from typing_extensions import Literal, TypeAlias
|
||||||
|
|
||||||
from .. import events, messages
|
from .. import events
|
||||||
from .._cache import LRUCache
|
from .._cache import LRUCache
|
||||||
from .._segment_tools import line_crop
|
from .._segment_tools import line_crop
|
||||||
from .._two_way_dict import TwoWayDict
|
from .._two_way_dict import TwoWayDict
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from typing import ClassVar
|
|||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
from rich.text import Text, TextType
|
from rich.text import Text, TextType
|
||||||
|
|
||||||
from ..message import Message
|
|
||||||
from ._tree import Tree, TreeNode, TOGGLE_STYLE
|
|
||||||
from .._types import MessageTarget
|
from .._types import MessageTarget
|
||||||
|
from ..message import Message
|
||||||
|
from ._tree import TOGGLE_STYLE, Tree, TreeNode
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from rich.console import RenderableType
|
|||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from .. import events
|
from .. import events
|
||||||
from ..reactive import Reactive, watch
|
from ..reactive import Reactive
|
||||||
from ..widget import Widget
|
from ..widget import Widget
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class Footer(Widget):
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
watch(self.screen, "focused", self._focus_changed)
|
self.watch(self.screen, "focused", self._focus_changed)
|
||||||
|
|
||||||
def _focus_changed(self, focused: Widget | None) -> None:
|
def _focus_changed(self, focused: Widget | None) -> None:
|
||||||
self._key_text = None
|
self._key_text = None
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from datetime import datetime
|
|||||||
|
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
|
from ..reactive import Reactive
|
||||||
from ..widget import Widget
|
from ..widget import Widget
|
||||||
from ..reactive import Reactive, watch
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderIcon(Widget):
|
class HeaderIcon(Widget):
|
||||||
@@ -133,5 +133,5 @@ class Header(Widget):
|
|||||||
def set_sub_title(sub_title: str) -> None:
|
def set_sub_title(sub_title: str) -> None:
|
||||||
self.query_one(HeaderTitle).sub_text = sub_title
|
self.query_one(HeaderTitle).sub_text = sub_title
|
||||||
|
|
||||||
watch(self.app, "title", set_title)
|
self.watch(self.app, "title", set_title)
|
||||||
watch(self.app, "sub_title", set_sub_title)
|
self.watch(self.app, "sub_title", set_sub_title)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import ClassVar
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
from rich.cells import cell_len, get_character_cell_size
|
from rich.cells import cell_len, get_character_cell_size
|
||||||
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
|
|
||||||
from textual.await_remove import AwaitRemove
|
from textual.await_remove import AwaitRemove
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from typing_extensions import Literal
|
|||||||
from .. import events
|
from .. import events
|
||||||
from ..css._error_tools import friendly_list
|
from ..css._error_tools import friendly_list
|
||||||
from ..reactive import Reactive, reactive
|
from ..reactive import Reactive, reactive
|
||||||
from ..widget import Widget, RenderResult
|
from ..widget import RenderResult, Widget
|
||||||
|
|
||||||
|
|
||||||
PlaceholderVariant = Literal["default", "size", "text"]
|
PlaceholderVariant = Literal["default", "size", "text"]
|
||||||
_VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [
|
_VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from rich.pretty import Pretty as PrettyRenderable
|
from rich.pretty import Pretty as PrettyRenderable
|
||||||
|
|
||||||
from ..widget import Widget
|
from ..widget import Widget
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ from ..binding import Binding, BindingType
|
|||||||
from ..geometry import Size
|
from ..geometry import Size
|
||||||
from ..message import Message
|
from ..message import Message
|
||||||
from ..reactive import reactive
|
from ..reactive import reactive
|
||||||
from ..widget import Widget
|
|
||||||
from ..scrollbar import ScrollBarRender
|
from ..scrollbar import ScrollBarRender
|
||||||
|
from ..widget import Widget
|
||||||
|
|
||||||
|
|
||||||
class Checkbox(Widget, can_focus=True):
|
class Switch(Widget, can_focus=True):
|
||||||
"""A checkbox widget that represents a boolean value.
|
"""A switch widget that represents a boolean value.
|
||||||
|
|
||||||
Can be toggled by clicking on it or through its [bindings][textual.widgets.Checkbox.BINDINGS].
|
Can be toggled by clicking on it or through its [bindings][textual.widgets.Switch.BINDINGS].
|
||||||
|
|
||||||
The checkbox widget also contains [component classes][textual.widgets.Checkbox.COMPONENT_CLASSES]
|
The switch widget also contains [component classes][textual.widgets.Switch.COMPONENT_CLASSES]
|
||||||
that enable more customization.
|
that enable more customization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -27,20 +27,20 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
"""
|
"""
|
||||||
| Key(s) | Description |
|
| Key(s) | Description |
|
||||||
| :- | :- |
|
| :- | :- |
|
||||||
| enter,space | Toggle the checkbox status. |
|
| enter,space | Toggle the switch state. |
|
||||||
"""
|
"""
|
||||||
|
|
||||||
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
||||||
"checkbox--switch",
|
"switch--switch",
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
| Class | Description |
|
| Class | Description |
|
||||||
| :- | :- |
|
| :- | :- |
|
||||||
| `checkbox--switch` | Targets the switch of the checkbox. |
|
| `switch--switch` | Targets the switch of the switch. |
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
Checkbox {
|
Switch {
|
||||||
border: tall transparent;
|
border: tall transparent;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -48,49 +48,49 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
padding: 0 2;
|
padding: 0 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox > .checkbox--switch {
|
Switch > .switch--switch {
|
||||||
background: $panel-darken-2;
|
background: $panel-darken-2;
|
||||||
color: $panel-lighten-2;
|
color: $panel-lighten-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox:hover {
|
Switch:hover {
|
||||||
border: tall $background;
|
border: tall $background;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox:focus {
|
Switch:focus {
|
||||||
border: tall $accent;
|
border: tall $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox.-on {
|
Switch.-on {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox.-on > .checkbox--switch {
|
Switch.-on > .switch--switch {
|
||||||
color: $success;
|
color: $success;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
value = reactive(False, init=False)
|
value = reactive(False, init=False)
|
||||||
"""The value of the checkbox; `True` for on and `False` for off."""
|
"""The value of the switch; `True` for on and `False` for off."""
|
||||||
|
|
||||||
slider_pos = reactive(0.0)
|
slider_pos = reactive(0.0)
|
||||||
"""The position of the slider."""
|
"""The position of the slider."""
|
||||||
|
|
||||||
class Changed(Message, bubble=True):
|
class Changed(Message, bubble=True):
|
||||||
"""Posted when the status of the checkbox changes.
|
"""Posted when the status of the switch changes.
|
||||||
|
|
||||||
Can be handled using `on_checkbox_changed` in a subclass of `Checkbox`
|
Can be handled using `on_switch_changed` in a subclass of `Switch`
|
||||||
or in a parent widget in the DOM.
|
or in a parent widget in the DOM.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
value: The value that the checkbox was changed to.
|
value: The value that the switch was changed to.
|
||||||
input: The `Checkbox` widget that was changed.
|
input: The `Switch` widget that was changed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sender: Checkbox, value: bool) -> None:
|
def __init__(self, sender: Switch, value: bool) -> None:
|
||||||
super().__init__(sender)
|
super().__init__(sender)
|
||||||
self.value: bool = value
|
self.value: bool = value
|
||||||
self.input: Checkbox = sender
|
self.input: Switch = sender
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -101,14 +101,14 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
id: str | None = None,
|
id: str | None = None,
|
||||||
classes: str | None = None,
|
classes: str | None = None,
|
||||||
):
|
):
|
||||||
"""Initialise the checkbox.
|
"""Initialise the switch.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The initial value of the checkbox. Defaults to False.
|
value: The initial value of the switch. Defaults to False.
|
||||||
animate: True if the checkbox should animate when toggled. Defaults to True.
|
animate: True if the switch should animate when toggled. Defaults to True.
|
||||||
name: The name of the checkbox.
|
name: The name of the switch.
|
||||||
id: The ID of the checkbox in the DOM.
|
id: The ID of the switch in the DOM.
|
||||||
classes: The CSS classes of the checkbox.
|
classes: The CSS classes of the switch.
|
||||||
"""
|
"""
|
||||||
super().__init__(name=name, id=id, classes=classes)
|
super().__init__(name=name, id=id, classes=classes)
|
||||||
if value:
|
if value:
|
||||||
@@ -128,7 +128,7 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
self.set_class(slider_pos == 1, "-on")
|
self.set_class(slider_pos == 1, "-on")
|
||||||
|
|
||||||
def render(self) -> RenderableType:
|
def render(self) -> RenderableType:
|
||||||
style = self.get_component_rich_style("checkbox--switch")
|
style = self.get_component_rich_style("switch--switch")
|
||||||
return ScrollBarRender(
|
return ScrollBarRender(
|
||||||
virtual_size=100,
|
virtual_size=100,
|
||||||
window_size=50,
|
window_size=50,
|
||||||
@@ -150,6 +150,6 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
self.toggle()
|
self.toggle()
|
||||||
|
|
||||||
def toggle(self) -> None:
|
def toggle(self) -> None:
|
||||||
"""Toggle the checkbox value. As a result of the value changing,
|
"""Toggle the switch value. As a result of the value changing,
|
||||||
a Checkbox.Changed message will be posted."""
|
a Switch.Changed message will be posted."""
|
||||||
self.value = not self.value
|
self.value = not self.value
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from ..app import ComposeResult
|
|
||||||
from ._static import Static
|
|
||||||
from ._button import Button
|
|
||||||
from ..containers import Container
|
|
||||||
|
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
|
|
||||||
|
from ..app import ComposeResult
|
||||||
|
from ..containers import Container
|
||||||
|
from ._button import Button
|
||||||
|
from ._static import Static
|
||||||
|
|
||||||
WELCOME_MD = """\
|
WELCOME_MD = """\
|
||||||
# Welcome!
|
# Welcome!
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ from typing import Iterable
|
|||||||
from rich.cells import cell_len
|
from rich.cells import cell_len
|
||||||
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
from rich.style import StyleType, Style
|
from rich.style import Style, StyleType
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from textual import events
|
from textual import events
|
||||||
from textual._layout_resolve import layout_resolve, Edge
|
from textual._layout_resolve import Edge, layout_resolve
|
||||||
from textual.keys import Keys
|
from textual.keys import Keys
|
||||||
from textual.reactive import Reactive
|
from textual.reactive import Reactive
|
||||||
from textual.renderables.text_opacity import TextOpacity
|
from textual.renderables.text_opacity import TextOpacity
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import pytest
|
|||||||
|
|
||||||
from tests.utilities.render import render
|
from tests.utilities.render import render
|
||||||
from textual.css._help_text import (
|
from textual.css._help_text import (
|
||||||
spacing_wrong_number_of_values_help_text,
|
|
||||||
spacing_invalid_value_help_text,
|
|
||||||
scalar_help_text,
|
|
||||||
string_enum_help_text,
|
|
||||||
color_property_help_text,
|
|
||||||
border_property_help_text,
|
|
||||||
layout_property_help_text,
|
|
||||||
fractional_property_help_text,
|
|
||||||
offset_property_help_text,
|
|
||||||
align_help_text,
|
align_help_text,
|
||||||
|
border_property_help_text,
|
||||||
|
color_property_help_text,
|
||||||
|
fractional_property_help_text,
|
||||||
|
layout_property_help_text,
|
||||||
|
offset_property_help_text,
|
||||||
offset_single_axis_help_text,
|
offset_single_axis_help_text,
|
||||||
|
scalar_help_text,
|
||||||
|
spacing_invalid_value_help_text,
|
||||||
|
spacing_wrong_number_of_values_help_text,
|
||||||
|
string_enum_help_text,
|
||||||
style_flags_property_help_text,
|
style_flags_property_help_text,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from textual.css.parse import substitute_references
|
|||||||
from textual.css.scalar import Scalar, Unit
|
from textual.css.scalar import Scalar, Unit
|
||||||
from textual.css.stylesheet import Stylesheet, StylesheetParseError
|
from textual.css.stylesheet import Stylesheet, StylesheetParseError
|
||||||
from textual.css.tokenize import tokenize
|
from textual.css.tokenize import tokenize
|
||||||
from textual.css.tokenizer import Token, ReferencedBy
|
from textual.css.tokenizer import ReferencedBy, Token
|
||||||
from textual.css.transition import Transition
|
from textual.css.transition import Transition
|
||||||
from textual.geometry import Spacing
|
from textual.geometry import Spacing
|
||||||
from textual.layouts.vertical import VerticalLayout
|
from textual.layouts.vertical import VerticalLayout
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
from textual.color import Color
|
from textual.color import Color
|
||||||
from textual.css.errors import StyleValueError
|
from textual.css.errors import StyleValueError
|
||||||
from textual.css.scalar import Scalar, Unit
|
from textual.css.scalar import Scalar, Unit
|
||||||
from textual.css.styles import Styles, RenderStyles
|
from textual.css.styles import RenderStyles, Styles
|
||||||
from textual.dom import DOMNode
|
from textual.dom import DOMNode
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,4 @@ _WINDOWS = sys.platform == "win32"
|
|||||||
# and the error messages suggest the event loop is being shutdown before async fixture
|
# and the error messages suggest the event loop is being shutdown before async fixture
|
||||||
# teardown code has finished running. These are very rare, but are much more of an issue on
|
# teardown code has finished running. These are very rare, but are much more of an issue on
|
||||||
# CI since they can delay builds that have passed locally.
|
# CI since they can delay builds that have passed locally.
|
||||||
pytestmark = pytest.mark.skipif(
|
pytestmark = pytest.mark.skipif(_MACOS_CI or _WINDOWS, reason="Issue #411")
|
||||||
_MACOS_CI or _WINDOWS, reason="Issue #411"
|
|
||||||
)
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user