After enabling spell checking with Squiggly the delete key and arrow keys stop working - actionscript-3

After enabling Squiggly on a TextFlow the delete key and arrow keys stop working. If I click around or switch applications and switch back it sometimes starts to work again. If I do not use Squiggly at all the delete and arrow keys work as expected.
Here is the code I'm using to enable Squiggly:
var locale:String = "en_US";
SpellUIForTLF.enableSpelling(myTextFlow, locale);
Has anyone encountered this and is there a fix?

It looks like a bug with TLF or the SpellUIForTLF class. In SpellUIForTLF class there is a method, "doSpellingJob" that is called. It is after this call that you want reselect the selection in the textflow.
// From customized SpellUIForTLF.as class
public function doSpellingJob():void
{
if (_spellingEnabled == false) return;
hh.clearSquiggles();
for (var idx:int = 0; idx < mTextFlow.flowComposer.numControllers; idx++)
{
var testController:ContainerController = mTextFlow.flowComposer.getControllerAt(idx);
//if (getValidFirstWordIndexTLF(testController) != -1)
spellCheckRangeTLF(getValidFirstWordIndexTLF(testController), getValidLastWordIndexTLF(testController));
}
if (hasEventListener(Event.COMPLETE)) {
dispatchEvent(new Event(Event.COMPLETE));
}
if (keepFocusOnTextFlow && !calledSetFocusOnce) {
setFocusOnTextFlowContainer();
calledSetFocusOnce = true;
}
}
public function setFocusOnTextFlowContainer():void {
//trace("Setting focus on textflow");
var testController:ContainerController;
if (mTextFlow.flowComposer) {
if (mTextFlow.flowComposer.numControllers) {
testController = mTextFlow.flowComposer.getControllerAt(0);
}
}
FlexGlobals.topLevelApplication.stage.focus = null;
if (testController) {
FlexGlobals.topLevelApplication.stage.focus = testController.container;
}
if (mTextFlow.interactionManager) {
//mTextFlow.flowComposer.updateAllControllers();
mTextFlow.interactionManager.setFocus();
var selectionState:SelectionState = mTextFlow.interactionManager.getSelectionState();
//TextObject(currentObject).textFlow.interactionManager.clearSelection();
mTextFlow.interactionManager.selectRange(selectionState.anchorPosition, selectionState.activePosition);
}
}
/**
* Sets the focus on the first container controller on the first composition complete.
* This is to fix a bug in some situations where the delete and arrow keys do not
* work after spell checking has been enabled before the text flow composition
* has completed. Usually there is also a delay with editing the first time the dictionary loads.
* */
public static var keepFocusOnTextFlow:Boolean;
public static var calledSetFocusOnce:Boolean;
public function SpellUIForTLF(textModel:TextFlow, lang:String)
{
...
calledSetFocusOnce = false;
if (keepFocusOnTextFlow) {
setFocusOnTextFlowContainer();
}
...
}

Related

AS3 - Multiple Images Turn On/Off Not Working with Second MovieClip

