Commit 5c815df2 authored by hugoziviani's avatar hugoziviani 💬 Committed by Alexandru Băluț
Browse files

markers: Allow seeking to the previous/next markers

Fixes #2454
parent fdb7a05b
Pipeline #263183 passed with stages
in 17 minutes and 22 seconds
......@@ -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()
......
......@@ -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)
......
# -*- 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)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment