[pitivi] markers: Allow seeking to the previous/next markers



commit 5c815df2d8b00bf739913db8476983161d4b5336
Author: Hugo Eduardo Ziviani <hugo_ziviani hotmail com>
Date:   Sun Jul 5 19:16:36 2020 -0300

    markers: Allow seeking to the previous/next markers
    
    Fixes #2454

 pitivi/timeline/markers.py     | 60 +++++++++++++++++++++++++++++++++++++++++-
 pitivi/timeline/timeline.py    |  4 +--
 tests/test_timeline_markers.py | 46 +++++++++++++++++++++++++++-----
 3 files changed, 100 insertions(+), 10 deletions(-)
---
diff --git a/pitivi/timeline/markers.py b/pitivi/timeline/markers.py
index e3b1286f3..ae6c0f6dc 100644
--- a/pitivi/timeline/markers.py
+++ b/pitivi/timeline/markers.py
@@ -16,6 +16,7 @@
 # License along with this program; if not, see <http://www.gnu.org/licenses/>.
 """Markers display and management."""
 from gettext import gettext as _
+from typing import Optional
 
 from gi.repository import Gdk
 from gi.repository import Gio
@@ -138,7 +139,64 @@ class MarkersBox(Gtk.EventBox, Zoomable, Loggable):
         self.add_marker_action.connect("activate", self._add_marker_cb)
         self.action_group.add_action(self.add_marker_action)
         self.app.shortcuts.add("markers.marker-add(@mx nothing)", ["<Primary><Shift>m"],
-                               self.add_marker_action, _("Add a marker"))
+                               self.add_marker_action,
+                               _("Add a marker"))
+
+        self.seek_backward_marker_action = Gio.SimpleAction.new("seek-backward-marker", None)
+        self.seek_backward_marker_action.connect("activate", self._seek_backward_marker_cb)
+        self.action_group.add_action(self.seek_backward_marker_action)
+        self.app.shortcuts.add("markers.seek-backward-marker", ["<Alt>Left"],
+                               self.seek_backward_marker_action,
+                               _("Seek to the first marker before the playhead"))
+
+        self.seek_forward_marker_action = Gio.SimpleAction.new("seek-forward-marker", None)
+        self.seek_forward_marker_action.connect("activate", self._seek_forward_marker_cb)
+        self.action_group.add_action(self.seek_forward_marker_action)
+        self.app.shortcuts.add("markers.seek-forward-marker", ["<Alt>Right"],
+                               self.seek_forward_marker_action,
+                               _("Seek to the first marker after the playhead"))
+
+    def _seek_backward_marker_cb(self, action, param):
+        current_position = self.app.project_manager.current_project.pipeline.get_position(fails=False)
+        position = self.first_marker(before=current_position)
+        if position is None:
+            return
+
+        self.app.project_manager.current_project.pipeline.simple_seek(position)
+        self.app.gui.editor.timeline_ui.timeline.scroll_to_playhead(align=Gtk.Align.CENTER, 
when_not_in_view=True)
+
+    def _seek_forward_marker_cb(self, action, param):
+        current_position = self.app.project_manager.current_project.pipeline.get_position(fails=False)
+        position = self.first_marker(after=current_position)
+        if position is None:
+            return
+
+        self.app.project_manager.current_project.pipeline.simple_seek(position)
+        self.app.gui.editor.timeline_ui.timeline.scroll_to_playhead(align=Gtk.Align.CENTER, 
when_not_in_view=True)
+
+    def first_marker(self, before: Optional[int] = None, after: Optional[int] = None) -> Optional[int]:
+        assert (after is not None) != (before is not None)
+
+        if after is not None:
+            start = after + 1
+            end = self.app.project_manager.current_project.ges_timeline.props.duration
+        else:
+            start = 0
+            end = before
+
+        if start >= end:
+            return None
+
+        markers_positions = list([ges_marker.props.position
+                                  for ges_marker in self.__markers_container.get_markers()
+                                  if start <= ges_marker.props.position < end])
+        if not markers_positions:
+            return None
+
+        if after is not None:
+            return min(markers_positions)
+        else:
+            return max(markers_positions)
 
     def _add_marker_cb(self, action, param):
         maybe = param.get_maybe()
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index e22d5e5d2..c9983464d 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -1768,14 +1768,14 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
         group.add_action(self.seek_forward_clip_action)
         self.app.shortcuts.add("timeline.seek-forward-clip", ["<Primary>Right"],
                                self.seek_forward_clip_action,
-                               _("Seeks to the first clip edge after the playhead."))
+                               _("Seek to the first clip edge after the playhead"))
 
         self.seek_backward_clip_action = Gio.SimpleAction.new("seek-backward-clip", None)
         self.seek_backward_clip_action.connect("activate", self._seek_backward_clip_cb)
         group.add_action(self.seek_backward_clip_action)
         self.app.shortcuts.add("timeline.seek-backward-clip", ["<Primary>Left"],
                                self.seek_backward_clip_action,
-                               _("Seeks to the first clip edge before the playhead."))
+                               _("Seek to the first clip edge before the playhead"))
 
         self.add_effect_action = Gio.SimpleAction.new("add-effect", None)
         self.add_effect_action.connect("activate", self.__add_effect_cb)
