[pitivi/gtktimeline] timeline: Rework layer controls



commit 7db4d908492508fd3b6c71f783586d6699c08662
Author: Thibault Saunier <tsaunier gnome org>
Date:   Tue May 19 11:28:00 2015 +0200

    timeline: Rework layer controls
    
    Summary:
      - Remove widget backing not implemented features.
      - Implement layer naming
      - Use a popover menu and a menu button to control layers
    
      + Fix layers reordering
    
    Reviewers: Mathieu_Du
    
    Differential Revision: http://phabricator.freedesktop.org/D188

 pitivi/timeline/layer.py    |  302 ++++++++++++-------------------------------
 pitivi/timeline/timeline.py |   40 +++++--
 pitivi/utils/ui.py          |    2 +-
 3 files changed, 115 insertions(+), 229 deletions(-)
---
diff --git a/pitivi/timeline/layer.py b/pitivi/timeline/layer.py
index 2f64b59..f5b2c1a 100644
--- a/pitivi/timeline/layer.py
+++ b/pitivi/timeline/layer.py
@@ -40,7 +40,7 @@ class BaseLayerControl(Gtk.Box, Loggable):
 
     __gtype_name__ = 'LayerControl'
 
-    def __init__(self, control_container, layer, layer_type, app):
+    def __init__(self, control_container, layer, app, type_name):
         Gtk.Box.__init__(self, spacing=0)
         Loggable.__init__(self)
 
@@ -48,6 +48,8 @@ class BaseLayerControl(Gtk.Box, Loggable):
         self._control_container = control_container
         self.layer = layer
         self._selected = False
+        self.__type_name = type_name
+        self.__meta_name = type_name + "::name"
 
         context = self.get_style_context()
 
@@ -62,99 +64,28 @@ class BaseLayerControl(Gtk.Box, Loggable):
         self.set_orientation(Gtk.Orientation.VERTICAL)
 
         table = Gtk.Table(n_rows=2, n_columns=2)
-        table.set_border_width(2)
+        table.set_border_width(ui.PADDING)
         table.set_row_spacings(3)
         table.set_col_spacings(3)
 
-        self.eventbox = Gtk.EventBox()
-        self.eventbox.add(table)
-        self.eventbox.connect("button_press_event", self._buttonPressCb)
-        self.pack_start(self.eventbox, True, True, 0)
+        self.pack_start(table, True, True, 0)
 
-        icon_mapping = {GES.TrackType.AUDIO: "audio-x-generic",
-                        GES.TrackType.VIDEO: "video-x-generic"}
-
-        # Folding button
-        # TODO use images
-        fold_button = TwoStateButton("▼", "▶")
-        fold_button.set_relief(Gtk.ReliefStyle.NONE)
-        fold_button.set_focus_on_click(False)
-        fold_button.connect("changed-state", self._foldingChangedCb)
-        table.attach(fold_button, 0, 1, 0, 1)
-
-        # Name entry
         self.name_entry = Gtk.Entry()
         self.name_entry.set_tooltip_text(
             _("Set a personalized name for this layer"))
