mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Unit tests for DataTable reverse sort
This commit is contained in:
@@ -39,6 +39,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixed issue with app not processing Paste event https://github.com/Textualize/textual/issues/1666
|
- Fixed issue with app not processing Paste event https://github.com/Textualize/textual/issues/1666
|
||||||
- Fixed glitch with view position with auto width inputs https://github.com/Textualize/textual/issues/1693
|
- Fixed glitch with view position with auto width inputs https://github.com/Textualize/textual/issues/1693
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Methods `MessagePump.emit` and `MessagePump.emit_no_wait` https://github.com/Textualize/textual/pull/1738
|
||||||
|
|
||||||
## [0.10.1] - 2023-01-20
|
## [0.10.1] - 2023-01-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ So, thanks to this bit of code in my `Activity` widget...
|
|||||||
parent.move_child(
|
parent.move_child(
|
||||||
self, before=parent.children.index( self ) - 1
|
self, before=parent.children.index( self ) - 1
|
||||||
)
|
)
|
||||||
self.emit_no_wait( self.Moved( self ) )
|
self.post_message_no_wait( self.Moved( self ) )
|
||||||
self.scroll_visible( top=True )
|
self.scroll_visible( top=True )
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class ColorButton(Static):
|
|||||||
self.styles.border = ("tall", self.color)
|
self.styles.border = ("tall", self.color)
|
||||||
|
|
||||||
async def on_click(self) -> None:
|
async def on_click(self) -> None:
|
||||||
# The emit method sends an event to a widget's parent
|
# The post_message method sends an event to be handled in the DOM
|
||||||
await self.emit(self.Selected(self, self.color))
|
await self.post_message(self.Selected(self, self.color))
|
||||||
|
|
||||||
def render(self) -> str:
|
def render(self) -> str:
|
||||||
return str(self.color)
|
return str(self.color)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ The message class is defined within the widget class itself. This is not strictl
|
|||||||
|
|
||||||
## Sending events
|
## Sending events
|
||||||
|
|
||||||
In the previous example we used [emit()][textual.message_pump.MessagePump.emit] to send an event to its parent. We could also have used [emit_no_wait()][textual.message_pump.MessagePump.emit_no_wait] for non async code. Sending messages in this way allows you to write custom widgets without needing to know in what context they will be used.
|
In the previous example we used [post_message()][textual.message_pump.MessagePump.post_message] to send an event to its parent. We could also have used [post_message_no_wait()][textual.message_pump.MessagePump.post_message_no_wait] for non async code. Sending messages in this way allows you to write custom widgets without needing to know in what context they will be used.
|
||||||
|
|
||||||
There are other ways of sending (posting) messages, which you may need to use less frequently.
|
There are other ways of sending (posting) messages, which you may need to use less frequently.
|
||||||
|
|
||||||
|
|||||||
@@ -371,11 +371,11 @@ We can perform this conversion by adding `self.scroll_offset` to `event.offset`.
|
|||||||
- Once we have the board coordinate underneath the mouse we divide the x coordinate by 8 and divide the y coordinate by 4 to give us the coordinate of a square.
|
- Once we have the board coordinate underneath the mouse we divide the x coordinate by 8 and divide the y coordinate by 4 to give us the coordinate of a square.
|
||||||
|
|
||||||
If the cursor square coordinate calculated in `on_mouse_move` changes, Textual will call `watch_cursor_square` with the previous coordinate and new coordinate of the square. This method works out the regions of the widget to update and essentially does the reverse of the steps we took to go from mouse coordinates to square coordinates.
|
If the cursor square coordinate calculated in `on_mouse_move` changes, Textual will call `watch_cursor_square` with the previous coordinate and new coordinate of the square. This method works out the regions of the widget to update and essentially does the reverse of the steps we took to go from mouse coordinates to square coordinates.
|
||||||
The `get_square_region` function calculates a [Region][textual.geometry.Region] object for each square and uses them as a positional argument in a call to [refresh][textual.widget.Widget.refresh]. Passing Regions to `refresh` tells Textual to update only the cells underneath those regions, and not the entire region.
|
The `get_square_region` function calculates a [Region][textual.geometry.Region] object for each square and uses them as a positional argument in a call to [refresh][textual.widget.Widget.refresh]. Passing Region objects to `refresh` tells Textual to update only the cells underneath those regions, and not the entire widget.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
Textual is smart about performing updates. If you refresh multiple regions (even if they overlap), Textual will combine them in to as few non-overlapping regions as possible.
|
Textual is smart about performing updates. If you refresh multiple regions, Textual will combine them into as few non-overlapping regions as possible.
|
||||||
|
|
||||||
The final step is to update the `render_line` method to use the cursor style when rendering the square underneath the mouse.
|
The final step is to update the `render_line` method to use the cursor style when rendering the square underneath the mouse.
|
||||||
|
|
||||||
|
|||||||
14
poetry.lock
generated
14
poetry.lock
generated
@@ -573,6 +573,14 @@ category = "main"
|
|||||||
optional = true
|
optional = true
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "msgpack-types"
|
||||||
|
version = "0.2.0"
|
||||||
|
description = "Type stubs for msgpack"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7,<4.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multidict"
|
name = "multidict"
|
||||||
version = "6.0.4"
|
version = "6.0.4"
|
||||||
@@ -1040,7 +1048,7 @@ dev = ["aiohttp", "click", "msgpack"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
content-hash = "efa7c78c6403931436fb415a21c935eaafd7859130a9da8ef7e5d79ddb34b14d"
|
content-hash = "425fac5cc893af33128a6baf4fc9e296781d322e64eea5d9341d1265c6637d3c"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiohttp = [
|
aiohttp = [
|
||||||
@@ -1548,6 +1556,10 @@ msgpack = [
|
|||||||
{file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"},
|
{file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"},
|
||||||
{file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"},
|
{file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"},
|
||||||
]
|
]
|
||||||
|
msgpack-types = [
|
||||||
|
{file = "msgpack-types-0.2.0.tar.gz", hash = "sha256:b6b7ce9f52599f9dc3497006be8cf6bed7bd2c83fa48c4df43ac6958b97b0720"},
|
||||||
|
{file = "msgpack_types-0.2.0-py3-none-any.whl", hash = "sha256:7e5bce9e3bba9fe08ed14005ad107aa44ea8d4b779ec28b8db880826d4c67303"},
|
||||||
|
]
|
||||||
multidict = [
|
multidict = [
|
||||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
|
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
|
||||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
|
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Jinja2 = "<3.1.0"
|
|||||||
syrupy = "^3.0.0"
|
syrupy = "^3.0.0"
|
||||||
mkdocs-rss-plugin = "^1.5.0"
|
mkdocs-rss-plugin = "^1.5.0"
|
||||||
httpx = "^0.23.1"
|
httpx = "^0.23.1"
|
||||||
|
msgpack-types = "^0.2.0"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
includes = "src"
|
includes = "src"
|
||||||
|
|||||||
@@ -16,27 +16,91 @@ if TYPE_CHECKING:
|
|||||||
INNER = 1
|
INNER = 1
|
||||||
OUTER = 2
|
OUTER = 2
|
||||||
|
|
||||||
BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
|
BORDER_CHARS: dict[
|
||||||
# Each string of the tuple represents a sub-tuple itself:
|
EdgeType, tuple[tuple[str, str, str], tuple[str, str, str], tuple[str, str, str]]
|
||||||
# - 1st string represents (top1, top2, top3)
|
] = {
|
||||||
# - 2nd string represents (mid1, mid2, mid3)
|
# Three tuples for the top, middle, and bottom rows.
|
||||||
# - 3rd string represents (bottom1, bottom2, bottom3)
|
# The sub-tuples are the characters for the left, center, and right borders.
|
||||||
"": (" ", " ", " "),
|
"": (
|
||||||
"ascii": ("+-+", "| |", "+-+"),
|
(" ", " ", " "),
|
||||||
"none": (" ", " ", " "),
|
(" ", " ", " "),
|
||||||
"hidden": (" ", " ", " "),
|
(" ", " ", " "),
|
||||||
"blank": (" ", " ", " "),
|
),
|
||||||
"round": ("╭─╮", "│ │", "╰─╯"),
|
"ascii": (
|
||||||
"solid": ("┌─┐", "│ │", "└─┘"),
|
("+", "-", "+"),
|
||||||
"double": ("╔═╗", "║ ║", "╚═╝"),
|
("|", " ", "|"),
|
||||||
"dashed": ("┏╍┓", "╏ ╏", "┗╍┛"),
|
("+", "-", "+"),
|
||||||
"heavy": ("┏━┓", "┃ ┃", "┗━┛"),
|
),
|
||||||
"inner": ("▗▄▖", "▐ ▌", "▝▀▘"),
|
"none": (
|
||||||
"outer": ("▛▀▜", "▌ ▐", "▙▄▟"),
|
(" ", " ", " "),
|
||||||
"hkey": ("▔▔▔", " ", "▁▁▁"),
|
(" ", " ", " "),
|
||||||
"vkey": ("▏ ▕", "▏ ▕", "▏ ▕"),
|
(" ", " ", " "),
|
||||||
"tall": ("▊▔▎", "▊ ▎", "▊▁▎"),
|
),
|
||||||
"wide": ("▁▁▁", "▎ ▋", "▔▔▔"),
|
"hidden": (
|
||||||
|
(" ", " ", " "),
|
||||||
|
(" ", " ", " "),
|
||||||
|
(" ", " ", " "),
|
||||||
|
),
|
||||||
|
"blank": (
|
||||||
|
(" ", " ", " "),
|
||||||
|
(" ", " ", " "),
|
||||||
|
(" ", " ", " "),
|
||||||
|
),
|
||||||
|
"round": (
|
||||||
|
("╭", "─", "╮"),
|
||||||
|
("│", " ", "│"),
|
||||||
|
("╰", "─", "╯"),
|
||||||
|
),
|
||||||
|
"solid": (
|
||||||
|
("┌", "─", "┐"),
|
||||||
|
("│", " ", "│"),
|
||||||
|
("└", "─", "┘"),
|
||||||
|
),
|
||||||
|
"double": (
|
||||||
|
("╔", "═", "╗"),
|
||||||
|
("║", " ", "║"),
|
||||||
|
("╚", "═", "╝"),
|
||||||
|
),
|
||||||
|
"dashed": (
|
||||||
|
("┏", "╍", "┓"),
|
||||||
|
("╏", " ", "╏"),
|
||||||
|
("┗", "╍", "┛"),
|
||||||
|
),
|
||||||
|
"heavy": (
|
||||||
|
("┏", "━", "┓"),
|
||||||
|
("┃", " ", "┃"),
|
||||||
|
("┗", "━", "┛"),
|
||||||
|
),
|
||||||
|
"inner": (
|
||||||
|
("▗", "▄", "▖"),
|
||||||
|
("▐", " ", "▌"),
|
||||||
|
("▝", "▀", "▘"),
|
||||||
|
),
|
||||||
|
"outer": (
|
||||||
|
("▛", "▀", "▜"),
|
||||||
|
("▌", " ", "▐"),
|
||||||
|
("▙", "▄", "▟"),
|
||||||
|
),
|
||||||
|
"hkey": (
|
||||||
|
("▔", "▔", "▔"),
|
||||||
|
(" ", " ", " "),
|
||||||
|
("▁", "▁", "▁"),
|
||||||
|
),
|
||||||
|
"vkey": (
|
||||||
|
("▏", " ", "▕"),
|
||||||
|
("▏", " ", "▕"),
|
||||||
|
("▏", " ", "▕"),
|
||||||
|
),
|
||||||
|
"tall": (
|
||||||
|
("▊", "▔", "▎"),
|
||||||
|
("▊", " ", "▎"),
|
||||||
|
("▊", "▁", "▎"),
|
||||||
|
),
|
||||||
|
"wide": (
|
||||||
|
("▁", "▁", "▁"),
|
||||||
|
("▎", " ", "▋"),
|
||||||
|
("▔", "▔", "▔"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Some of the borders are on the widget background and some are on the background of the parent
|
# Some of the borders are on the widget background and some are on the background of the parent
|
||||||
@@ -44,22 +108,86 @@ BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
|
|||||||
BORDER_LOCATIONS: dict[
|
BORDER_LOCATIONS: dict[
|
||||||
EdgeType, tuple[tuple[int, int, int], tuple[int, int, int], tuple[int, int, int]]
|
EdgeType, tuple[tuple[int, int, int], tuple[int, int, int], tuple[int, int, int]]
|
||||||
] = {
|
] = {
|
||||||
"": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
"": (
|
||||||
"ascii": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"none": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"hidden": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"blank": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
),
|
||||||
"round": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
"ascii": (
|
||||||
"solid": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"double": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"dashed": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"heavy": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
),
|
||||||
"inner": ((1, 1, 1), (1, 1, 1), (1, 1, 1)),
|
"none": (
|
||||||
"outer": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"hkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"vkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
(0, 0, 0),
|
||||||
"tall": ((2, 0, 1), (2, 0, 1), (2, 0, 1)),
|
),
|
||||||
"wide": ((1, 1, 1), (0, 1, 3), (1, 1, 1)),
|
"hidden": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"blank": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"round": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"solid": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"double": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"dashed": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"heavy": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"inner": (
|
||||||
|
(1, 1, 1),
|
||||||
|
(1, 1, 1),
|
||||||
|
(1, 1, 1),
|
||||||
|
),
|
||||||
|
"outer": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"hkey": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"vkey": (
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
(0, 0, 0),
|
||||||
|
),
|
||||||
|
"tall": (
|
||||||
|
(2, 0, 1),
|
||||||
|
(2, 0, 1),
|
||||||
|
(2, 0, 1),
|
||||||
|
),
|
||||||
|
"wide": (
|
||||||
|
(1, 1, 1),
|
||||||
|
(0, 1, 3),
|
||||||
|
(1, 1, 1),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
|
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
from typing import Callable
|
||||||
|
|
||||||
__all__ = ["cell_len"]
|
__all__ = ["cell_len"]
|
||||||
|
|
||||||
|
|
||||||
|
cell_len: Callable[[str], int]
|
||||||
try:
|
try:
|
||||||
from rich.cells import cached_cell_len as cell_len
|
from rich.cells import cached_cell_len as cell_len
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
COLOR_NAME_TO_RGB: dict[str, tuple[int, int, int] | tuple[int, int, int, float]] = {
|
COLOR_NAME_TO_RGB: dict[str, tuple[int, int, int] | tuple[int, int, int, int]] = {
|
||||||
# Let's start with a specific pseudo-color::
|
# Let's start with a specific pseudo-color::
|
||||||
"transparent": (0, 0, 0, 0),
|
"transparent": (0, 0, 0, 0),
|
||||||
# Then, the 16 common ANSI colors:
|
# Then, the 16 common ANSI colors:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
|
|
||||||
from time import sleep, perf_counter
|
from time import sleep, perf_counter
|
||||||
from asyncio import get_running_loop
|
from asyncio import get_running_loop, Future
|
||||||
from threading import Thread, Event
|
from threading import Thread, Event
|
||||||
|
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ class Sleeper(Thread):
|
|||||||
self._exit = False
|
self._exit = False
|
||||||
self._sleep_time = 0.0
|
self._sleep_time = 0.0
|
||||||
self._event = Event()
|
self._event = Event()
|
||||||
self.future = None
|
self.future: Future | None = None
|
||||||
self._loop = get_running_loop()
|
self._loop = get_running_loop()
|
||||||
super().__init__(daemon=True)
|
super().__init__(daemon=True)
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ class Sleeper(Thread):
|
|||||||
sleep(self._sleep_time)
|
sleep(self._sleep_time)
|
||||||
self._event.clear()
|
self._event.clear()
|
||||||
# self.future.set_result(None)
|
# self.future.set_result(None)
|
||||||
|
assert self.future is not None
|
||||||
self._loop.call_soon_threadsafe(self.future.set_result, None)
|
self._loop.call_soon_threadsafe(self.future.set_result, None)
|
||||||
|
|
||||||
async def sleep(self, sleep_time: float) -> None:
|
async def sleep(self, sleep_time: float) -> None:
|
||||||
@@ -33,9 +34,6 @@ class Sleeper(Thread):
|
|||||||
self._event.set()
|
self._event.set()
|
||||||
await future
|
await future
|
||||||
|
|
||||||
# await self._async_event.wait()
|
|
||||||
# self._async_event.clear()
|
|
||||||
|
|
||||||
|
|
||||||
async def check_sleeps() -> None:
|
async def check_sleeps() -> None:
|
||||||
sleeper = Sleeper()
|
sleeper = Sleeper()
|
||||||
@@ -46,7 +44,6 @@ async def check_sleeps() -> None:
|
|||||||
|
|
||||||
while perf_counter() - start < sleep_for:
|
while perf_counter() - start < sleep_for:
|
||||||
sleep(0)
|
sleep(0)
|
||||||
# await sleeper.sleep(sleep_for)
|
|
||||||
elapsed = perf_counter() - start
|
elapsed = perf_counter() - start
|
||||||
return elapsed
|
return elapsed
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ try:
|
|||||||
import ctypes
|
import ctypes
|
||||||
from ctypes.wintypes import LARGE_INTEGER
|
from ctypes.wintypes import LARGE_INTEGER
|
||||||
|
|
||||||
kernel32 = ctypes.windll.kernel32
|
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
|
||||||
except Exception:
|
except Exception:
|
||||||
sleep = time_sleep
|
sleep = time_sleep
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -431,7 +431,9 @@ class Color(NamedTuple):
|
|||||||
suggested_color = None
|
suggested_color = None
|
||||||
if not color_text.startswith(("#", "rgb", "hsl")):
|
if not color_text.startswith(("#", "rgb", "hsl")):
|
||||||
# Seems like we tried to use a color name: let's try to find one that is close enough:
|
# Seems like we tried to use a color name: let's try to find one that is close enough:
|
||||||
suggested_color = get_suggestion(color_text, COLOR_NAME_TO_RGB.keys())
|
suggested_color = get_suggestion(
|
||||||
|
color_text, list(COLOR_NAME_TO_RGB.keys())
|
||||||
|
)
|
||||||
if suggested_color:
|
if suggested_color:
|
||||||
error_message += f"; did you mean '{suggested_color}'?"
|
error_message += f"; did you mean '{suggested_color}'?"
|
||||||
raise ColorParseError(error_message, suggested_color)
|
raise ColorParseError(error_message, suggested_color)
|
||||||
@@ -447,10 +449,10 @@ class Color(NamedTuple):
|
|||||||
) = color_match.groups()
|
) = color_match.groups()
|
||||||
|
|
||||||
if rgb_hex_triple is not None:
|
if rgb_hex_triple is not None:
|
||||||
r, g, b = rgb_hex_triple
|
r, g, b = rgb_hex_triple # type: ignore[misc]
|
||||||
color = cls(int(f"{r}{r}", 16), int(f"{g}{g}", 16), int(f"{b}{b}", 16))
|
color = cls(int(f"{r}{r}", 16), int(f"{g}{g}", 16), int(f"{b}{b}", 16))
|
||||||
elif rgb_hex_quad is not None:
|
elif rgb_hex_quad is not None:
|
||||||
r, g, b, a = rgb_hex_quad
|
r, g, b, a = rgb_hex_quad # type: ignore[misc]
|
||||||
color = cls(
|
color = cls(
|
||||||
int(f"{r}{r}", 16),
|
int(f"{r}{r}", 16),
|
||||||
int(f"{g}{g}", 16),
|
int(f"{g}{g}", 16),
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ class Bullet:
|
|||||||
self, console: Console, options: ConsoleOptions
|
self, console: Console, options: ConsoleOptions
|
||||||
) -> RenderResult:
|
) -> RenderResult:
|
||||||
yield _markup_and_highlight(self.markup)
|
yield _markup_and_highlight(self.markup)
|
||||||
yield from self.examples
|
if self.examples is not None:
|
||||||
|
yield from self.examples
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
@@ -76,7 +77,9 @@ class HelpText:
|
|||||||
context around the issue. These are rendered below the summary. Defaults to None.
|
context around the issue. These are rendered below the summary. Defaults to None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, summary: str, *, bullets: Iterable[Bullet] = None) -> None:
|
def __init__(
|
||||||
|
self, summary: str, *, bullets: Iterable[Bullet] | None = None
|
||||||
|
) -> None:
|
||||||
self.summary: str = summary
|
self.summary: str = summary
|
||||||
self.bullets: Iterable[Bullet] | None = bullets or []
|
self.bullets: Iterable[Bullet] | None = bullets or []
|
||||||
|
|
||||||
@@ -87,6 +90,7 @@ class HelpText:
|
|||||||
self, console: Console, options: ConsoleOptions
|
self, console: Console, options: ConsoleOptions
|
||||||
) -> RenderResult:
|
) -> RenderResult:
|
||||||
tree = Tree(_markup_and_highlight(f"[b blue]{self.summary}"), guide_style="dim")
|
tree = Tree(_markup_and_highlight(f"[b blue]{self.summary}"), guide_style="dim")
|
||||||
for bullet in self.bullets:
|
if self.bullets is not None:
|
||||||
tree.add(bullet)
|
for bullet in self.bullets:
|
||||||
|
tree.add(bullet)
|
||||||
yield tree
|
yield tree
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ class DevtoolsClient:
|
|||||||
change, it will update its own Console to ensure it renders at
|
change, it will update its own Console to ensure it renders at
|
||||||
the correct width for server-side display.
|
the correct width for server-side display.
|
||||||
"""
|
"""
|
||||||
|
assert self.websocket is not None
|
||||||
async for message in self.websocket:
|
async for message in self.websocket:
|
||||||
if message.type == aiohttp.WSMsgType.TEXT:
|
if message.type == aiohttp.WSMsgType.TEXT:
|
||||||
message_json = json.loads(message.data)
|
message_json = json.loads(message.data)
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
import json
|
import json
|
||||||
import pickle
|
import pickle
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from typing import cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiohttp import WSMessage, WSMsgType
|
from aiohttp import WSMessage, WSMsgType
|
||||||
from aiohttp.abc import Request
|
from aiohttp.abc import Request
|
||||||
@@ -232,17 +231,16 @@ class ClientHandler:
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await self.service.send_server_info(client_handler=self)
|
await self.service.send_server_info(client_handler=self)
|
||||||
async for message in self.websocket:
|
async for websocket_message in self.websocket:
|
||||||
message = cast(WSMessage, message)
|
if websocket_message.type in (WSMsgType.TEXT, WSMsgType.BINARY):
|
||||||
|
message: dict[str, Any]
|
||||||
if message.type in (WSMsgType.TEXT, WSMsgType.BINARY):
|
|
||||||
try:
|
try:
|
||||||
if isinstance(message.data, bytes):
|
if isinstance(websocket_message.data, bytes):
|
||||||
message = msgpack.unpackb(message.data)
|
message = msgpack.unpackb(websocket_message.data)
|
||||||
else:
|
else:
|
||||||
message = json.loads(message.data)
|
message = json.loads(websocket_message.data)
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.service.console.print(escape(str(message.data)))
|
self.service.console.print(escape(str(websocket_message.data)))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
type = message.get("type")
|
type = message.get("type")
|
||||||
@@ -253,7 +251,7 @@ class ClientHandler:
|
|||||||
and not self.service.shutdown_event.is_set()
|
and not self.service.shutdown_event.is_set()
|
||||||
):
|
):
|
||||||
await self.incoming_queue.put(message)
|
await self.incoming_queue.put(message)
|
||||||
elif message.type == WSMsgType.ERROR:
|
elif websocket_message.type == WSMsgType.ERROR:
|
||||||
self.service.console.print(
|
self.service.console.print(
|
||||||
DevConsoleNotice("Websocket error occurred", level="error")
|
DevConsoleNotice("Websocket error occurred", level="error")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -580,34 +580,6 @@ class MessagePump(metaclass=MessagePumpMeta):
|
|||||||
async def on_callback(self, event: events.Callback) -> None:
|
async def on_callback(self, event: events.Callback) -> None:
|
||||||
await invoke(event.callback)
|
await invoke(event.callback)
|
||||||
|
|
||||||
def emit_no_wait(self, message: Message) -> bool:
|
|
||||||
"""Send a message to the _parent_, non async version.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message: A message object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if the message was posted successfully.
|
|
||||||
"""
|
|
||||||
if self._parent:
|
|
||||||
return self._parent._post_message_from_child_no_wait(message)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def emit(self, message: Message) -> bool:
|
|
||||||
"""Send a message to the _parent_.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message: A message object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if the message was posted successfully.
|
|
||||||
"""
|
|
||||||
if self._parent:
|
|
||||||
return await self._parent._post_message_from_child(message)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# TODO: Does dispatch_key belong on message pump?
|
# TODO: Does dispatch_key belong on message pump?
|
||||||
async def dispatch_key(self, event: events.Key) -> bool:
|
async def dispatch_key(self, event: events.Key) -> bool:
|
||||||
"""Dispatch a key event to method.
|
"""Dispatch a key event to method.
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ def blend_colors(color1: Color, color2: Color, ratio: float) -> Color:
|
|||||||
Returns:
|
Returns:
|
||||||
A Color representing the blending of the two supplied colors.
|
A Color representing the blending of the two supplied colors.
|
||||||
"""
|
"""
|
||||||
|
assert color1.triplet is not None
|
||||||
|
assert color2.triplet is not None
|
||||||
r1, g1, b1 = color1.triplet
|
r1, g1, b1 = color1.triplet
|
||||||
r2, g2, b2 = color2.triplet
|
r2, g2, b2 = color2.triplet
|
||||||
|
|
||||||
|
|||||||
@@ -280,10 +280,12 @@ class ScrollBar(Widget):
|
|||||||
self.mouse_over = False
|
self.mouse_over = False
|
||||||
|
|
||||||
async def action_scroll_down(self) -> None:
|
async def action_scroll_down(self) -> None:
|
||||||
await self.emit(ScrollDown(self) if self.vertical else ScrollRight(self))
|
await self.post_message(
|
||||||
|
ScrollDown(self) if self.vertical else ScrollRight(self)
|
||||||
|
)
|
||||||
|
|
||||||
async def action_scroll_up(self) -> None:
|
async def action_scroll_up(self) -> None:
|
||||||
await self.emit(ScrollUp(self) if self.vertical else ScrollLeft(self))
|
await self.post_message(ScrollUp(self) if self.vertical else ScrollLeft(self))
|
||||||
|
|
||||||
def action_grab(self) -> None:
|
def action_grab(self) -> None:
|
||||||
self.capture_mouse()
|
self.capture_mouse()
|
||||||
@@ -324,7 +326,7 @@ class ScrollBar(Widget):
|
|||||||
* (self.window_virtual_size / self.window_size)
|
* (self.window_virtual_size / self.window_size)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await self.emit(ScrollTo(self, x=x, y=y))
|
await self.post_message(ScrollTo(self, x=x, y=y))
|
||||||
event.stop()
|
event.stop()
|
||||||
|
|
||||||
async def _on_click(self, event: events.Click) -> None:
|
async def _on_click(self, event: events.Click) -> None:
|
||||||
|
|||||||
@@ -2465,12 +2465,12 @@ class Widget(DOMNode):
|
|||||||
def _on_focus(self, event: events.Focus) -> None:
|
def _on_focus(self, event: events.Focus) -> None:
|
||||||
self.has_focus = True
|
self.has_focus = True
|
||||||
self.refresh()
|
self.refresh()
|
||||||
self.emit_no_wait(events.DescendantFocus(self))
|
self.post_message_no_wait(events.DescendantFocus(self))
|
||||||
|
|
||||||
def _on_blur(self, event: events.Blur) -> None:
|
def _on_blur(self, event: events.Blur) -> None:
|
||||||
self.has_focus = False
|
self.has_focus = False
|
||||||
self.refresh()
|
self.refresh()
|
||||||
self.emit_no_wait(events.DescendantBlur(self))
|
self.post_message_no_wait(events.DescendantBlur(self))
|
||||||
|
|
||||||
def _on_descendant_blur(self, event: events.DescendantBlur) -> None:
|
def _on_descendant_blur(self, event: events.DescendantBlur) -> None:
|
||||||
if self._has_focus_within:
|
if self._has_focus_within:
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class Button(Static, can_focus=True):
|
|||||||
# Manage the "active" effect:
|
# Manage the "active" effect:
|
||||||
self._start_active_affect()
|
self._start_active_affect()
|
||||||
# ...and let other components know that we've just been clicked:
|
# ...and let other components know that we've just been clicked:
|
||||||
self.emit_no_wait(Button.Pressed(self))
|
self.post_message_no_wait(Button.Pressed(self))
|
||||||
|
|
||||||
def _start_active_affect(self) -> None:
|
def _start_active_affect(self) -> None:
|
||||||
"""Start a small animation to show the button was clicked."""
|
"""Start a small animation to show the button was clicked."""
|
||||||
@@ -267,7 +267,7 @@ class Button(Static, can_focus=True):
|
|||||||
async def _on_key(self, event: events.Key) -> None:
|
async def _on_key(self, event: events.Key) -> None:
|
||||||
if event.key == "enter" and not self.disabled:
|
if event.key == "enter" and not self.disabled:
|
||||||
self._start_active_affect()
|
self._start_active_affect()
|
||||||
await self.emit(Button.Pressed(self))
|
await self.post_message(Button.Pressed(self))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def success(
|
def success(
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
"""The position of the slider."""
|
"""The position of the slider."""
|
||||||
|
|
||||||
class Changed(Message, bubble=True):
|
class Changed(Message, bubble=True):
|
||||||
"""Emitted when the status of the checkbox changes.
|
"""Posted when the status of the checkbox changes.
|
||||||
|
|
||||||
Can be handled using `on_checkbox_changed` in a subclass of `Checkbox`
|
Can be handled using `on_checkbox_changed` in a subclass of `Checkbox`
|
||||||
or in a parent widget in the DOM.
|
or in a parent widget in the DOM.
|
||||||
@@ -122,7 +122,7 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
self.animate("slider_pos", target_slider_pos, duration=0.3)
|
self.animate("slider_pos", target_slider_pos, duration=0.3)
|
||||||
else:
|
else:
|
||||||
self.slider_pos = target_slider_pos
|
self.slider_pos = target_slider_pos
|
||||||
self.emit_no_wait(self.Changed(self, self.value))
|
self.post_message_no_wait(self.Changed(self, self.value))
|
||||||
|
|
||||||
def watch_slider_pos(self, slider_pos: float) -> None:
|
def watch_slider_pos(self, slider_pos: float) -> None:
|
||||||
self.set_class(slider_pos == 1, "-on")
|
self.set_class(slider_pos == 1, "-on")
|
||||||
@@ -151,5 +151,5 @@ class Checkbox(Widget, can_focus=True):
|
|||||||
|
|
||||||
def toggle(self) -> None:
|
def toggle(self) -> None:
|
||||||
"""Toggle the checkbox value. As a result of the value changing,
|
"""Toggle the checkbox value. As a result of the value changing,
|
||||||
a Checkbox.Changed message will be emitted."""
|
a Checkbox.Changed message will be posted."""
|
||||||
self.value = not self.value
|
self.value = not self.value
|
||||||
|
|||||||
@@ -245,9 +245,9 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
hover_coordinate: Reactive[Coordinate] = Reactive(Coordinate(0, 0), repaint=False)
|
hover_coordinate: Reactive[Coordinate] = Reactive(Coordinate(0, 0), repaint=False)
|
||||||
|
|
||||||
class CellHighlighted(Message, bubble=True):
|
class CellHighlighted(Message, bubble=True):
|
||||||
"""Emitted when the cursor moves to highlight a new cell.
|
"""Posted when the cursor moves to highlight a new cell.
|
||||||
It's only relevant when the `cursor_type` is `"cell"`.
|
It's only relevant when the `cursor_type` is `"cell"`.
|
||||||
It's also emitted when the cell cursor is re-enabled (by setting `show_cursor=True`),
|
It's also posted when the cell cursor is re-enabled (by setting `show_cursor=True`),
|
||||||
and when the cursor type is changed to `"cell"`. Can be handled using
|
and when the cursor type is changed to `"cell"`. Can be handled using
|
||||||
`on_data_table_cell_highlighted` in a subclass of `DataTable` or in a parent
|
`on_data_table_cell_highlighted` in a subclass of `DataTable` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -277,7 +277,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
yield "cell_key", self.cell_key
|
yield "cell_key", self.cell_key
|
||||||
|
|
||||||
class CellSelected(Message, bubble=True):
|
class CellSelected(Message, bubble=True):
|
||||||
"""Emitted by the `DataTable` widget when a cell is selected.
|
"""Posted by the `DataTable` widget when a cell is selected.
|
||||||
It's only relevant when the `cursor_type` is `"cell"`. Can be handled using
|
It's only relevant when the `cursor_type` is `"cell"`. Can be handled using
|
||||||
`on_data_table_cell_selected` in a subclass of `DataTable` or in a parent
|
`on_data_table_cell_selected` in a subclass of `DataTable` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -307,7 +307,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
yield "cell_key", self.cell_key
|
yield "cell_key", self.cell_key
|
||||||
|
|
||||||
class RowHighlighted(Message, bubble=True):
|
class RowHighlighted(Message, bubble=True):
|
||||||
"""Emitted when a row is highlighted. This message is only emitted when the
|
"""Posted when a row is highlighted. This message is only posted when the
|
||||||
`cursor_type` is set to `"row"`. Can be handled using `on_data_table_row_highlighted`
|
`cursor_type` is set to `"row"`. Can be handled using `on_data_table_row_highlighted`
|
||||||
in a subclass of `DataTable` or in a parent widget in the DOM.
|
in a subclass of `DataTable` or in a parent widget in the DOM.
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
yield "row_key", self.row_key
|
yield "row_key", self.row_key
|
||||||
|
|
||||||
class RowSelected(Message, bubble=True):
|
class RowSelected(Message, bubble=True):
|
||||||
"""Emitted when a row is selected. This message is only emitted when the
|
"""Posted when a row is selected. This message is only posted when the
|
||||||
`cursor_type` is set to `"row"`. Can be handled using
|
`cursor_type` is set to `"row"`. Can be handled using
|
||||||
`on_data_table_row_selected` in a subclass of `DataTable` or in a parent
|
`on_data_table_row_selected` in a subclass of `DataTable` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -348,7 +348,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
yield "row_key", self.row_key
|
yield "row_key", self.row_key
|
||||||
|
|
||||||
class ColumnHighlighted(Message, bubble=True):
|
class ColumnHighlighted(Message, bubble=True):
|
||||||
"""Emitted when a column is highlighted. This message is only emitted when the
|
"""Posted when a column is highlighted. This message is only posted when the
|
||||||
`cursor_type` is set to `"column"`. Can be handled using
|
`cursor_type` is set to `"column"`. Can be handled using
|
||||||
`on_data_table_column_highlighted` in a subclass of `DataTable` or in a parent
|
`on_data_table_column_highlighted` in a subclass of `DataTable` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -371,7 +371,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
yield "column_key", self.column_key
|
yield "column_key", self.column_key
|
||||||
|
|
||||||
class ColumnSelected(Message, bubble=True):
|
class ColumnSelected(Message, bubble=True):
|
||||||
"""Emitted when a column is selected. This message is only emitted when the
|
"""Posted when a column is selected. This message is only posted when the
|
||||||
`cursor_type` is set to `"column"`. Can be handled using
|
`cursor_type` is set to `"column"`. Can be handled using
|
||||||
`on_data_table_column_selected` in a subclass of `DataTable` or in a parent
|
`on_data_table_column_selected` in a subclass of `DataTable` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -629,7 +629,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self._clear_caches()
|
self._clear_caches()
|
||||||
if show_cursor and self.cursor_type != "none":
|
if show_cursor and self.cursor_type != "none":
|
||||||
# When we re-enable the cursor, apply highlighting and
|
# When we re-enable the cursor, apply highlighting and
|
||||||
# emit the appropriate [Row|Column|Cell]Highlighted event.
|
# post the appropriate [Row|Column|Cell]Highlighted event.
|
||||||
self._scroll_cursor_into_view(animate=False)
|
self._scroll_cursor_into_view(animate=False)
|
||||||
if self.cursor_type == "cell":
|
if self.cursor_type == "cell":
|
||||||
self._highlight_coordinate(self.cursor_coordinate)
|
self._highlight_coordinate(self.cursor_coordinate)
|
||||||
@@ -655,7 +655,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self, old_coordinate: Coordinate, new_coordinate: Coordinate
|
self, old_coordinate: Coordinate, new_coordinate: Coordinate
|
||||||
) -> None:
|
) -> None:
|
||||||
if old_coordinate != new_coordinate:
|
if old_coordinate != new_coordinate:
|
||||||
# Refresh the old and the new cell, and emit the appropriate
|
# Refresh the old and the new cell, and post the appropriate
|
||||||
# message to tell users of the newly highlighted row/cell/column.
|
# message to tell users of the newly highlighted row/cell/column.
|
||||||
if self.cursor_type == "cell":
|
if self.cursor_type == "cell":
|
||||||
self.refresh_coordinate(old_coordinate)
|
self.refresh_coordinate(old_coordinate)
|
||||||
@@ -668,7 +668,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
self._highlight_column(new_coordinate.column)
|
self._highlight_column(new_coordinate.column)
|
||||||
|
|
||||||
def _highlight_coordinate(self, coordinate: Coordinate) -> None:
|
def _highlight_coordinate(self, coordinate: Coordinate) -> None:
|
||||||
"""Apply highlighting to the cell at the coordinate, and emit event."""
|
"""Apply highlighting to the cell at the coordinate, and post event."""
|
||||||
self.refresh_coordinate(coordinate)
|
self.refresh_coordinate(coordinate)
|
||||||
try:
|
try:
|
||||||
cell_value = self.get_value_at(coordinate)
|
cell_value = self.get_value_at(coordinate)
|
||||||
@@ -678,7 +678,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
cell_key = self.coordinate_to_cell_key(coordinate)
|
cell_key = self.coordinate_to_cell_key(coordinate)
|
||||||
self.emit_no_wait(
|
self.post_message_no_wait(
|
||||||
DataTable.CellHighlighted(
|
DataTable.CellHighlighted(
|
||||||
self, cell_value, coordinate=coordinate, cell_key=cell_key
|
self, cell_value, coordinate=coordinate, cell_key=cell_key
|
||||||
)
|
)
|
||||||
@@ -701,19 +701,21 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
return CellKey(row_key, column_key)
|
return CellKey(row_key, column_key)
|
||||||
|
|
||||||
def _highlight_row(self, row_index: int) -> None:
|
def _highlight_row(self, row_index: int) -> None:
|
||||||
"""Apply highlighting to the row at the given index, and emit event."""
|
"""Apply highlighting to the row at the given index, and post event."""
|
||||||
self.refresh_row(row_index)
|
self.refresh_row(row_index)
|
||||||
is_valid_row = row_index < len(self.data)
|
is_valid_row = row_index < len(self.data)
|
||||||
if is_valid_row:
|
if is_valid_row:
|
||||||
row_key = self._row_locations.get_key(row_index)
|
row_key = self._row_locations.get_key(row_index)
|
||||||
self.emit_no_wait(DataTable.RowHighlighted(self, row_index, row_key))
|
self.post_message_no_wait(
|
||||||
|
DataTable.RowHighlighted(self, row_index, row_key)
|
||||||
|
)
|
||||||
|
|
||||||
def _highlight_column(self, column_index: int) -> None:
|
def _highlight_column(self, column_index: int) -> None:
|
||||||
"""Apply highlighting to the column at the given index, and emit event."""
|
"""Apply highlighting to the column at the given index, and post event."""
|
||||||
self.refresh_column(column_index)
|
self.refresh_column(column_index)
|
||||||
if column_index < len(self.columns):
|
if column_index < len(self.columns):
|
||||||
column_key = self._column_locations.get_key(column_index)
|
column_key = self._column_locations.get_key(column_index)
|
||||||
self.emit_no_wait(
|
self.post_message_no_wait(
|
||||||
DataTable.ColumnHighlighted(self, column_index, column_key)
|
DataTable.ColumnHighlighted(self, column_index, column_key)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -938,7 +940,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
|
|
||||||
# If a position has opened for the cursor to appear, where it previously
|
# If a position has opened for the cursor to appear, where it previously
|
||||||
# could not (e.g. when there's no data in the table), then a highlighted
|
# could not (e.g. when there's no data in the table), then a highlighted
|
||||||
# event is emitted, since there's now a highlighted cell when there wasn't
|
# event is posted, since there's now a highlighted cell when there wasn't
|
||||||
# before.
|
# before.
|
||||||
cell_now_available = self.row_count == 1 and len(self.columns) > 0
|
cell_now_available = self.row_count == 1 and len(self.columns) > 0
|
||||||
visible_cursor = self.show_cursor and self.cursor_type != "none"
|
visible_cursor = self.show_cursor and self.cursor_type != "none"
|
||||||
@@ -1542,15 +1544,15 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
def action_select_cursor(self) -> None:
|
def action_select_cursor(self) -> None:
|
||||||
self._set_hover_cursor(False)
|
self._set_hover_cursor(False)
|
||||||
if self.show_cursor and self.cursor_type != "none":
|
if self.show_cursor and self.cursor_type != "none":
|
||||||
self._emit_selected_message()
|
self._post_selected_message()
|
||||||
|
|
||||||
def _emit_selected_message(self):
|
def _post_selected_message(self):
|
||||||
"""Emit the appropriate message for a selection based on the `cursor_type`."""
|
"""Post the appropriate message for a selection based on the `cursor_type`."""
|
||||||
cursor_coordinate = self.cursor_coordinate
|
cursor_coordinate = self.cursor_coordinate
|
||||||
cursor_type = self.cursor_type
|
cursor_type = self.cursor_type
|
||||||
cell_key = self.coordinate_to_cell_key(cursor_coordinate)
|
cell_key = self.coordinate_to_cell_key(cursor_coordinate)
|
||||||
if cursor_type == "cell":
|
if cursor_type == "cell":
|
||||||
self.emit_no_wait(
|
self.post_message_no_wait(
|
||||||
DataTable.CellSelected(
|
DataTable.CellSelected(
|
||||||
self,
|
self,
|
||||||
self.get_value_at(cursor_coordinate),
|
self.get_value_at(cursor_coordinate),
|
||||||
@@ -1561,8 +1563,10 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
elif cursor_type == "row":
|
elif cursor_type == "row":
|
||||||
row_index, _ = cursor_coordinate
|
row_index, _ = cursor_coordinate
|
||||||
row_key, _ = cell_key
|
row_key, _ = cell_key
|
||||||
self.emit_no_wait(DataTable.RowSelected(self, row_index, row_key))
|
self.post_message_no_wait(DataTable.RowSelected(self, row_index, row_key))
|
||||||
elif cursor_type == "column":
|
elif cursor_type == "column":
|
||||||
_, column_index = cursor_coordinate
|
_, column_index = cursor_coordinate
|
||||||
_, column_key = cell_key
|
_, column_key = cell_key
|
||||||
self.emit_no_wait(DataTable.ColumnSelected(self, column_index, column_key))
|
self.post_message_no_wait(
|
||||||
|
DataTable.ColumnSelected(self, column_index, column_key)
|
||||||
|
)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class DirectoryTree(Tree[DirEntry]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class FileSelected(Message, bubble=True):
|
class FileSelected(Message, bubble=True):
|
||||||
"""Emitted when a file is selected.
|
"""Posted when a file is selected.
|
||||||
|
|
||||||
Can be handled using `on_directory_tree_file_selected` in a subclass of
|
Can be handled using `on_directory_tree_file_selected` in a subclass of
|
||||||
`DirectoryTree` or in a parent widget in the DOM.
|
`DirectoryTree` or in a parent widget in the DOM.
|
||||||
@@ -173,7 +173,7 @@ class DirectoryTree(Tree[DirEntry]):
|
|||||||
if not dir_entry.loaded:
|
if not dir_entry.loaded:
|
||||||
self.load_directory(event.node)
|
self.load_directory(event.node)
|
||||||
else:
|
else:
|
||||||
self.emit_no_wait(self.FileSelected(self, dir_entry.path))
|
self.post_message_no_wait(self.FileSelected(self, dir_entry.path))
|
||||||
|
|
||||||
def on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
|
def on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
|
||||||
event.stop()
|
event.stop()
|
||||||
@@ -181,4 +181,4 @@ class DirectoryTree(Tree[DirEntry]):
|
|||||||
if dir_entry is None:
|
if dir_entry is None:
|
||||||
return
|
return
|
||||||
if not dir_entry.is_dir:
|
if not dir_entry.is_dir:
|
||||||
self.emit_no_wait(self.FileSelected(self, dir_entry.path))
|
self.post_message_no_wait(self.FileSelected(self, dir_entry.path))
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class Input(Widget, can_focus=True):
|
|||||||
max_size: reactive[int | None] = reactive(None)
|
max_size: reactive[int | None] = reactive(None)
|
||||||
|
|
||||||
class Changed(Message, bubble=True):
|
class Changed(Message, bubble=True):
|
||||||
"""Emitted when the value changes.
|
"""Posted when the value changes.
|
||||||
|
|
||||||
Can be handled using `on_input_changed` in a subclass of `Input` or in a parent
|
Can be handled using `on_input_changed` in a subclass of `Input` or in a parent
|
||||||
widget in the DOM.
|
widget in the DOM.
|
||||||
@@ -155,7 +155,7 @@ class Input(Widget, can_focus=True):
|
|||||||
self.input: Input = sender
|
self.input: Input = sender
|
||||||
|
|
||||||
class Submitted(Message, bubble=True):
|
class Submitted(Message, bubble=True):
|
||||||
"""Emitted when the enter key is pressed within an `Input`.
|
"""Posted when the enter key is pressed within an `Input`.
|
||||||
|
|
||||||
Can be handled using `on_input_submitted` in a subclass of `Input` or in a
|
Can be handled using `on_input_submitted` in a subclass of `Input` or in a
|
||||||
parent widget in the DOM.
|
parent widget in the DOM.
|
||||||
@@ -244,7 +244,7 @@ class Input(Widget, can_focus=True):
|
|||||||
async def watch_value(self, value: str) -> None:
|
async def watch_value(self, value: str) -> None:
|
||||||
if self.styles.auto_dimensions:
|
if self.styles.auto_dimensions:
|
||||||
self.refresh(layout=True)
|
self.refresh(layout=True)
|
||||||
await self.emit(self.Changed(self, value))
|
await self.post_message(self.Changed(self, value))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cursor_width(self) -> int:
|
def cursor_width(self) -> int:
|
||||||
@@ -479,4 +479,4 @@ class Input(Widget, can_focus=True):
|
|||||||
self.cursor_position = 0
|
self.cursor_position = 0
|
||||||
|
|
||||||
async def action_submit(self) -> None:
|
async def action_submit(self) -> None:
|
||||||
await self.emit(self.Submitted(self, self.value))
|
await self.post_message(self.Submitted(self, self.value))
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class ListItem(Widget, can_focus=False):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def on_click(self, event: events.Click) -> None:
|
def on_click(self, event: events.Click) -> None:
|
||||||
self.emit_no_wait(self._ChildClicked(self))
|
self.post_message_no_wait(self._ChildClicked(self))
|
||||||
|
|
||||||
def watch_highlighted(self, value: bool) -> None:
|
def watch_highlighted(self, value: bool) -> None:
|
||||||
self.set_class(value, "--highlight")
|
self.set_class(value, "--highlight")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
|||||||
index = reactive(0, always_update=True)
|
index = reactive(0, always_update=True)
|
||||||
|
|
||||||
class Highlighted(Message, bubble=True):
|
class Highlighted(Message, bubble=True):
|
||||||
"""Emitted when the highlighted item changes.
|
"""Posted when the highlighted item changes.
|
||||||
|
|
||||||
Highlighted item is controlled using up/down keys.
|
Highlighted item is controlled using up/down keys.
|
||||||
Can be handled using `on_list_view_highlighted` in a subclass of `ListView`
|
Can be handled using `on_list_view_highlighted` in a subclass of `ListView`
|
||||||
@@ -50,7 +50,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
|||||||
self.item: ListItem | None = item
|
self.item: ListItem | None = item
|
||||||
|
|
||||||
class Selected(Message, bubble=True):
|
class Selected(Message, bubble=True):
|
||||||
"""Emitted when a list item is selected, e.g. when you press the enter key on it.
|
"""Posted when a list item is selected, e.g. when you press the enter key on it.
|
||||||
|
|
||||||
Can be handled using `on_list_view_selected` in a subclass of `ListView` or in
|
Can be handled using `on_list_view_selected` in a subclass of `ListView` or in
|
||||||
a parent widget in the DOM.
|
a parent widget in the DOM.
|
||||||
@@ -125,7 +125,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
|||||||
new_child = None
|
new_child = None
|
||||||
|
|
||||||
self._scroll_highlighted_region()
|
self._scroll_highlighted_region()
|
||||||
self.emit_no_wait(self.Highlighted(self, new_child))
|
self.post_message_no_wait(self.Highlighted(self, new_child))
|
||||||
|
|
||||||
def append(self, item: ListItem) -> AwaitMount:
|
def append(self, item: ListItem) -> AwaitMount:
|
||||||
"""Append a new ListItem to the end of the ListView.
|
"""Append a new ListItem to the end of the ListView.
|
||||||
@@ -155,7 +155,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
|||||||
|
|
||||||
def action_select_cursor(self) -> None:
|
def action_select_cursor(self) -> None:
|
||||||
selected_child = self.highlighted_child
|
selected_child = self.highlighted_child
|
||||||
self.emit_no_wait(self.Selected(self, selected_child))
|
self.post_message_no_wait(self.Selected(self, selected_child))
|
||||||
|
|
||||||
def action_cursor_down(self) -> None:
|
def action_cursor_down(self) -> None:
|
||||||
self.index += 1
|
self.index += 1
|
||||||
@@ -166,7 +166,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False):
|
|||||||
def on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:
|
def on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:
|
||||||
self.focus()
|
self.focus()
|
||||||
self.index = self.children.index(event.sender)
|
self.index = self.children.index(event.sender)
|
||||||
self.emit_no_wait(self.Selected(self, event.sender))
|
self.post_message_no_wait(self.Selected(self, event.sender))
|
||||||
|
|
||||||
def _scroll_highlighted_region(self) -> None:
|
def _scroll_highlighted_region(self) -> None:
|
||||||
"""Used to keep the highlighted index within vision"""
|
"""Used to keep the highlighted index within vision"""
|
||||||
|
|||||||
Reference in New Issue
Block a user