diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 1cc8b50a0..f96035dbc 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -562,10 +562,10 @@ class Compositor: screen_region = Region(0, 0, width, height) update_regions = self._dirty_regions.copy() + self._dirty_regions.clear() if screen_region in update_regions: # If one of the updates is the entire screen, then we only need one update - update_regions.clear() - self._dirty_regions.clear() + update_regions = {screen_region} if update_regions: # Create a crop regions that surrounds all updates @@ -632,7 +632,8 @@ class Compositor: def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: - yield self.render() + if self._dirty_regions: + yield self.render() def update_widgets(self, widgets: set[Widget]) -> None: """Update a given widget in the composition. diff --git a/src/textual/app.py b/src/textual/app.py index b8bbfdd76..117df8625 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -504,7 +504,7 @@ class App(Generic[ReturnType], DOMNode): self.log(f" loaded {self.css_path!r} in {elapsed:.0f} ms") except Exception as error: # TODO: Catch specific exceptions - self.console.bell() + self.bell() self.log(error) else: self.stylesheet = stylesheet @@ -672,13 +672,14 @@ class App(Generic[ReturnType], DOMNode): def fatal_error(self) -> None: """Exits the app after an unhandled exception.""" - self.console.bell() + self.bell() traceback = Traceback( show_locals=True, width=None, locals_max_length=5, suppress=[rich] ) self._exit_renderables.append( Segments(self.console.render(traceback, self.console.options)) ) + self._print_error_renderables() self.close_messages_no_wait() def _print_error_renderables(self) -> None: @@ -754,6 +755,7 @@ class App(Generic[ReturnType], DOMNode): ) if self._log_file is not None: self._log_file.close() + self._log_console = None def on_mount(self) -> None: widgets = list(self.compose()) diff --git a/src/textual/screen.py b/src/textual/screen.py index d5a5da141..e784d3a9b 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -91,6 +91,9 @@ class Screen(Widget): # Check for any widgets marked as 'dirty' (needs a repaint) if self._dirty_widgets: self._update_timer.resume() + if self._layout_required: + self._refresh_layout() + self._layout_required = False def _on_update(self) -> None: """Called by the _update_timer.""" @@ -101,7 +104,7 @@ class Screen(Widget): self._dirty_widgets.clear() self._update_timer.pause() - def refresh_layout(self) -> None: + def _refresh_layout(self) -> None: """Refresh the layout (can change size and positions of widgets).""" if not self.size: return @@ -151,14 +154,15 @@ class Screen(Widget): async def handle_layout(self, message: messages.Layout) -> None: message.stop() - self.refresh_layout() + self._layout_required = True + self.check_idle() def on_mount(self, event: events.Mount) -> None: self._update_timer = self.set_interval(1 / 20, self._on_update, pause=True) async def on_resize(self, event: events.Resize) -> None: self.size_updated(event.size, event.virtual_size, event.container_size) - self.refresh_layout() + self._refresh_layout() event.stop() async def _on_mouse_move(self, event: events.MouseMove) -> None: diff --git a/src/textual/views/_dock_view.py b/src/textual/views/_dock_view.py index 98367be6b..b859d9081 100644 --- a/src/textual/views/_dock_view.py +++ b/src/textual/views/_dock_view.py @@ -42,7 +42,7 @@ class DockView(Screen): await self.mount(widget) else: await self.mount(**{name: widget}) - await self.refresh_layout() + await self._refresh_layout() async def dock_grid( self, @@ -68,5 +68,5 @@ class DockView(Screen): await self.mount(view) else: await self.mount(**{name: view}) - await self.refresh_layout() + await self._refresh_layout() return grid diff --git a/src/textual/widget.py b/src/textual/widget.py index 82f2c2809..8e85cf6e1 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -775,9 +775,6 @@ class Widget(DOMNode): """Check if a layout has been requested.""" return self._layout_required - def _reset_check_layout(self) -> None: - self._layout_required = False - def get_style_at(self, x: int, y: int) -> Style: offset_x, offset_y = self.screen.get_offset(self) return self.screen.get_style_at(x + offset_x, y + offset_y) @@ -835,7 +832,7 @@ class Widget(DOMNode): """ if self.check_layout(): - self._reset_check_layout() + self._layout_required = False self.screen.post_message_no_wait(messages.Layout(self)) elif self._repaint_required: self.emit_no_wait(messages.Update(self, self)) @@ -881,7 +878,7 @@ class Widget(DOMNode): widgets = list(self.compose()) if widgets: self.mount(*widgets) - self.screen.refresh() + self.screen.refresh(repaint=False, layout=True) def on_leave(self) -> None: self.mouse_over = False