Commit d612d927 authored by yatinmaan's avatar yatinmaan

WIP: Port Effects UI to ListBox

parent 3c719067
Pipeline #88162 failed with stage
in 19 minutes and 39 seconds
......@@ -35,6 +35,7 @@ import sys
import threading
from gettext import gettext as _
import cairo
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import GES
......@@ -55,7 +56,16 @@ from pitivi.utils.widgets import GstElementSettingsWidget
(VIDEO_EFFECT, AUDIO_EFFECT) = list(range(1, 3))
AUDIO_EFFECTS_CATEGORIES = ()
AUDIO_EFFECTS_CATEGORIES = (
(_("Audio"), (
"pitch", "freeverb", "removesilence", "festival", "speed",
"audiorate", "volume", "equalizer-nbands", "equalizer-3bands",
"equalizer-10bands", "rglimiter", "rgvolume", "audiopanorama",
"audioinvert", "audiokaraoke", "audioamplify", "audiodynamic",
"audiocheblimit", "audiochebband", "audioiirfilter", "audiowsinclimit",
"audiowsincband", "audiofirfilter", "audioecho", "scaletempo", "stereo",
)),
)
ALLOWED_ONLY_ONCE_EFFECTS = ['videoflip']
......@@ -156,7 +166,8 @@ GlobalSettings.addConfigSection('effect-library')
COL_ELEMENT_NAME,
COL_ICON) = list(range(6))
ICON_WIDTH = 48 + 2 * 6 # 48 pixels, plus a margin on each side
ICON_WIDTH = 80
ICON_HEIGHT = 45
class EffectInfo(object):
......@@ -182,11 +193,12 @@ class EffectInfo(object):
# We can afford to scale the images here, the impact is negligible
icon = GdkPixbuf.Pixbuf.new_from_file_at_size(
os.path.join(pixdir, self.effect_name + ".png"),
ICON_WIDTH, ICON_WIDTH)
ICON_WIDTH, ICON_HEIGHT) # TODO Use new thumbnails
# An empty except clause is bad, but "gi._glib.GError" is not helpful.
except:
icon = GdkPixbuf.Pixbuf.new_from_file(
os.path.join(pixdir, "defaultthumbnail.svg"))
icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(
os.path.join(pixdir, "defaultthumbnail.svg"), ICON_WIDTH, ICON_HEIGHT, False)
# TODO Rework the svg to 16:9 ratio
return icon
@property
......@@ -377,62 +389,16 @@ class EffectListWidget(Gtk.Box, Loggable):
builder = Gtk.Builder()
builder.add_from_file(os.path.join(get_ui_dir(), "effectslibrary.ui"))
builder.connect_signals(self)
toolbar = builder.get_object("effectslibrary_toolbar")
toolbar = builder.get_object("effectslibrary_toolbar") # TODO Remove/Rework
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR)
self.video_togglebutton = builder.get_object("video_togglebutton")
self.audio_togglebutton = builder.get_object("audio_togglebutton")
self.categoriesWidget = builder.get_object("categories")
self.searchEntry = builder.get_object("search_entry")
# Store
self.storemodel = Gtk.ListStore(
str, str, int, object, str, GdkPixbuf.Pixbuf)
self.storemodel.set_sort_column_id(
COL_NAME_TEXT, Gtk.SortType.ASCENDING)
# Create the filter for searching the storemodel.
self.model_filter = self.storemodel.filter_new()
self.model_filter.set_visible_func(self._setRowVisible, data=None)
self.view = Gtk.TreeView(model=self.model_filter)
self.view.props.headers_visible = False
self.view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
icon_col = Gtk.TreeViewColumn()
icon_col.set_spacing(SPACING)
icon_col.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
icon_col.props.fixed_width = ICON_WIDTH
icon_cell = Gtk.CellRendererPixbuf()
icon_cell.props.xpad = 6
icon_col.pack_start(icon_cell, True)
icon_col.add_attribute(icon_cell, "pixbuf", COL_ICON)
text_col = Gtk.TreeViewColumn()
text_col.set_expand(True)
text_col.set_spacing(SPACING)
text_col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
text_cell = Gtk.CellRendererText()
text_cell.props.yalign = 0.0
text_cell.props.xpad = 6
text_cell.set_property("ellipsize", Pango.EllipsizeMode.END)
text_col.pack_start(text_cell, True)
text_col.set_cell_data_func(
text_cell, self.viewDescriptionCellDataFunc, None)
self.view.append_column(icon_col)
self.view.append_column(text_col)
self.view.connect("query-tooltip", self._treeViewQueryTooltipCb)
self.view.props.has_tooltip = True
# Make the treeview a drag source which provides effects.
self.view.enable_model_drag_source(
Gdk.ModifierType.BUTTON1_MASK, [EFFECT_TARGET_ENTRY], Gdk.DragAction.COPY)
self.view.connect("button-press-event", self._buttonPressEventCb)
self.view.connect("select-cursor-row", self._enterPressEventCb)
self.view.connect("drag-data-get", self._dndDragDataGetCb)
self.view = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
Gtk.ScrolledWindow
scrollwin = Gtk.ScrolledWindow()
scrollwin.props.hscrollbar_policy = Gtk.PolicyType.NEVER
scrollwin.props.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
......@@ -443,103 +409,114 @@ class EffectListWidget(Gtk.Box, Loggable):
# Delay the loading of the available effects so the application
# starts faster.
GLib.idle_add(self._loadAvailableEffectsCb)
self.populate_categories_widget()
GLib.idle_add(self._load_available_effects_cb)
# Individually show the tab's widgets.
# If you use self.show_all(), the tab will steal focus on startup.
scrollwin.show_all()
toolbar.show_all()
def _treeViewQueryTooltipCb(self, view, x, y, keyboard_mode, tooltip):
is_row, x, y, model, path, tree_iter = view.get_tooltip_context(
x, y, keyboard_mode)
if not is_row:
return False
def _load_available_effects_cb(self):
categories = sorted(set(self.app.effects.video_categories +
self.app.effects.audio_categories)) # TODO? Merge Categories
view.set_tooltip_row(tooltip, path)
tooltip.set_markup(self.formatDescription(model, tree_iter))
return True
self._add_category_widgets(categories)
self.view.foreach(self._add_effect_widgets)
self.view.show_all()
def viewDescriptionCellDataFunc(self, unused_column, cell, model, iter_, unused_data):
cell.props.markup = self.formatDescription(model, iter_)
return False
def formatDescription(self, model, iter_):
name, element_name, desc = model.get(iter_, COL_NAME_TEXT, COL_ELEMENT_NAME, COL_DESC_TEXT)
escape = GLib.markup_escape_text
return "<b>%s</b>\n%s" % (escape(name), escape(desc))
def _add_category_widgets(self, categories):
for category in categories:
widget = self._create_category_widget(category)
self.view.add(widget)
def _loadAvailableEffectsCb(self):
self._addFactories(self.app.effects.video_effects, VIDEO_EFFECT)
self._addFactories(self.app.effects.audio_effects, AUDIO_EFFECT)
return False
def _add_effect_widgets(self, row, *unused_data):
expander = row.get_children()[0]
listbox = expander.get_child()
effects = self.app.effects.video_effects + self.app.effects.audio_effects
for effect in effects:
name = effect.get_name()
def _addFactories(self, elements, effectType):
for element in elements:
name = element.get_name()
if name in HIDDEN_EFFECTS:
continue
effect_info = self.app.effects.getInfo(name)
self.storemodel.append([effect_info.human_name,
effect_info.description,
effectType,
effect_info.categories,
name,
effect_info.icon])
def populate_categories_widget(self):
self.categoriesWidget.get_model().clear()
icon_column = self.view.get_column(0)
if self._effectType is VIDEO_EFFECT:
for category in self.app.effects.video_categories:
self.categoriesWidget.append_text(category)
icon_column.props.visible = True
else:
for category in self.app.effects.audio_categories:
self.categoriesWidget.append_text(category)
icon_column.props.visible = False
self.categoriesWidget.set_active(0)
effect_info = self.app.effects.getInfo(name)
def _dndDragDataGetCb(self, unused_view, drag_context, selection_data, unused_info, unused_timestamp):
data = bytes(self.getSelectedEffect(), "UTF-8")
if expander.get_label() in effect_info.categories:
widget = self._create_effect_widget(name)
listbox.add(widget)
def _create_category_widget(self, category):
box = Gtk.Box()
expander = Gtk.Expander(label=category, margin=10)
listbox = Gtk.ListBox(activate_on_single_click=False)
listbox.connect("row-activated", self._add_selected_effect)
expander.add(listbox)
box.pack_start(expander, True, True, 0)
return box
def _create_effect_widget(self, effect_name):
effect_info = self.app.effects.getInfo(effect_name)
effect_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, margin=5)
effect_box.effect_name = effect_name
effect_box.set_tooltip_text(effect_info.description)
label = Gtk.Label(effect_info.human_name, xalign=0)
icon = Gtk.Image.new_from_pixbuf(effect_info.icon)
# TODO Use a real icon instead of a toogle button
fav_icon = Gtk.Image.new_from_stock("gtk-about", Gtk.IconSize.BUTTON)
fav_toggle = Gtk.ToggleButton()
fav_toggle.set_tooltip_text(_("Add to Favourites"))
fav_toggle.add(fav_icon)
effect_box.pack_start(icon, False, True, 5)
effect_box.pack_start(label, True, True, 0)
effect_box.pack_end(fav_toggle, False, True, 5)
# Set up drag behavoir
eventbox = Gtk.EventBox(visible_window=False)
eventbox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [EFFECT_TARGET_ENTRY], Gdk.DragAction.COPY)
eventbox.connect("drag-data-get", self._dndDragDataGetCb)
eventbox.connect("drag-begin", self._dndDragBeginCb)
eventbox.add(effect_box)
row = Gtk.ListBoxRow(selectable=False)
row.add(eventbox)
return row
def _dndDragDataGetCb(self, eventbox, drag_context, selection_data, unused_info, unused_timestamp):
effect_box = eventbox.get_child()
data = bytes(effect_box.effect_name, "UTF-8")
selection_data.set(drag_context.list_targets()[0], 0, data)
def _rowUnderMouseSelected(self, view, event):
result = view.get_path_at_pos(int(event.x), int(event.y))
if result:
path = result[0]
selection = view.get_selection()
return selection.path_is_selected(path) and\
selection.count_selected_rows() > 0
return False
def _enterPressEventCb(self, unused_view, unused_event=None):
self._addSelectedEffect()
def _buttonPressEventCb(self, view, event):
chain_up = True
def _dndDragBeginCb(self, eventbox, context):
effect_box = eventbox.get_child()
effect_info = self.app.effects.getInfo(effect_box.effect_name)
# Draw drag-icon
icon = effect_info.icon
icon_height = icon.get_height()
icon_width = icon.get_width()
if event.button == 3:
chain_up = False
elif event.type == getattr(Gdk.EventType, '2BUTTON_PRESS'):
self._addSelectedEffect()
else:
chain_up = not self._rowUnderMouseSelected(view, event)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, icon_width, icon_height)
ctx = cairo.Context(surface)
# Center the icon around the cursor.
ctx.translate(icon_width / 2, icon_height / 2)
surface.set_device_offset(-icon_width / 2, -icon_height / 2)
if chain_up:
self._draggedItems = None
else:
self._draggedItems = self.getSelectedEffect()
Gdk.cairo_set_source_pixbuf(ctx, icon, 0, 0)
ctx.paint_with_alpha(0.35)
Gtk.TreeView.do_button_press_event(view, event)
return True
Gtk.drag_set_icon_surface(context, surface)
def _addSelectedEffect(self):
def _add_selected_effect(self, unused_listbox, row):
"""Adds the selected effect to the single selected clip, if any."""
effect = self.getSelectedEffect()
effect_info = self.app.effects.getInfo(effect)
effect_box = row.get_child().get_child()
effect_info = self.app.effects.getInfo(effect_box.effect_name)
if not effect_info:
return
timeline = self.app.gui.editor.timeline_ui.timeline
......@@ -553,34 +530,6 @@ class EffectListWidget(Gtk.Box, Loggable):
toplevel=True):
clip.ui.add_effect(effect_info)
def getSelectedEffect(self):
if self._draggedItems:
return self._draggedItems
model, rows = self.view.get_selection().get_selected_rows()
path = self.model_filter.convert_path_to_child_path(rows[0])
return self.storemodel[path][COL_ELEMENT_NAME]
def _toggleViewTypeCb(self, widget):
"""Switches the view mode between video and audio.
This makes the two togglebuttons behave like a group of radiobuttons.
"""
if widget is self.video_togglebutton:
self.audio_togglebutton.set_active(not widget.get_active())
else:
assert widget is self.audio_togglebutton
self.video_togglebutton.set_active(not widget.get_active())
if self.video_togglebutton.get_active():
self._effectType = VIDEO_EFFECT
else:
self._effectType = AUDIO_EFFECT
self.populate_categories_widget()
self.model_filter.refilter()
def _categoryChangedCb(self, unused_combobox):
self.model_filter.refilter()
def _searchEntryChangedCb(self, unused_entry):
self.model_filter.refilter()
......
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