simplify layout

This commit is contained in:
Will McGugan
2021-08-27 11:45:59 +01:00
parent 1e24aeadfd
commit c492df381e
6 changed files with 23 additions and 8 deletions

3
notes/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Developer notes
These are notes made by the developer, and _not_ to be considered documentation.

11
notes/refresh.md Normal file
View File

@@ -0,0 +1,11 @@
# Refresh system
This note describes how Textual updates widgets on-screen.
if widget has made some changes and wishes to update visuals it can call Widget.refresh. There are two flags on this method; `repaint` which will repaint just the widget, and `layout` which will re-layout the screen. A layout must be done if the widget has changed size / position / visibility. Otherwise repaint will refresh just the widget area.
A refresh won't happen immediately when `refresh()` is called, rather it sets internal flags. The `on_idle` method of Widget checks these flags. This is so that multiple changes made to the UI while processing events don't cause excessive repainting of the screen (which makes the UI slow and jumpy).
In the case of a repaint. The Widget.on_idle handler will emit (send to the parent) an UpdateMessage. This message will be handled by the parent view, which will update the widget (a particular part of the screen).
In the case of a layout. The Widget.on_idle handler will emit a LayoutMessage. This message will be handled by the parent view, which calls refresh_layout on the root view, which will layout and repaint the entire screen.

View File

@@ -74,6 +74,7 @@ class Size(NamedTuple):
height: int
def __bool__(self) -> bool:
"""A Size is Falsey if it has area 0"""
return self.width * self.height != 0
@property
@@ -193,10 +194,12 @@ class Region(NamedTuple):
@property
def x_max(self) -> int:
"""Maximum X value (non inclusive)"""
return self.x + self.width
@property
def y_max(self) -> int:
"""Maximum Y value (non inclusive)"""
return self.y + self.height
@property
@@ -226,10 +229,12 @@ class Region(NamedTuple):
@property
def x_range(self) -> range:
"""A range object for X coordinates"""
return range(self.x, self.x + self.width)
@property
def y_range(self) -> range:
"""A range object for Y coordinates"""
return range(self.y, self.y + self.height)
def __add__(self, other: Any) -> Region:

View File

@@ -13,10 +13,9 @@ if TYPE_CHECKING:
@rich.repr.auto
class UpdateMessage(Message, verbosity=3):
def __init__(self, sender: MessagePump, widget: Widget, layout: bool = False):
def __init__(self, sender: MessagePump, widget: Widget):
super().__init__(sender)
self.widget = widget
self.layout = layout
def __rich_repr__(self) -> rich.repr.Result:
yield self.sender
@@ -24,7 +23,7 @@ class UpdateMessage(Message, verbosity=3):
def __eq__(self, other: object) -> bool:
if isinstance(other, UpdateMessage):
return self.widget == other.widget and self.layout == other.layout
return self.widget == other.widget
return NotImplemented
def can_replace(self, message: Message) -> bool:

View File

@@ -94,15 +94,12 @@ class View(Widget):
widget = message.widget
assert isinstance(widget, Widget)
if message.layout:
await self.root_view.refresh_layout()
self.log("LAYOUT")
display_update = self.root_view.layout.update_widget(self.console, widget)
if display_update is not None:
self.app.display(display_update)
async def message_layout(self, message: LayoutMessage) -> None:
message.stop()
await self.root_view.refresh_layout()
self.app.refresh()

View File

@@ -288,7 +288,7 @@ class Widget(MessagePump):
elif self.check_repaint():
self.render_cache = None
self.reset_check_repaint()
await self.emit(UpdateMessage(self, self, layout=False))
await self.emit(UpdateMessage(self, self))
async def focus(self) -> None:
await self.app.set_focus(self)