So far the features I’ve written about are used with both Captivate 4 and 5. I’m all for keeping widgets version neutral, but we gotta start moving with the times right? Enter feature number 3: Captivate 5 events!

What’s an ‘Event’?
Have you ever watched a track race? I’ll take your silence as a yes. Well, you know when all the runners are lined up at the start of the race, waiting for the official to fire the starter pistol? Think of your widget as one of those runners, and events as the pistol firing. When the pistol is fired the runner will start running. Same with a widget, when an event happens, code will start running. However, the athlete will only start running if he is listening for the shot. In the same way, our widget will only respond to an event if it is listening for it.
How do I get my widget to listen for one of these Events?
Any object that can listen for events has a method called addEventListener() for this very purpose. To set the type of event you wish to listen for, as well as the action to be taken when this event happens, you must pass this information into addEventListener()’s parameters. Parameters are things we pass into the method’s parentheses () to customize its behaviour. For example: If you pass in a number into the parenthases of the gotoAndPlay() method, your flash movie will jump to the frame of that number. So gotoAndPlay(4) will make the movie go to the fourth frame and play. If a method has multiple parameters, then they are separated by a comma. addEventListener has a lot of parameters, but for our purposes, we’ll only look at the first two.
The first parameter is the event we want to listen to. In ActionScript, events are categorized into classes. All events that are caused by the mouse are available through the MouseEvent class, all events that are caused by the keyboard are available through the KeyboardEvent class, and all events that deal with widgets are available through the WidgetEvent and QuestionWidgetEvent classes. If you’re going to listen for any of these events, you need to import those classes into your widget.
The second parameter is where we pass in a function to call when the event is dispatched (when an event has been ‘dispatched’, that mens it has ‘happened’).
So say we wanted our widget to run the code in the ‘onMoviePaused’ function when the Captivate Movie is paused, here’s how we would write that event listener…
1 | addEventListener(WidgetEvent.PAUSE, onMoviePaused); |
…and here is how this would fit in to a widget’s class…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package { import widgetfactory.StaticWidget; // REMEMBER TO IMPORT THE EVENT CLASS import widgetfactory.events.WidgetEvent; public class AttentiveWidget extends StaticWidget { public function AttentiveWidget () { addEventListener(WidgetEvent.PAUSE, onMoviePaused); } private function onMoviePaused(e:WidgetEvent):void { // Your code here will be run when the movie is paused. } } } |
Notice how the onMoviePaused function has (e:WidgetEvent) between its braces? Well this is what a parameter looks like from the function’s point of view. According to the function, a parameter is just another variable, and what we’re doing in between the braces is declaring that variable. The part before the :is the variable name (e), and the part after the : is the data type (WidgetEvent). Why do we need to create this parameter? Well when the event is dispatched, the widget will call the onMoviePaused function, but it will also pass in information about the current WidgetEvent. If you leave this information out, then you’ll get an error, because the onMoviePaused function isn’t expecting anything to be passed into it.
So now that we’ve covered how to listen and respond to events, let’s find out what events we can respond to.
New To Captivate 5 Events
Like I said above, WidgetEvent and QuestionWidgetEvent hold all the events that deal with widgets. Here’s a list of all the new events in Captivate 5, and an example of how you would listen to that event. All these events are only dispatched in the output Captivate Movie. Remember, unless otherwise stated, these events will only be dispatched in Captivate 5 or above.
WidgetEvent.ENTER_SLIDE
This event is dispatched on the first frame of a new slide, the only exception being the first slide.
1 | addEventListener(WidgetEvent.ENTER_SLIDE, onEnterSlide); |
WidgetEvent.EXIT_SLIDE
This event is dispatched on the last frame of a slide. Useful if you need to do a little cleanup on the slide to make it look like it did at the start. Remember, the audience can always rewind the movie and watch it again.
1 | addEventListener(WidgetEvent.EXIT_SLIDE, onExitSlide); |
WidgetEvent.PAUSE
This event is dispatched when the movie pauses. The movie may pause for a number of reasons. It may be that the audience has clicked the pause button on the playbar, or it may be that some object (say a button) has paused Captivate internally. Remember that (e:WidgetEvent) event information parameter thingo? Well, that little ‘e’ has a property called isActOfUser. If that property is true, it was the audience who paused the movie. If it’s false, then the movie was paused by some Captivate operation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | addEventListener(WidgetEvent.PAUSE, onMoviePaused); private function onMoviePaused (e:WidgetEvent):void { if (e.isActOfUser) { // The user paused the movie! How dare they! } else { // Captivate paused the movie... I'll let it slide. } } |
WidgetEvent.RESUME
After the movie has been paused, hopefully at some point it will be resumed again. This event is dispatched when it is. This event also has the isActOfUser property to inform us of whether it was the audience, or Captivate, that resumed the movie
1 | addEventListener(WidgetEvent.PAUSE, onMovieResumed); |
WidgetEvent.MOVIE_START
Remember how I said that the ENTER_SLIDE event is not dispatched at the beginning of the first slide? Well this event is. Very useful if you need to set up stuff right at the start of the movie. Or if you want to do some covert monitoring on the Captivate Movie…
There are two unique things with this event:
- There is also a movieStart() Template Method.
- This event also works in Captivate 4 if widgets are set up to be externalized. (Update: As of WidgetFactory 5.1, this Template Method works in Captivate 4 regardless of it being externalized)
1 | addEventListener(WidgetEvent.MOVIE_START, onCaptivateMovieStart); |
WidgetEvent.MOVIE_END
This event is dispatched right at the end of the Captivate Movie. This would be useful for a widget that confirms whether or not the user has finished watching the lesson. Hmm… Interesting…
1 | addEventListener(WidgetEvent.MOVIE_END, onCaptivateMovieEnd); |
WidgetEvent.INTERACTIVE_ITEM_SUBMITTED
This one is interesting. It’s dispatched when an interactive object (anything that has success and failure captions) sends information to Captivate on the result of the user’s interaction with it. You can also read the data that the interactive item sent… Well… You will be able to in the next patch release of WidgetFactory. Didn’t quite have enough time to squeeze it in this release. Stay tuned!
1 | addEventListener(WidgetEvent.INTERACTIVE_ITEM_SUBMITTED, onInteractiveItemSubmit); |
QuestionWidgetEvent.QUESTION_SUBMITTED
Like the INTERACTIVE_ITEM_SUBMITTED event, only for questions. Yes, you can read the data about how the question was answered, and yes, that ability is not in the current WidgetFactory but will be soon.
BTW: All the other events in the QuestionWidgetEvent class can only be used if your widget is a Question Widget. This one however, can be used regardless of the type of widget.
1 | addEventListener(QuestionWidgetEvent.QUESTION_SUBMITTED, onQuestionSubmit); |
Conclusion
So, lots of new stuff to play around with. These events can be used to create some interesting widgets. Let me know how you use them, I’d be very interested to know.
I hope to post #2 next week, but I’ll have to see how I go. Next week is going to be unusually busy as I’m helping out Allen Partridge with his free eSeminar on building widgets! We’re going to be building a widget from scratch. Tune in, it’s going to be awesome.


This is another great tutorial. Was wondering if you had a quick way of detecting if a slide is a captivate question.
if (cpVariables.cpInfoCurrentSlideType == “QuestionSlide”) will tell you if the current slide being viewed is a question slide.
If you want to make sure that the slide the widget is on is a question slide, try: if (widgetSlide.toString() == “[object rdQSlide]“)
thanks heaps!!!
any ideas on how you would get an array of each slides SlideType
the reason I ask is ive developed a captivate loader and am trying to deactivate my pagination when the captivate is a question slide to wait for the answer and I figure out the way to get the array and thanks heaps by the way. but im still having trouble getting a listener to my embedded captivate files.
eg
if(topic[num] == “[object rdQSlide]“){
currenttopic.addEventListener(QuestionWidgetEvent.QUESTION_SUBMITTED, onQuestionSubmit);
}
ps: class deffinition error if you dont use QuestionWidgetEvent for your example above
Okay, well you can get an array of slides like so:
var slidesArray:Array = new Array();
for (var i:int = 0; i <= widgetSlide.parent.numChildren – 1; i++) // Loop through parent's children
{
if (widgetSlide.parent.getChildAt(i).hasOwnProperty("isSlideActive")) // If the current child has the property we're looking for
slidesArray.push(widgetslide.parent.getChildAt(i)); // Add it to the array
}
Also, you add the QuestionWidgetEvent.QUESTION_SUBMITTED event listener to the widget itself, not the slide.
Sorry Im probably realy anoying was wondering if you could give me a hand I dont think I have explained my self well.
have you played much with listening for quiz events on loaded CP files.
Ive made a preloading widget with my own pagination. my pagination detects when it is a question slide and deactivates. but I cant figure out how to listen for completion of a question so I can reinable my pagination and move to the next slide.
any help with this would be greatly appreciated.
Where have you added your QuestionWidgetEvent.QUESTION_SUBMITTED listener?
well thats the thing because I have embedded the captivate file it doesnt contain a widget and only the parent file can access the widget events.
I’m afraid I don’t quite understand you. Are you writing this code in a widget, or a normal swf that’s being imported into Captivate as an animation?
in a widget I use the dialogue properties to select a captivate file then in runtime I load it then if its has a quiz slide i need to listen for the submition of this question and call a function to update my pagination.
Is this in an aggregated project? I don’t quite understand what you mean by ‘selecting a Captivate File’.
Anyway. Just to make sure. You’ve added the QuestionWidgetEvent.QUESTION_SUBMITTED listener to the widget, not the slide right?
mm it doesnt seem to work
public function loadTopic():void {
l.contentLoaderInfo.addEventListener(Event.COMPLETE, populateCanvas);
l.load(new URLRequest(properties.fileRefference));
}
public function populateCanvas(event:Event):void {
canvas.addChild(l);
canvas.addEventListener(QuestionWidgetEvent.QUESTION_SUBMITTED, questSub);
}
public function questSub(event:QuestionWidgetEvent):void {
//do stuff
}
So I take it that the Canvas is the widget, and this code is in another swf that’s put into Captivate as an animation, is that right?
The canvas is a empty movie clip that exist in the widget and the other swf is a captivate swf containing a quiz
And you’re trying to listen for the quiz completing in the Captivate Movie you’re loading in. So this is one of those Captivate inside of Captivate things.
Ah, I see…
The widget will only pick up events from quizzes inside its Captivate Movie. I recommend sticking another widget in the loaded Captivate Movie which listens for the QuestionWidgetEvent.QUESTION_SUBMITTED event, and responds to it by dispatching another custom event that uses bubbles. Then the top widget can listen for the custom event. Something like this:
Sub-Widget:
addEventListener(QuestionWidgetEvent.QUESTION_SUBMITTED, onQuestionSubmitted);
private function onQuestionSubmitted(e:QuestionWidgetEvent):void
{
dispatchEvent(new Event(“customEventName”, true, false));
}
In the top widget:
addEventListener(“customEventName”, onCustomEvent);
// Function will run as if it was responding to the question submitted.
private function onCustomEvent(e:Event):void
{
// Do stuff
}
Thanks heaps tristan your a saviour
only one little problem it triggers twice
fixed it awsome hey most greatly apprecitly
Your welcome.
Glad we worked it out.
hey Ive come accross a new issue that my question widgets don’t apear onRuntime the first time I view the slide I navigate out and navigate back then they appear
Hi JJL,
Sorry I haven’t replied so far. I’ve been a bit flooded the past couple of days.
Is this the same widget we were talking about above?
What type of widget is it? (Static, Interactive, Question)
What are you trying to show?
Have you tried bringing it into a blank movie and seeing if it works there?
hey tristan,
ahh dont worry about that buddy just glad you can help when you can I realy appreciate your time and understand how buisy you must be.
Well Im currently developing a series of question widgets. they sit in a captivate file that im loading into my interactive widget that is a loader that yourself and Wyevs have been helping me with. it sits in a captivate file and loads other captivate files.
now the first time I load it the question widget doesnt enterRuntime if I go out and reload it it works fine.
Now ive come up with a work around by just making all the functionality work in its basic form without entering runtime but the triggerSubmitProcess() doesnt seem to trigger on my first instance.
any ideas??
and what would be preventing your enterRuntime function from being called in my loader.
Sorry jjl. Not many ideas.
The only thing I can think of is if you’ve unticked the visibility for the widget in Captivate. Currently that stops enterRuntime from triggering (for the moment, working on it
)
Without seeing your project or code, I can’t really tell what’s going on.
so if i had a widget that was part of a captivate master slide for all slides, how do i attach a ENTER_SLIDE or EXIT_SLIDE event that wouldn’t fire off for every slide when i enter/exit just one slide?
in other words, if my master slide is used 8 times, and i enter/exit slide 1, i get 8 events fired instead of just the one. the only workaround i have is to put in a condition to check that the widgetSlide is the currentSlide – i am just hoping for smthg more elegant …
I’m afraid that’s not the way master slides work.
When the Captivate Movie is published, objects on the master slide are copied to the slides that point to them. So instead of having one widget appearing and disappearing over eight slides, You’ll have eight widgets, one placed on each sllide.
So you need to treat that circumstance as if the user pasted the widget on to the slide and set it to appear till End of Slide.
I built an interface widget that in Captivate the timing is set for the Entire Project. This supposedly sets the widget to the top most layer. Unfortunately, the Captivate-generated Table of Contents is above the widget. Is there any way to get the TOC behind the widget?
Hi Dave,
The reason why this isn’t happening is that the slide container (which the widget is inside) appears bellow the TOC. So no matter how high inside the slide your interface appears, the TOC will always be in front. Probably the best way to get around this is rather than adding the interface as a child to the widget, add it as a child to the ‘captivateMainTimeline’ property. This is the same container that holds the TOC, so your interface can appear in front of it. Just make sure to remove your interface from captivateMainTimeline in the exitRuntime() template method.