[pitivi] timeline: Scroll to playhead only when necessary



commit 48837db68ab8062e2abcdeb1aa9663cc7cee501b
Author: Alexandru Băluț <alexandru balut gmail com>
Date:   Tue Oct 27 15:34:38 2015 +0100

    timeline: Scroll to playhead only when necessary
    
    Scrolling to playhead moves the timeline, so it can be disruptive when
    editing.
    
    Instead of using __disableCenterPlayhead to specify when scrolling to
    playhead should *not* be performed, now we know exactly when we want to
    scroll to playhead:
    - When zooming using the scale at the top-left of the timeline.
    - When playing and the playhead is about to exit the view.
    
    Only if the playhead is out of the view:
    - When seeking using the buttons below the viewer.
    - When changing the timecode under the viewer.
    - When seeking using the left/right keys.
    
    Reviewed-by: Thibault Saunier <tsaunier gnome org>
    Differential Revision: https://phabricator.freedesktop.org/D525

 pitivi/timeline/timeline.py |   70 ++++++++++++++++++++++++-------------------
 pitivi/utils/pipeline.py    |    5 ++-
 pitivi/utils/widgets.py     |    2 +-
 pitivi/viewer.py            |    8 +++--
 4 files changed, 49 insertions(+), 36 deletions(-)
---
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 933e4c0..0de0bfd 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -277,7 +277,6 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
         self.__last_position = 0
         self.__playhead = VerticalBar("PlayHead")
         self.layout.put(self.__playhead, self.nsToPixel(self.__last_position), 0)
-        self.__disableCenterPlayhead = False
         self._scrubbing = False
         self._scrolling = False
 
@@ -396,23 +395,43 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
     def _durationChangedCb(self, bTimeline, pspec):
         self.queue_draw()
 
-    def scrollToPlayhead(self,):
-        if self.__disableCenterPlayhead or self.parent.ruler.pressed:
-            self.__disableCenterPlayhead = False
-            return
-        self.debug("Scrolling to playhead")
+    def scrollToPlayhead(self, align=None, when_not_in_view=False):
+        """
+        Scroll so that the playhead is in view.
 
+        @param align: Where the playhead should be post-scroll.
+        @type align: L{Gtk.Align}
+        @param when_not_in_view: Whether to scroll only if the playhead is not
+                                 visible.
+        """
+        self.debug("Scrolling to playhead")
         self.__setLayoutSize()
-        self.hadj.set_value(self.nsToPixel(self.__last_position) -
-                            self.layout.get_allocation().width / 2)
+        layout_width = self.layout.get_allocation().width
+        if when_not_in_view:
+            x = self.nsToPixel(self.__last_position) - self.hadj.get_value()
+            if x >= 0 and x <= layout_width:
+                return
+
+        # Deciding the new position of the playhead in the timeline's view.
+        if align == Gtk.Align.START:
+            delta = 100
+        elif align == Gtk.Align.END:
+            delta = layout_width - 100
+        else:
+            # Center.
+            delta = layout_width / 2
+        self.hadj.set_value(self.nsToPixel(self.__last_position) - delta)
 
-    def _positionCb(self, unused_pipeline, position):
+    def _positionCb(self, pipeline, position):
         if self.__last_position == position:
             return
 
         self.__last_position = position
-        self.scrollToPlayhead()
-        self.layout.move(self.__playhead, max(0, self.nsToPixel(self.__last_position)), 0)
+        layout_width = self.layout.get_allocation().width
+        x = max(0, self.nsToPixel(self.__last_position))
+        self.layout.move(self.__playhead, x, 0)
+        if pipeline.playing() and x - self.hadj.get_value() > layout_width - 100:
+            self.scrollToPlayhead(Gtk.Align.START)
 
     # snapping indicator
     def _snapCb(self, unused_timeline, unused_obj1, unused_obj2, position):
@@ -523,6 +542,7 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
         return x, y
 
     # Gtk events management
+
     def __scrollEventCb(self, unused_widget, event):
         res, delta_x, delta_y = event.get_scroll_deltas()
         if not res:
@@ -547,26 +567,17 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
             elif delta_y < 0:
                 self.parent.scroll_up()
         elif event.get_state() & Gdk.ModifierType.CONTROL_MASK:
-            event_widget = self.get_event_widget(event)
-            x, unused_y = event_widget.translate_coordinates(self, event.x, event.y)
+            # Remember the time at the mouse position so it remains
+            # in the same position.
             x -= CONTROL_WIDTH
             mouse_position = self.pixelToNs(x + self.hadj.get_value())
-
-            rescroll = False
-            self.__disableCenterPlayhead = True
             if delta_y > 0:
-                rescroll = True
                 Zoomable.zoomOut()
-                self.queue_draw()
             elif delta_y < 0:
-                rescroll = True
                 Zoomable.zoomIn()
+            if delta_y > 0 or delta_y < 0:
                 self.queue_draw()
-            self.__disableCenterPlayhead = False
-
-            if rescroll:
-                diff = x - (self.layout.get_allocation().width / 2)
-                self.hadj.set_value(self.nsToPixel(mouse_position) - (self.layout.get_allocation().width / 
2) - diff)
+                self.hadj.set_value(self.nsToPixel(mouse_position) - x)
         else:
             if delta_y > 0:
                 self.parent.scroll_right()
