diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py index 5271d21fd3d2442d9499f5686b76c9d880caabae..5ea9341081d7651cfc715f453afcd057b427b347 100644 --- a/pitivi/timeline/timeline.py +++ b/pitivi/timeline/timeline.py @@ -278,7 +278,8 @@ class LayersLayout(Gtk.Layout, Zoomable, Loggable): """Sets the size of the scrollable area to fit the layers_vbox.""" self.log("The size of the layers_vbox changed: %sx%s", allocation.width, allocation.height) self.props.width = allocation.width - self.props.height = allocation.height + # The additional space is for the 'Add layer' button. + self.props.height = allocation.height + LAYER_HEIGHT / 2 class Timeline(Gtk.EventBox, Zoomable, Loggable): @@ -324,6 +325,12 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable): scrolled_window.add(self._layers_controls_vbox) hbox.pack_start(scrolled_window, False, False, 0) + self.add_layer_button = Gtk.Button.new_with_label("Add layer") + self.add_layer_button.props.margin = SPACING + self.add_layer_button.set_halign(Gtk.Align.CENTER) + self.add_layer_button.show() + self._layers_controls_vbox.pack_end(self.add_layer_button, False, False, 0) + self.get_style_context().add_class("Timeline") self.props.expand = True self.get_accessible().set_name("timeline canvas") @@ -1085,23 +1092,27 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable): self.debug("Layers still being shuffled, not updating widgets: %s", priorities) return self.debug("Updating layers widgets positions") - for ges_layer in self.ges_timeline.get_layers(): + self.__update_separator(0) + for ges_layer in ges_layers: self.__update_layer(ges_layer) + self.__update_separator(ges_layer.props.priority + 1) + + def __update_separator(self, priority): + """Sets the position of the separators in their parent.""" + position = priority * 2 + controls_separator, layers_separator = self._separators[priority] + vbox = self._layers_controls_vbox + vbox.child_set_property(controls_separator, "position", position) + vbox = self.layout.layers_vbox + vbox.child_set_property(layers_separator, "position", position) def __update_layer(self, ges_layer): """Sets the position of the layer and its controls in their parent.""" position = ges_layer.props.priority * 2 + 1 - - # Update the position of the LayerControls and Layer widgets and - # also the position of the separators below them. - controls_separator, layers_separator = self._separators[ges_layer.props.priority + 1] - vbox = self.layout.layers_vbox - vbox.child_set_property(ges_layer.ui, "position", position) - vbox.child_set_property(layers_separator, "position", position + 1) - vbox = self._layers_controls_vbox vbox.child_set_property(ges_layer.control_ui, "position", position) - vbox.child_set_property(controls_separator, "position", position + 1) + vbox = self.layout.layers_vbox + vbox.child_set_property(ges_layer.ui, "position", position) def _remove_layer(self, ges_layer): self.info("Removing layer: %s", ges_layer.props.priority) @@ -1620,6 +1631,13 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable): self.app.shortcuts.add("timeline.paste-clips", ["v"], _("Paste selected clips")) + self.add_layer_action = Gio.SimpleAction.new("add-layer", None) + self.timeline.add_layer_button.connect("clicked", self.__add_layer_cb) + self.add_layer_action.connect("activate", self.__add_layer_cb) + group.add_action(self.add_layer_action) + self.app.shortcuts.add("timeline.add-layer", ["n"], + _("Add layer")) + if in_devel(): self.gapless_action = Gio.SimpleAction.new("toggle-gapless-mode", None) self.gapless_action.connect("activate", self._gaplessmode_toggled_cb) @@ -1837,6 +1855,13 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable): self.__copied_group = copied_group_shallow_copy.copy(True) copied_group_shallow_copy.ungroup(recursive=False) + def __add_layer_cb(self, unused_action, unused_parameter): + with self.app.action_log.started("add layer", + finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline), + toplevel=True): + priority = len(self.ges_timeline.get_layers()) + self.timeline.create_layer(priority) + def _alignSelectedCb(self, unused_action, unused_parameter): if not self.ges_timeline: return diff --git a/tests/test_undo_timeline.py b/tests/test_undo_timeline.py index 99cdff38b5053e467cc7b831a9b37d6fb6962773..28e88dcfcdb3c726b646438ee667884568125cb0 100644 --- a/tests/test_undo_timeline.py +++ b/tests/test_undo_timeline.py @@ -21,7 +21,6 @@ from unittest import mock from gi.repository import Gdk from gi.repository import GES -from gi.repository import GLib from gi.repository import Gst from gi.repository import GstController from gi.repository import Gtk @@ -361,6 +360,38 @@ class TestLayerObserver(BaseTestUndoTimeline): self.action_log.redo() self.assertFalse(clip1 in self.getTimelineClips()) + def test_layer_added(self): + self.setup_timeline_container() + layers = self.timeline.get_layers() + self.assertEqual(len(layers), 1) + + clip = GES.TitleClip() + self.layer.add_clip(clip) + + self.timeline_container.add_layer_action.emit("activate", None) + + layers = self.timeline.get_layers() + self.assertEqual(len(layers), 2) + self.assertEqual(layers[0], self.layer) + self.check_layers(layers) + self.assertEqual(layers[0].get_clips(), [clip]) + self.assertEqual(layers[1].get_clips(), []) + + self.action_log.undo() + layers = self.timeline.get_layers() + self.assertEqual(len(layers), 1) + self.assertEqual(layers[0], self.layer) + self.check_layers(layers) + self.assertEqual(layers[0].get_clips(), [clip]) + + self.action_log.redo() + layers = self.timeline.get_layers() + self.assertEqual(len(layers), 2) + self.assertEqual(layers[0], self.layer) + self.check_layers(layers) + self.assertEqual(layers[0].get_clips(), [clip]) + self.assertEqual(layers[1].get_clips(), []) + def test_ungroup_group_clip(self): # This test is in TestLayerObserver because the relevant operations # recorded are clip-added and clip-removed. @@ -1065,8 +1096,6 @@ class TestDragDropUndo(BaseTestUndoTimeline): self.assertEqual(layers[0].get_clips(), []) self.assertEqual(layers[1].get_clips(), [clip]) - return clip, event, timeline_ui - def test_clip_dragged_to_create_layer_above_denied(self): """Checks clip dropped onto the separator above without hovering.""" clip, event, timeline_ui = self.clip_dragged_to_create_layer(False)