Ok this has been driving me insane. My AS3 knowledge isn't the best in the world, but I'm trying to work out where I'm going wrong with all of this.
Basically, What I'm trying to do is at certain times, make visible/invisble two different MovieClips.
The weird thing is, one is responding. And the other isn't. They are both identical aside from jpeg contents and names. Is there a setting I'm missing? Both have matched MovieClip names and Instance names... but when I use the code below, HOP1 turns off/on, but HOP2 refuses to! Am i just missing some stupidly obvious preference?
I will mention, I'll have to modify the code to work with two different MovieClips, but right now I just want both files to turn off!
package {
import flash.display.MovieClip;
import flash.events.TimerEvent;
import flash.ui.Mouse;
import flash.utils.Timer;
import com.boo.CustomDate;
import com.boo.ScreensaverSimple;
public class Generic extends MovieClip {
// This is where you can set the Hour of Power time start and end time (in 24 hour format e.g. 1330 for 1:30pm)
// If there is no hour of power, simply set both numbers to 0
private var HourOfPowerStartTime:Number = 0;
private var HourOfPowerEndTime:Number = 0;
private var ss:ScreensaverSimple;
public var time_check_timer:Timer;
private var delay_add_timer:Timer;
public function Generic() {
Mouse.hide();
ss = new ScreensaverSimple;
ss.setScreensaver(screens);
HOP2.visible = false;
time_check_timer = new Timer(1000);
time_check_timer.addEventListener(TimerEvent.TIMER, checkTime);
delay_add_timer = new Timer(1,1);
delay_add_timer.addEventListener(TimerEvent.TIMER, addAllChildren);
delay_add_timer.start();
}
public function addAllChildren(evt:TimerEvent=null):void {
delay_add_timer.removeEventListener(TimerEvent.TIMER, addAllChildren);
delay_add_timer.stop();
delay_add_timer = null;
time_check_timer.start();
checkTime();
}
public function checkTime(evt:TimerEvent=null):void {
checkHOP2();
}
private function checkHOP1():void {
if(HourOfPowerStartTime == 0 && HourOfPowerEndTime == 0)
{
if(HOP2.visible == true)
{
HOP2.visible = false;
}
return;
}
var CurrentTime:Number = CustomDate.return24HourNumber();
if(CurrentTime >= HourOfPowerStartTime && CurrentTime <= HourOfPowerEndTime)
{
if(HOP2.visible == false)
{
HOP2.visible = true;
}
}
else
{
if(HOP2.visible == true)
{
HOP2.visible = false;
}
}
}
}
}
if(HOP2.visible == true)
{
HOP2.visible = false;
}
Fist thing the if condition is complete redundant here. If you think about it, those lines work exactly the same as this one alone:
HOP2.visible = false;
Also (HOP2.visible == true) would be exactly the same as (HOP2.visible) and also you can assign value of condition check directly to variable. Generally you can reduce your function to:
private function checkHOP1():void {
HOP2.visible = (HourOfPowerStartTime || HourOfPowerEndTime);
if (!HOP2.visible) return;
var CurrentTime:Number = CustomDate.return24HourNumber();
HOP2.visible = (CurrentTime >= HourOfPowerStartTime && CurrentTime <= HourOfPowerEndTime);
}
Then I see you call to checkHOP2() :
public function checkTime(evt:TimerEvent=null):void {
checkHOP2();
}
but I don't see the checkHOP2() function defined in code you gave.
Similarly I don't see form where you call your checkHOP1() function you have posted. And also I don't get why change HOP2 instance inside function named checkHOP1() . Is it suppose to be some kind of obfuscation?

A-star implementation, not finding shortest path issue

