[pitivi/gtktimeline] timeline: Rework layer controls
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi/gtktimeline] timeline: Rework layer controls
- Date: Tue, 19 May 2015 19:30:54 +0000 (UTC)
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]