mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
docstrings and tidy
This commit is contained in:
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
|
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
|
||||||
from rich.segment import Segment, SegmentLines
|
from rich.segment import Segment, SegmentLines
|
||||||
from rich.style import Style
|
from rich.style import Style, StyleType
|
||||||
|
|
||||||
from .css.types import EdgeStyle
|
from .css.types import EdgeStyle
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
"""
|
||||||
|
Style properties are descriptors which allow the Styles object to accept different types when
|
||||||
|
setting attributes. This gives the developer more freedom in how to express style information.
|
||||||
|
|
||||||
|
Descriptors also play nicely with Mypy, which is aware that attributes can have different types
|
||||||
|
when setting and getting.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, NamedTuple, Sequence, TYPE_CHECKING
|
from typing import Iterable, NamedTuple, Sequence, TYPE_CHECKING
|
||||||
@@ -125,6 +134,20 @@ class Edges(NamedTuple):
|
|||||||
if left[0]:
|
if left[0]:
|
||||||
yield "left", left
|
yield "left", left
|
||||||
|
|
||||||
|
def spacing(self) -> tuple[int, int, int, int]:
|
||||||
|
"""Get spacing created by borders.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[int, int, int, int]: Spacing for top, right, bottom, and left.
|
||||||
|
"""
|
||||||
|
top, right, bottom, left = self
|
||||||
|
return (
|
||||||
|
1 if top[0] else 0,
|
||||||
|
1 if right[0] else 0,
|
||||||
|
1 if bottom[0] else 0,
|
||||||
|
1 if left[0] else 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BorderProperty:
|
class BorderProperty:
|
||||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
The StylesBuilder object takes tokens parsed from the CSS and converts
|
||||||
|
to the appropriate internal types.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import cast, Iterable, NoReturn
|
from typing import cast, Iterable, NoReturn
|
||||||
|
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.color import ANSI_COLOR_NAMES, Color
|
from rich.color import Color
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
from .constants import VALID_BORDER, VALID_EDGE, VALID_DISPLAY, VALID_VISIBILITY
|
from .constants import VALID_BORDER, VALID_EDGE, VALID_DISPLAY, VALID_VISIBILITY
|
||||||
from .errors import DeclarationError, StyleValueError
|
from .errors import DeclarationError
|
||||||
from ._error_tools import friendly_list
|
from ._error_tools import friendly_list
|
||||||
from .._easing import EASING
|
from .._easing import EASING
|
||||||
from ..geometry import Offset, Spacing, SpacingDimensions
|
from ..geometry import Spacing, SpacingDimensions
|
||||||
from .model import Declaration
|
from .model import Declaration
|
||||||
from .scalar import Scalar, ScalarOffset, Unit, ScalarError
|
from .scalar import Scalar, ScalarOffset, Unit, ScalarError
|
||||||
from .styles import DockGroup, Styles
|
from .styles import DockGroup, Styles
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
from dataclasses import dataclass, field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Space:
|
|
||||||
top: int = 0
|
|
||||||
right: int = 0
|
|
||||||
bottom: int = 0
|
|
||||||
left: int = 0
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{self.top}, {self.right}, {self.bottom}, {self.left}"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Edge:
|
|
||||||
line: str = "none"
|
|
||||||
style: str = "default"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Border:
|
|
||||||
top: Edge = field(default_factory=Edge)
|
|
||||||
right: Edge = field(default_factory=Edge)
|
|
||||||
bottom: Edge = field(default_factory=Edge)
|
|
||||||
left: Edge = field(default_factory=Edge)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Box:
|
|
||||||
padding: Space = field(default_factory=Space)
|
|
||||||
margin: Space = field(default_factory=Space)
|
|
||||||
border: Border = field(default_factory=Border)
|
|
||||||
outline: Border = field(default_factory=Border)
|
|
||||||
dispay: bool = True
|
|
||||||
visible: bool = True
|
|
||||||
text: str = ""
|
|
||||||
opacity: float = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from rich import print
|
|
||||||
|
|
||||||
box = Box()
|
|
||||||
print(box)
|
|
||||||
@@ -9,6 +9,15 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
def match(selector_sets: Iterable[SelectorSet], node: DOMNode) -> bool:
|
def match(selector_sets: Iterable[SelectorSet], node: DOMNode) -> bool:
|
||||||
|
"""Check if a given selector matches any of the given selector sets.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selector_sets (Iterable[SelectorSet]): Iterable of selector sets.
|
||||||
|
node (DOMNode): DOM node.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the node matches the selector, otherwise False.
|
||||||
|
"""
|
||||||
return any(
|
return any(
|
||||||
_check_selectors(selector_set.selectors, node) for selector_set in selector_sets
|
_check_selectors(selector_set.selectors, node) for selector_set in selector_sets
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
"""
|
||||||
|
A DOMQuery is a set of DOM nodes associated with a given CSS selector.
|
||||||
|
|
||||||
|
This set of nodes may be further filtered with the filter method. Additional methods apply
|
||||||
|
actions to the nodes in the query.
|
||||||
|
|
||||||
|
If this sounds like JQuery, a (once) popular JS library, it is no coincidence.
|
||||||
|
|
||||||
|
DOMQuery objects are typically created by Widget.filter method.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
@@ -38,6 +51,7 @@ class DOMQuery:
|
|||||||
return len(self._nodes)
|
return len(self._nodes)
|
||||||
|
|
||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
|
"""True if non-empty, otherwise False."""
|
||||||
return bool(self._nodes)
|
return bool(self._nodes)
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[DOMNode]:
|
def __iter__(self) -> Iterator[DOMNode]:
|
||||||
@@ -47,22 +61,39 @@ class DOMQuery:
|
|||||||
yield self._nodes
|
yield self._nodes
|
||||||
|
|
||||||
def filter(self, selector: str) -> DOMQuery:
|
def filter(self, selector: str) -> DOMQuery:
|
||||||
|
"""Filter this set by the given CSS selector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selector (str): A CSS selector.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DOMQuery: New DOM Query.
|
||||||
|
"""
|
||||||
selector_set = parse_selectors(selector)
|
selector_set = parse_selectors(selector)
|
||||||
query = DOMQuery()
|
query = DOMQuery()
|
||||||
query._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
query._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def first(self) -> DOMNode:
|
def first(self) -> DOMNode:
|
||||||
|
"""Get the first matched node.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DOMNode: A DOM Node.
|
||||||
|
"""
|
||||||
|
# TODO: Better response to empty query than an IndexError
|
||||||
return self._nodes[0]
|
return self._nodes[0]
|
||||||
|
|
||||||
def add_class(self, *class_names: str) -> None:
|
def add_class(self, *class_names: str) -> None:
|
||||||
|
"""Add the given class name(s) to nodes."""
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
node.add_class(*class_names)
|
node.add_class(*class_names)
|
||||||
|
|
||||||
def remove_class(self, *class_names: str) -> None:
|
def remove_class(self, *class_names: str) -> None:
|
||||||
|
"""Remove the given class names from the nodes."""
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
node.remove_class(*class_names)
|
node.remove_class(*class_names)
|
||||||
|
|
||||||
def toggle_class(self, *class_names: str) -> None:
|
def toggle_class(self, *class_names: str) -> None:
|
||||||
|
"""Toggle the given class names from matched nodes."""
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
node.toggle_class(*class_names)
|
node.toggle_class(*class_names)
|
||||||
|
|||||||
@@ -154,6 +154,16 @@ class Styles:
|
|||||||
"min_height",
|
"min_height",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gutter(self) -> Spacing:
|
||||||
|
"""Get the gutter (additional space reserved for margin / padding / border).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Spacing: [description]
|
||||||
|
"""
|
||||||
|
gutter = self.margin + self.padding + self.border.spacing
|
||||||
|
return gutter
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@lru_cache(maxsize=1024)
|
@lru_cache(maxsize=1024)
|
||||||
def parse(cls, css: str, path: str) -> Styles:
|
def parse(cls, css: str, path: str) -> Styles:
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ class DOMNode(MessagePump):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self) -> DOMNode:
|
def parent(self) -> DOMNode:
|
||||||
|
"""Get the parent node.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NoParent: If this is the root node.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DOMNode: The node which is the direct parent of this node.
|
||||||
|
"""
|
||||||
if self._parent is None:
|
if self._parent is None:
|
||||||
raise NoParent(f"{self} has no parent")
|
raise NoParent(f"{self} has no parent")
|
||||||
assert isinstance(self._parent, DOMNode)
|
assert isinstance(self._parent, DOMNode)
|
||||||
@@ -57,10 +65,24 @@ class DOMNode(MessagePump):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> str | None:
|
def id(self) -> str | None:
|
||||||
|
"""The ID of this node, or None if the node has no ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str | None): A Node ID or None.
|
||||||
|
"""
|
||||||
return self._id
|
return self._id
|
||||||
|
|
||||||
@id.setter
|
@id.setter
|
||||||
def id(self, new_id: str) -> str:
|
def id(self, new_id: str) -> str:
|
||||||
|
"""Sets the ID (may only be done once).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_id (str): ID for this node.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the ID has already been set.
|
||||||
|
|
||||||
|
"""
|
||||||
if self._id is not None:
|
if self._id is not None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Node 'id' attribute may not be changed once set (current id={self._id!r})"
|
"Node 'id' attribute may not be changed once set (current id={self._id!r})"
|
||||||
@@ -83,10 +105,20 @@ class DOMNode(MessagePump):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def css_type(self) -> str:
|
def css_type(self) -> str:
|
||||||
|
"""Gets the CSS type, used by the CSS.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A type used in CSS (lower cased class name).
|
||||||
|
"""
|
||||||
return self.__class__.__name__.lower()
|
return self.__class__.__name__.lower()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def css_path(self) -> list[DOMNode]:
|
def css_path(self) -> list[DOMNode]:
|
||||||
|
"""A list of nodes from the root to this node, forming a "path".
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[DOMNode]: List of Nodes, starting with the root and ending with this node.
|
||||||
|
"""
|
||||||
result: list[DOMNode] = [self]
|
result: list[DOMNode] = [self]
|
||||||
append = result.append
|
append = result.append
|
||||||
|
|
||||||
@@ -137,6 +169,11 @@ class DOMNode(MessagePump):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tree(self) -> Tree:
|
def tree(self) -> Tree:
|
||||||
|
"""Get a Rich tree object which will recursively render the structure of the node tree.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tree: A Rich object which may be printed.
|
||||||
|
"""
|
||||||
highlighter = ReprHighlighter()
|
highlighter = ReprHighlighter()
|
||||||
tree = Tree(highlighter(repr(self)))
|
tree = Tree(highlighter(repr(self)))
|
||||||
|
|
||||||
@@ -163,6 +200,11 @@ class DOMNode(MessagePump):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def add_child(self, node: DOMNode) -> None:
|
def add_child(self, node: DOMNode) -> None:
|
||||||
|
"""Add a new child node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node (DOMNode): A DOM node.
|
||||||
|
"""
|
||||||
self.children._append(node)
|
self.children._append(node)
|
||||||
node.set_parent(self)
|
node.set_parent(self)
|
||||||
|
|
||||||
|
|||||||
@@ -507,5 +507,14 @@ class Spacing(NamedTuple):
|
|||||||
return cls(top, right, bottom, left)
|
return cls(top, right, bottom, left)
|
||||||
raise ValueError(f"1, 2 or 4 integers required for spacing; {len(pad)} given")
|
raise ValueError(f"1, 2 or 4 integers required for spacing; {len(pad)} given")
|
||||||
|
|
||||||
|
def __add__(self, other: object) -> Spacing:
|
||||||
|
if isinstance(other, tuple):
|
||||||
|
top1, right1, bottom1, left1 = self
|
||||||
|
top2, right2, bottom2, left2 = other
|
||||||
|
return Spacing(
|
||||||
|
top1 + top2, right1 + right2, bottom1 + bottom2, left1 + left2
|
||||||
|
)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
NULL_OFFSET = Offset(0, 0)
|
NULL_OFFSET = Offset(0, 0)
|
||||||
|
|||||||
@@ -84,18 +84,12 @@ class View(Widget):
|
|||||||
def scroll(self) -> Offset:
|
def scroll(self) -> Offset:
|
||||||
return Offset(self.scroll_x, self.scroll_y)
|
return Offset(self.scroll_x, self.scroll_y)
|
||||||
|
|
||||||
# def __rich_console__(
|
|
||||||
# self, console: Console, options: ConsoleOptions
|
|
||||||
# ) -> RenderResult:
|
|
||||||
# return
|
|
||||||
# yield
|
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
yield "name", self.name
|
yield "name", self.name
|
||||||
|
|
||||||
def __getitem__(self, widget_name: str) -> Widget:
|
def __getitem__(self, widget_id: str) -> Widget:
|
||||||
try:
|
try:
|
||||||
return self.get_child(widget_name)
|
return self.get_child_by_id(widget_id)
|
||||||
except errors.MissingWidget as error:
|
except errors.MissingWidget as error:
|
||||||
raise KeyError(str(error))
|
raise KeyError(str(error))
|
||||||
|
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ class Widget(DOMNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
||||||
# if name is None:
|
if name is None:
|
||||||
# class_name = self.__class__.__name__
|
class_name = self.__class__.__name__
|
||||||
# Widget._counts.setdefault(class_name, 0)
|
Widget._counts.setdefault(class_name, 0)
|
||||||
# Widget._counts[class_name] += 1
|
Widget._counts[class_name] += 1
|
||||||
# _count = self._counts[class_name]
|
_count = self._counts[class_name]
|
||||||
# name = f"{class_name}{_count}"
|
name = f"{class_name}{_count}"
|
||||||
|
|
||||||
self._size = Size(0, 0)
|
self._size = Size(0, 0)
|
||||||
self._repaint_required = False
|
self._repaint_required = False
|
||||||
@@ -82,26 +82,6 @@ class Widget(DOMNode):
|
|||||||
|
|
||||||
super().__init__(name=name, id=id)
|
super().__init__(name=name, id=id)
|
||||||
|
|
||||||
# visible: Reactive[bool] = Reactive(True, layout=True)
|
|
||||||
layout_size: Reactive[int | None] = Reactive(None, layout=True)
|
|
||||||
layout_fraction: Reactive[int] = Reactive(1, layout=True)
|
|
||||||
layout_min_size: Reactive[int] = Reactive(1, layout=True)
|
|
||||||
# layout_offset_x: Reactive[float] = Reactive(0.0, layout=True)
|
|
||||||
# layout_offset_y: Reactive[float] = Reactive(0.0, layout=True)
|
|
||||||
|
|
||||||
# style: Reactive[str | None] = Reactive(None)
|
|
||||||
padding: Reactive[Spacing | None] = Reactive(None, layout=True)
|
|
||||||
margin: Reactive[Spacing | None] = Reactive(None, layout=True)
|
|
||||||
border: Reactive[str] = Reactive("none", layout=True)
|
|
||||||
border_style: Reactive[str] = Reactive("green")
|
|
||||||
border_title: Reactive[TextType] = Reactive("")
|
|
||||||
|
|
||||||
def validate_padding(self, padding: SpacingDimensions) -> Spacing:
|
|
||||||
return Spacing.unpack(padding)
|
|
||||||
|
|
||||||
def validate_margin(self, margin: SpacingDimensions) -> Spacing:
|
|
||||||
return Spacing.unpack(margin)
|
|
||||||
|
|
||||||
def __init_subclass__(cls, can_focus: bool = True) -> None:
|
def __init_subclass__(cls, can_focus: bool = True) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
cls.can_focus = can_focus
|
cls.can_focus = can_focus
|
||||||
@@ -117,16 +97,43 @@ class Widget(DOMNode):
|
|||||||
renderable = self.render_styled()
|
renderable = self.render_styled()
|
||||||
return renderable
|
return renderable
|
||||||
|
|
||||||
def get_child(self, name: str | None = None, id: str | None = None) -> Widget:
|
def get_child_by_id(self, id: str) -> Widget:
|
||||||
if name is not None:
|
"""Get a child with a given id.
|
||||||
for widget in self.children:
|
|
||||||
if widget.name == name:
|
Args:
|
||||||
return cast(Widget, widget)
|
id (str): A Widget id.
|
||||||
if id is not None:
|
|
||||||
for widget in self.children:
|
Raises:
|
||||||
if widget.id == id:
|
errors.MissingWidget: If the widget was not found.
|
||||||
return cast(Widget, widget)
|
|
||||||
raise errors.MissingWidget(f"Widget named {name!r} was not found in {self}")
|
Returns:
|
||||||
|
Widget: A child widget.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for widget in self.children:
|
||||||
|
if widget.id == id:
|
||||||
|
return cast(Widget, widget)
|
||||||
|
raise errors.MissingWidget(f"Widget with id=={id!r} was not found in {self}")
|
||||||
|
|
||||||
|
def get_child_by_name(self, name: str) -> Widget:
|
||||||
|
"""Get a child widget with a given name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): A name. Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.MissingWidget: If no Widget is found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Widget: A Widget with the given name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for widget in self.children:
|
||||||
|
if widget.name == name:
|
||||||
|
return cast(Widget, widget)
|
||||||
|
raise errors.MissingWidget(
|
||||||
|
f"Widget with name=={name!r} was not found in {self}"
|
||||||
|
)
|
||||||
|
|
||||||
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
|
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
|
||||||
watch(self, attribute_name, callback)
|
watch(self, attribute_name, callback)
|
||||||
@@ -196,12 +203,12 @@ class Widget(DOMNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def gutter(self) -> Spacing:
|
def gutter(self) -> Spacing:
|
||||||
mt, mr, mb, bl = self.margin or (0, 0, 0, 0)
|
"""Get additional space reserved by margin / padding / border.
|
||||||
pt, pr, pb, pl = self.padding or (0, 0, 0, 0)
|
|
||||||
border = 1 if self.border else 0
|
Returns:
|
||||||
gutter = Spacing(
|
Spacing: [description]
|
||||||
mt + pt + border, mr + pr + border, mb + pb + border, bl + pl + border
|
"""
|
||||||
)
|
gutter = self.styles.gutter
|
||||||
return gutter
|
return gutter
|
||||||
|
|
||||||
def on_style_change(self) -> None:
|
def on_style_change(self) -> None:
|
||||||
@@ -312,9 +319,20 @@ class Widget(DOMNode):
|
|||||||
await self.app.set_focus(self)
|
await self.app.set_focus(self)
|
||||||
|
|
||||||
async def capture_mouse(self, capture: bool = True) -> None:
|
async def capture_mouse(self, capture: bool = True) -> None:
|
||||||
|
"""Capture (or release) the mouse.
|
||||||
|
|
||||||
|
When captured, all mouse coordinates will go to this widget even when the pointer is not directly over the widget.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
capture (bool, optional): True to capture or False to release. Defaults to True.
|
||||||
|
"""
|
||||||
await self.app.capture_mouse(self if capture else None)
|
await self.app.capture_mouse(self if capture else None)
|
||||||
|
|
||||||
async def release_mouse(self) -> None:
|
async def release_mouse(self) -> None:
|
||||||
|
"""Release the mouse.
|
||||||
|
|
||||||
|
Mouse events will only be sent when the mouse is over the widget.
|
||||||
|
"""
|
||||||
await self.app.capture_mouse(None)
|
await self.app.capture_mouse(None)
|
||||||
|
|
||||||
async def broker_event(self, event_name: str, event: events.Event) -> bool:
|
async def broker_event(self, event_name: str, event: events.Event) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user