mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-09-14 04:45:08 +00:00
Merge pull request #849 from kdmukai/optimize_text_scroll_animation
[Performance] Scrolling text can cause delays moving to next screen
This commit is contained in:
@@ -392,7 +392,13 @@ class TextArea(BaseComponent):
|
||||
# Guaranteed to be a single line of text, possibly wider than self.width
|
||||
self.text_lines = [{"text": self.text, "text_width": full_text_width}]
|
||||
self.text_width = full_text_width
|
||||
if self.text_width > self.visible_width:
|
||||
|
||||
# Technically, the math says that we should enable scrolling for as little as
|
||||
# 1px beyond the available width, but it's not worth having the text twitch
|
||||
# back and forth by such a small amount.
|
||||
min_scrollable_diff = 1
|
||||
|
||||
if self.text_width > self.visible_width + min_scrollable_diff:
|
||||
# We'll have to left justify the text and scroll it (if scrolling is enabled,
|
||||
# otherwise it'll just run off the right edge).
|
||||
self.is_text_centered = False
|
||||
@@ -569,7 +575,7 @@ class TextArea(BaseComponent):
|
||||
self.horizontal_scroll_position = 0
|
||||
self.scroll_increment_sign = 1 # flip to negative to scroll text to the right
|
||||
|
||||
self.renderer = Renderer.get_instance()
|
||||
self.renderer = Renderer.get_instance()
|
||||
|
||||
|
||||
def stop_scrolling(self):
|
||||
@@ -590,45 +596,57 @@ class TextArea(BaseComponent):
|
||||
readability. 45px/sec is better but still perceptually a bit stuttery.
|
||||
"""
|
||||
max_scroll = self.rendered_text_img.width - self.visible_width
|
||||
last_render_time = None
|
||||
|
||||
# The scrolling pauses at the start and end of the text line. These vars track
|
||||
# when we started holding and how long we should hold for.
|
||||
hold_started_at = None
|
||||
cur_hold_duration = None
|
||||
|
||||
while self.keep_running:
|
||||
if not self.scrolling_active:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
with self.renderer.lock:
|
||||
if not self.scrolling_active:
|
||||
# We were stopped while waiting for the lock
|
||||
if cur_hold_duration is not None:
|
||||
# We're currently holding; see if we've held long enough
|
||||
hold_time_elapsed = time.time() - hold_started_at
|
||||
if hold_time_elapsed < cur_hold_duration:
|
||||
# Still have to hold longer; skip scrolling logic
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
else:
|
||||
# We've held long enough; reset the vars and resume scrolling
|
||||
hold_started_at = None
|
||||
cur_hold_duration = None
|
||||
|
||||
else:
|
||||
# We're not holding, but if we've reached either end, we need to start
|
||||
# holding.
|
||||
if self.horizontal_scroll_position == 0:
|
||||
# Pause on initial (left-justified) position...
|
||||
hold_started_at = time.time()
|
||||
cur_hold_duration = self.begin_hold_secs
|
||||
|
||||
# Next scroll direction will be left
|
||||
self.scroll_increment_sign = 1
|
||||
|
||||
# Don't count those pause seconds
|
||||
last_render_time = None
|
||||
continue
|
||||
|
||||
img = self.rendered_text_img.crop((self.horizontal_scroll_position, 0, self.horizontal_scroll_position + self.visible_width, self.rendered_text_img.height))
|
||||
self.renderer.canvas.paste(img, (self.screen_x, self.screen_y - self.scroll_y))
|
||||
self.renderer.show_image()
|
||||
elif self.horizontal_scroll_position == max_scroll:
|
||||
# ...and slight pause at end of scroll
|
||||
hold_started_at = time.time()
|
||||
cur_hold_duration = self.end_hold_secs
|
||||
|
||||
if self.horizontal_scroll_position == 0:
|
||||
# Pause on initial (left-justified) position...
|
||||
time.sleep(self.begin_hold_secs)
|
||||
# Don't count those pause seconds
|
||||
last_render_time = None
|
||||
|
||||
# Don't count those pause seconds
|
||||
last_render_time = None
|
||||
# Scroll will be to the right
|
||||
self.scroll_increment_sign = -1
|
||||
continue
|
||||
|
||||
# Scroll the text left
|
||||
self.scroll_increment_sign = 1
|
||||
|
||||
elif self.horizontal_scroll_position == max_scroll:
|
||||
# ...and slight pause at end of scroll
|
||||
time.sleep(self.end_hold_secs)
|
||||
|
||||
# Don't count those pause seconds
|
||||
last_render_time = None
|
||||
|
||||
# Scroll the text right
|
||||
self.scroll_increment_sign = -1
|
||||
|
||||
else:
|
||||
# No need to CPU limit when running in its own thread?
|
||||
time.sleep(0.02)
|
||||
|
||||
next_render_time = time.time()
|
||||
|
||||
if not last_render_time:
|
||||
@@ -636,18 +654,39 @@ class TextArea(BaseComponent):
|
||||
# "get off zero" for the real increment calc logic to kick in.
|
||||
scroll_position_increment = 1 * self.scroll_increment_sign
|
||||
else:
|
||||
# Calculate how far to scroll based on time elapsed since last render
|
||||
scroll_position_increment = int(self.horizontal_scroll_speed * (next_render_time - last_render_time) * self.scroll_increment_sign)
|
||||
|
||||
# Only render an update if we're going to move at least 1px
|
||||
if abs(scroll_position_increment) > 0:
|
||||
self.horizontal_scroll_position += scroll_position_increment
|
||||
self.horizontal_scroll_position = max(0, min(self.horizontal_scroll_position, max_scroll))
|
||||
# max: Don't over-scroll when returning to the left edge (0)
|
||||
# min: Don't over-scroll when revealing the right edge (max_scroll)
|
||||
self.horizontal_scroll_position = max(
|
||||
0,
|
||||
min(self.horizontal_scroll_position + scroll_position_increment, max_scroll)
|
||||
)
|
||||
|
||||
# Render the scroll update
|
||||
with self.renderer.lock:
|
||||
if not self.scrolling_active:
|
||||
# We were stopped while waiting for the lock
|
||||
continue
|
||||
|
||||
# The pre-rendered text img slides within a cropping window
|
||||
img = self.rendered_text_img.crop((self.horizontal_scroll_position, 0, self.horizontal_scroll_position + self.visible_width, self.rendered_text_img.height))
|
||||
self.renderer.canvas.paste(img, (self.screen_x, self.screen_y - self.scroll_y))
|
||||
self.renderer.show_image()
|
||||
|
||||
last_render_time = next_render_time
|
||||
|
||||
else:
|
||||
# Wait to accumulate more time before scrolling
|
||||
# Wait to accumulate more time so we can scroll at least 1px
|
||||
pass
|
||||
|
||||
# Free up the processor for a bit each loop
|
||||
time.sleep(0.02)
|
||||
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Even if we need to animate for scrolling, all instances should explicitly render
|
||||
|
||||
Reference in New Issue
Block a user