-        self.name_entry.set_property(
-            "primary-icon-name", icon_mapping[layer_type])
-        self.name_entry.connect("button_press_event", self._buttonPressCb)
-#        self.name_entry.drag_dest_unset()
-        self.name_entry.set_sensitive(False)
-
-        # 'Solo' toggle button
-        self.solo_button = Gtk.ToggleButton()
-        self.solo_button.set_tooltip_markup(_("<b>Solo mode</b>\n"
-                                              "Other non-soloed layers will be disabled as long as "
-                                              "this is enabled."))
-        solo_image = Gtk.Image()
-        solo_image.set_from_icon_name(
-            "avatar-default-symbolic", Gtk.IconSize.MENU)
-        self.solo_button.add(solo_image)
-        self.solo_button.connect("toggled", self._soloToggledCb)
-        self.solo_button.set_relief(Gtk.ReliefStyle.NONE)
-        self.solo_button.set_sensitive(False)
-
-        # CheckButton
-        visible_option = Gtk.CheckButton()
-        visible_option.connect("toggled", self._visibilityChangedCb)
-        visible_option.set_active(True)
-        visible_option.set_sensitive(False)
-        visible_option.set_tooltip_markup(_("<b>Enable or disable this layer</b>\n"
-                                            "Disabled layers will not play nor render."))
-
-        # Upper bar
-        upper = Gtk.Box()
-        upper.set_orientation(Gtk.Orientation.HORIZONTAL)
-        upper.pack_start(self.name_entry, True, True, 0)
-        upper.pack_start(self.solo_button, False, False, 0)
-        upper.pack_start(visible_option, False, False, 0)
-
-        # Lower bar
-        self.lower_hbox = Gtk.Box()
-        self.lower_hbox.set_orientation(Gtk.Orientation.HORIZONTAL)
-        self.lower_hbox.set_sensitive(False)
-
-        table.attach(upper, 1, 2, 0, 1)
-        table.attach(self.lower_hbox, 1, 2, 1, 2)
+        self.name_entry.set_property("secondary-icon-name", self._getIconName())
+        self.name_entry.connect("key-press-event", self._keyPressCb)
+        if layer.bLayer.get_meta(self.__meta_name) is None:
+            self.layer.bLayer.set_meta(self.__meta_name, '%s %d'
+                                       % (self.__type_name, layer.bLayer.get_priority()))
+        self.name_entry.set_text(self.layer.bLayer.get_meta(self.__meta_name))
 
-        self.show_all()
+        table.attach(self.name_entry, 0, 2, 0, 2)
 
-        # Popup Menu
-        self.popup = Gtk.Menu()
-        layer_delete = Gtk.MenuItem.new_with_label(_("_Delete layer"))
-        layer_delete.connect("activate", self._deleteLayerCb)
-        self.layer_up = Gtk.MenuItem.new_with_label(_("Move layer up"))
-        self.layer_up.connect("activate", self._moveLayerCb, -1)
-        self.layer_down = Gtk.MenuItem.new_with_label(_("Move layer down"))
-        self.layer_down.connect("activate", self._moveLayerCb, 1)
-        self.layer_first = Gtk.MenuItem.new_with_label(_("Move layer to top"))
-        self.layer_first.connect("activate", self._moveLayerCb, -2)
-        self.layer_last = Gtk.MenuItem.new_with_label(
-            _("Move layer to bottom"))
-        self.layer_last.connect("activate", self._moveLayerCb, 2)
-
-        self.popup.append(self.layer_first)
-        self.popup.append(self.layer_up)
-        self.popup.append(self.layer_down)
-        self.popup.append(self.layer_last)
-        self.popup.append(Gtk.SeparatorMenuItem())
-        self.popup.append(layer_delete)
-        for menu_item in self.popup:
-            menu_item.set_use_underline(True)
-        self.popup.show_all()
+        self.show_all()
 
-        # Drag and drop
+    def _getIconName(self):
+        return None
 
     def getSelected(self):
         return self._selected
@@ -166,75 +97,23 @@ class BaseLayerControl(Gtk.Box, Loggable):
 
     selected = property(getSelected, setSelected, None, "Selection state")
 
-    def _foldingChangedCb(self, unused_button, state):
-        if state:
-            self.lower_hbox.show()
-        else:
-            self.lower_hbox.hide()
-
-    def _visibilityChangedCb(self, button):
-        if button.get_active():
-            button.set_tooltip_text(_("Make layer invisible"))
-        else:
-            button.set_tooltip_text(_("Make layer visible"))
-
-    def _soloToggledCb(self, button):
-        """
-        Send TimelineControls the new solo-ed layer
-        """
-        if button.get_active():
-            # Disable all other layers
-            self._app.gui.timeline_ui.controls.soloLayer(self.layer)
-        else:
-            # Enable all layers
-            self._app.gui.timeline_ui.controls.soloLayer(None)
-
-    def _buttonPressCb(self, unused_widget, event):
-        """
-        Look if user selected layer or wants popup menu
-        """
-        # FIXME!! self._control_container.selectLayerControl(self)
-        if event.button == 3:
-            self.popup.popup(None, None, None, None, event.button, event.time)
-
     def _selectionChangedCb(self):
         """
         Called when the selection state changes
         """
         if self.selected:
-            self.eventbox.override_background_color(
-                Gtk.StateType.NORMAL, self.SELECTED_COLOR)
             self.name_entry.override_background_color(
                 Gtk.StateType.NORMAL, self.SELECTED_COLOR)
         else:
