No one’s perfect. We all get it wrong occasionally. However at some point, I think this happened soon after pencils came along, people started to expect that they would be able to redo their answer if they got it wrong. Thus the eraser was born. It morphed into a number of forms until becoming the clear button we know and love today.
For Question Widgets, when the audience clicks the clear button, it should return to an unanswered state.
How do we know when to clear the question?
You may be able to guess this for yourself by now, but there’s a Template Method for that – clearAnswers(). clearAnswers() is called whenever the question’s clear button is clicked.
Like enableAnswers() and disableAnswers(), clearAnswers() is a public function, not protected.
1 2 3 4 | override public function clearAnswers():void { } |
How do we clear the question?
You see the hard part is every question is different. For a multiple choice question, clearing would be deselecting each answer. For a drag and drop question, clearing would mean returning all the drag objects back to their starting position. For a crossword puzzle, clearing would mean removing all the letters from the crossword boxes. For each question type, the clearing process is different.
Let’s look at last post’s widget for an example of clearing a question. To reset that question, all you need to do is return the circle to its starting point.
1 2 3 4 5 6 | override public function clearAnswers():void { // Snap the circle back to the top left point. circle.x = 0; circle.y = 0; } |
So that’s not that painful. Actually, once you’ve worked out how to clear the question, the code behind it is usually not painful at all. I’m writing this sentence because I feel I should say more on this subject.
Here are some tips for clearing your question:
- Get familiar with Arrays. Quite often your question will involve multiple objects, so you will need to record where each object was at the start of the question in the array. Then when you clear the question, read the array back and apply the values to your objects.
- Template Methods aren’t just called by WidgetFactory, you can also call them yourself. In the last post I mentioned how you may need to use enableAnswers() to reset the question as well as add interactivity. Instead of writing all that clearing code in enableAnswers(), just call clearAnswers() again.
- Usually when you clear the answer, everything just snaps back to its original state. However there is nothing that says the reset has to be instantaneous. If you’ve got a crossword puzzle widget, why not have the letters fade gradually from the puzzle? If you’ve got a drag and drop question, why not have the drag objects tween back to their starting position? I did that with TweenLite for last week’s Simple Circle question.
We’re drawing to the end of the Question Widget saga. We know how to score our question, we know how to enable or disable our question, and we now know how to clear our question. In the next post, we’ll look at how to control what comes up in our question’s Review Area.





Pingback: Tweets that mention Question Widgets – Part 3 | The Widget King -- Topsy.com
Hi Tristan,
Thanks so much for this blog! Saved me a few times.
Using the WidgetFactory for a few question items and haven’t seen this in my browsing and searches:
In a question which affords the user two attempts, how do I have the widget display the “Try Again” captivate text blurb on the first incorrect attempt?
Thanks! Brian
Hi Brian,
The ‘try again’ caption appears when Captivate picks up the isCompleteAnswer property as being false. It doesn’t appear if isCompleteAnswer is set to true, and it doesn’t care what isCorrectAnswer equals.
Hi Tristan,
I’ve got a question widget and I’m not sure if I’m approaching it right and hoped you could set me in the right direction.
Use Case: The learner moves the mouse in a circular pattern to rotate the item and a text box shows the angle. Once the user clicks the stage, the rotation is locked and the chosen angle compared to the correct number.
Approach:I’ve got the enableAnswers and disableAnswers adding and removing the eventListeners (respectively) and am using
for both the rotate and kill functions. The rotation is controlled via an ENTER_FRAME function called when the enableAnswers() adds the event listener. The kill function removes the rotate listener and checks the chosen angle against the correct angle. In my submit function, I also remove the listeners then make use of the isCompleteAnswer and isCorrectAnswer properties.
Issue:Once the submit function has been called the user cannot advance, so he’s received feedback balloon to click anywhere to continue but nothing happens (but handcursor is alive on the stage). Likewise, when the user clicks the submit button prior to locking the rotation (isCompleteAnswer is false) and he gets the yellow balloon stating he must answer the question, the screen is locked up and I can only advance with the control buttons at the bottom.
Questions:Why does the screen not take a click and advance? I have only removed stage click listener for the kill function, so why would that interfere with Captivate’s advance click? Should I not be using
? I tried using an invisible movie clip the area of the stage and assigned the listeners to that instead, but it didn’t work either. Should I have a stage listener and manually have it advance instead? Should I be using WidgetQuestionEvents? Would love your insight on this!
Thanks!
Brian
Hey Brian,
As far as I understand from your description, the user is setting the angle with the mouse from the very start of the slide, and continues doing that until the kill function is called. I’d think what would probably be better is if the user could control when they’re setting the angle by holding down the mouse button and releasing. I suggest having a visible dial graphic on stage (which you probably already do anyway) and have the user drag the mouse up and down on that to set the angle.
In that case you would listen for a MouseEvent.MOUSE_DOWN event being dispatched on the dial graphic. In the MOUSE_DOWN handler you’d start listening for the ENTER_FRAME event, you’d also start listening for the MouseEvent.MOUSE_UP event (I’d suggest adding this listener to the stage, as the user may not MOUSE_UP over the dial graphic). In the MOUSE_UP handler, remove your ENTER_FRAME listener and the MOUSE_UP listener.
All up that would look like this:
dial.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
function onMouseDown (e:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
function onMouseUp (e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
removeEventListener (Event.ENTER_FRAME, onEnterFrame);
}
The QuestionWidget’s submit() template method is called when the submit button has been clicked. I’d suggest you move the code that works out whether the user’s selected angle is correct to this function. That way you can remove the Kill button and use the submit button to do both jobs. I’d also set isCompleteAnswer to TRUE from the beginning of the widget and just concern yourself with whether the answer is correct or not.
Try those things and let me know if you’re still getting the problem with the success and failure captions.
Tristan,
Hi Tristan,
Thanks for the advice! I implemented suggested changes as it is much better from both a programming and usability perspective to have the mouse down while rotating. I now only have the isCompleteAnswer = true (not testing) and it’s in the enterRuntime() function along with the MouseDown listener:
2
3
4
5
6
7
8
9
10
11
...
pc.addEventListener(MouseEvent.MOUSE_DOWN, mouseIsDown);
pc.buttonMode = true;
isCompleteAnswer = true;
private function mouseIsDown(e:Event):void {
addEventListener(Event.ENTER_FRAME, rotate);
stage.addEventListener(MouseEvent.MOUSE_UP, letGo);
addChild(box);
}
I removed the enableAnswers() and disableAnswers() in this case to keep it simpler in troubleshooting. The rotate function tracks the mouse movement and rotates the pc accordingly, displaying the current angle in a box, with a call to a rounding function to keep the angle +- five degrees:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mouseXCoord = mouseX - pc.x;
mouseYCoord = (mouseY - pc.y) * -1;
angle = Math.atan(mouseYCoord/mouseXCoord)/(Math.PI/180); // get angle of mouse coords
if (mouseXCoord < 0) {
angle += 180;
}
if (mouseXCoord >= 0 && mouseYCoord < 0) {
angle += 360;
}
angle = roundToNearest(5, angle); // mousing accuracy +- 5 degrees
pc.rotation = (int(angle)*-1) + 90;
angle -= 90;
angle360 = int(angle); // keep 0 - 360 format to compare
if (angle > 180) { // convert to 0 - 180 format for user display
angle = int((angle -= 360)*-1);
}
feedback.text = angle360.toString(); // used for debugging only
angleText.text = Math.abs(angle).toString(); // adjust for negative numbers due to 90 degree diff
}
private function roundToNearest(roundTo:Number, angleValue:Number):Number {
return Math.round(angleValue/roundTo)*roundTo;
}
letGo is the MouseUp function which removes the EnterFrame and MouseUp listeners and grabs the chosen angle.
The submit function compares the chosen angle to the correct number and sets isCorrectAnswer accordingly. It also removes the MouseDown listener and changes the buttonMode to false.
2
3
4
5
6
7
8
9
10
11
removeEventListener(Event.ENTER_FRAME, rotate);
stage.removeEventListener(MouseEvent.MOUSE_UP, letGo);
angleChosen = angle360; // capture chosen angle at time of release
}
override protected function submit():void {
pc.removeEventListener(MouseEvent.MOUSE_DOWN, mouseIsDown);
pc.buttonMode = false;
isCorrectAnswer = (angleChosen == -45) ? true : false;
}
Despite the relative simplicity of the code, I’m still not able to advance upon answering the question. I get the success and failure captions as appropriate but can’t move from there unless via the forward button in the playback controls (which I intend to remove in production). The hand cursor is still showing for the whole slide, even though I’ve removed the listener. The cptx 5 file for this prototype has only one other question before the quiz results slide and I’ve got it publishing to Flash Player 10 in AS3. Upon submit, all event listeners have been removed. Not sure what I could be doing wrong.
Thanks again,
Brian
Update:
The question will now advance. The widget was fine the whole time. Captivate 5 was the culprit. In making a full sized widget, I’d dragged the “Type your question here” text just outside the slide (seeing as it won’t let you delete it and I had instructions built into the widget). Apparently, after pulling my hair out assuming it was the code, it was that which was causing it to not advance correctly. I tried whiting out the text and putting it back on the slide, and it worked! Hope this helps someone. Thanks again for your advice!
I’m glad you found the solution. I had a feeling that issue wasn’t related to the widget. I can’t tell you how many times I’ve tried to track down a bug in a widget, only to find out the Captivate project was corrupt. Fortunately Captivate 5.5 doesn’t seem to corrupt nearly as much.