I have code that is supposed to find the shortest path from point A to point B. To do this i am using a A-star variation. I am using a 2d array to represent a 2d grid but my path does not take diagonal shortcuts, only left, right, up, and down. So far everything works fine except it does not always find the shortest path possible. I want to know what is going wrong, why it is going wrong, and how I can fix it. Thank you in advance.
Here is a picture to illustrate what exactly is happening:
and here is my code (path finding class first, then its helper class):
BTW: Math vector is nothing more than just a geometric point class, and both playerTileLocation and enemyTileLocation are just points that correspond to the start and end nodes on the grid. Also i use the class AStarNode as the nodes for all the tiles on the map, instead of a regular object.
package {
import src.Characters.Character;
import src.InGame.Map;
import src.Maths.MathVector;
public final class BaseAI {
// REPRESENTS UP, DOWN, RIGHT, AND LEFT OF ANY ONE NODE
private static const bordersOfNode:Array = new Array(
new MathVector( -1, 0), new MathVector(1, 0), new MathVector(0, -1), new MathVector(0, 1));
private var _player:Character;
private var map:Map;
private var playerTileLocation:MathVector;
private var openList:Array;
private var closedList:Array;
// 2D ARRAY OF MAP TILES (I DON'T USE HERE, BUT I PLAN TO IN FUTURE)
private var mapArray:Array;
private var originNode:AStarNode;
private var complete:Boolean;
public function BaseAI(_player:Character,map:Map):void {
this._player = _player;
this.map = map;
openList = new Array();
closedList = new Array();
mapArray = map.tiles;
}
public function get player():Character {
return this._player;
}
public function calculatePlayerTileLocation():void {
playerTileLocation = map.worldToTilePoint(player.groundPosition);
}
//WILL EVENTUAL RETURN A DIRECTION FOR THE ENEMY TO TAKE THAT ITERATION (EVERY 1-2 SECONDS)
public function getDirection(enemy:Character):String {
var enemyTileLocation:MathVector = map.worldToTilePoint(enemy.groundPosition);
originNode = new AStarNode(enemyTileLocation, playerTileLocation);
originNode.setAsOrigin();
openList = [originNode];
closedList = [];
complete = false;
var currentNode:AStarNode;
var examiningNode:AStarNode;
while (!complete) {
openList.sortOn("F", Array.NUMERIC);
currentNode = openList[0];
closedList.push(currentNode);
openList.splice(0, 1);
for (var i in bordersOfNode) {
examiningNode = new AStarNode(new MathVector(currentNode.X + bordersOfNode[i].x, currentNode.Y + bordersOfNode[i].y),playerTileLocation);
if (map.isOpenTile(map.getTile(examiningNode.X, examiningNode.Y)) && !examiningNode.isThisInArray(closedList)) {
if (!examiningNode.isThisInArray(openList)) {
openList.push(examiningNode);
examiningNode.parentNode = currentNode;
}else {
}
if (examiningNode.X == playerTileLocation.x && examiningNode.Y == playerTileLocation.y) {
complete = true;
var done:Boolean = false;
var thisNode:AStarNode;
thisNode = examiningNode;
while (!done) {
if (thisNode.checkIfOrigin()) {
done = true;
}else {
thisNode = thisNode.parentNode;
}
}
}
}
}
}
}
}
}
package {
import src.Maths.MathVector;
internal final class AStarNode {
private var _X:int;
private var _Y:int;
private var _G:int;
private var _H:int;
private var _F:int;
private var _parentNode:AStarNode;
private var _isOrigin:Boolean;
public static const VERTICAL:uint = 10;
public function AStarNode(thisNodeLocation:MathVector, targetNodeLocation:MathVector) {
X = thisNodeLocation.x;
Y = thisNodeLocation.y;
H = Math.abs(X - targetNodeLocation.x) + Math.abs(Y - targetNodeLocation.y);
G = 0;
F = H + G;
}
public function set X(newX:int):void {
this._X = newX;
}
public function get X():int {
return this._X;
}
public function set Y(newY:int):void {
this._Y = newY;
}
public function get Y():int {
return this._Y;
}
public function set G(newG:int):void {
this._G = newG;
}
public function get G():int {
return this._G;
}
public function set H(newH:int):void {
this._H = newH;
}
public function get H():int {
return this._H;
}
public function set F(newF:int):void {
this._F = newF;
}
public function get F():int {
return this._F;
}
public function set parentNode(newParentNode:AStarNode):void {
this._parentNode = newParentNode;
}
public function get parentNode():AStarNode {
return this._parentNode;
}
public function setAsOrigin():void {
_isOrigin = true;
}
public function checkIfOrigin():Boolean {
return _isOrigin;
}
public function isThisInArray(arrayToCheck:Array):Boolean {
for (var i in arrayToCheck) {
if (arrayToCheck[i].X == this.X && arrayToCheck[i].Y == this.Y) {
return true
}
}
return false
}
}
enter code here
}
A quick glance through your code raises the idea of wrong heuristics. Your G value is always 0 in a node, at lease I do not see where it could change. However, in A-star algorithm for your task (finding the shortest path with obstacles) it should represent the number of steps already made to reach the cell. That would allow the algorithm to replace the long path with a shorter one.
The one time I coded an A star 'algorithm' I used a 2-dimensional Array for the grid (as you have). At the start of the search each grid location's 'searched' property was set to false. Each grid location would also have an Array of connecting directions; options that the player could choose to move in - some might be open, some might be blocked and inaccessible.
I would start the search by checking the starting grid position for how many direction options it had. For each option I would push a 'path' Array into a _paths Array. Each 'path' Array would end up containing a sequence of 'moves' (0 for up, 1 for right, 2 for down and 3 for left). So for each initial path, I would push in the corresponding starting move. I would also set the grid position's 'searched' property to true.
I would then iterate through each path, running through that sequence of moves to get to the most recently added location. I would check if that location was the target location. If not I would mark that location as searched then check which directions were available, ignoring locations that had already been searched. If non were available, the path would be closed and 'spliced' from the Array of paths.
Otherwise ByteArray 'deep copies' of the current path Array were made for each available move option, in excess of the first move option. A move in one direction was added to the current path and the new paths, in their respective directions.
If the number of paths ever reaches 0, there is not a path between the 2 locations.
I think that was about it. I hope that's helpful.
Note that the search does not need to be 'directed' toward the target; what I've suggested searches all possible paths and just 'happens' to find the most direct route by killing paths that try to check locations that have already been searched (meaning some other path has got there first and is therefore shorter).

