Improve auto-scrolling after pasting very large documents

There were 2 problems.

When pasting very large documents, the height calculations will be
temporarily incorrect while the content is rendered over several frames.
This is addressed by waiting for the UI to be idle to scroll.

Additionally, the scroll time (typically 200ms) needs an adjustment as
well. Starting at 200ms, it now scales linearly with distance, amounting
to roughly 4 seconds with Pandoc's user guide.
ft.font-size^2
Gonçalo Silva 2019-04-11 03:33:31 +01:00
parent bf657891c6
commit ac7e18b0b3
2 changed files with 20 additions and 20 deletions

View File

@ -1,47 +1,47 @@
class Scroller: class Scroller:
def __init__(self, scrolled_window, target_pos, source_pos, duration=2000): def __init__(self, scrolled_window, source_pos, target_pos):
super().__init__() super().__init__()
self.scrolled_window = scrolled_window self.scrolled_window = scrolled_window
self.target_pos = target_pos
self.source_pos = source_pos self.source_pos = source_pos
self.duration = duration self.target_pos = target_pos
self.duration = max(200, (target_pos - source_pos) / 50) * 1000
self.is_started = False self.is_started = False
self.is_setup = False
self.start_time = 0 self.start_time = 0
self.end_time = 0 self.end_time = 0
self.tick_callback_id = 0 self.tick_callback_id = 0
def start(self): def start(self):
self.tick_callback_id = self.scrolled_window.add_tick_callback(self.on_tick, self) self.is_started = True
self.tick_callback_id = self.scrolled_window.add_tick_callback(self.on_tick)
def end(self): def end(self):
self.is_started = False
self.scrolled_window.remove_tick_callback(self.tick_callback_id) self.scrolled_window.remove_tick_callback(self.tick_callback_id)
self.is_started = False
def do_start(self, time): def setup(self, time):
self.is_started = True
self.start_time = time self.start_time = time
self.end_time = time + self.duration * 100 self.end_time = time + self.duration
self.is_setup = True
@staticmethod def on_tick(self, widget, frame_clock):
def on_tick(widget, frame_clock, scroller):
def ease_out_cubic(value): def ease_out_cubic(value):
return pow(value - 1, 3) + 1 return pow(value - 1, 3) + 1
now = frame_clock.get_frame_time() now = frame_clock.get_frame_time()
if not scroller.is_started: if not self.is_setup:
scroller.do_start(now) self.setup(now)
if now < scroller.end_time: if now < self.end_time:
time = float(now - scroller.start_time) / float(scroller.end_time - scroller.start_time) time = float(now - self.start_time) / float(self.end_time - self.start_time)
else: else:
time = 1 time = 1
scroller.end() self.end()
time = ease_out_cubic(time) time = ease_out_cubic(time)
pos = scroller.source_pos + (time * (scroller.target_pos - scroller.source_pos)) pos = self.source_pos + (time * (self.target_pos - self.source_pos))
widget.get_vadjustment().props.value = pos widget.get_vadjustment().props.value = pos
return True return True

View File

@ -8,7 +8,7 @@ from uberwriter.text_view_drag_drop_handler import DragDropHandler, TARGET_URI,
from uberwriter.scroller import Scroller from uberwriter.scroller import Scroller
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject from gi.repository import Gtk, Gdk, GObject, GLib
import logging import logging
LOGGER = logging.getLogger('uberwriter') LOGGER = logging.getLogger('uberwriter')
@ -104,7 +104,7 @@ class TextView(Gtk.TextView):
def on_text_changed(self, *_): def on_text_changed(self, *_):
self.markup.apply() self.markup.apply()
self.scroll_to() GLib.idle_add(self.scroll_to)
def on_size_allocate(self, *_): def on_size_allocate(self, *_):
self.update_vertical_margin() self.update_vertical_margin()
@ -186,7 +186,7 @@ class TextView(Gtk.TextView):
if self.scroller and self.scroller.is_started: if self.scroller and self.scroller.is_started:
self.scroller.end() self.scroller.end()
if target_pos: if target_pos:
self.scroller = Scroller(scrolled_window, target_pos, va.props.value) self.scroller = Scroller(scrolled_window, va.props.value, target_pos)
self.scroller.start() self.scroller.start()
def on_mark_set(self, _text_buffer, _location, mark, _data=None): def on_mark_set(self, _text_buffer, _location, mark, _data=None):