-            self.eventbox.override_background_color(
-                Gtk.StateType.NORMAL, self.UNSELECTED_COLOR)
             self.name_entry.override_background_color(
                 Gtk.StateType.NORMAL, self.UNSELECTED_COLOR)
 
         # continue GTK signal propagation
         return True
 
-    def _deleteLayerCb(self, unused_widget):
-        self._app.action_log.begin("delete layer")
-        bLayer = self.layer.bLayer
-        bTimeline = bLayer.get_timeline()
-        bTimeline.remove_layer(bLayer)
-        bTimeline.get_asset().pipeline.commit_timeline()
-        self._app.action_log.commit()
-
-    def _moveLayerCb(self, unused_widget, step):
-        index = self.layer.bLayer.get_priority()
-        if abs(step) == 1:
-            index += step
-        elif step == -2:
-            index = 0
-        else:
-            index = len(self.layer.bLayer.get_timeline().get_layers()) - 1
-            # if audio, set last position
-
-        self._app.moveLayer(self, index)
-        # self._app.timeline.parent.app.gui.timeline_ui.controls.moveControlWidget(self, index)
+    def _keyPressCb(self, unused_widget, event):
+        self.layer.bLayer.set_meta(self.__meta_name, self.name_entry.get_text())
+        self._app.project_manager.current_project.setModificationState(True)
 
     def getHeight(self):
         return self.get_allocation().height
@@ -254,39 +133,6 @@ class BaseLayerControl(Gtk.Box, Loggable):
         else:
             self.sep.hide()
 
