[pitivi] editorperspective: Add UI intro
- From: Alexandru Băluț <alexbalut src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] editorperspective: Add UI intro
- Date: Sat, 22 Feb 2020 22:36:31 +0000 (UTC)
commit 9ec3e8e157ef3601f326063f2768256ef1410c20
Author: 1-2-ka-4-4-2-ka-1 <pratyushtiwarimj gmail com>
Date: Sat Jan 4 17:15:24 2020 +0530
editorperspective: Add UI intro
First version shows an overview of the UI sections.
Fixes #2361
data/ui/mainmenubutton.ui | 19 +++-
pitivi/action_search_bar.py | 11 +-
pitivi/editorperspective.py | 9 ++
pitivi/interactiveintro.py | 260 ++++++++++++++++++++++++++++++++++++++++++++
pitivi/timeline/timeline.py | 6 +-
5 files changed, 297 insertions(+), 8 deletions(-)
---
diff --git a/data/ui/mainmenubutton.ui b/data/ui/mainmenubutton.ui
index a4fcee34..73a4a029 100644
--- a/data/ui/mainmenubutton.ui
+++ b/data/ui/mainmenubutton.ui
@@ -185,6 +185,23 @@
<property name="position">10</property>
</packing>
</child>
+ <child>
+ <object class="GtkModelButton" id="menu_interactive_intro">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Start the intro to Pitivi for new
users</property>
+ <property name="margin_top">1</property>
+ <property name="margin_bottom">1</property>
+ <property name="action_name">editor.interactive-intro</property>
+ <property name="text" translatable="yes">Interactive Intro</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">11</property>
+ </packing>
+ </child>
<child>
<object class="GtkModelButton" id="menu_about">
<property name="visible">True</property>
@@ -198,7 +215,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">11</property>
+ <property name="position">12</property>
</packing>
</child>
</object>
diff --git a/pitivi/action_search_bar.py b/pitivi/action_search_bar.py
index 1622125d..c81897b3 100644
--- a/pitivi/action_search_bar.py
+++ b/pitivi/action_search_bar.py
@@ -53,7 +53,7 @@ class ActionSearchBar(Gtk.Window):
self.vbox.pack_start(self.results_window, True, True, 0)
def setup_results_window(self):
- self.list_model = Gtk.ListStore(str, int, Gdk.ModifierType, Gio.SimpleAction, object, bool)
+ self.list_model = Gtk.ListStore(str, int, Gdk.ModifierType, Gio.SimpleAction, object, bool, bool)
self.model_filter = self.list_model.filter_new()
disable_groups = ["medialibrary"]
@@ -63,14 +63,17 @@ class ActionSearchBar(Gtk.Window):
for group in self.app.shortcuts.group_actions:
if group not in disable_groups:
for action, title, action_object in self.app.shortcuts.group_actions[group]:
- accelerator_parsed = Gtk.accelerator_parse(self.app.get_accels_for_action(action)[0])
+ accels = self.app.get_accels_for_action(action)
+ accel = accels[0] if accels else ""
+ accelerator_parsed = Gtk.accelerator_parse(accel)
disabled = not action_object.props.enabled
self.list_model.append([title,
accelerator_parsed.accelerator_key,
accelerator_parsed.accelerator_mods,
action_object,
title.lower().split(" "),
- disabled])
+ disabled,
+ bool(accels)])
self.model_filter.set_visible_func(self.filter_func)
self.treeview = Gtk.TreeView.new_with_model(self.model_filter)
@@ -90,7 +93,7 @@ class ActionSearchBar(Gtk.Window):
accel_renderer.props.accel_mode = Gtk.CellRendererAccelMode.OTHER
accel_renderer.props.foreground_rgba = color_insensitive
accel_renderer.props.foreground_set = True
- shortcut_column = Gtk.TreeViewColumn("Shortcut", accel_renderer, accel_key=1, accel_mods=2)
+ shortcut_column = Gtk.TreeViewColumn("Shortcut", accel_renderer, accel_key=1, accel_mods=2,
visible=6)
self.treeview.append_column(shortcut_column)
self.__select_row(self.model_filter.get_iter_first())
diff --git a/pitivi/editorperspective.py b/pitivi/editorperspective.py
index bfbe0cea..8b921515 100644
--- a/pitivi/editorperspective.py
+++ b/pitivi/editorperspective.py
@@ -29,6 +29,7 @@ from pitivi.configure import APPNAME
from pitivi.configure import get_ui_dir
from pitivi.dialogs.missingasset import MissingAssetDialog
from pitivi.effects import EffectListWidget
+from pitivi.interactiveintro import InteractiveIntro
from pitivi.mediafilespreviewer import PreviewWidget
from pitivi.medialibrary import MediaLibraryWidget
from pitivi.perspective import Perspective
@@ -232,6 +233,9 @@ class EditorPerspective(Perspective, Loggable):
self.timeline_ui = TimelineContainer(self.app)
self.toplevel_widget.pack2(self.timeline_ui, resize=True, shrink=False)
+ self.intro = InteractiveIntro(self.app)
+ self.headerbar.pack_end(self.intro.intro_button)
+
# Setup shortcuts for HeaderBar buttons and menu items.
self._create_actions()
@@ -334,6 +338,7 @@ class EditorPerspective(Perspective, Loggable):
os.path.join(get_ui_dir(), "mainmenubutton.ui"))
self.menu_button = self.builder.get_object("menubutton")
+ self.keyboard_shortcuts_button = self.builder.get_object("menu_shortcuts")
headerbar.pack_end(self.menu_button)
headerbar.pack_end(self.save_button)
@@ -378,6 +383,10 @@ class EditorPerspective(Perspective, Loggable):
self.project_settings_action.connect("activate", self.__project_settings_cb)
group.add_action(self.project_settings_action)
+ group.add_action(self.intro.intro_action)
+ self.app.shortcuts.add("editor.interactive-intro", [], self.intro.intro_action,
+ _("Quick intros to Pitivi"), group="win")
+
self.import_asset_action = Gio.SimpleAction.new("import-asset", None)
self.import_asset_action.connect("activate", self.__import_asset_cb)
group.add_action(self.import_asset_action)
diff --git a/pitivi/interactiveintro.py b/pitivi/interactiveintro.py
new file mode 100644
index 00000000..ddb266b6
--- /dev/null
+++ b/pitivi/interactiveintro.py
@@ -0,0 +1,260 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2020, Pratyush Tiwari <pratyushtiwarimj gmail com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+from gettext import gettext as _
+
+from gi.repository import Gdk
+from gi.repository import Gio
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gtk
+
+
+INTERACTIVE_INTRO_CSS = """
+@keyframes intro-highlight {
+ from { box-shadow: 0px 0px 10px @theme_selected_bg_color, inset 0px 0px 4px @theme_selected_bg_color; }
+ to { box-shadow: 0px 0px 4px @theme_selected_bg_color, inset 0px 0px 10px @theme_selected_bg_color; }
+}
+
+.intro-highlight {
+ animation: intro-highlight 1.5s infinite alternate;
+}
+
+
+#control-popover > box > button {
+ margin: 10px;
+}
+
+
+#intro-popover {
+ background-color: @theme_selected_bg_color;
+}
+
+#intro-popover > box {
+ margin: 10px;
+}
+
+#intro-popover > box > label {
+ font-size: 18pt;
+ font-weight: bold;
+ margin: 10px;
+ color: @theme_fg_color;
+}
+"""
+
+
+class InteractiveIntro(GObject.Object):
+ """Interactive GUI intro for newcomers.
+
+ Attributes:
+ intro_action (Gio.SimpleAction): The action for showing the control
+ popover.
+ intro_button (Gtk.Button): The button to be displayed in the headerbar
+ for showing the control popover.
+ widget (Gtk.Widget): The current widget being highlighted.
+ """
+
+ def __init__(self, app):
+ GObject.Object.__init__(self)
+ self.app = app
+ self.widget = None
+ self.parent_widget = None
+ self.tips = []
+ self._advance_handler_id = 0
+ self._hiding_popover_when_advancing = False
+
+ self.__setup_css()
+
+ self.intro_action = Gio.SimpleAction.new("interactive-intro", None)
+ self.intro_action.connect("activate", self._control_intro_cb)
+
+ self.intro_button = self.__create_intro_button()
+
+ def __setup_css(self):
+ css_provider = Gtk.CssProvider()
+ css_provider.load_from_data(INTERACTIVE_INTRO_CSS.encode("UTF-8"))
+ screen = Gdk.Screen.get_default()
+ style_context = self.app.gui.get_style_context()
+ style_context.add_provider_for_screen(screen, css_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+ def __create_intro_button(self):
+ """Creates a button for controlling the intro."""
+ button = Gtk.Button.new_from_icon_name(
+ "org.pitivi.Pitivi-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
+ button.set_always_show_image(True)
+ button.props.no_show_all = True
+ button.props.relief = Gtk.ReliefStyle.NONE
+ # We could set_action_name, but we don't know here the group
+ # in which the action will be added, so it's simpler this way.
+ button.connect("clicked", self._intro_button_clicked_cb)
+ return button
+
+ def __create_popover(self, text):
+ popover = Gtk.Popover()
+ popover.set_position(Gtk.PositionType.BOTTOM)
+ popover.set_modal(True)
+ popover.connect("closed", self._intro_popover_closed_cb)
+ popover.set_name("intro-popover")
+
+ label = Gtk.Label()
+ label.set_markup(text)
+ label.set_line_wrap(True)
+ label.set_max_width_chars(24)
+
+ img = Gtk.Image.new_from_icon_name("dialog-information-symbolic", Gtk.IconSize.DIALOG)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ vbox.pack_start(img, False, False, 5)
+ vbox.pack_end(label, False, False, 5)
+ vbox.show_all()
+
+ popover.add(vbox)
+
+ return popover
+
+ def _intro_popover_closed_cb(self, unused_widget):
+ if self._hiding_popover_when_advancing:
+ if self.parent_widget:
+ self.parent_widget.hide()
+ self.parent_widget = None
+ return
+
+ self.stop_tour()
+
+ def _control_intro_cb(self, unused_action, unused_param):
+ """Handles the activation of the interactive intro action."""
+ if self.running:
+ self.stop_tour()
+ else:
+ self.show_control_popover()
+
+ def _intro_button_clicked_cb(self, button):
+ self.intro_action.activate()
+
+ @property
+ def running(self):
+ return self._advance_handler_id != 0
+
+ def show_control_popover(self):
+ # pylint: disable=attribute-defined-outside-init
+ overview_button = Gtk.Button.new_with_label(_("Start Overview"))
+ overview_button.connect("clicked", self._overview_button_clicked_cb)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ vbox.pack_start(overview_button, False, True, 10)
+
+ self.intro_button.show()
+ self.intro_button.get_style_context().add_class("intro-highlight")
+
+ self.control_popover = Gtk.Popover()
+ self.control_popover.set_name("control-popover")
+ self.control_popover.add(vbox)
+ self.control_popover.set_relative_to(self.intro_button)
+ self.control_popover.show_all()
+ self.control_popover.popup()
+ self.control_popover.connect("closed", self._control_popover_closed_cb)
+
+ def _control_popover_closed_cb(self, unused_widget):
+ self.intro_button.get_style_context().remove_class("intro-highlight")
+
+ def set_current_widget(self, widget):
+ """Switches the focus to a new widget, highlighting it."""
+ if self.widget:
+ self.widget.get_style_context().remove_class("intro-highlight")
+ self.widget = None
+
+ if widget:
+ self.widget = widget
+ self.widget.get_style_context().add_class("intro-highlight")
+
+ if isinstance(self.widget, Gtk.ModelButton):
+ self.parent_widget = self._find_parent(self.widget, Gtk.Popover)
+ if self.parent_widget:
+ self.parent_widget.popup()
+
+ def _find_parent(self, widget, parent_class):
+ while widget:
+ if isinstance(widget, parent_class):
+ return widget
+
+ widget = widget.props.parent
+
+ return None
+
+ def stop_tour(self):
+ if self._advance_handler_id:
+ GLib.source_remove(self._advance_handler_id)
+ self._advance_handler_id = 0
+
+ self.set_current_widget(None)
+
+ self.intro_button.hide()
+
+ def _overview_button_clicked_cb(self, unused_widget):
+ """Starts the UI overview."""
+ self.control_popover.popdown()
+ editor = self.app.gui.editor
+ self.tips = [
+ (self.intro_button, _("Welcome to Pitivi!"),
+ Gtk.PositionType.BOTTOM, False, 4000),
+ (editor.medialibrary.iconview_scrollwin, _("Drag files here to import them"),
+ Gtk.PositionType.RIGHT, True, 5000),
+ (editor.timeline_ui.timeline, _("Drag clips in the timeline to arrange them"),
+ Gtk.PositionType.TOP, True, 7000),
+ (editor.timeline_ui.ruler, _("Right-click anywhere to move the playhead"),
+ Gtk.PositionType.TOP, True, 6000),
+ (editor.viewer.playpause_button, _("Preview with the play button"),
+ Gtk.PositionType.TOP, False, 5000),
+ (editor.render_button, _("Export your project to share it"),
+ Gtk.PositionType.BOTTOM, False, 5000),
+ (editor.keyboard_shortcuts_button, _("See all the keyboard shortcuts"),
+ Gtk.PositionType.LEFT, False, 6000)]
+
+ self.advance_popover_func(previous_popover=None)
+
+ def advance_popover_func(self, previous_popover):
+ """Moves the popover to the next widget."""
+ if previous_popover:
+ self._hiding_popover_when_advancing = True
+ try:
+ previous_popover.popdown()
+ finally:
+ self._hiding_popover_when_advancing = False
+
+ if not self.tips:
+ self.stop_tour()
+ return False
+
+ widget, text, position_type, center, timeout = self.tips.pop(0)
+ self.set_current_widget(widget)
+
+ popover = self.__create_popover(text)
+ popover.set_relative_to(widget)
+
+ if center:
+ rect = Gdk.Rectangle()
+ rect.x = widget.get_allocated_width() / 2
+ rect.y = widget.get_allocated_height() / 2
+ rect.width = 4
+ rect.height = 4
+ popover.set_pointing_to(rect)
+
+ popover.set_position(position_type)
+ popover.popup()
+
+ self._advance_handler_id = GLib.timeout_add(timeout, self.advance_popover_func, popover)
+ return False
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 9c4cb512..f561dca9 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -1577,8 +1577,8 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
def _create_ui(self):
left_size_group = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL)
- zoom_box = ZoomBox(self)
- left_size_group.add_widget(zoom_box)
+ self.zoom_box = ZoomBox(self)
+ left_size_group.add_widget(self.zoom_box)
self.timeline = Timeline(self.app, left_size_group)
@@ -1607,7 +1607,7 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
self.markers = MarkersBox(self.app, hadj=self.timeline.hadj)
self.attach(self.markers, 1, 0, 1, 1)
- self.attach(zoom_box, 0, 1, 1, 1)
+ self.attach(self.zoom_box, 0, 1, 1, 1)
self.attach(self.ruler, 1, 1, 1, 1)
self.attach(self.timeline, 0, 2, 2, 1)
self.attach(self.vscrollbar, 2, 2, 1, 1)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]