[ease] Transition previews in the inspector.



commit 54bcaf2318a89027a0cd1b2e884b9278800c90f8
Author: Nate Stedman <natesm gmail com>
Date:   Wed Jun 2 17:36:57 2010 -0400

    Transition previews in the inspector.

 src/Document.vala       |   18 ++++++++
 src/Slide.vala          |   45 +++++++++++++++++++++
 src/SlideActor.vala     |   54 ++++++++++++++++++++++++-
 src/TransitionPane.vala |  102 ++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 207 insertions(+), 12 deletions(-)
---
diff --git a/src/Document.vala b/src/Document.vala
index 87eaec9..101c5f4 100644
--- a/src/Document.vala
+++ b/src/Document.vala
@@ -74,6 +74,24 @@ public class Ease.Document : GLib.Object
 		slides.insert(index, s);
 	}
 	
+	/**
+	 * Returns whether or not the Document has a { link Slide} after the
+	 * passed in { link Slide}.
+	 *
+	 * @param slide
+	 */
+	public bool has_next_slide(Slide slide)
+	{
+		for (int i = 0; i < slides.size - 1; i++)
+		{
+			if (slides.get(i) == slide)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	
 	public void export_to_html(Gtk.Window window)
 	{
 		// make an HTMLExporter
diff --git a/src/Slide.vala b/src/Slide.vala
index 5a3350a..ae72741 100644
--- a/src/Slide.vala
+++ b/src/Slide.vala
@@ -45,6 +45,15 @@ public class Ease.Slide
 	public double transition_time { get; set; }
 	
 	/**
+	 * The duration of this Slide's transition, in milliseconds
+	 */
+	public uint transition_msecs
+	{
+		get { return (uint)(transition_time * 1000); }
+		set { transition_time = value / 1000f; }
+	}
+	
+	/**
 	 * If the slide advances automatically or on key press
 	 */
 	public bool automatically_advance { get; set; }
@@ -83,6 +92,42 @@ public class Ease.Slide
 	public int count { get { return elements.size; } }
 	
 	/**
+	 * The next Slide in this Slide's { link Document}.
+	 */
+	public Slide? next
+	{
+		owned get
+		{
+			for (int i = 0; i < parent.slides.size - 1; i++)
+			{
+				if (parent.slides.get(i) == this)
+				{
+					return parent.slides.get(i + 1);
+				}
+			}
+			return null;
+		}
+	}
+	
+	/**
+	 * The previous Slide in this Slide's { link Document}.
+	 */
+	public Slide? previous
+	{
+		owned get
+		{
+			for (int i = 1; i < parent.slides.size; i++)
+			{
+				if (parent.slides.get(i) == this)
+				{
+					return parent.slides.get(i - 1);
+				}
+			}
+			return null;
+		}
+	}
+	
+	/**
 	 * Create a new Slide.
 	 */
 	public Slide() {}
diff --git a/src/SlideActor.vala b/src/SlideActor.vala
index a476367..e9b9565 100644
--- a/src/SlideActor.vala
+++ b/src/SlideActor.vala
@@ -63,7 +63,7 @@ public class Ease.SlideActor : Clutter.Group
 	private const int REFLECTION_OPACITY = 70;
 
 	public SlideActor.from_slide(Document document, Slide s, bool clip,
-	                              ActorContext ctx)
+	                             ActorContext ctx)
 	{
 		slide = s;
 		context = ctx;
@@ -98,7 +98,53 @@ public class Ease.SlideActor : Clutter.Group
 
 		add_actor(contents);
 	}
-
+	
+	/**
+	 * Instantiates a SlideActor of a single color. Used for transition previews
+	 * with no "next" slide.
+	 *
+	 * @param document The { link Document} this slide is "part of", to make it
+	 * the proper size.
+	 * @param color The background color.
+	 */
+	public SlideActor.blank(Document document, Clutter.Color color)
+	{
+		// create the background
+		background = new Clutter.Rectangle();
+		((Clutter.Rectangle)background).color = color;
+		
+		// create a blank contents actor
+		contents = new Clutter.Group();
+		
+		// set the background size
+		background.width = document.width;
+		background.height = document.height;
+	}
+	
+	/**
+	 * Resets all transformations on this SlideActor.
+	 */
+	public void reset(Clutter.Group container)
+	{
+		reset_actor(this);
+		reset_actor(background);
+		reset_actor(contents);
+		stack(container);
+	}
+	
+	private void reset_actor(Clutter.Actor actor)
+	{
+		actor.depth = 0;
+		actor.opacity = 255;
+		actor.rotation_angle_x = 0;
+		actor.rotation_angle_y = 0;
+		actor.rotation_angle_z = 0;
+		actor.scale_x = 1;
+		actor.scale_y = 1;
+		actor.x = 0;
+		actor.y = 0;
+	}
+	
 	public void relayout()
 	{
 		set_background();
@@ -159,6 +205,10 @@ public class Ease.SlideActor : Clutter.Group
 		{
 			contents.reparent(this);
 		}
+		if (get_parent() != container)
+		{
+			reparent(container);
+		}
 	}
 
 	// unstack the actor, layering it with another actor
diff --git a/src/TransitionPane.vala b/src/TransitionPane.vala
index 9729879..a503d41 100644
--- a/src/TransitionPane.vala
+++ b/src/TransitionPane.vala
@@ -29,12 +29,16 @@ public class Ease.TransitionPane : InspectorPane
 	private Gtk.SpinButton delay;
 	
 	// transition preview
-	private Gtk.AspectFrame aspect;
 	private GtkClutter.Embed preview;
+	private Clutter.Group preview_group;
 	private Gtk.Alignment preview_align;
 	private SlideActor current_slide;
 	private SlideActor new_slide;
+	private Clutter.Timeline preview_alarm;
+	
+	// constants
 	private const int PREVIEW_HEIGHT = 150;
+	private const uint PREVIEW_DELAY = 500;
 	
 	public TransitionPane()
 	{
@@ -42,12 +46,17 @@ public class Ease.TransitionPane : InspectorPane
 		
 		// preview
 		preview = new GtkClutter.Embed();
-		preview.set_size_request(0, PREVIEW_HEIGHT);
 		((Clutter.Stage)(preview.get_stage())).color = {0, 0, 0, 255};
-		aspect = new Gtk.AspectFrame("", 0.5f, 0.5f, 0.5f, false);
-		aspect.add(preview);
-		aspect.shadow_type = Gtk.ShadowType.IN;
-		pack_start(aspect, false, false, 5);
+		
+		preview_align = new Gtk.Alignment(0.5f, 0.5f, 1, 1);
+		var frame = new Gtk.Frame(null);
+		frame.shadow_type = Gtk.ShadowType.IN;
+		preview_align.add(preview);
+		frame.add(preview_align);
+		
+		pack_start(frame, false, false, 5);
+		preview_group = new Clutter.Group();
+		((Clutter.Stage)preview.get_stage()).add_actor(preview_group);
 		
 		// transition selection
 		var vbox = new Gtk.VBox(false, 0);
@@ -66,7 +75,7 @@ public class Ease.TransitionPane : InspectorPane
 		vbox.pack_start(align, false, false, 0);
 		hbox.pack_start(vbox, true, true, 5);
 		
-		// transition transition_time
+		// transition time
 		vbox = new Gtk.VBox(false, 0);
 		align = new Gtk.Alignment(0, 0, 0, 0);
 		align.add(new Gtk.Label(_("Duration")));
@@ -177,13 +186,59 @@ public class Ease.TransitionPane : InspectorPane
 		delay.value_changed.connect(() => {
 			slide.advance_delay = delay.get_value();
 		});
+		
+		// automatically scale the preview to fit in the embed
+		preview.get_stage().allocation_changed.connect((box, flags) => {
+			preview_group.scale_x = (box.x2 - box.x1) / slide.parent.width;
+			preview_group.scale_y = (box.y2 - box.y1) / slide.parent.height;
+		});
+		
+		// automatically set the correct aspect ratio for the preview
+		preview_align.size_allocate.connect((widget, allocation) => {
+			if (slide == null) return;
+			
+			preview_align.height_request =
+				(int)(allocation.width / slide.parent.aspect);
+		});
 	}
 	
-	protected override void slide_updated()
+	private void animate_preview()
+	{
+		current_slide.reset(preview_group);
+		new_slide.reset(preview_group);
+		new_slide.opacity = 0;
+		
+		preview_alarm = new Clutter.Timeline(PREVIEW_DELAY);
+		preview_alarm.completed.connect(() => {
+			animate_preview_start();
+		});
+		preview_alarm.start();
+	}
+	
+	private void animate_preview_start()
+	{
+		new_slide.opacity = 255;
+		
+		current_slide.transition(new_slide, preview_group);
+		
+		preview_alarm = new Clutter.Timeline(slide.transition_msecs);
+		preview_alarm.completed.connect(() => {
+			animate_preview_delay();
+		});
+		preview_alarm.start();
+	}
+	
+	private void animate_preview_delay()
 	{
-		// set preview aspect ratio
-		aspect.ratio = (float)slide.parent.width / (float)slide.parent.height;
+		preview_alarm = new Clutter.Timeline(PREVIEW_DELAY);
+		preview_alarm.completed.connect(() => {
+			animate_preview();
+		});
+		preview_alarm.start();
+	}
 	
+	protected override void slide_updated()
+	{
 		// set transition time box
 		transition_time.set_value(slide.transition_time);
 		
@@ -195,6 +250,33 @@ public class Ease.TransitionPane : InspectorPane
 		start_transition.set_active(slide.automatically_advance ? 1 : 0);
 		delay.set_value(slide.advance_delay);
 		delay.sensitive = slide.automatically_advance;
+		
+		// size the preview box
+		Gtk.Allocation alloc = Gtk.Allocation();
+		preview_align.get_allocation(alloc);
+		preview_align.height_request = (int)(alloc.width / slide.parent.aspect);
+		
+		// remove the old preview slide actors
+		preview_group.remove_all();
+		
+		// add new slide previews
+		current_slide = new SlideActor.from_slide(slide.parent, slide, true,
+		                                          ActorContext.PRESENTATION);
+		
+		new_slide = slide.parent.has_next_slide(slide) ?
+		            new SlideActor.from_slide(slide.parent, slide.next, true,
+		                                      ActorContext.PRESENTATION) :
+		            new SlideActor.blank(slide.parent, { 0, 0, 0, 255 });
+		
+		preview_group.add_actor(current_slide);
+		preview_group.add_actor(new_slide);
+		
+		// start the preview animation
+		if (preview_alarm != null)
+		{
+			preview_alarm.stop();
+		}
+		animate_preview();
 	}
 }
 



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