diff --git a/tests/test_timeline_markers.py b/tests/test_timeline_markers.py
index 14884fba3..eb65451b5 100644
--- a/tests/test_timeline_markers.py
+++ b/tests/test_timeline_markers.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # Pitivi video editor
-# Copyright (c) 2009, Alessandro Decina <alessandro d gmail com>
-# Copyright (c) 2014, Alex Băluț <alexandru balut gmail com>
+# Copyright (c) 2019, Millan Castro <m castrovilarino gmail com>
+# Copyright (c) 2021, Alex Băluț <alexandru balut gmail com>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -33,7 +33,6 @@ class TestMarkers(common.TestCase):
         """Checks the add marker UI."""
         markers = self.timeline.get_marker_list("markers")
         marker_box = self.timeline_container.markers
-        marker_box.markers_container = markers
 
         x = 100
         event = mock.Mock(spec=Gdk.EventButton)
@@ -56,7 +55,6 @@ class TestMarkers(common.TestCase):
         """Checks the remove marker UI."""
         markers = self.timeline.get_marker_list("markers")
         marker_box = self.timeline_container.markers
-        marker_box.markers_container = markers
 
         x = 200
         position = Zoomable.pixel_to_ns(x)
@@ -82,7 +80,6 @@ class TestMarkers(common.TestCase):
         """Checks the move marker UI."""
         markers = self.timeline.get_marker_list("markers")
         marker_box = self.timeline_container.markers
-        marker_box.markers_container = markers
 
         x1 = 300
         position1 = Zoomable.pixel_to_ns(x1)
@@ -110,13 +107,11 @@ class TestMarkers(common.TestCase):
 
         self.assert_markers(markers, [(position2, None)])
 
-    # pylint: disable=unbalanced-tuple-unpacking
     @common.setup_timeline
     def test_marker_comment_ui(self):
         """Checks the comments marker UI."""
         markers = self.timeline.get_marker_list("markers")
         marker_box = self.timeline_container.markers
-        marker_box.markers_container = markers
 
         x = 500
         position = Zoomable.pixel_to_ns(x)
@@ -152,3 +147,40 @@ class TestMarkers(common.TestCase):
         self.assertEqual(len(stack.done_actions), 1, stack.done_actions)
 
         self.assert_markers(markers, [(position, "comment")])
+
+    def check_seek(self, action, current_position, expected_position):
+        pipeline = self.project.pipeline
+        with mock.patch.object(pipeline, "get_position") as get_position:
+            get_position.return_value = current_position
+            with mock.patch.object(pipeline, "simple_seek") as simple_seek:
+                action.activate()
+                if expected_position is None:
+                    simple_seek.assert_not_called()
+                else:
+                    simple_seek.assert_called_once_with(expected_position)
+
+    @common.setup_timeline
+    def test_seeking_actions(self):
+        """Checks the seeking actions."""
+        # The seek logic ignores the markers past the timeline duration
+        # since it's not possible to seek there.
+        self.add_clip(self.timeline.layers[0], start=0, duration=20)
+
+        markers = self.timeline.get_marker_list("markers")
+        marker_box = self.timeline_container.markers
+
+        marker_box.markers_container.add(10)
+        marker_box.markers_container.add(12)
+        self.assert_markers(markers, [(10, None), (12, None)])
+
+        self.check_seek(marker_box.seek_forward_marker_action, 9, 10)
+        self.check_seek(marker_box.seek_forward_marker_action, 10, 12)
+        self.check_seek(marker_box.seek_forward_marker_action, 11, 12)
+        self.check_seek(marker_box.seek_forward_marker_action, 12, None)
+        self.check_seek(marker_box.seek_forward_marker_action, 13, None)
+
+        self.check_seek(marker_box.seek_backward_marker_action, 9, None)
+        self.check_seek(marker_box.seek_backward_marker_action, 10, None)
+        self.check_seek(marker_box.seek_backward_marker_action, 11, 10)
+        self.check_seek(marker_box.seek_backward_marker_action, 12, 10)
+        self.check_seek(marker_box.seek_backward_marker_action, 13, 12)


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