mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Fix frame rate limiter (#2318)
* simplify * fix for frame rate limiter * fix update * fix update * update comment * No need for lock * remove comment * fix for glitched test * force update * implement dim fix * docstrings * foreground fix * cached filters * cache default * fix for filter tests * docstring * optimization * Update src/textual/filter.py Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update src/textual/constants.py Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * fix cache * remove comment [skip ci] --------- Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
This commit is contained in:
@@ -245,19 +245,15 @@ class ChopsUpdate(CompositorUpdate):
|
|||||||
append(strip.render(console))
|
append(strip.render(console))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if x < x1:
|
strip = strip.crop(0, min(end, x2) - x)
|
||||||
strip = strip.crop(x1 - x, x2 - x)
|
append(move_to(x, y).segment.text)
|
||||||
append(move_to(x1, y).segment.text)
|
|
||||||
else:
|
|
||||||
strip = strip.crop(0, x2 - x)
|
|
||||||
append(move_to(x, y).segment.text)
|
|
||||||
|
|
||||||
append(strip.render(console))
|
append(strip.render(console))
|
||||||
|
|
||||||
if y != last_y:
|
if y != last_y:
|
||||||
append("\n")
|
append("\n")
|
||||||
|
|
||||||
return "".join(sequences)
|
terminal_sequences = "".join(sequences)
|
||||||
|
return terminal_sequences
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
yield from ()
|
yield from ()
|
||||||
|
|||||||
@@ -117,12 +117,8 @@ def dim_style(style: Style, background: Color, factor: float) -> Style:
|
|||||||
style
|
style
|
||||||
+ Style.from_color(
|
+ Style.from_color(
|
||||||
dim_color(
|
dim_color(
|
||||||
(
|
(background.rich_color if style.bgcolor.is_default else style.bgcolor),
|
||||||
background.rich_color
|
style.color,
|
||||||
if style.bgcolor.is_default
|
|
||||||
else (style.bgcolor or DEFAULT_COLOR)
|
|
||||||
),
|
|
||||||
style.color or DEFAULT_COLOR,
|
|
||||||
factor,
|
factor,
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ class Pilot(Generic[ReturnType]):
|
|||||||
delay: Seconds to pause, or None to wait for cpu idle.
|
delay: Seconds to pause, or None to wait for cpu idle.
|
||||||
"""
|
"""
|
||||||
# These sleep zeros, are to force asyncio to give up a time-slice,
|
# These sleep zeros, are to force asyncio to give up a time-slice,
|
||||||
|
self.app.screen._on_timer_update() # Force one last repaint
|
||||||
if delay is None:
|
if delay is None:
|
||||||
await wait_for_idle(0)
|
await wait_for_idle(0)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ from .widget import Widget
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Final
|
from typing_extensions import Final
|
||||||
|
|
||||||
# Screen updates will be batched so that they don't happen more often than 120 times per second:
|
# Screen updates will be batched so that they don't happen more often than 60 times per second:
|
||||||
UPDATE_PERIOD: Final[float] = 1 / 120
|
UPDATE_PERIOD: Final[float] = 1 / 60
|
||||||
|
|
||||||
ScreenResultType = TypeVar("ScreenResultType")
|
ScreenResultType = TypeVar("ScreenResultType")
|
||||||
"""The result type of a screen."""
|
"""The result type of a screen."""
|
||||||
@@ -437,42 +437,49 @@ class Screen(Generic[ScreenResultType], Widget):
|
|||||||
event.prevent_default()
|
event.prevent_default()
|
||||||
|
|
||||||
if not self.app._batch_count and self.is_current:
|
if not self.app._batch_count and self.is_current:
|
||||||
async with self.app._dom_lock:
|
if (
|
||||||
if self.is_current:
|
self._layout_required
|
||||||
if self._layout_required:
|
or self._scroll_required
|
||||||
self._refresh_layout()
|
or self._repaint_required
|
||||||
self._layout_required = False
|
or self._dirty_widgets
|
||||||
self._scroll_required = False
|
):
|
||||||
self._dirty_widgets.clear()
|
self._update_timer.resume()
|
||||||
elif self._scroll_required:
|
|
||||||
self._refresh_layout(scroll=True)
|
|
||||||
self._scroll_required = False
|
|
||||||
|
|
||||||
if self._repaint_required:
|
|
||||||
self._dirty_widgets.clear()
|
|
||||||
self._dirty_widgets.add(self)
|
|
||||||
self._repaint_required = False
|
|
||||||
|
|
||||||
if self._dirty_widgets:
|
|
||||||
self._update_timer.resume()
|
|
||||||
|
|
||||||
# The Screen is idle - a good opportunity to invoke the scheduled callbacks
|
# The Screen is idle - a good opportunity to invoke the scheduled callbacks
|
||||||
await self._invoke_and_clear_callbacks()
|
|
||||||
|
if self._callbacks:
|
||||||
|
self._on_timer_update()
|
||||||
|
|
||||||
def _on_timer_update(self) -> None:
|
def _on_timer_update(self) -> None:
|
||||||
"""Called by the _update_timer."""
|
"""Called by the _update_timer."""
|
||||||
# Render widgets together
|
|
||||||
if self._dirty_widgets and self.is_current:
|
|
||||||
self._compositor.update_widgets(self._dirty_widgets)
|
|
||||||
update = self._compositor.render_update(
|
|
||||||
screen_stack=self.app._background_screens
|
|
||||||
)
|
|
||||||
self.app._display(self, update)
|
|
||||||
self._dirty_widgets.clear()
|
|
||||||
if self._callbacks:
|
|
||||||
self.post_message(events.InvokeCallbacks())
|
|
||||||
|
|
||||||
self._update_timer.pause()
|
self._update_timer.pause()
|
||||||
|
if self.is_current:
|
||||||
|
if self._layout_required:
|
||||||
|
self._refresh_layout()
|
||||||
|
self._layout_required = False
|
||||||
|
self._scroll_required = False
|
||||||
|
self._dirty_widgets.clear()
|
||||||
|
elif self._scroll_required:
|
||||||
|
self._refresh_layout(scroll=True)
|
||||||
|
self._scroll_required = False
|
||||||
|
|
||||||
|
if self._repaint_required:
|
||||||
|
self._update_timer.resume()
|
||||||
|
self._dirty_widgets.clear()
|
||||||
|
self._dirty_widgets.add(self)
|
||||||
|
self._repaint_required = False
|
||||||
|
|
||||||
|
if self._dirty_widgets and self.is_current:
|
||||||
|
self._compositor.update_widgets(self._dirty_widgets)
|
||||||
|
update = self._compositor.render_update(
|
||||||
|
screen_stack=self.app._background_screens
|
||||||
|
)
|
||||||
|
self.app._display(self, update)
|
||||||
|
self._dirty_widgets.clear()
|
||||||
|
|
||||||
|
if self._callbacks:
|
||||||
|
self.post_message(events.InvokeCallbacks())
|
||||||
|
|
||||||
async def _on_invoke_callbacks(self, event: events.InvokeCallbacks) -> None:
|
async def _on_invoke_callbacks(self, event: events.InvokeCallbacks) -> None:
|
||||||
"""Handle PostScreenUpdate events, which are sent after the screen is updated"""
|
"""Handle PostScreenUpdate events, which are sent after the screen is updated"""
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ class Strip:
|
|||||||
cached_strip = Strip(
|
cached_strip = Strip(
|
||||||
filter.apply(self._segments, background), self._cell_length
|
filter.apply(self._segments, background), self._cell_length
|
||||||
)
|
)
|
||||||
|
self._filter_cache[(filter, background)] = cached_strip
|
||||||
return cached_strip
|
return cached_strip
|
||||||
|
|
||||||
def style_links(self, link_id: str, link_style: Style) -> Strip:
|
def style_links(self, link_id: str, link_style: Style) -> Strip:
|
||||||
@@ -312,7 +313,7 @@ class Strip:
|
|||||||
]
|
]
|
||||||
return Strip(segments, self._cell_length)
|
return Strip(segments, self._cell_length)
|
||||||
|
|
||||||
def crop(self, start: int, end: int) -> Strip:
|
def crop(self, start: int, end: int | None = None) -> Strip:
|
||||||
"""Crop a strip between two cell positions.
|
"""Crop a strip between two cell positions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -323,7 +324,7 @@ class Strip:
|
|||||||
A new Strip.
|
A new Strip.
|
||||||
"""
|
"""
|
||||||
start = max(0, start)
|
start = max(0, start)
|
||||||
end = min(self.cell_length, end)
|
end = self.cell_length if end is None else min(self.cell_length, end)
|
||||||
if start == 0 and end == self.cell_length:
|
if start == 0 and end == self.cell_length:
|
||||||
return self
|
return self
|
||||||
cache_key = (start, end)
|
cache_key = (start, end)
|
||||||
|
|||||||
Reference in New Issue
Block a user