-    def updateMenuSensitivity(self, position):
-        """
-        Update Menu item sensitivity
-
-        0 = first item -> disable "up" and "first"
-        -1 = last item -> disable "down" and "last"
-        -2 = first and last item -> all disabled
-        """
-        for menu_item in (self.layer_up, self.layer_first,
-                          self.layer_down, self.layer_last):
-            menu_item.set_sensitive(True)
-
-        if position == -2 or position == 0:
-            self.layer_first.set_sensitive(False)
-            self.layer_up.set_sensitive(False)
-
-        if position == -2 or position == -1:
-            self.layer_down.set_sensitive(False)
-            self.layer_last.set_sensitive(False)
-
-    def setSeparatorHighlight(self, highlighted):
-        """
-        Sets if the Separator should be highlighted
-
-        Used for visual drag'n'drop feedback
-        """
-        if highlighted:
-            self.sep.override_background_color(
-                Gtk.StateType.NORMAL, self.SELECTED_COLOR)
-        else:
-            self.sep.override_background_color(
-                Gtk.StateType.NORMAL, self.UNSELECTED_COLOR)
-
 
 class VideoLayerControl(BaseLayerControl):
     """
@@ -296,23 +142,10 @@ class VideoLayerControl(BaseLayerControl):
     __gtype_name__ = 'VideoLayerControl'
 
     def __init__(self, control_container, layer, app):
-        BaseLayerControl.__init__(
-            self, control_container, layer, GES.TrackType.VIDEO, app)
-
-        opacity = Gtk.Label(label=_("Opacity:"))
+        BaseLayerControl.__init__(self, control_container, layer, app, "video")
 
-        # Opacity scale
-        opacity_adjust = Gtk.Adjustment(
-            value=100, upper=100, step_increment=5, page_increment=10)
-        opacity_scale = Gtk.Scale.new(
-            Gtk.Orientation.HORIZONTAL, adjustment=opacity_adjust)
-        opacity_scale.set_value_pos(Gtk.PositionType.LEFT)
-        opacity_scale.set_digits(0)
-        opacity_scale.set_tooltip_text(_("Change video opacity"))
-
-        self.lower_hbox.pack_start(opacity, False, False, 0)
-        self.lower_hbox.pack_start(opacity_scale, True, True, 0)
-        self.lower_hbox.show_all()
+    def _getIconName(self):
+        return "video-x-generic"
 
 
 class AudioLayerControl(BaseLayerControl):
@@ -323,27 +156,10 @@ class AudioLayerControl(BaseLayerControl):
     __gtype_name__ = 'AudioLayerControl'
 
     def __init__(self, control_container, layer, app):
-        BaseLayerControl.__init__(
-            self, control_container, layer, GES.TrackType.AUDIO, app)
-
-        volume = Gtk.Label(label=_("Vol:"))
-        volume_button = Gtk.VolumeButton(size=Gtk.IconSize.MENU)
+        BaseLayerControl.__init__(self, control_container, layer, app, "audio")
 
-        panning = Gtk.Label(label=_("Pan:"))
-        # Volume scale
-        panning_adjust = Gtk.Adjustment(
-            value=0, lower=-100, upper=100, step_increment=5, page_increment=10)
-        panning_scale = Gtk.Scale.new(
-            Gtk.Orientation.HORIZONTAL, adjustment=panning_adjust)
-        panning_scale.set_value_pos(Gtk.PositionType.LEFT)
-        panning_scale.set_digits(0)
-        panning_scale.set_tooltip_text(_("Change audio panning"))
-
-        self.lower_hbox.pack_start(volume, False, False, 0)
-        self.lower_hbox.pack_start(volume_button, False, False, 0)
-        self.lower_hbox.pack_start(panning, False, False, 0)
-        self.lower_hbox.pack_start(panning_scale, True, True, 0)
-        self.lower_hbox.show_all()
+    def _getIconName(self):
+        return "audio-x-generic"
 
 
 class TwoStateButton(Gtk.Button):
@@ -403,32 +219,82 @@ class LayerControls(Gtk.Bin, Loggable):
 
         ebox = Gtk.EventBox()
         self.add(ebox)
-        self._hbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        self._hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
         ebox.add(self._hbox)
         self.bLayer = bLayer
         self.app = app
 
         sep = SpacedSeparator()
-        self._hbox.pack_start(sep, False, False, 5)
+        self._vbox.pack_start(sep, False, False, 5)
+
+        # Popup Menu
+        popup = Gtk.Box.new(Gtk.Orientation.VERTICAL, 2)
+        layer_delete = Gtk.Button.new_with_label(_("_Delete layer"))
+        layer_delete.connect("clicked", self._deleteLayerCb)
+
+        layer_up = Gtk.Button.new_with_label(_("Move layer up"))
+        layer_up.connect("clicked", self._moveLayerCb, -1)
+        layer_first = Gtk.Button.new_with_label(_("Move layer to top"))
+        layer_first.connect("clicked", self._moveLayerCb, -2)
+        layer_down = Gtk.Button.new_with_label(_("Move layer down"))
+        layer_down.connect("clicked", self._moveLayerCb, 1)
+        layer_last = Gtk.Button.new_with_label(_("Move layer to bottom"))
+        layer_last.connect("clicked", self._moveLayerCb, 2)
+
+        popup.add(layer_first)
+        popup.add(layer_up)
+        popup.add(layer_down)
+        popup.add(layer_last)
+        popup.add(layer_delete)
+        popup.show_all()
+        for menu_item in popup:
+            menu_item.set_use_underline(True)
+
+        menubutton = Gtk.MenuButton.new()
+        popover = Gtk.Popover.new(menubutton)
+        popover.add(popup)
+        menubutton.set_popover(popover)
+        menubutton.props.direction = Gtk.ArrowType.RIGHT
+        self._hbox.add(menubutton)
+        self._hbox.add(self._vbox)
+        popover.props.position = Gtk.PositionType.LEFT
 
         self.video_control = VideoLayerControl(None, self, self.app)
         self.video_control.set_visible(True)
-        self.video_control.props.width_request = ui.CONTROL_WIDTH
         self.video_control.props.height_request = ui.LAYER_HEIGHT / 2
-        self._hbox.add(self.video_control)
+        self._vbox.add(self.video_control)
 
         self.audio_control = AudioLayerControl(None, self, self.app)
         self.audio_control.set_visible(True)
         self.audio_control.props.height_request = ui.LAYER_HEIGHT / 2
-        self.audio_control.props.width_request = ui.CONTROL_WIDTH
-        self._hbox.add(self.audio_control)
+        self._vbox.add(self.audio_control)
+
+        sep = SpacedSeparator()
+        self._vbox.pack_start(sep, False, False, 5)
 
-        self._hbox.props.vexpand = False
-        self._hbox.props.width_request = ui.CONTROL_WIDTH
+        self._vbox.props.vexpand = False
         self.props.width_request = ui.CONTROL_WIDTH
+        self.props.height_request = ui.LAYER_HEIGHT
 
-        sep = SpacedSeparator()
-        self._hbox.pack_start(sep, False, False, 5)
+    def _deleteLayerCb(self, unused_widget):
+        self.app.action_log.begin("delete layer")
+        bLayer = self.bLayer
+        bTimeline = bLayer.get_timeline()
+        bTimeline.remove_layer(bLayer)
+        bTimeline.get_asset().pipeline.commit_timeline()
+        self.app.action_log.commit()
+
+    def _moveLayerCb(self, unused_widget, step):
+        index = self.bLayer.get_priority()
+        if abs(step) == 1:
+            index += step
+        elif step == -2:
+            index = 0
+        else:
+            index = len(self.bLayer.get_timeline().get_layers()) - 1
+            # if audio, set last position
+        self.bLayer.get_timeline().ui.moveLayer(self.bLayer, index)
 
 
 class LayerLayout(Gtk.Layout, Loggable):
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index d5b173f..fb61078 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -758,6 +758,17 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
     def _layerAddedCb(self, timeline, bLayer):
         self._addLayer(bLayer)
 
+    def moveLayer(self, bLayer, index):
+        layers = self.bTimeline.get_layers()
+        layer = layers.pop(bLayer.get_priority())
+        layers.insert(index, layer)
+
+        i = 0
+        for layer in layers:
+            layer.set_priority(i)
+            i += 1
+        self.bTimeline.commit()
+
     def _addLayer(self, bLayer):
         layer_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
 
@@ -777,27 +788,23 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
         self.__layers_controls_vbox.pack_start(bLayer.control_ui, False, False, 0)
 
         bLayer.ui.connect("remove-me", self._removeLayerCb)
+        bLayer.connect("notify::priority", self.__layerPriorityChangedCb)
 
         self.show_all()
 
     def _removeLayerCb(self, layer):
         self.bTimeline.remove_layer(layer.bLayer)
 
-    def _removeLayer(self, bLayer):
-        self.info("Removing layer: %s" % bLayer.props.priority)
-        self.__layers_vbox.remove(bLayer.ui.get_parent())
-        self.__layers_controls_vbox.remove(bLayer.control_ui)
-
-        self._layers.remove(bLayer.ui)
-        bLayer.ui.release()
-        bLayer.ui = None
-        bLayer.control_ui = None
+    def __layerPriorityChangedCb(self, bTimeline, pspec):
+        self.__resetLayersByPriority()
 
+    def __resetLayersByPriority(self, reset=False):
         self._layers.sort(key=lambda layer: layer.bLayer.props.priority)
         i = 0
         self.debug("Reseting layers priorities")
         for layer in self._layers:
-            layer.bLayer.props.priority = i
+            if reset:
+                layer.bLayer.props.priority = i
 
             self.__layers_vbox.child_set_property(layer.get_parent(),
                                                   "position",
@@ -809,6 +816,19 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
 
             i += 1
 
+    def _removeLayer(self, bLayer):
+        self.info("Removing layer: %s" % bLayer.props.priority)
+        self.__layers_vbox.remove(bLayer.ui.get_parent())
+        self.__layers_controls_vbox.remove(bLayer.control_ui)
+        bLayer.disconnect_by_func(self.__layerPriorityChangedCb)
+
+        self._layers.remove(bLayer.ui)
+        bLayer.ui.release()
+        bLayer.ui = None
+        bLayer.control_ui = None
+
+        self.__resetLayersByPriority(True)
+
     def _layerRemovedCb(self, unused_timeline, layer):
         self._removeLayer(layer)
 
diff --git a/pitivi/utils/ui.py b/pitivi/utils/ui.py
index c2f2062..174d649 100644
--- a/pitivi/utils/ui.py
+++ b/pitivi/utils/ui.py
@@ -52,7 +52,7 @@ from pitivi.configure import get_pixmap_dir
 # Dimensions in pixels
 TRACK_SPACING = 8
 EXPANDED_SIZE = 65
-CONTROL_WIDTH = 300
+CONTROL_WIDTH = 240
 
 PADDING = 6
 SPACING = 10


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