[ease/video] [video] Added inspector for video.
- From: Nate Stedman <natesm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ease/video] [video] Added inspector for video.
- Date: Fri, 30 Jul 2010 05:45:21 +0000 (UTC)
commit 77f99527f3cc7d0d1f5b4d1c7ab4f520c9988222
Author: Nate Stedman <natesm gmail com>
Date: Fri Jul 30 01:44:26 2010 -0400
[video] Added inspector for video.
- Video can be automatically played or not
- Nice video playback controls
- Video can be muted
- Different actions to take when a video completes.
data/ui/inspector-element-video.ui | 118 +++++++++++++++++++++
ease-core/ease-actor.vala | 12 ++
ease-core/ease-element.vala | 8 ++
ease-core/ease-slide.vala | 5 +
ease-core/ease-theme.vala | 7 ++
ease-core/ease-video-actor.vala | 25 ++++-
ease-core/ease-video-element.vala | 203 +++++++++++++++++++++++++++++++++++-
src/ease-editor-embed.vala | 41 ++++++--
src/ease-player.vala | 13 +++
9 files changed, 417 insertions(+), 15 deletions(-)
---
diff --git a/data/ui/inspector-element-video.ui b/data/ui/inspector-element-video.ui
new file mode 100644
index 0000000..e6d54f9
--- /dev/null
+++ b/data/ui/inspector-element-video.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkAlignment" id="root">
+ <property name="visible">True</property>
+ <property name="top_padding">4</property>
+ <property name="bottom_padding">4</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkVBox" id="root-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkCheckButton" id="play-auto">
+ <property name="label" translatable="yes">_Automatically start playback</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="mute">
+ <property name="label" translatable="yes">_Mute Audio</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">When video playback ends:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="end-action">
+ <property name="height_request">30</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fill-slide">
+ <property name="label" translatable="yes">_Fill Slide</property>
+ <property name="height_request">30</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/ease-core/ease-actor.vala b/ease-core/ease-actor.vala
index a7311e4..cfc4f79 100644
--- a/ease-core/ease-actor.vala
+++ b/ease-core/ease-actor.vala
@@ -88,6 +88,18 @@ public abstract class Ease.Actor : Clutter.Group
editor_rect.height = e.height;
add_actor(editor_rect);
}
+
+ // update the actor's position when changed in the element
+ e.notify["x"].connect((o, p) => x = element.x);
+ e.notify["y"].connect((o, p) => y = element.y);
+ e.notify["width"].connect((o, p) => {
+ width = element.width;
+ contents.width = element.width;
+ });
+ e.notify["height"].connect((o, p) => {
+ height = element.height;
+ contents.height = element.height;
+ });
}
/**
diff --git a/ease-core/ease-element.vala b/ease-core/ease-element.vala
index a6f7473..eed4bd3 100644
--- a/ease-core/ease-element.vala
+++ b/ease-core/ease-element.vala
@@ -142,6 +142,14 @@ public abstract class Ease.Element : GLib.Object, UndoSource
}
/**
+ * Requests that the presentation be advanced a slide.
+ */
+ internal void request_advance()
+ {
+ parent.request_advance(this);
+ }
+
+ /**
* Creates the actual HTML markup for this Element.
*
* @param html The HTML string in its current state.
diff --git a/ease-core/ease-slide.vala b/ease-core/ease-slide.vala
index 702cb37..2ea4b8b 100644
--- a/ease-core/ease-slide.vala
+++ b/ease-core/ease-slide.vala
@@ -120,6 +120,11 @@ public class Ease.Slide : GLib.Object, UndoSource
public int count { get { return elements.size; } }
/**
+ * Requests that the player advance past this Slide.
+ */
+ public signal void request_advance(Element sender);
+
+ /**
* The next Slide in this Slide's { link Document}.
*/
public Slide? next
diff --git a/ease-core/ease-theme.vala b/ease-core/ease-theme.vala
index c49062c..8490031 100644
--- a/ease-core/ease-theme.vala
+++ b/ease-core/ease-theme.vala
@@ -109,6 +109,13 @@ public class Ease.Theme : GLib.Object
// video properties
public const string VIDEO_PLAY_AUTO = "video-play-automatically";
+ public const string VIDEO_MUTE = "video-mute";
+ public const string VIDEO_END_ACTION = "video-end-action";
+
+ // video end actions
+ public const string VIDEO_END_STOP = "video-end-stop";
+ public const string VIDEO_END_LOOP = "video-end-loop";
+ public const string VIDEO_END_CONTINUE = "video-end-continue";
// generic element properties
public const string E_IDENTIFIER = "element-identifier";
diff --git a/ease-core/ease-video-actor.vala b/ease-core/ease-video-actor.vala
index 6582831..0b02f7f 100644
--- a/ease-core/ease-video-actor.vala
+++ b/ease-core/ease-video-actor.vala
@@ -60,7 +60,7 @@ public class Ease.VideoActor : Actor, Clutter.Media
/**
* Easing for the button fadeout.
*/
- private const int ALPHA_OPACITY = Clutter.AnimationMode.LINEAR;
+ private const Clutter.AnimationMode ALPHA_OPACITY = Clutter.AnimationMode.LINEAR;
/**
* Easing for the button scale out.
@@ -105,6 +105,10 @@ public class Ease.VideoActor : Actor, Clutter.Media
// play the video if it's in the presentation
if (c == ActorContext.PRESENTATION)
{
+ // mute the video if requested
+ set_audio_volume(e.mute ? 0 : 1);
+
+ // if the video should automatically play, play it
if (e.play_auto)
{
video.set_playing(true);
@@ -141,10 +145,27 @@ public class Ease.VideoActor : Actor, Clutter.Media
"opacity", 255);
return true;
});
+
+ // perform the video's end action when requested
+ video.eos.connect((v) => {
+ switch ((element as VideoElement).end_action)
+ {
+ case VideoEndAction.STOP:
+ break;
+ case VideoEndAction.LOOP:
+ set_progress(0);
+ video.set_playing(true);
+ break;
+ case VideoEndAction.CONTINUE:
+ element.request_advance();
+ break;
+ }
+ });
}
else
{
// FIXME: toggle playback to get a frame
+ set_audio_volume(0);
video.set_playing(true);
video.set_playing(false);
}
@@ -183,7 +204,7 @@ public class Ease.VideoActor : Actor, Clutter.Media
cr.line_to(e.width, 0);
cr.line_to(0, e.height);
cr.close_path();
- cr.set_source_rgba(0, 0, 0, 0.8);
+ cr.set_source_rgba(0, 0, 0, 0.7);
cr.fill();
// create the action button
diff --git a/ease-core/ease-video-element.vala b/ease-core/ease-video-element.vala
index ee35ff6..7abbf4c 100644
--- a/ease-core/ease-video-element.vala
+++ b/ease-core/ease-video-element.vala
@@ -21,11 +21,27 @@
*/
public class Ease.VideoElement : MediaElement
{
+ private const string UI_FILE = "inspector-element-video.ui";
+ private bool silence_undo;
+
/**
* If the video should begin playing automatically, or display a play
* button.
*/
- public bool play_auto { get; set; default = false; }
+ internal bool play_auto { get; set; default = false; }
+
+ /**
+ * If the video should be muted.
+ */
+ internal VideoEndAction end_action
+ {
+ get; set; default = VideoEndAction.STOP;
+ }
+
+ /**
+ * If the video should be muted.
+ */
+ internal bool mute { get; set; default = false; }
public VideoElement()
{
@@ -36,6 +52,9 @@ public class Ease.VideoElement : MediaElement
{
base.from_json(obj);
play_auto = obj.get_string_member(Theme.VIDEO_PLAY_AUTO).to_bool();
+ mute = obj.get_string_member(Theme.VIDEO_MUTE).to_bool();
+ end_action = VideoEndAction.from_string(
+ obj.get_string_member(Theme.VIDEO_END_ACTION));
}
public override Actor actor(ActorContext c)
@@ -47,6 +66,8 @@ public class Ease.VideoElement : MediaElement
{
var obj = base.to_json();
obj.set_string_member(Theme.VIDEO_PLAY_AUTO, play_auto.to_string());
+ obj.set_string_member(Theme.VIDEO_MUTE, mute.to_string());
+ obj.set_string_member(Theme.VIDEO_END_ACTION, end_action.to_string());
return obj;
}
@@ -78,11 +99,114 @@ public class Ease.VideoElement : MediaElement
return html;
}
+ /**
+ * { inheritDoc}
+ */
public override Gtk.Widget inspector_widget()
{
- var label = new Gtk.Label("No inspector for videos right now...");
- label.show();
- return label;
+ var builder = new Gtk.Builder();
+ try
+ {
+ builder.add_from_file(data_path(Path.build_filename(Temp.UI_DIR,
+ UI_FILE)));
+ }
+ catch (Error e) { error("Error loading UI: %s", e.message); }
+
+ var m_button = builder.get_object("mute") as Gtk.CheckButton;
+ var a_button = builder.get_object("play-auto") as Gtk.CheckButton;
+
+ // connect the "fill slide" button
+ (builder.get_object("fill-slide") as Gtk.Button).clicked.connect(() => {
+ // don't create an unneeded undoaction
+ if (width == parent.width && height == parent.height &&
+ x == 0 && y == 0) return;
+
+ // create an undo acton
+ var action = new UndoAction(this, "x");
+ action.add(this, "y");
+ action.add(this, "width");
+ action.add(this, "height");
+
+ // set the position of the element
+ x = 0;
+ y = 0;
+ width = parent.width;
+ height = parent.height;
+
+ // emit the undoaction
+ undo(action);
+ });
+
+ // setup mute and autoplay checkboxes
+ m_button.active = mute;
+ a_button.active = play_auto;
+
+ m_button.toggled.connect(() => {
+ mute = m_button.active;
+
+ var action = new UndoAction(this, "mute");
+ action.applied.connect((a) => m_button.active = mute);
+ undo(action);
+ });
+
+ a_button.toggled.connect(() => {
+ play_auto = a_button.active;
+
+ var action = new UndoAction(this, "play-auto");
+ action.applied.connect((a) => {
+ a_button.active = play_auto;
+ });
+ undo(action);
+ });
+
+ // setup end action combo box
+ var combo = builder.get_object("end-action") as Gtk.ComboBox;
+ set_combobox(combo);
+
+ // update the end action when the combo box is changed
+ combo.changed.connect(on_set_end_action);
+
+ // return the root widget
+ return builder.get_object("root") as Gtk.Widget;
+ }
+
+ private void on_set_end_action(Gtk.ComboBox sender)
+ {
+ var undo_action = new UndoAction(this, "end-action");
+
+ undo_action.applied.connect((a) => {
+ silence_undo = true;
+ set_combobox(sender);
+ silence_undo = false;
+ });
+
+ VideoEndAction action;
+ Gtk.TreeIter itr;
+ sender.model.get_iter_first(out itr);
+ for (int i = 0; i < sender.get_active(); i++)
+ {
+ sender.model.iter_next(ref itr);
+ }
+ sender.model.get(itr, 1, out action);
+ end_action = action;
+ if (!silence_undo) undo(undo_action);
+ }
+
+ private void set_combobox(Gtk.ComboBox combo)
+ {
+ combo.model = VideoEndAction.list_store();
+ VideoEndAction action;
+ Gtk.TreeIter itr;
+ combo.model.get_iter_first(out itr);
+ do
+ {
+ combo.model.get(itr, 1, out action);
+ if (action == end_action)
+ {
+ combo.set_active_iter(itr);
+ break;
+ }
+ } while (combo.model.iter_next(ref itr));
}
public override void cairo_render(Cairo.Context context) throws Error
@@ -91,3 +215,74 @@ public class Ease.VideoElement : MediaElement
}
}
+internal enum Ease.VideoEndAction
+{
+ STOP,
+ LOOP,
+ CONTINUE;
+
+ /**
+ * Returns a string representation of this VideoEndAction.
+ */
+ public string to_string()
+ {
+ switch (this)
+ {
+ case STOP: return Theme.VIDEO_END_STOP;
+ case LOOP: return Theme.VIDEO_END_LOOP;
+ case CONTINUE: return Theme.VIDEO_END_CONTINUE;
+ }
+ return "undefined";
+ }
+
+ /**
+ * Creates a VideoEndAction from a string representation.
+ */
+ public static VideoEndAction from_string(string str)
+ {
+ switch (str)
+ {
+ case Theme.VIDEO_END_STOP: return STOP;
+ case Theme.VIDEO_END_LOOP: return LOOP;
+ case Theme.VIDEO_END_CONTINUE: return CONTINUE;
+ }
+
+ warning("%s is not a video aend action", str);
+ return STOP;
+ }
+
+ /**
+ * Returns a string description of the VideoEndAction
+ */
+ public string description()
+ {
+ switch (this)
+ {
+ case STOP: return _("Stop playback");
+ case LOOP: return _("Loop");
+ case CONTINUE: return _("Continue to next slide");
+ }
+ return "undefined";
+ }
+
+ /**
+ * Creates a ListStore with the first column set as the description
+ * and the second column set as the VideoEndAction.
+ */
+ public static Gtk.ListStore list_store()
+ {
+ var store = new Gtk.ListStore(2, typeof(string),
+ typeof(VideoEndAction));
+ Gtk.TreeIter itr;
+
+ store.append(out itr);
+ store.set(itr, 0, STOP.description(), 1, STOP);
+ store.append(out itr);
+ store.set(itr, 0, LOOP.description(), 1, LOOP);
+ store.append(out itr);
+ store.set(itr, 0, CONTINUE.description(), 1, CONTINUE);
+
+ return store;
+ }
+}
+
diff --git a/src/ease-editor-embed.vala b/src/ease-editor-embed.vala
index fc4036f..ef30781 100644
--- a/src/ease-editor-embed.vala
+++ b/src/ease-editor-embed.vala
@@ -52,7 +52,20 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
/**
* The currently selected { link Actor}.
*/
- internal Actor selected { get; private set; }
+ internal Actor selected
+ {
+ get { return selected_priv; }
+ private set
+ {
+ if (selected_priv != null)
+ {
+ selected_priv.notify.disconnect(on_selected_notify);
+ }
+ selected_priv = value;
+ if (value != null) value.notify.connect(on_selected_notify);
+ }
+ }
+ private Actor selected_priv;
/**
* If the selected { link Actor} is being edited.
@@ -571,8 +584,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
mouse_x = event.x;
mouse_y = event.y;
- position_selection();
-
selected.element.changed();
}
return true;
@@ -679,8 +690,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
mouse_x = event.x;
mouse_y = event.y;
- position_selection();
-
selected.element.changed();
return true;
@@ -738,7 +747,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
undo(new UndoAction(selected.element, "y"));
selected.translate(0, shift ?
-NUDGE_SHIFT_PIXELS : -NUDGE_PIXELS);
- position_selection();
selected.element.changed();
return true;
@@ -749,7 +757,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
undo(new UndoAction(selected.element, "y"));
selected.translate(0, shift ?
NUDGE_SHIFT_PIXELS : NUDGE_PIXELS);
- position_selection();
selected.element.changed();
return true;
@@ -760,7 +767,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
undo(new UndoAction(selected.element, "x"));
selected.translate(shift ?
-NUDGE_SHIFT_PIXELS : -NUDGE_PIXELS, 0);
- position_selection();
selected.element.changed();
return true;
@@ -771,7 +777,6 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
undo(new UndoAction(selected.element, "x"));
selected.translate(shift ?
NUDGE_SHIFT_PIXELS : NUDGE_PIXELS, 0);
- position_selection();
selected.element.changed();
return true;
@@ -818,5 +823,23 @@ internal class Ease.EditorEmbed : ScrollableEmbed, UndoSource
keys_connected = false;
key_press_event.disconnect(on_key_press_event);
}
+
+ /**
+ * Notifies of changes to the selected actor.
+ */
+ internal void on_selected_notify(GLib.Object object, GLib.ParamSpec pspec)
+ {
+ if (selection_rectangle == null) return;
+
+ switch (pspec.name)
+ {
+ case "x":
+ case "y":
+ case "width":
+ case "height":
+ position_selection();
+ break;
+ }
+ }
}
diff --git a/src/ease-player.vala b/src/ease-player.vala
index f917181..b38aa07 100644
--- a/src/ease-player.vala
+++ b/src/ease-player.vala
@@ -244,6 +244,7 @@ internal class Ease.Player : GLib.Object
if (slide_index == 0)
{
create_current_slide(slide);
+ slide.request_advance.connect(on_request_advance);
current_slide.stack(container);
current_slide.opacity = 0;
current_slide.animate(Clutter.AnimationMode.EASE_IN_SINE,
@@ -259,8 +260,10 @@ internal class Ease.Player : GLib.Object
// otherwise, animate as usual
else
{
+ old_slide.slide.request_advance.disconnect(on_request_advance);
old_slide = current_slide;
create_current_slide(slide);
+ slide.request_advance.connect(on_request_advance);
container.add_actor(current_slide);
if (old_slide.slide.transition_time > 0)
@@ -322,4 +325,14 @@ internal class Ease.Player : GLib.Object
advance_alarm.start();
}
}
+
+ /**
+ * This is requested by video actors that have finished playing. As the
+ * calling function is on Slide, element plugins should never be aware
+ * that this functionality exists.
+ */
+ private void on_request_advance(Element element)
+ {
+ advance();
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]