Resolving a 1069 error between a child object and a parent function

I've shortened the code samples below so that it's more readable. Here's the rub:I create a whole bunch of MovieClips containing the letters a-z. These MovieClips are children of a parent MovieClip called "levelTwo", where levelTwo is a kind of a logic manager for the level.
When they're clicked, they send off some data to levelTwo for evaluation (It's Hangman). Problem is that on click I get a 1069 error. It WAS a 1061 error until I added the event.target.parent bit in LetterButton.
Here's the relevant code:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class LetterButton extends MovieClip {
var buttonText:String;
public function LetterButton(lText:String,objX:int,objY:int)
{
trace ("Creating new button");
x = objX;
y = objY;
buttonText = lText;
letterText.text = buttonText;
this.stop();
addEventListener(MouseEvent.MOUSE_OVER,onMouseOver);
addEventListener(MouseEvent.MOUSE_OUT,onMouseOut);
this.addEventListener(MouseEvent.CLICK,onMouseClick);
}
private function onMouseOver(event:Event):void
{
gotoAndStop(2);
letterText.text = buttonText;
//trace ("You're over me and my text is " + buttonText);
}
private function onMouseOut(event:Event):void
{
gotoAndStop(1);
letterText.text = buttonText;
//trace ("You're out of me and my text is " + buttonText);
}
private function onMouseClick(event:Event):void
{
trace ("I am clicked and I am " + buttonText);
event.target.parent.checkGuess(buttonText);
}
}
}
And the relevant bit from levelTwo:
public function checkGuess(guess:String):void
{ //Check to see if the guess matches the string
trace ("Guess: "+guess);
for(var i:int=0;i<answer.length;i++)
{
if(guess == answer.charAt(i))
{
censoredAnswer[i] = guess;
trace ("Got one right");
answerField.text = answerRedisplay(); //Do it now or it won't update for the check
}
}
if (answerField.text == answer)
{
setWin();
}
}
I see no magic here. You subscribe to LetterButton instance, so you get event.target reference pointing to it.
I think a better approach would be to subscribe to parent clip, that contains all the LetterButton instances. MouseEvent.CLICK is a bubbling event, so you will get your handler triggered on parent every time something is clicked inside it.
See the sample code.
// inside level two class
addEventListener(MouseEvent.CLICK,onMouseClick);
private function onMouseClick(event:MouseEvent):void
{
var target:LetterButton = event.target as LetterButton;
if (target == null) {
return;
}
// here we know, that some LetterButton instance was clicked
// and target var holds it's reference
// either make buttonText public, or create a getter/accessor
// var text:String = target.buttonText;
var text:String = target.getButtonTextSomehow();
checkGuess(text);
}

What is the most effective way to test for combined keyboard arrow direction in ActionScript 3.0?

