mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix for simple case
This commit is contained in:
@@ -3,10 +3,6 @@ from textual.app import App
|
||||
|
||||
class ExampleApp(App):
|
||||
|
||||
CSS = """
|
||||
|
||||
"""
|
||||
|
||||
COLORS = [
|
||||
"white",
|
||||
"maroon",
|
||||
@@ -22,15 +18,12 @@ class ExampleApp(App):
|
||||
|
||||
def on_mount(self):
|
||||
self.styles.background = "darkblue"
|
||||
self.bind("t", "tree")
|
||||
|
||||
def on_key(self, event):
|
||||
if event.key.isdigit():
|
||||
self.styles.background = self.COLORS[int(event.key)]
|
||||
self.bell()
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.tree)
|
||||
|
||||
app = ExampleApp()
|
||||
app.run()
|
||||
|
||||
@@ -5,7 +5,7 @@ from textual.widget import Widget
|
||||
class WidthApp(App):
|
||||
CSS = """
|
||||
Widget {
|
||||
background: blue;
|
||||
background: blue 50%;
|
||||
width: 50%;
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -12,7 +12,7 @@ TEXT = Text.from_markup(lorem)
|
||||
|
||||
|
||||
class TextWidget(Widget):
|
||||
def render(self, style):
|
||||
def render(self):
|
||||
return TEXT
|
||||
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ class Compositor:
|
||||
# Keep a copy of the old map because we're going to compare it with the update
|
||||
old_map = self.map.copy()
|
||||
old_widgets = old_map.keys()
|
||||
map, widgets = self._arrange_root(parent)
|
||||
map, widgets = self._arrange_root(parent, size)
|
||||
|
||||
# parent.log(map)
|
||||
|
||||
@@ -246,17 +246,16 @@ class Compositor:
|
||||
resized_widgets = {
|
||||
widget
|
||||
for widget, (region, *_) in map.items()
|
||||
if widget in old_widgets and widget.size != region.size
|
||||
if widget in old_widgets and old_map[widget].region.size != region.size
|
||||
}
|
||||
|
||||
# Gets pairs of tuples of (Widget, MapGeometry) which have changed
|
||||
# i.e. if something is moved / deleted / added
|
||||
screen = size.region
|
||||
|
||||
if screen not in self._dirty_regions:
|
||||
crop_screen = screen.intersection
|
||||
changes: set[tuple[Widget, MapGeometry]] = (
|
||||
self.map.items() ^ old_map.items()
|
||||
)
|
||||
changes = map.items() ^ old_map.items()
|
||||
self._dirty_regions.update(
|
||||
[
|
||||
crop_screen(map_geometry.visible_region)
|
||||
@@ -270,7 +269,9 @@ class Compositor:
|
||||
resized=resized_widgets,
|
||||
)
|
||||
|
||||
def _arrange_root(self, root: Widget) -> tuple[CompositorMap, set[Widget]]:
|
||||
def _arrange_root(
|
||||
self, root: Widget, size: Size
|
||||
) -> tuple[CompositorMap, set[Widget]]:
|
||||
"""Arrange a widgets children based on its layout attribute.
|
||||
|
||||
Args:
|
||||
@@ -282,7 +283,7 @@ class Compositor:
|
||||
"""
|
||||
|
||||
ORIGIN = Offset(0, 0)
|
||||
size = root.size
|
||||
|
||||
map: CompositorMap = {}
|
||||
widgets: set[Widget] = set()
|
||||
get_order = attrgetter("order")
|
||||
|
||||
@@ -526,7 +526,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.screen.refresh(layout=True)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Blank("red")
|
||||
return Blank()
|
||||
|
||||
def query(self, selector: str | None = None) -> DOMQuery:
|
||||
"""Get a DOM query in the current screen.
|
||||
@@ -737,6 +737,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
driver = self._driver = self.driver_class(self.console, self)
|
||||
driver.start_application_mode()
|
||||
with redirect_stdout(StdoutRedirector(self.devtools, self._log_file)): # type: ignore
|
||||
try:
|
||||
mount_event = events.Mount(sender=self)
|
||||
await self.dispatch_message(mount_event)
|
||||
@@ -745,8 +746,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.stylesheet.update(self)
|
||||
self.refresh()
|
||||
await self.animator.start()
|
||||
|
||||
with redirect_stdout(StdoutRedirector(self.devtools, self._log_file)): # type: ignore
|
||||
await self._ready()
|
||||
await super().process_messages()
|
||||
await self.animator.stop()
|
||||
@@ -872,7 +871,11 @@ class App(Generic[ReturnType], DOMNode):
|
||||
await self.close_messages()
|
||||
|
||||
def refresh(self, *, repaint: bool = True, layout: bool = False) -> None:
|
||||
self._display(self.screen._compositor)
|
||||
self.screen.refresh(repaint=repaint, layout=layout)
|
||||
# self._display(self.screen._compositor.render())
|
||||
|
||||
def _paint(self):
|
||||
self._display(self.screen._compositor.render())
|
||||
|
||||
def refresh_css(self, animate: bool = True) -> None:
|
||||
"""Refresh CSS.
|
||||
@@ -1052,11 +1055,11 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
async def handle_update(self, message: messages.Update) -> None:
|
||||
message.stop()
|
||||
self.app.refresh()
|
||||
self._paint()
|
||||
|
||||
async def handle_layout(self, message: messages.Layout) -> None:
|
||||
message.stop()
|
||||
self.app.refresh()
|
||||
self._paint()
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
if event.key == "tab":
|
||||
@@ -1071,6 +1074,9 @@ class App(Generic[ReturnType], DOMNode):
|
||||
await self.close_messages()
|
||||
|
||||
async def on_resize(self, event: events.Resize) -> None:
|
||||
event.stop()
|
||||
self.screen._screen_resized(event.size)
|
||||
|
||||
await self.screen.post_message(event)
|
||||
|
||||
async def action_press(self, key: str) -> None:
|
||||
|
||||
@@ -476,7 +476,6 @@ class Styles(StylesBase):
|
||||
return self._rules.get(rule, default)
|
||||
|
||||
def refresh(self, *, layout: bool = False) -> None:
|
||||
print(self, self.node, "REFRESH", layout)
|
||||
if self.node is not None:
|
||||
self.node.refresh(layout=layout)
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ class Blank:
|
||||
"""Draw solid background color."""
|
||||
|
||||
def __init__(self, color: Color | str = "transparent") -> None:
|
||||
background = (
|
||||
color.rich_color
|
||||
if isinstance(color, Color)
|
||||
else Color.parse(color).rich_color
|
||||
background = color if isinstance(color, Color) else Color.parse(color)
|
||||
self._style = (
|
||||
Style()
|
||||
if background.is_transparent
|
||||
else Style.from_color(None, background.rich_color)
|
||||
)
|
||||
self._style = Style.from_color(None, background)
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
|
||||
@@ -9,9 +9,11 @@ from rich.style import Style
|
||||
|
||||
from . import events, messages, errors
|
||||
|
||||
from .geometry import Offset, Region
|
||||
from .color import Color
|
||||
from .geometry import Offset, Region, Size
|
||||
from ._compositor import Compositor, MapGeometry
|
||||
from .reactive import Reactive
|
||||
from .renderables.blank import Blank
|
||||
from .widget import Widget
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
@@ -31,10 +33,6 @@ class Screen(Widget):
|
||||
Screen {
|
||||
layout: vertical;
|
||||
overflow-y: auto;
|
||||
/*
|
||||
background: $surface;
|
||||
color: $text-surface;
|
||||
*/
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -45,11 +43,15 @@ class Screen(Widget):
|
||||
self._compositor = Compositor()
|
||||
self._dirty_widgets: set[Widget] = set()
|
||||
|
||||
@property
|
||||
def is_transparent(self) -> bool:
|
||||
return False
|
||||
|
||||
def watch_dark(self, dark: bool) -> None:
|
||||
pass
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return self.app.render()
|
||||
return Blank()
|
||||
|
||||
def get_offset(self, widget: Widget) -> Offset:
|
||||
"""Get the absolute offset of a given Widget.
|
||||
@@ -114,15 +116,16 @@ class Screen(Widget):
|
||||
self._dirty_widgets.clear()
|
||||
self._update_timer.pause()
|
||||
|
||||
def _refresh_layout(self) -> None:
|
||||
def _refresh_layout(self, size: Size | None = None, full: bool = False) -> None:
|
||||
"""Refresh the layout (can change size and positions of widgets)."""
|
||||
if not self.size:
|
||||
size = self.size if size is None else size
|
||||
if not size:
|
||||
return
|
||||
# This paint the entire screen, so replaces the batched dirty widgets
|
||||
|
||||
self._compositor.update_widgets(self._dirty_widgets)
|
||||
self._update_timer.pause()
|
||||
try:
|
||||
hidden, shown, resized = self._compositor.reflow(self, self.size)
|
||||
hidden, shown, resized = self._compositor.reflow(self, size)
|
||||
|
||||
Hide = events.Hide
|
||||
Show = events.Show
|
||||
@@ -131,6 +134,7 @@ class Screen(Widget):
|
||||
for widget in shown:
|
||||
widget.post_message_no_wait(Show(self))
|
||||
|
||||
# We want to send a resize event to widgets that were just added or change since last layout
|
||||
send_resize = shown | resized
|
||||
|
||||
for (
|
||||
@@ -151,7 +155,7 @@ class Screen(Widget):
|
||||
self.app.on_exception(error)
|
||||
return
|
||||
|
||||
display_update = self._compositor.render()
|
||||
display_update = self._compositor.render(full=full)
|
||||
if display_update is not None:
|
||||
self.app._display(display_update)
|
||||
|
||||
@@ -172,9 +176,13 @@ class Screen(Widget):
|
||||
UPDATE_PERIOD, self._on_update, name="screen_update", pause=True
|
||||
)
|
||||
|
||||
def _screen_resized(self, size: Size):
|
||||
self._refresh_layout(size, full=True)
|
||||
|
||||
async def on_resize(self, event: events.Resize) -> None:
|
||||
self.size_updated(event.size, event.virtual_size, event.container_size)
|
||||
event.stop()
|
||||
# self._size = event.size
|
||||
# self._refresh_layout(event.size, full=True)
|
||||
|
||||
async def _on_mouse_move(self, event: events.MouseMove) -> None:
|
||||
try:
|
||||
|
||||
@@ -769,7 +769,6 @@ class Widget(DOMNode):
|
||||
self._size = size
|
||||
self._virtual_size = virtual_size
|
||||
self._container_size = container_size
|
||||
|
||||
if self.is_container:
|
||||
self._refresh_scrollbars()
|
||||
width, height = self.container_size
|
||||
|
||||
Reference in New Issue
Block a user