mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
event verbosity
This commit is contained in:
@@ -35,7 +35,7 @@ class GridTest(App):
|
|||||||
area3=Placeholder(name="area3"),
|
area3=Placeholder(name="area3"),
|
||||||
area4=Placeholder(name="area4"),
|
area4=Placeholder(name="area4"),
|
||||||
)
|
)
|
||||||
await self.view.update_layout()
|
self.view.refresh(layout=True)
|
||||||
|
|
||||||
|
|
||||||
GridTest.run(title="Grid Test", log="textual.log")
|
GridTest.run(title="Grid Test", log="textual.log")
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ class App(MessagePump):
|
|||||||
active_app.set(self)
|
active_app.set(self)
|
||||||
driver = self._driver = self.driver_class(self.console, self)
|
driver = self._driver = self.driver_class(self.console, self)
|
||||||
|
|
||||||
|
log("---")
|
||||||
log(f"driver={self.driver_class}")
|
log(f"driver={self.driver_class}")
|
||||||
|
|
||||||
await self.dispatch_message(events.Load(sender=self))
|
await self.dispatch_message(events.Load(sender=self))
|
||||||
@@ -332,7 +333,7 @@ class App(MessagePump):
|
|||||||
driver.disable_input()
|
driver.disable_input()
|
||||||
await self.close_messages()
|
await self.close_messages()
|
||||||
|
|
||||||
def refresh(self) -> None:
|
def refresh(self, repaint: bool = True, layout: bool = False) -> None:
|
||||||
sync_available = os.environ.get("TERM_PROGRAM", "") != "Apple_Terminal"
|
sync_available = os.environ.get("TERM_PROGRAM", "") != "Apple_Terminal"
|
||||||
if not self._closed:
|
if not self._closed:
|
||||||
console = self.console
|
console = self.console
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ class Event(Message):
|
|||||||
super().__init_subclass__(bubble=bubble, verbosity=verbosity)
|
super().__init_subclass__(bubble=bubble, verbosity=verbosity)
|
||||||
|
|
||||||
|
|
||||||
class Null(Event):
|
class Null(Event, verbosity=3):
|
||||||
def can_replace(self, message: Message) -> bool:
|
def can_replace(self, message: Message) -> bool:
|
||||||
return isinstance(message, Null)
|
return isinstance(message, Null)
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
class Callback(Event, bubble=False):
|
class Callback(Event, bubble=False, verbosity=3):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, sender: MessageTarget, callback: Callable[[], Awaitable[None]]
|
self, sender: MessageTarget, callback: Callable[[], Awaitable[None]]
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -88,7 +88,7 @@ class Action(Event, bubble=True):
|
|||||||
yield "action", self.action
|
yield "action", self.action
|
||||||
|
|
||||||
|
|
||||||
class Resize(Event):
|
class Resize(Event, verbosity=2):
|
||||||
"""Sent when the app or widget has been resized."""
|
"""Sent when the app or widget has been resized."""
|
||||||
|
|
||||||
__slots__ = ["size"]
|
__slots__ = ["size"]
|
||||||
@@ -360,7 +360,7 @@ class DoubleClick(MouseEvent):
|
|||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
class Timer(Event):
|
class Timer(Event, verbosity=3):
|
||||||
__slots__ = ["time", "count", "callback"]
|
__slots__ = ["time", "count", "callback"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|||||||
@@ -421,7 +421,6 @@ class Layout(ABC):
|
|||||||
|
|
||||||
def update_widget(self, console: Console, widget: Widget) -> LayoutUpdate | None:
|
def update_widget(self, console: Console, widget: Widget) -> LayoutUpdate | None:
|
||||||
|
|
||||||
log("UPDATE", widget)
|
|
||||||
if widget not in self.regions:
|
if widget not in self.regions:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -430,18 +429,7 @@ class Layout(ABC):
|
|||||||
if not region.size:
|
if not region.size:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
widget._clear_render_cache()
|
widget.clear_render_cache()
|
||||||
# if not region or not clip:
|
|
||||||
# return
|
|
||||||
|
|
||||||
# widget._clear_render_cache()
|
|
||||||
# widget.render_lines()
|
|
||||||
|
|
||||||
# new_lines = console.render_lines(
|
|
||||||
# widget, console.options.update_dimensions(region.width, region.height)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# self.regions[widget] = (region, clip, new_lines)
|
|
||||||
|
|
||||||
update_region = region.intersection(clip)
|
update_region = region.intersection(clip)
|
||||||
update_lines = self.render(console, update_region).lines
|
update_lines = self.render(console, update_region).lines
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class LayoutMap:
|
|||||||
sub_map = widget.layout.generate_map(
|
sub_map = widget.layout.generate_map(
|
||||||
console, region.size, clip, widget.scroll
|
console, region.size, clip, widget.scroll
|
||||||
)
|
)
|
||||||
for widget, (sub_region, sub_order, sub_clip) in sub_map.items():
|
for sub_widget, (sub_region, sub_order, sub_clip) in sub_map.items():
|
||||||
sub_region += region.origin
|
sub_region += region.origin
|
||||||
sub_clip = sub_clip.intersection(clip)
|
sub_clip = sub_clip.intersection(clip)
|
||||||
self.add_widget(console, widget, sub_region, sub_order, sub_clip)
|
self.add_widget(console, sub_widget, sub_region, sub_order, sub_clip)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import CancelledError
|
from asyncio import CancelledError
|
||||||
from asyncio import Queue, QueueEmpty, Task
|
from asyncio import Queue, QueueEmpty, Task
|
||||||
|
import inspect
|
||||||
from typing import TYPE_CHECKING, Awaitable, Iterable, Callable
|
from typing import TYPE_CHECKING, Awaitable, Iterable, Callable
|
||||||
from weakref import WeakSet
|
from weakref import WeakSet
|
||||||
|
|
||||||
@@ -24,6 +25,10 @@ class NoParent(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CallbackError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MessagePumpClosed(Exception):
|
class MessagePumpClosed(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -301,4 +306,11 @@ class MessagePump:
|
|||||||
event.prevent_default()
|
event.prevent_default()
|
||||||
event.stop()
|
event.stop()
|
||||||
if event.callback is not None:
|
if event.callback is not None:
|
||||||
await event.callback()
|
try:
|
||||||
|
callback_result = event.callback()
|
||||||
|
if inspect.isawaitable(callback_result):
|
||||||
|
await callback_result
|
||||||
|
except Exception as error:
|
||||||
|
raise CallbackError(
|
||||||
|
f"unable to run callback {event.callback!r}; {error}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
class UpdateMessage(Message):
|
class UpdateMessage(Message, verbosity=3):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
sender: MessagePump,
|
sender: MessagePump,
|
||||||
@@ -30,6 +30,6 @@ class UpdateMessage(Message):
|
|||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
class LayoutMessage(Message):
|
class LayoutMessage(Message, verbosity=3):
|
||||||
def can_replace(self, message: Message) -> bool:
|
def can_replace(self, message: Message) -> bool:
|
||||||
return isinstance(message, LayoutMessage)
|
return isinstance(message, LayoutMessage)
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ class Reactive(Generic[ReactiveType]):
|
|||||||
self.check_watchers(obj, name, current_value)
|
self.check_watchers(obj, name, current_value)
|
||||||
|
|
||||||
if self.layout:
|
if self.layout:
|
||||||
obj.require_layout()
|
obj.refresh(layout=True)
|
||||||
elif self.repaint:
|
elif self.repaint:
|
||||||
obj.require_repaint()
|
obj.refresh()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_watchers(cls, obj: Reactable, name: str, old_value: Any) -> None:
|
def check_watchers(cls, obj: Reactable, name: str, old_value: Any) -> None:
|
||||||
|
|||||||
@@ -57,40 +57,6 @@ class View(Widget):
|
|||||||
|
|
||||||
virtual_size: Reactive[Size] = Reactive(Size(0, 0))
|
virtual_size: Reactive[Size] = Reactive(Size(0, 0))
|
||||||
|
|
||||||
# @property
|
|
||||||
# def virtual_size(self) -> Dimensions:
|
|
||||||
# return self.layout.map.size if self.layout.map else Dimensions(0, 0)
|
|
||||||
|
|
||||||
# virtual_width: Reactive[int | None] = Reactive(None)
|
|
||||||
# virtual_height: Reactive[int | None] = Reactive(None)
|
|
||||||
|
|
||||||
# @property
|
|
||||||
# def virtual_size(self) -> Dimensions:
|
|
||||||
# virtual_width = self.virtual_width
|
|
||||||
# virtual_height = self.virtual_height
|
|
||||||
# return Dimensions(
|
|
||||||
# (virtual_width if virtual_width is not None else self.size.width),
|
|
||||||
# (virtual_height if virtual_height is not None else self.size.height),
|
|
||||||
# )
|
|
||||||
|
|
||||||
# @virtual_size.setter
|
|
||||||
# def virtual_size(self, size: tuple[int, int]) -> None:
|
|
||||||
# width, height = size
|
|
||||||
# self.virtual_width = width
|
|
||||||
# self.virtual_height = height
|
|
||||||
|
|
||||||
# @property
|
|
||||||
# def offset(self) -> Point:
|
|
||||||
# return Point(self.offset_x, self.offset_y)
|
|
||||||
|
|
||||||
# @property
|
|
||||||
# def viewport(self) -> Region:
|
|
||||||
# virtual_width = self.virtual_width
|
|
||||||
# virtual_height = self.virtual_height
|
|
||||||
# width = virtual_width if virtual_width is not None else self.size.width
|
|
||||||
# height = virtual_height if virtual_height is not None else self.size.height
|
|
||||||
# return Region(self.offset_x, self.offset_y, width, height)
|
|
||||||
|
|
||||||
def __rich_console__(
|
def __rich_console__(
|
||||||
self, console: Console, options: ConsoleOptions
|
self, console: Console, options: ConsoleOptions
|
||||||
) -> RenderResult:
|
) -> RenderResult:
|
||||||
@@ -109,7 +75,7 @@ class View(Widget):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_root_view(self) -> bool:
|
def is_root_view(self) -> bool:
|
||||||
return self._parent and self.parent is self.app
|
return bool(self._parent and self.parent is self.app)
|
||||||
|
|
||||||
def is_mounted(self, widget: Widget) -> bool:
|
def is_mounted(self, widget: Widget) -> bool:
|
||||||
return widget in self.widgets
|
return widget in self.widgets
|
||||||
@@ -159,16 +125,12 @@ class View(Widget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
width, height = self.console.size
|
width, height = self.console.size
|
||||||
# virtual_width, virtual_height = self.virtual_size
|
|
||||||
hidden, shown, resized = self.layout.reflow(
|
hidden, shown, resized = self.layout.reflow(
|
||||||
self.console, width, height, self.scroll
|
self.console, width, height, self.scroll
|
||||||
)
|
)
|
||||||
assert self.layout.map is not None
|
assert self.layout.map is not None
|
||||||
self.virtual_size = self.layout.map.virtual_size
|
# self.virtual_size = self.layout.map.virtual_size
|
||||||
# for widget, region in self.layout:
|
self.log("VIRTUAL_SIZE", self, type(self.layout), self.virtual_size)
|
||||||
# widget._update_size(region.size)
|
|
||||||
|
|
||||||
# self.app.refresh()
|
|
||||||
|
|
||||||
for widget in hidden:
|
for widget in hidden:
|
||||||
widget.post_message_no_wait(events.Hide(self))
|
widget.post_message_no_wait(events.Hide(self))
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ class WindowView(View, layout=VerticalLayout):
|
|||||||
await self.emit(VirtualSizeChange(self))
|
await self.emit(VirtualSizeChange(self))
|
||||||
|
|
||||||
async def watch_virtual_size(self, size: Size) -> None:
|
async def watch_virtual_size(self, size: Size) -> None:
|
||||||
|
self.log("VIRTUAL SIZE CHAGE")
|
||||||
await self.emit(VirtualSizeChange(self))
|
await self.emit(VirtualSizeChange(self))
|
||||||
|
|
||||||
async def on_resize(self, event: events.Resize) -> None:
|
# async def on_resize(self, event: events.Resize) -> None:
|
||||||
# self.layout.renders.pop(self.widget)
|
# # self.layout.renders.pop(self.widget)
|
||||||
self.require_repaint()
|
# self.require_repaint()
|
||||||
|
|||||||
@@ -123,10 +123,6 @@ class Widget(MessagePump):
|
|||||||
|
|
||||||
def _update_size(self, size: Size) -> None:
|
def _update_size(self, size: Size) -> None:
|
||||||
self._size = size
|
self._size = size
|
||||||
# if self._render_cache and self._render_cache.size != size:
|
|
||||||
# self.render_lines()
|
|
||||||
# self.require_repaint()
|
|
||||||
# self.size = size
|
|
||||||
|
|
||||||
def render_lines(self) -> RenderCache:
|
def render_lines(self) -> RenderCache:
|
||||||
width, height = self.size
|
width, height = self.size
|
||||||
@@ -161,7 +157,7 @@ class Widget(MessagePump):
|
|||||||
lines = self.render_cache.lines
|
lines = self.render_cache.lines
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _clear_render_cache(self) -> None:
|
def clear_render_cache(self) -> None:
|
||||||
self.render_cache = None
|
self.render_cache = None
|
||||||
|
|
||||||
def require_repaint(self) -> None:
|
def require_repaint(self) -> None:
|
||||||
@@ -199,17 +195,22 @@ class Widget(MessagePump):
|
|||||||
async def forward_event(self, event: events.Event) -> None:
|
async def forward_event(self, event: events.Event) -> None:
|
||||||
await self.post_message(event)
|
await self.post_message(event)
|
||||||
|
|
||||||
async def refresh(self) -> None:
|
def refresh(self, repaint: bool = True, layout: bool = False) -> None:
|
||||||
"""Re-render the window and repaint it."""
|
"""Initiate a refresh of the widget.
|
||||||
self.require_repaint()
|
|
||||||
await self.repaint()
|
|
||||||
|
|
||||||
async def repaint(self) -> None:
|
This method sets an internal flag to perform a refresh, which will be done on the
|
||||||
"""Instructs parent to repaint this widget."""
|
next idle event. Only one refresh will be done even if this method is called multiple times.
|
||||||
await self.emit(UpdateMessage(self, self))
|
|
||||||
|
|
||||||
async def update_layout(self) -> None:
|
Args:
|
||||||
await self.emit(LayoutMessage(self))
|
repaint (bool, optional): Repaint the widget (will call render() again). Defaults to True.
|
||||||
|
layout (bool, optional): Also layout widgets in the view. Defaults to False.
|
||||||
|
"""
|
||||||
|
if layout:
|
||||||
|
self._layout_required = True
|
||||||
|
elif repaint:
|
||||||
|
self.clear_render_cache()
|
||||||
|
self._repaint_required = True
|
||||||
|
self.post_message_no_wait(events.Null(self))
|
||||||
|
|
||||||
def render(self) -> RenderableType:
|
def render(self) -> RenderableType:
|
||||||
"""Get renderable for widget.
|
"""Get renderable for widget.
|
||||||
@@ -231,25 +232,17 @@ class Widget(MessagePump):
|
|||||||
self.log(self, "IS NOT RUNNING")
|
self.log(self, "IS NOT RUNNING")
|
||||||
return await super().post_message(message)
|
return await super().post_message(message)
|
||||||
|
|
||||||
# async def on_event(self, event: events.Event) -> None:
|
|
||||||
# if isinstance(event, events.Resize):
|
|
||||||
# if self.size != event.size:
|
|
||||||
# # self.size = event.size
|
|
||||||
# self.require_repaint()
|
|
||||||
# await super().on_event(event)
|
|
||||||
|
|
||||||
async def on_resize(self, event: events.Resize) -> None:
|
async def on_resize(self, event: events.Resize) -> None:
|
||||||
self.log("RESIZE", self)
|
|
||||||
self.render_lines()
|
self.render_lines()
|
||||||
|
|
||||||
async def on_idle(self, event: events.Idle) -> None:
|
async def on_idle(self, event: events.Idle) -> None:
|
||||||
if self.check_layout():
|
if self.check_layout():
|
||||||
self.reset_check_repaint()
|
self.reset_check_repaint()
|
||||||
self.reset_check_layout()
|
self.reset_check_layout()
|
||||||
await self.update_layout()
|
await self.emit(LayoutMessage(self))
|
||||||
elif self.check_repaint():
|
elif self.check_repaint():
|
||||||
self.reset_check_repaint()
|
self.reset_check_repaint()
|
||||||
await self.repaint()
|
await self.emit(UpdateMessage(self, self))
|
||||||
|
|
||||||
async def focus(self) -> None:
|
async def focus(self) -> None:
|
||||||
await self.app.set_focus(self)
|
await self.app.set_focus(self)
|
||||||
@@ -276,10 +269,6 @@ class Widget(MessagePump):
|
|||||||
if key_method is not None:
|
if key_method is not None:
|
||||||
await key_method()
|
await key_method()
|
||||||
|
|
||||||
# async def on_repaint(self) -> None:
|
|
||||||
# if self._render_cache is None or self._render_cache.size != self.size:
|
|
||||||
# self._render_cache = self.render_lines()
|
|
||||||
|
|
||||||
async def on_mouse_down(self, event: events.MouseUp) -> None:
|
async def on_mouse_down(self, event: events.MouseUp) -> None:
|
||||||
await self.broker_event("mouse.down", event)
|
await self.broker_event("mouse.down", event)
|
||||||
|
|
||||||
|
|||||||
@@ -185,12 +185,9 @@ class ScrollView(View):
|
|||||||
self.animate("y", self.target_y, speed=150, easing="out_cubic")
|
self.animate("y", self.target_y, speed=150, easing="out_cubic")
|
||||||
|
|
||||||
async def message_virtual_size_change(self, message: Message) -> None:
|
async def message_virtual_size_change(self, message: Message) -> None:
|
||||||
|
|
||||||
virtual_size = self.window.virtual_size
|
virtual_size = self.window.virtual_size
|
||||||
self.log("VIRTUAL_SIZE", self.size, virtual_size)
|
|
||||||
self.x = self.validate_x(self.x)
|
self.x = self.validate_x(self.x)
|
||||||
self.y = self.validate_y(self.y)
|
self.y = self.validate_y(self.y)
|
||||||
self.log(self.y)
|
|
||||||
self.vscroll.virtual_size = virtual_size.height
|
self.vscroll.virtual_size = virtual_size.height
|
||||||
self.vscroll.window_size = self.size.height
|
self.vscroll.window_size = self.size.height
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Static(Widget):
|
|||||||
self.padding = padding
|
self.padding = padding
|
||||||
|
|
||||||
def render(self) -> RenderableType:
|
def render(self) -> RenderableType:
|
||||||
self.log("RENDERING", self.renderable)
|
# self.log("RENDERING", self.renderable)
|
||||||
renderable = self.renderable
|
renderable = self.renderable
|
||||||
if self.padding:
|
if self.padding:
|
||||||
renderable = Padding(renderable, self.padding)
|
renderable = Padding(renderable, self.padding)
|
||||||
|
|||||||
Reference in New Issue
Block a user