I need to monitor the direction a user is indicating using the four directional arrow keys on a keyboard in ActionScript 3.0 and I want to know the most efficient and effective way to do this.
I've got several ideas of how to do it, and I'm not sure which would be best. I've found that when tracking Keyboard.KEY_DOWN events, the event repeats as long as the key is down, so the event function is repeated as well. This broke the method I had originally chosen to use, and the methods I've been able to think of require a lot of comparison operators.
The best way I've been able to think of would be to use bitwise operators on a uint variable. Here's what I'm thinking
var _direction:uint = 0x0; // The Current Direction
That's the current direction variable. In the Keyboard.KEY_DOWN event handler I'll have it check what key is down, and use a bitwise AND operation to see if it's already toggled on, and if it's not, I'll add it in using basic addition. So, up would be 0x1 and down would be 0x2 and both up and down would be 0x3, for example. It would look something like this:
private function keyDownHandler(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case Keyboard.UP:
if(!(_direction & 0x1)) _direction += 0x1;
break;
case Keyboard.DOWN:
if(!(_direction & 0x2)) _direction += 0x2;
break;
// And So On...
}
}
The keyUpHandler wouldn't need the if operation since it only triggers once when the key goes up, instead of repeating. I'll be able to test the current direction by using a switch statement labeled with numbers from 0 to 15 for the sixteen possible combinations. That should work, but it doesn't seem terribly elegant to me, given all of the if statements in the repeating keyDown event handler, and the huge switch.
private function checkDirection():void
{
switch(_direction)
{
case 0:
// Center
break;
case 1:
// Up
break;
case 2:
// Down
break;
case 3:
// Up and Down
break;
case 4:
// Left
break;
// And So On...
}
}
Is there a better way to do this?
You can keep track of whether each key is down or not by listening for all KEY_DOWN and KEY_UP events, and storing each key state in an array. I wrote a class a while ago to do just that (included at the end of my answer).
Then you are no longer tied to the event model to know if a certain key is down or not; you can periodically check every frame (or every timer interval). So you could have a function like:
function enterFrameCallback(e:Event):void
{
var speed:Number = 1.0; // net pixels per frame movement
thing.x += (
-(int)Input.isKeyDown(Keyboard.LEFT)
+(int)Input.isKeyDown(Keyboard.RIGHT)
) * speed;
thing.y += (
-(int)Input.isKeyDown(Keyboard.UP)
+(int)Input.isKeyDown(Keyboard.DOWN)
) * speed;
}
which would take into account all possible combinations of arrow key presses. If you want the net displacement to be constant (e.g. when going right and down at same time, the object moves X pixels diagonally, as opposed to X pixels in both horizontal and vertical directions), the code becomes:
function enterFrameCallback(e:Event):void
{
var speed:Number = 1.0; // net pixels per frame movement
var displacement:Point = new Point();
displacement.x = (
-(int)Input.isKeyDown(Keyboard.LEFT)
+(int)Input.isKeyDown(Keyboard.RIGHT)
);
displacement.y = (
-(int)Input.isKeyDown(Keyboard.UP)
+(int)Input.isKeyDown(Keyboard.DOWN)
);
displacement.normalize(speed);
thing.x += displacement.x;
thing.y += displacement.y;
}
Here is the Input class I wrote (don't forget to call init from the document class). Note that it also keeps track of mouse stuff; you can delete that if you don't need it:
/*******************************************************************************
* DESCRIPTION: Defines a simple input class that allows the programmer to
* determine at any instant whether a specific key is down or not,
* or if the mouse button is down or not (and where the cursor
* is respective to a certain DisplayObject).
* USAGE: Call init once before using any other methods, and pass a reference to
* the stage. Use the public methods commented below to query input states.
*******************************************************************************/
package
{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.display.Stage;
import flash.geom.Point;
import flash.display.DisplayObject;
public class Input
{
private static var keyState:Array = new Array();
private static var _mouseDown:Boolean = false;
private static var mouseLoc:Point = new Point();
private static var mouseDownLoc:Point = new Point();
// Call before any other functions in this class:
public static function init(stage:Stage):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 10);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 10);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 10);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 10);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false, 10);
}
// Call to query whether a certain keyboard key is down.
// For a non-printable key: Input.isKeyDown(Keyboard.KEY)
// For a letter (case insensitive): Input.isKeyDown('A')
public static function isKeyDown(key:*):Boolean
{
if (typeof key == "string") {
key = key.toUpperCase().charCodeAt(0);
}
return keyState[key];
}
// Property that is true if the mouse is down, false otherwise:
public static function get mouseDown():Boolean
{
return _mouseDown;
}
// Gets the current coordinates of the mouse with respect to a certain DisplayObject.
// Leaving out the DisplayObject paramter will return the mouse location with respect
// to the stage (global coordinates):
public static function getMouseLoc(respectiveTo:DisplayObject = null):Point
{
if (respectiveTo == null) {
return mouseLoc.clone();
}
return respectiveTo.globalToLocal(mouseLoc);
}
// Gets the coordinates where the mouse was when it was last down or up, with respect
// to a certain DisplayObject. Leaving out the DisplayObject paramter will return the
// location with respect to the stage (global coordinates):
public static function getMouseDownLoc(respectiveTo:DisplayObject = null):Point
{
if (respectiveTo == null) {
return mouseDownLoc.clone();
}
return respectiveTo.globalToLocal(mouseDownLoc);
}
// Resets the state of the keyboard and mouse:
public static function reset():void
{
for (var i:String in keyState) {
keyState[i] = false;
}
_mouseDown = false;
mouseLoc = new Point();
mouseDownLoc = new Point();
}
///// PRIVATE METHODS BEWLOW /////
private static function onMouseDown(e:MouseEvent):void
{
_mouseDown = true;
mouseDownLoc = new Point(e.stageX, e.stageY);
}
private static function onMouseUp(e:MouseEvent):void
{
_mouseDown = false;
mouseDownLoc = new Point(e.stageX, e.stageY);
}
private static function onMouseMove(e:MouseEvent):void
{
mouseLoc = new Point(e.stageX, e.stageY);
}
private static function onKeyDown(e:KeyboardEvent):void
{
keyState[e.keyCode] = true;
}
private static function onKeyUp(e:KeyboardEvent):void
{
keyState[e.keyCode] = false;
}
}
}