@@ -579,8 +590,6 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
         event_widget = self.get_event_widget(event)
 
         self.debug("PRESSED %s", event)
-        self.__disableCenterPlayhead = True
-
         res, button = event.get_button()
         if res and button == 1:
             self.draggingElement = self._getParentOfType(event_widget, Clip)
@@ -917,8 +926,8 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
     # Interface Zoomable
     def zoomChanged(self):
         self.updatePosition()
-        self.layout.move(self.__playhead, self.nsToPixel(self.__last_position), 0)
-        self.scrollToPlayhead()
+        x = max(0, self.nsToPixel(self.__last_position))
+        self.layout.move(self.__playhead, x, 0)
         self.queue_draw()
 
     def __getEditingMode(self):
@@ -1556,9 +1565,6 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
         self.timeline.queue_draw()
         return False
 
-    def scrollToPlayhead(self):
-        self.timeline.scrollToPlayhead()
-
     def _deleteSelected(self, unused_action):
         if self.bTimeline:
             self.app.action_log.begin("delete clip")
@@ -1751,11 +1757,13 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
                 self._seeker.seekRelative(0 - Gst.SECOND)
             else:
                 self._project.pipeline.stepFrame(self._framerate, -1)
+            self.timeline.scrollToPlayhead(align=Gtk.Align.CENTER, when_not_in_view=True)
         elif event.keyval == Gdk.KEY_Right:
             if self._shiftMask:
                 self._seeker.seekRelative(Gst.SECOND)
             else:
                 self._project.pipeline.stepFrame(self._framerate, 1)
+            self.timeline.scrollToPlayhead(align=Gtk.Align.CENTER, when_not_in_view=True)
 
     def do_key_release_event(self, event):
         if event.keyval == Gdk.KEY_Shift_L:
diff --git a/pitivi/utils/pipeline.py b/pitivi/utils/pipeline.py
index 0c7dafe..4d85809 100644
--- a/pitivi/utils/pipeline.py
+++ b/pitivi/utils/pipeline.py
@@ -306,8 +306,11 @@ class SimplePipeline(GObject.Object, Loggable):
         """
         self.setState(Gst.State.READY)
 
+    def playing(self):
+        return self.getState() == Gst.State.PLAYING
+
     def togglePlayback(self):
-        if self.getState() == Gst.State.PLAYING:
+        if self.playing():
             self.pause()
         else:
             self.play()
diff --git a/pitivi/utils/widgets.py b/pitivi/utils/widgets.py
index 5dc614e..14da275 100644
--- a/pitivi/utils/widgets.py
+++ b/pitivi/utils/widgets.py
@@ -1074,7 +1074,7 @@ class ZoomBox(Gtk.Grid, Zoomable):
                                         "optional-action-type": True})
 
         if self._manual_set is False:
-            self.timeline.scrollToPlayhead()
+            self.timeline.timeline.scrollToPlayhead()
 
     def _zoomFitCb(self, unused_button):
         self.timeline.zoomFit()
diff --git a/pitivi/viewer.py b/pitivi/viewer.py
index d2eb20e..f548856 100644
--- a/pitivi/viewer.py
+++ b/pitivi/viewer.py
@@ -295,11 +295,9 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.target.setDisplayAspectRatio(ratio)
 
     def _entryActivateCb(self, unused_entry):
-        self._seekFromTimecodeWidget()
-
-    def _seekFromTimecodeWidget(self):
         nanoseconds = self.timecode_entry.getWidgetValue()
         self.seeker.seek(nanoseconds)
+        self.app.gui.timeline_ui.timeline.scrollToPlayhead(align=Gtk.Align.CENTER, when_not_in_view=True)
 
     # Active Timeline calllbacks
     def _durationChangedCb(self, unused_pipeline, duration):
@@ -315,21 +313,25 @@ class ViewerContainer(Gtk.Box, Loggable):
     def _goToStartCb(self, unused_button):
         self.seeker.seek(0)
         self.app.gui.focusTimeline()
+        self.app.gui.timeline_ui.timeline.scrollToPlayhead(align=Gtk.Align.START, when_not_in_view=True)
 
     def _backCb(self, unused_button):
         # Seek backwards one second
         self.seeker.seekRelative(0 - Gst.SECOND)
         self.app.gui.focusTimeline()
+        self.app.gui.timeline_ui.timeline.scrollToPlayhead(align=Gtk.Align.END, when_not_in_view=True)
 
     def _forwardCb(self, unused_button):
         # Seek forward one second
         self.seeker.seekRelative(Gst.SECOND)
         self.app.gui.focusTimeline()
+        self.app.gui.timeline_ui.timeline.scrollToPlayhead(align=Gtk.Align.START, when_not_in_view=True)
 
     def _goToEndCb(self, unused_button):
         end = self.app.project_manager.current_project.pipeline.getDuration()
         self.seeker.seek(end)
         self.app.gui.focusTimeline()
+        self.app.gui.timeline_ui.timeline.scrollToPlayhead(align=Gtk.Align.CENTER, when_not_in_view=True)
 
     # Public methods for controlling playback
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]