mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Reactivity improvements
This commit is contained in:
@@ -414,6 +414,10 @@ class App(Generic[ReturnType], DOMNode):
|
||||
"""ReturnType | None: The return type of the app."""
|
||||
return self._return_value
|
||||
|
||||
def _post_mount(self):
|
||||
"""Called after the object has been mounted."""
|
||||
Reactive._initialize_object(self)
|
||||
|
||||
def animate(
|
||||
self,
|
||||
attribute: str,
|
||||
|
||||
@@ -5,7 +5,7 @@ from textual._easing import EASING
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.cli.previews.borders import TEXT
|
||||
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.widget import Widget
|
||||
from textual.widgets import Button, Footer, Label, Input
|
||||
@@ -23,8 +23,8 @@ class EasingButtons(Widget):
|
||||
|
||||
|
||||
class Bar(Widget):
|
||||
position = Reactive.init(START_POSITION)
|
||||
animation_running = Reactive(False)
|
||||
position = reactive(START_POSITION)
|
||||
animation_running = reactive(False)
|
||||
|
||||
DEFAULT_CSS = """
|
||||
|
||||
@@ -53,8 +53,8 @@ class Bar(Widget):
|
||||
|
||||
|
||||
class EasingApp(App):
|
||||
position = Reactive.init(START_POSITION)
|
||||
duration = Reactive.var(1.0)
|
||||
position = reactive(START_POSITION)
|
||||
duration = var(1.0)
|
||||
|
||||
def on_load(self):
|
||||
self.bind(
|
||||
|
||||
@@ -358,7 +358,10 @@ class MessagePump(metaclass=MessagePumpMeta):
|
||||
finally:
|
||||
# This is critical, mount may be waiting
|
||||
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:
|
||||
"""Process messages until the queue is closed."""
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Generic,
|
||||
Type,
|
||||
TypeVar,
|
||||
@@ -16,7 +17,7 @@ from typing import (
|
||||
import rich.repr
|
||||
|
||||
from . import events
|
||||
from ._callback import count_parameters, invoke
|
||||
from ._callback import count_parameters
|
||||
from ._types import MessageTarget
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -28,15 +29,6 @@ if TYPE_CHECKING:
|
||||
ReactiveType = TypeVar("ReactiveType")
|
||||
|
||||
|
||||
class _NotSet:
|
||||
pass
|
||||
|
||||
|
||||
_NOT_SET = _NotSet()
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
class Reactive(Generic[ReactiveType]):
|
||||
"""Reactive descriptor.
|
||||
@@ -50,7 +42,7 @@ class Reactive(Generic[ReactiveType]):
|
||||
compute: Run compute methods when attribute is changed. Defaults to True.
|
||||
"""
|
||||
|
||||
_reactives: TypeVar[dict[str, object]] = {}
|
||||
_reactives: ClassVar[dict[str, object]] = {}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -77,37 +69,6 @@ class Reactive(Generic[ReactiveType]):
|
||||
yield "always_update", self._always_update
|
||||
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
|
||||
def var(
|
||||
cls,
|
||||
|
||||
@@ -360,6 +360,10 @@ class Widget(DOMNode):
|
||||
def offset(self, offset: Offset) -> None:
|
||||
self.styles.offset = ScalarOffset.from_offset(offset)
|
||||
|
||||
def _post_mount(self):
|
||||
"""Called after the object has been mounted."""
|
||||
Reactive._initialize_object(self)
|
||||
|
||||
ExpectType = TypeVar("ExpectType", bound="Widget")
|
||||
|
||||
@overload
|
||||
|
||||
@@ -11,7 +11,7 @@ from rich.text import Text, TextType
|
||||
from .. import events
|
||||
from ..css._error_tools import friendly_list
|
||||
from ..message import Message
|
||||
from ..reactive import Reactive
|
||||
from ..reactive import reactive
|
||||
from ..widgets import Static
|
||||
|
||||
|
||||
@@ -151,13 +151,13 @@ class Button(Static, can_focus=True):
|
||||
ACTIVE_EFFECT_DURATION = 0.3
|
||||
"""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."""
|
||||
|
||||
variant = Reactive.init("default")
|
||||
variant = reactive("default")
|
||||
"""The variant name for the button."""
|
||||
|
||||
disabled = Reactive(False)
|
||||
disabled = reactive(False)
|
||||
"""The disabled state of the button; `True` if disabled, `False` if not."""
|
||||
|
||||
class Pressed(Message, bubble=True):
|
||||
|
||||
Reference in New Issue
Block a user