Actionscript 3.0: display object walker in strict mode

This is a document class for a display object walker. Make sure to turn off the strict mode (howto here) when testing the class. Also put some stuff on the stage. When the strict mode is turned off the object walker works just fine. However, I want to make it work in strict mode too. I have tried changing the problematic parts, and addig (dispObj as DisplayObject), with no luck.
package {
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
public class DisplayWalker extends MovieClip {
public function DisplayWalker() {
showChildren(stage, 0);
}
private function padIndent(indents:int):String {
var indent:String = "";
for (var i:uint = 0; i < indents; i++) {
indent += " ";
}
return indent;
}
private function showChildren(dispObj:DisplayObject, indentLevel:Number):void {
for (var i:uint = 0; i < dispObj.numChildren; i++) {
var obj:DisplayObject = dispObj.getChildAt(i);
if (obj is DisplayObjectContainer) {
trace(padIndent(indentLevel), obj, obj.name);
showChildren(obj, indentLevel + 1);
} else {
trace(padIndent(indentLevel), obj);
}
}
}
}
}
Your class will generate compile time errors in Strict mode because you're trying to access the numChildren and getChildAt methods, which aren't available on the DisplayObject class, but first on one of it's subclasses, DisplayObjectContainer.
The reason it is working in non-Strict mode is that, at runtime, you're effectively passing in subclasses of DisplayObjectContainer (Stage, Sprite, etc).
Just replace DisplayObject with DisplayObjectContainer as the type for dispObj in your showChildren method. DisplayObjects cannot have children and are always leafs in the display object tree, something your showChildren method will have to account for.
Stiggler is on the right track, but properly didn't see that you already check for DisplayObjectContainers.
You just need to modify your code slightly. I didn't test the code, but in any case you should be able to figure it out ;)
private function showChildren(dispObj:DisplayObject, indentLevel:Number):void
{
var dOC:DisplayObjectContainer = dispObj as DisplayObjectContainer;
if(dOC == null)
{
trace(padIndent(indentLevel),obj);
}
else
{
trace(padIndent(indentLevel), obj, obj.name);
var obj:DisplayObject = null;
for (var i:uint = 0; i < dispObj.numChildren; i++)
{
obj = dOC.getChildAt(i);
showChildren(obj, indentLevel + 1);
}
}
}