Update mobile version to mobile v2.2.1

The android version just got a much needed update to fix some resolution issues on devices with cutouts.

It turns out the mobile source was actually pretty out of date, like 3 versions out of date! This commit brings it up to date.

All the changes have just been about keeping the game running on modern devices, though. The biggest change was adding the Starling library to the project, which made the game GPU powered and sped the whole thing up.
This commit is contained in:
Terry Cavanagh
2022-12-02 18:19:58 +01:00
parent 86d90a1296
commit 72d018ea04
140 changed files with 30533 additions and 2409 deletions

View File

@@ -0,0 +1,34 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
/** An EnterFrameEvent is triggered once per frame and is dispatched to all objects in the
* display tree.
*
* It contains information about the time that has passed since the last frame. That way, you
* can easily make animations that are independent of the frame rate, taking the passed time
* into account.
*/
public class EnterFrameEvent extends Event
{
/** Event type for a display object that is entering a new frame. */
public static const ENTER_FRAME:String = "enterFrame";
/** Creates an enter frame event with the passed time. */
public function EnterFrameEvent(type:String, passedTime:Number, bubbles:Boolean=false)
{
super(type, bubbles, passedTime);
}
/** The time that has passed since the last frame (in seconds). */
public function get passedTime():Number { return data as Number; }
}
}

View File

@@ -0,0 +1,190 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.utils.getQualifiedClassName;
import starling.core.starling_internal;
import starling.utils.StringUtil;
use namespace starling_internal;
/** Event objects are passed as parameters to event listeners when an event occurs.
* This is Starling's version of the Flash Event class.
*
* <p>EventDispatchers create instances of this class and send them to registered listeners.
* An event object contains information that characterizes an event, most importantly the
* event type and if the event bubbles. The target of an event is the object that
* dispatched it.</p>
*
* <p>For some event types, this information is sufficient; other events may need additional
* information to be carried to the listener. In that case, you can subclass "Event" and add
* properties with all the information you require. The "EnterFrameEvent" is an example for
* this practice; it adds a property about the time that has passed since the last frame.</p>
*
* <p>Furthermore, the event class contains methods that can stop the event from being
* processed by other listeners - either completely or at the next bubble stage.</p>
*
* @see EventDispatcher
*/
public class Event
{
/** Event type for a display object that is added to a parent. */
public static const ADDED:String = "added";
/** Event type for a display object that is added to the stage */
public static const ADDED_TO_STAGE:String = "addedToStage";
/** Event type for a display object that is entering a new frame. */
public static const ENTER_FRAME:String = "enterFrame";
/** Event type for a display object that is removed from its parent. */
public static const REMOVED:String = "removed";
/** Event type for a display object that is removed from the stage. */
public static const REMOVED_FROM_STAGE:String = "removedFromStage";
/** Event type for a triggered button. */
public static const TRIGGERED:String = "triggered";
/** Event type for a resized Flash Player. */
public static const RESIZE:String = "resize";
/** Event type that may be used whenever something finishes. */
public static const COMPLETE:String = "complete";
/** Event type for a (re)created stage3D rendering context. */
public static const CONTEXT3D_CREATE:String = "context3DCreate";
/** Event type that is dispatched by the Starling instance directly before rendering. */
public static const RENDER:String = "render";
/** Event type that indicates that the root DisplayObject has been created. */
public static const ROOT_CREATED:String = "rootCreated";
/** Event type for an animated object that requests to be removed from the juggler. */
public static const REMOVE_FROM_JUGGLER:String = "removeFromJuggler";
/** Event type that is dispatched by the AssetManager after a context loss. */
public static const TEXTURES_RESTORED:String = "texturesRestored";
/** Event type that is dispatched by the AssetManager when a file/url cannot be loaded. */
public static const IO_ERROR:String = "ioError";
/** Event type that is dispatched by the AssetManager when a file/url cannot be loaded. */
public static const SECURITY_ERROR:String = "securityError";
/** Event type that is dispatched by the AssetManager when an xml or json file couldn't
* be parsed. */
public static const PARSE_ERROR:String = "parseError";
/** Event type that is dispatched by the Starling instance when it encounters a problem
* from which it cannot recover, e.g. a lost device context. */
public static const FATAL_ERROR:String = "fatalError";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const CHANGE:String = "change";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const CANCEL:String = "cancel";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const SCROLL:String = "scroll";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const OPEN:String = "open";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const CLOSE:String = "close";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const SELECT:String = "select";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const READY:String = "ready";
/** An event type to be utilized in custom events. Not used by Starling right now. */
public static const UPDATE:String = "update";
private static var sEventPool:Vector.<Event> = new <Event>[];
private var _target:EventDispatcher;
private var _currentTarget:EventDispatcher;
private var _type:String;
private var _bubbles:Boolean;
private var _stopsPropagation:Boolean;
private var _stopsImmediatePropagation:Boolean;
private var _data:Object;
/** Creates an event object that can be passed to listeners. */
public function Event(type:String, bubbles:Boolean=false, data:Object=null)
{
_type = type;
_bubbles = bubbles;
_data = data;
}
/** Prevents listeners at the next bubble stage from receiving the event. */
public function stopPropagation():void
{
_stopsPropagation = true;
}
/** Prevents any other listeners from receiving the event. */
public function stopImmediatePropagation():void
{
_stopsPropagation = _stopsImmediatePropagation = true;
}
/** Returns a description of the event, containing type and bubble information. */
public function toString():String
{
return StringUtil.format("[{0} type=\"{1}\" bubbles={2}]",
getQualifiedClassName(this).split("::").pop(), _type, _bubbles);
}
/** Indicates if event will bubble. */
public function get bubbles():Boolean { return _bubbles; }
/** The object that dispatched the event. */
public function get target():EventDispatcher { return _target; }
/** The object the event is currently bubbling at. */
public function get currentTarget():EventDispatcher { return _currentTarget; }
/** A string that identifies the event. */
public function get type():String { return _type; }
/** Arbitrary data that is attached to the event. */
public function get data():Object { return _data; }
// properties for internal use
/** @private */
internal function setTarget(value:EventDispatcher):void { _target = value; }
/** @private */
internal function setCurrentTarget(value:EventDispatcher):void { _currentTarget = value; }
/** @private */
internal function setData(value:Object):void { _data = value; }
/** @private */
internal function get stopsPropagation():Boolean { return _stopsPropagation; }
/** @private */
internal function get stopsImmediatePropagation():Boolean { return _stopsImmediatePropagation; }
// event pooling
/** @private */
starling_internal static function fromPool(type:String, bubbles:Boolean=false, data:Object=null):Event
{
if (sEventPool.length) return sEventPool.pop().reset(type, bubbles, data);
else return new Event(type, bubbles, data);
}
/** @private */
starling_internal static function toPool(event:Event):void
{
event._data = event._target = event._currentTarget = null;
sEventPool[sEventPool.length] = event; // avoiding 'push'
}
/** @private */
starling_internal function reset(type:String, bubbles:Boolean=false, data:Object=null):Event
{
_type = type;
_bubbles = bubbles;
_data = data;
_target = _currentTarget = null;
_stopsPropagation = _stopsImmediatePropagation = false;
return this;
}
}
}

View File

@@ -0,0 +1,215 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.utils.Dictionary;
import starling.core.starling_internal;
import starling.display.DisplayObject;
use namespace starling_internal;
/** The EventDispatcher class is the base class for all classes that dispatch events.
* This is the Starling version of the Flash class with the same name.
*
* <p>The event mechanism is a key feature of Starling's architecture. Objects can communicate
* with each other through events. Compared the the Flash event system, Starling's event system
* was simplified. The main difference is that Starling events have no "Capture" phase.
* They are simply dispatched at the target and may optionally bubble up. They cannot move
* in the opposite direction.</p>
*
* <p>As in the conventional Flash classes, display objects inherit from EventDispatcher
* and can thus dispatch events. Beware, though, that the Starling event classes are
* <em>not compatible with Flash events:</em> Starling display objects dispatch
* Starling events, which will bubble along Starling display objects - but they cannot
* dispatch Flash events or bubble along Flash display objects.</p>
*
* @see Event
* @see starling.display.DisplayObject DisplayObject
*/
public class EventDispatcher
{
private var _eventListeners:Dictionary;
/** Helper object. */
private static var sBubbleChains:Array = [];
/** Creates an EventDispatcher. */
public function EventDispatcher()
{ }
/** Registers an event listener at a certain object. */
public function addEventListener(type:String, listener:Function):void
{
if (_eventListeners == null)
_eventListeners = new Dictionary();
var listeners:Vector.<Function> = _eventListeners[type] as Vector.<Function>;
if (listeners == null)
_eventListeners[type] = new <Function>[listener];
else if (listeners.indexOf(listener) == -1) // check for duplicates
listeners[listeners.length] = listener; // avoid 'push'
}
/** Removes an event listener from the object. */
public function removeEventListener(type:String, listener:Function):void
{
if (_eventListeners)
{
var listeners:Vector.<Function> = _eventListeners[type] as Vector.<Function>;
var numListeners:int = listeners ? listeners.length : 0;
if (numListeners > 0)
{
// we must not modify the original vector, but work on a copy.
// (see comment in 'invokeEvent')
var index:int = listeners.indexOf(listener);
if (index != -1)
{
var restListeners:Vector.<Function> = listeners.slice(0, index);
for (var i:int=index+1; i<numListeners; ++i)
restListeners[i-1] = listeners[i];
_eventListeners[type] = restListeners;
}
}
}
}
/** Removes all event listeners with a certain type, or all of them if type is null.
* Be careful when removing all event listeners: you never know who else was listening. */
public function removeEventListeners(type:String=null):void
{
if (type && _eventListeners)
delete _eventListeners[type];
else
_eventListeners = null;
}
/** Dispatches an event to all objects that have registered listeners for its type.
* If an event with enabled 'bubble' property is dispatched to a display object, it will
* travel up along the line of parents, until it either hits the root object or someone
* stops its propagation manually. */
public function dispatchEvent(event:Event):void
{
var bubbles:Boolean = event.bubbles;
if (!bubbles && (_eventListeners == null || !(event.type in _eventListeners)))
return; // no need to do anything
// we save the current target and restore it later;
// this allows users to re-dispatch events without creating a clone.
var previousTarget:EventDispatcher = event.target;
event.setTarget(this);
if (bubbles && this is DisplayObject) bubbleEvent(event);
else invokeEvent(event);
if (previousTarget) event.setTarget(previousTarget);
}
/** @private
* Invokes an event on the current object. This method does not do any bubbling, nor
* does it back-up and restore the previous target on the event. The 'dispatchEvent'
* method uses this method internally. */
internal function invokeEvent(event:Event):Boolean
{
var listeners:Vector.<Function> = _eventListeners ?
_eventListeners[event.type] as Vector.<Function> : null;
var numListeners:int = listeners == null ? 0 : listeners.length;
if (numListeners)
{
event.setCurrentTarget(this);
// we can enumerate directly over the vector, because:
// when somebody modifies the list while we're looping, "addEventListener" is not
// problematic, and "removeEventListener" will create a new Vector, anyway.
for (var i:int=0; i<numListeners; ++i)
{
var listener:Function = listeners[i] as Function;
var numArgs:int = listener.length;
if (numArgs == 0) listener();
else if (numArgs == 1) listener(event);
else listener(event, event.data);
if (event.stopsImmediatePropagation)
return true;
}
return event.stopsPropagation;
}
else
{
return false;
}
}
/** @private */
internal function bubbleEvent(event:Event):void
{
// we determine the bubble chain before starting to invoke the listeners.
// that way, changes done by the listeners won't affect the bubble chain.
var chain:Vector.<EventDispatcher>;
var element:DisplayObject = this as DisplayObject;
var length:int = 1;
if (sBubbleChains.length > 0) { chain = sBubbleChains.pop(); chain[0] = element; }
else chain = new <EventDispatcher>[element];
while ((element = element.parent) != null)
chain[int(length++)] = element;
for (var i:int=0; i<length; ++i)
{
var stopPropagation:Boolean = chain[i].invokeEvent(event);
if (stopPropagation) break;
}
chain.length = 0;
sBubbleChains[sBubbleChains.length] = chain; // avoid 'push'
}
/** Dispatches an event with the given parameters to all objects that have registered
* listeners for the given type. The method uses an internal pool of event objects to
* avoid allocations. */
public function dispatchEventWith(type:String, bubbles:Boolean=false, data:Object=null):void
{
if (bubbles || hasEventListener(type))
{
var event:Event = Event.fromPool(type, bubbles, data);
dispatchEvent(event);
Event.toPool(event);
}
}
/** If called with one argument, figures out if there are any listeners registered for
* the given event type. If called with two arguments, also determines if a specific
* listener is registered. */
public function hasEventListener(type:String, listener:Function=null):Boolean
{
var listeners:Vector.<Function> = _eventListeners ? _eventListeners[type] : null;
if (listeners == null) return false;
else
{
if (listener != null) return listeners.indexOf(listener) != -1;
else return listeners.length != 0;
}
}
}
}

View File

@@ -0,0 +1,88 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
/** A KeyboardEvent is dispatched in response to user input through a keyboard.
*
* <p>This is Starling's version of the Flash KeyboardEvent class. It contains the same
* properties as the Flash equivalent.</p>
*
* <p>To be notified of keyboard events, add an event listener to any display object that
* is part of your display tree. Starling has no concept of a "Focus" like native Flash.</p>
*
* @see starling.display.Stage
*/
public class KeyboardEvent extends Event
{
/** Event type for a key that was released. */
public static const KEY_UP:String = "keyUp";
/** Event type for a key that was pressed. */
public static const KEY_DOWN:String = "keyDown";
private var _charCode:uint;
private var _keyCode:uint;
private var _keyLocation:uint;
private var _altKey:Boolean;
private var _ctrlKey:Boolean;
private var _shiftKey:Boolean;
private var _isDefaultPrevented:Boolean;
/** Creates a new KeyboardEvent. */
public function KeyboardEvent(type:String, charCode:uint=0, keyCode:uint=0,
keyLocation:uint=0, ctrlKey:Boolean=false,
altKey:Boolean=false, shiftKey:Boolean=false)
{
super(type, false, keyCode);
_charCode = charCode;
_keyCode = keyCode;
_keyLocation = keyLocation;
_ctrlKey = ctrlKey;
_altKey = altKey;
_shiftKey = shiftKey;
}
// prevent default
/** Cancels the keyboard event's default behavior. This will be forwarded to the native
* flash KeyboardEvent. */
public function preventDefault():void
{
_isDefaultPrevented = true;
}
/** Checks whether the preventDefault() method has been called on the event. */
public function isDefaultPrevented():Boolean { return _isDefaultPrevented; }
// properties
/** Contains the character code of the key. */
public function get charCode():uint { return _charCode; }
/** The key code of the key. */
public function get keyCode():uint { return _keyCode; }
/** Indicates the location of the key on the keyboard. This is useful for differentiating
* keys that appear more than once on a keyboard. @see Keylocation */
public function get keyLocation():uint { return _keyLocation; }
/** Indicates whether the Alt key is active on Windows or Linux;
* indicates whether the Option key is active on Mac OS. */
public function get altKey():Boolean { return _altKey; }
/** Indicates whether the Ctrl key is active on Windows or Linux;
* indicates whether either the Ctrl or the Command key is active on Mac OS. */
public function get ctrlKey():Boolean { return _ctrlKey; }
/** Indicates whether the Shift key modifier is active (true) or inactive (false). */
public function get shiftKey():Boolean { return _shiftKey; }
}
}

View File

@@ -0,0 +1,44 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.geom.Point;
/** A ResizeEvent is dispatched by the stage when the size of the Flash container changes.
* Use it to update the Starling viewport and the stage size.
*
* <p>The event contains properties containing the updated width and height of the Flash
* player. If you want to scale the contents of your stage to fill the screen, update the
* <code>Starling.current.viewPort</code> rectangle accordingly. If you want to make use of
* the additional screen estate, update the values of <code>stage.stageWidth</code> and
* <code>stage.stageHeight</code> as well.</p>
*
* @see starling.display.Stage
* @see starling.core.Starling
*/
public class ResizeEvent extends Event
{
/** Event type for a resized Flash player. */
public static const RESIZE:String = "resize";
/** Creates a new ResizeEvent. */
public function ResizeEvent(type:String, width:int, height:int, bubbles:Boolean=false)
{
super(type, bubbles, new Point(width, height));
}
/** The updated width of the player. */
public function get width():int { return (data as Point).x; }
/** The updated height of the player. */
public function get height():int { return (data as Point).y; }
}
}

View File

@@ -0,0 +1,240 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.geom.Point;
import starling.display.DisplayObject;
import starling.utils.StringUtil;
/** A Touch object contains information about the presence or movement of a finger
* or the mouse on the screen.
*
* <p>You receive objects of this type from a TouchEvent. When such an event is triggered,
* you can query it for all touches that are currently present on the screen. One touch
* object contains information about a single touch; it always transitions through a series
* of TouchPhases. Have a look at the TouchPhase class for more information.</p>
*
* <strong>The position of a touch</strong>
*
* <p>You can get the current and previous position in stage coordinates with the corresponding
* properties. However, you'll want to have the position in a different coordinate system
* most of the time. For this reason, there are methods that convert the current and previous
* touches into the local coordinate system of any object.</p>
*
* @see TouchEvent
* @see TouchPhase
*/
public class Touch
{
private var _id:int;
private var _globalX:Number;
private var _globalY:Number;
private var _previousGlobalX:Number;
private var _previousGlobalY:Number;
private var _tapCount:int;
private var _phase:String;
private var _target:DisplayObject;
private var _timestamp:Number;
private var _pressure:Number;
private var _width:Number;
private var _height:Number;
private var _cancelled:Boolean;
private var _bubbleChain:Vector.<EventDispatcher>;
/** Helper object. */
private static var sHelperPoint:Point = new Point();
/** Creates a new Touch object. */
public function Touch(id:int)
{
_id = id;
_tapCount = 0;
_phase = TouchPhase.HOVER;
_pressure = _width = _height = 1.0;
_bubbleChain = new <EventDispatcher>[];
}
/** Converts the current location of a touch to the local coordinate system of a display
* object. If you pass an <code>out</code>-point, the result will be stored in this point
* instead of creating a new object.*/
public function getLocation(space:DisplayObject, out:Point=null):Point
{
sHelperPoint.setTo(_globalX, _globalY);
return space.globalToLocal(sHelperPoint, out);
}
/** Converts the previous location of a touch to the local coordinate system of a display
* object. If you pass an <code>out</code>-point, the result will be stored in this point
* instead of creating a new object.*/
public function getPreviousLocation(space:DisplayObject, out:Point=null):Point
{
sHelperPoint.setTo(_previousGlobalX, _previousGlobalY);
return space.globalToLocal(sHelperPoint, out);
}
/** Returns the movement of the touch between the current and previous location.
* If you pass an <code>out</code>-point, the result will be stored in this point instead
* of creating a new object. */
public function getMovement(space:DisplayObject, out:Point=null):Point
{
if (out == null) out = new Point();
getLocation(space, out);
var x:Number = out.x;
var y:Number = out.y;
getPreviousLocation(space, out);
out.setTo(x - out.x, y - out.y);
return out;
}
/** Indicates if the target or one of its children is touched. */
public function isTouching(target:DisplayObject):Boolean
{
return _bubbleChain.indexOf(target) != -1;
}
/** Returns a description of the object. */
public function toString():String
{
return StringUtil.format("[Touch {0}: globalX={1}, globalY={2}, phase={3}]",
_id, _globalX, _globalY, _phase);
}
/** Creates a clone of the Touch object. */
public function clone():Touch
{
var clone:Touch = new Touch(_id);
clone._globalX = _globalX;
clone._globalY = _globalY;
clone._previousGlobalX = _previousGlobalX;
clone._previousGlobalY = _previousGlobalY;
clone._phase = _phase;
clone._tapCount = _tapCount;
clone._timestamp = _timestamp;
clone._pressure = _pressure;
clone._width = _width;
clone._height = _height;
clone._cancelled = _cancelled;
clone.target = _target;
return clone;
}
// helper methods
private function updateBubbleChain():void
{
if (_target)
{
var length:int = 1;
var element:DisplayObject = _target;
_bubbleChain.length = 1;
_bubbleChain[0] = element;
while ((element = element.parent) != null)
_bubbleChain[int(length++)] = element;
}
else
{
_bubbleChain.length = 0;
}
}
// properties
/** The identifier of a touch. '0' for mouse events, an increasing number for touches. */
public function get id():int { return _id; }
/** The previous x-position of the touch in stage coordinates. */
public function get previousGlobalX():Number { return _previousGlobalX; }
/** The previous y-position of the touch in stage coordinates. */
public function get previousGlobalY():Number { return _previousGlobalY; }
/** The x-position of the touch in stage coordinates. If you change this value,
* the previous one will be moved to "previousGlobalX". */
public function get globalX():Number { return _globalX; }
public function set globalX(value:Number):void
{
_previousGlobalX = _globalX != _globalX ? value : _globalX; // isNaN check
_globalX = value;
}
/** The y-position of the touch in stage coordinates. If you change this value,
* the previous one will be moved to "previousGlobalY". */
public function get globalY():Number { return _globalY; }
public function set globalY(value:Number):void
{
_previousGlobalY = _globalY != _globalY ? value : _globalY; // isNaN check
_globalY = value;
}
/** The number of taps the finger made in a short amount of time. Use this to detect
* double-taps / double-clicks, etc. */
public function get tapCount():int { return _tapCount; }
public function set tapCount(value:int):void { _tapCount = value; }
/** The current phase the touch is in. @see TouchPhase */
public function get phase():String { return _phase; }
public function set phase(value:String):void { _phase = value; }
/** The display object at which the touch occurred. */
public function get target():DisplayObject { return _target; }
public function set target(value:DisplayObject):void
{
if (_target != value)
{
_target = value;
updateBubbleChain();
}
}
/** The moment the touch occurred (in seconds since application start). */
public function get timestamp():Number { return _timestamp; }
public function set timestamp(value:Number):void { _timestamp = value; }
/** A value between 0.0 and 1.0 indicating force of the contact with the device.
* If the device does not support detecting the pressure, the value is 1.0. */
public function get pressure():Number { return _pressure; }
public function set pressure(value:Number):void { _pressure = value; }
/** Width of the contact area.
* If the device does not support detecting the pressure, the value is 1.0. */
public function get width():Number { return _width; }
public function set width(value:Number):void { _width = value; }
/** Height of the contact area.
* If the device does not support detecting the pressure, the value is 1.0. */
public function get height():Number { return _height; }
public function set height(value:Number):void { _height = value; }
/** Indicates if the touch has been cancelled, which may happen when the app moves into
* the background ('Event.DEACTIVATE'). @default false */
public function get cancelled():Boolean { return _cancelled; }
public function set cancelled(value:Boolean):void { _cancelled = value; }
// internal methods
/** @private
* Dispatches a touch event along the current bubble chain (which is updated each time
* a target is set). */
internal function dispatchEvent(event:TouchEvent):void
{
if (_target) event.dispatch(_bubbleChain);
}
/** @private */
internal function get bubbleChain():Vector.<EventDispatcher>
{
return _bubbleChain.concat();
}
}
}

View File

@@ -0,0 +1,218 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import starling.core.starling_internal;
import starling.display.DisplayObject;
use namespace starling_internal;
/** A TouchEvent is triggered either by touch or mouse input.
*
* <p>In Starling, both touch events and mouse events are handled through the same class:
* TouchEvent. To process user input from a touch screen or the mouse, you have to register
* an event listener for events of the type <code>TouchEvent.TOUCH</code>. This is the only
* event type you need to handle; the long list of mouse event types as they are used in
* conventional Flash are mapped to so-called "TouchPhases" instead.</p>
*
* <p>The difference between mouse input and touch input is that</p>
*
* <ul>
* <li>only one mouse cursor can be present at a given moment and</li>
* <li>only the mouse can "hover" over an object without a pressed button.</li>
* </ul>
*
* <strong>Which objects receive touch events?</strong>
*
* <p>In Starling, any display object receives touch events, as long as the
* <code>touchable</code> property of the object and its parents is enabled. There
* is no "InteractiveObject" class in Starling.</p>
*
* <strong>How to work with individual touches</strong>
*
* <p>The event contains a list of all touches that are currently present. Each individual
* touch is stored in an object of type "Touch". Since you are normally only interested in
* the touches that occurred on top of certain objects, you can query the event for touches
* with a specific target:</p>
*
* <code>var touches:Vector.&lt;Touch&gt; = touchEvent.getTouches(this);</code>
*
* <p>This will return all touches of "this" or one of its children. When you are not using
* multitouch, you can also access the touch object directly, like this:</p>
*
* <code>var touch:Touch = touchEvent.getTouch(this);</code>
*
* @see Touch
* @see TouchPhase
*/
public class TouchEvent extends Event
{
/** Event type for touch or mouse input. */
public static const TOUCH:String = "touch";
private var _shiftKey:Boolean;
private var _ctrlKey:Boolean;
private var _timestamp:Number;
private var _visitedObjects:Vector.<EventDispatcher>;
/** Helper object. */
private static var sTouches:Vector.<Touch> = new <Touch>[];
/** Creates a new TouchEvent instance. */
public function TouchEvent(type:String, touches:Vector.<Touch>=null, shiftKey:Boolean=false,
ctrlKey:Boolean=false, bubbles:Boolean=true)
{
super(type, bubbles, touches);
_shiftKey = shiftKey;
_ctrlKey = ctrlKey;
_visitedObjects = new <EventDispatcher>[];
updateTimestamp(touches);
}
/** @private */
internal function resetTo(type:String, touches:Vector.<Touch>=null, shiftKey:Boolean=false,
ctrlKey:Boolean=false, bubbles:Boolean=true):TouchEvent
{
super.reset(type, bubbles, touches);
_shiftKey = shiftKey;
_ctrlKey = ctrlKey;
_visitedObjects.length = 0;
updateTimestamp(touches);
return this;
}
private function updateTimestamp(touches:Vector.<Touch>):void
{
_timestamp = -1.0;
var numTouches:int = touches ? touches.length : 0;
for (var i:int=0; i<numTouches; ++i)
if (touches[i].timestamp > _timestamp)
_timestamp = touches[i].timestamp;
}
/** Returns a list of touches that originated over a certain target. If you pass an
* <code>out</code>-vector, the touches will be added to this vector instead of creating
* a new object. */
public function getTouches(target:DisplayObject, phase:String=null,
out:Vector.<Touch>=null):Vector.<Touch>
{
if (out == null) out = new <Touch>[];
var allTouches:Vector.<Touch> = data as Vector.<Touch>;
var numTouches:int = allTouches.length;
for (var i:int=0; i<numTouches; ++i)
{
var touch:Touch = allTouches[i];
var correctTarget:Boolean = touch.isTouching(target);
var correctPhase:Boolean = (phase == null || phase == touch.phase);
if (correctTarget && correctPhase)
out[out.length] = touch; // avoiding 'push'
}
return out;
}
/** Returns a touch that originated over a certain target.
*
* @param target The object that was touched; may also be a parent of the actual
* touch-target.
* @param phase The phase the touch must be in, or null if you don't care.
* @param id The ID of the requested touch, or -1 if you don't care.
*/
public function getTouch(target:DisplayObject, phase:String=null, id:int=-1):Touch
{
getTouches(target, phase, sTouches);
var numTouches:int = sTouches.length;
if (numTouches > 0)
{
var touch:Touch = null;
if (id < 0) touch = sTouches[0];
else
{
for (var i:int=0; i<numTouches; ++i)
if (sTouches[i].id == id) { touch = sTouches[i]; break; }
}
sTouches.length = 0;
return touch;
}
else return null;
}
/** Indicates if a target is currently being touched or hovered over. */
public function interactsWith(target:DisplayObject):Boolean
{
var result:Boolean = false;
getTouches(target, null, sTouches);
for (var i:int=sTouches.length-1; i>=0; --i)
{
if (sTouches[i].phase != TouchPhase.ENDED)
{
result = true;
break;
}
}
sTouches.length = 0;
return result;
}
// custom dispatching
/** @private
* Dispatches the event along a custom bubble chain. During the lifetime of the event,
* each object is visited only once. */
internal function dispatch(chain:Vector.<EventDispatcher>):void
{
if (chain && chain.length)
{
var chainLength:int = bubbles ? chain.length : 1;
var previousTarget:EventDispatcher = target;
setTarget(chain[0] as EventDispatcher);
for (var i:int=0; i<chainLength; ++i)
{
var chainElement:EventDispatcher = chain[i] as EventDispatcher;
if (_visitedObjects.indexOf(chainElement) == -1)
{
var stopPropagation:Boolean = chainElement.invokeEvent(this);
_visitedObjects[_visitedObjects.length] = chainElement;
if (stopPropagation) break;
}
}
setTarget(previousTarget);
}
}
// properties
/** The time the event occurred (in seconds since application launch). */
public function get timestamp():Number { return _timestamp; }
/** All touches that are currently available. */
public function get touches():Vector.<Touch> { return (data as Vector.<Touch>).concat(); }
/** Indicates if the shift key was pressed when the event occurred. */
public function get shiftKey():Boolean { return _shiftKey; }
/** Indicates if the ctrl key was pressed when the event occurred. (Mac OS: Cmd or Ctrl) */
public function get ctrlKey():Boolean { return _ctrlKey; }
}
}

View File

@@ -0,0 +1,104 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.display.BitmapData;
import flash.display.Shape;
import flash.geom.Point;
import starling.core.Starling;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;
/** The TouchMarker is used internally to mark touches created through "simulateMultitouch". */
internal class TouchMarker extends Sprite
{
private var _center:Point;
private var _texture:Texture;
public function TouchMarker()
{
_center = new Point();
_texture = createTexture();
for (var i:int=0; i<2; ++i)
{
var marker:Image = new Image(_texture);
marker.pivotX = _texture.width / 2;
marker.pivotY = _texture.height / 2;
marker.touchable = false;
addChild(marker);
}
}
public override function dispose():void
{
_texture.dispose();
super.dispose();
}
public function moveMarker(x:Number, y:Number, withCenter:Boolean=false):void
{
if (withCenter)
{
_center.x += x - realMarker.x;
_center.y += y - realMarker.y;
}
realMarker.x = x;
realMarker.y = y;
mockMarker.x = 2*_center.x - x;
mockMarker.y = 2*_center.y - y;
}
public function moveCenter(x:Number, y:Number):void
{
_center.x = x;
_center.y = y;
moveMarker(realX, realY); // reset mock position
}
private function createTexture():Texture
{
var scale:Number = Starling.contentScaleFactor;
var radius:Number = 12 * scale;
var width:int = 32 * scale;
var height:int = 32 * scale;
var thickness:Number = 1.5 * scale;
var shape:Shape = new Shape();
// draw dark outline
shape.graphics.lineStyle(thickness, 0x0, 0.3);
shape.graphics.drawCircle(width/2, height/2, radius + thickness);
// draw white inner circle
shape.graphics.beginFill(0xffffff, 0.4);
shape.graphics.lineStyle(thickness, 0xffffff);
shape.graphics.drawCircle(width/2, height/2, radius);
shape.graphics.endFill();
var bmpData:BitmapData = new BitmapData(width, height, true, 0x0);
bmpData.draw(shape);
return Texture.fromBitmapData(bmpData, false, false, scale);
}
private function get realMarker():Image { return getChildAt(0) as Image; }
private function get mockMarker():Image { return getChildAt(1) as Image; }
public function get realX():Number { return realMarker.x; }
public function get realY():Number { return realMarker.y; }
public function get mockX():Number { return mockMarker.x; }
public function get mockY():Number { return mockMarker.y; }
}
}

View File

@@ -0,0 +1,53 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import starling.errors.AbstractClassError;
/** A class that provides constant values for the phases of a touch object.
*
* <p>A touch moves through at least the following phases in its life:</p>
*
* <code>BEGAN -> MOVED -> ENDED</code>
*
* <p>Furthermore, a touch can enter a <code>STATIONARY</code> phase. That phase does not
* trigger a touch event itself, and it can only occur in multitouch environments. Picture a
* situation where one finger is moving and the other is stationary. A touch event will
* be dispatched only to the object under the <em>moving</em> finger. In the list of touches
* of that event, you will find the second touch in the stationary phase.</p>
*
* <p>Finally, there's the <code>HOVER</code> phase, which is exclusive to mouse input. It is
* the equivalent of a <code>MouseOver</code> event in Flash when the mouse button is
* <em>not</em> pressed.</p>
*/
public final class TouchPhase
{
/** @private */
public function TouchPhase() { throw new AbstractClassError(); }
/** Only available for mouse input: the cursor hovers over an object <em>without</em> a
* pressed button. */
public static const HOVER:String = "hover";
/** The finger touched the screen just now, or the mouse button was pressed. */
public static const BEGAN:String = "began";
/** The finger moves around on the screen, or the mouse is moved while the button is
* pressed. */
public static const MOVED:String = "moved";
/** The finger or mouse (with pressed button) has not moved since the last frame. */
public static const STATIONARY:String = "stationary";
/** The finger was lifted from the screen or from the mouse button. */
public static const ENDED:String = "ended";
}
}

View File

@@ -0,0 +1,481 @@
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.events
{
import flash.geom.Point;
import flash.utils.getDefinitionByName;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Stage;
/** The TouchProcessor is used to convert mouse and touch events of the conventional
* Flash stage to Starling's TouchEvents.
*
* <p>The Starling instance listens to mouse and touch events on the native stage. The
* attributes of those events are enqueued (right as they are happening) in the
* TouchProcessor.</p>
*
* <p>Once per frame, the "advanceTime" method is called. It analyzes the touch queue and
* figures out which touches are active at that moment; the properties of all touch objects
* are updated accordingly.</p>
*
* <p>Once the list of touches has been finalized, the "processTouches" method is called
* (that might happen several times in one "advanceTime" execution; no information is
* discarded). It's responsible for dispatching the actual touch events to the Starling
* display tree.</p>
*
* <strong>Subclassing TouchProcessor</strong>
*
* <p>You can extend the TouchProcessor if you need to have more control over touch and
* mouse input. For example, you could filter the touches by overriding the "processTouches"
* method, throwing away any touches you're not interested in and passing the rest to the
* super implementation.</p>
*
* <p>To use your custom TouchProcessor, assign it to the "Starling.touchProcessor"
* property.</p>
*
* <p>Note that you should not dispatch TouchEvents yourself, since they are
* much more complex to handle than conventional events (e.g. it must be made sure that an
* object receives a TouchEvent only once, even if it's manipulated with several fingers).
* Always use the base implementation of "processTouches" to let them be dispatched. That
* said: you can always dispatch your own custom events, of course.</p>
*/
public class TouchProcessor
{
private var _stage:Stage;
private var _root:DisplayObject;
private var _elapsedTime:Number;
private var _lastTaps:Vector.<Touch>;
private var _shiftDown:Boolean = false;
private var _ctrlDown:Boolean = false;
private var _multitapTime:Number = 0.3;
private var _multitapDistance:Number = 25;
private var _touchEvent:TouchEvent;
private var _touchMarker:TouchMarker;
private var _simulateMultitouch:Boolean;
/** A vector of arrays with the arguments that were passed to the "enqueue"
* method (the oldest being at the end of the vector). */
protected var _queue:Vector.<Array>;
/** The list of all currently active touches. */
protected var _currentTouches:Vector.<Touch>;
/** Helper objects. */
private static var sUpdatedTouches:Vector.<Touch> = new <Touch>[];
private static var sHoveringTouchData:Vector.<Object> = new <Object>[];
private static var sHelperPoint:Point = new Point();
/** Creates a new TouchProcessor that will dispatch events to the given stage. */
public function TouchProcessor(stage:Stage)
{
_root = _stage = stage;
_elapsedTime = 0.0;
_currentTouches = new <Touch>[];
_queue = new <Array>[];
_lastTaps = new <Touch>[];
_touchEvent = new TouchEvent(TouchEvent.TOUCH);
_stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
_stage.addEventListener(KeyboardEvent.KEY_UP, onKey);
monitorInterruptions(true);
}
/** Removes all event handlers on the stage and releases any acquired resources. */
public function dispose():void
{
monitorInterruptions(false);
_stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
_stage.removeEventListener(KeyboardEvent.KEY_UP, onKey);
if (_touchMarker) _touchMarker.dispose();
}
/** Analyzes the current touch queue and processes the list of current touches, emptying
* the queue while doing so. This method is called by Starling once per frame. */
public function advanceTime(passedTime:Number):void
{
var i:int;
var touch:Touch;
_elapsedTime += passedTime;
sUpdatedTouches.length = 0;
// remove old taps
if (_lastTaps.length > 0)
{
for (i=_lastTaps.length-1; i>=0; --i)
if (_elapsedTime - _lastTaps[i].timestamp > _multitapTime)
_lastTaps.removeAt(i);
}
while (_queue.length > 0)
{
// Set touches that were new or moving to phase 'stationary'.
for each (touch in _currentTouches)
if (touch.phase == TouchPhase.BEGAN || touch.phase == TouchPhase.MOVED)
touch.phase = TouchPhase.STATIONARY;
// analyze new touches, but each ID only once
while (_queue.length > 0 &&
!containsTouchWithID(sUpdatedTouches, _queue[_queue.length-1][0]))
{
var touchArgs:Array = _queue.pop();
touch = createOrUpdateTouch(
touchArgs[0], touchArgs[1], touchArgs[2], touchArgs[3],
touchArgs[4], touchArgs[5], touchArgs[6]);
sUpdatedTouches[sUpdatedTouches.length] = touch; // avoiding 'push'
}
// process the current set of touches (i.e. dispatch touch events)
processTouches(sUpdatedTouches, _shiftDown, _ctrlDown);
// remove ended touches
for (i=_currentTouches.length-1; i>=0; --i)
if (_currentTouches[i].phase == TouchPhase.ENDED)
_currentTouches.removeAt(i);
sUpdatedTouches.length = 0;
}
}
/** Dispatches TouchEvents to the display objects that are affected by the list of
* given touches. Called internally by "advanceTime". To calculate updated targets,
* the method will call "hitTest" on the "root" object.
*
* @param touches a list of all touches that have changed just now.
* @param shiftDown indicates if the shift key was down when the touches occurred.
* @param ctrlDown indicates if the ctrl or cmd key was down when the touches occurred.
*/
protected function processTouches(touches:Vector.<Touch>,
shiftDown:Boolean, ctrlDown:Boolean):void
{
var touch:Touch;
sHoveringTouchData.length = 0;
// the same touch event will be dispatched to all targets;
// the 'dispatch' method makes sure each bubble target is visited only once.
_touchEvent.resetTo(TouchEvent.TOUCH, _currentTouches, shiftDown, ctrlDown);
// hit test our updated touches
for each (touch in touches)
{
// hovering touches need special handling (see below)
if (touch.phase == TouchPhase.HOVER && touch.target)
sHoveringTouchData[sHoveringTouchData.length] = {
touch: touch,
target: touch.target,
bubbleChain: touch.bubbleChain
}; // avoiding 'push'
if (touch.phase == TouchPhase.HOVER || touch.phase == TouchPhase.BEGAN)
{
sHelperPoint.setTo(touch.globalX, touch.globalY);
touch.target = _root.hitTest(sHelperPoint);
}
}
// if the target of a hovering touch changed, we dispatch the event to the previous
// target to notify it that it's no longer being hovered over.
for each (var touchData:Object in sHoveringTouchData)
if (touchData.touch.target != touchData.target)
_touchEvent.dispatch(touchData.bubbleChain);
// dispatch events for the rest of our updated touches
for each (touch in touches)
touch.dispatchEvent(_touchEvent);
// clean up any references
_touchEvent.resetTo(TouchEvent.TOUCH);
}
/** Enqueues a new touch our mouse event with the given properties. */
public function enqueue(touchID:int, phase:String, globalX:Number, globalY:Number,
pressure:Number=1.0, width:Number=1.0, height:Number=1.0):void
{
_queue.unshift(arguments);
// multitouch simulation (only with mouse)
if (_ctrlDown && _touchMarker && touchID == 0)
{
_touchMarker.moveMarker(globalX, globalY, _shiftDown);
_queue.unshift([1, phase, _touchMarker.mockX, _touchMarker.mockY]);
}
}
/** Enqueues an artificial touch that represents the mouse leaving the stage.
*
* <p>On OS X, we get mouse events from outside the stage; on Windows, we do not.
* This method enqueues an artificial hover point that is just outside the stage.
* That way, objects listening for HOVERs over them will get notified everywhere.</p>
*/
public function enqueueMouseLeftStage():void
{
var mouse:Touch = getCurrentTouch(0);
if (mouse == null || mouse.phase != TouchPhase.HOVER) return;
var offset:int = 1;
var exitX:Number = mouse.globalX;
var exitY:Number = mouse.globalY;
var distLeft:Number = mouse.globalX;
var distRight:Number = _stage.stageWidth - distLeft;
var distTop:Number = mouse.globalY;
var distBottom:Number = _stage.stageHeight - distTop;
var minDist:Number = Math.min(distLeft, distRight, distTop, distBottom);
// the new hover point should be just outside the stage, near the point where
// the mouse point was last to be seen.
if (minDist == distLeft) exitX = -offset;
else if (minDist == distRight) exitX = _stage.stageWidth + offset;
else if (minDist == distTop) exitY = -offset;
else exitY = _stage.stageHeight + offset;
enqueue(0, TouchPhase.HOVER, exitX, exitY);
}
/** Force-end all current touches. Changes the phase of all touches to 'ENDED' and
* immediately dispatches a new TouchEvent (if touches are present). Called automatically
* when the app receives a 'DEACTIVATE' event. */
public function cancelTouches():void
{
if (_currentTouches.length > 0)
{
// abort touches
for each (var touch:Touch in _currentTouches)
{
if (touch.phase == TouchPhase.BEGAN || touch.phase == TouchPhase.MOVED ||
touch.phase == TouchPhase.STATIONARY)
{
touch.phase = TouchPhase.ENDED;
touch.cancelled = true;
}
}
// dispatch events
processTouches(_currentTouches, _shiftDown, _ctrlDown);
}
// purge touches
_currentTouches.length = 0;
_queue.length = 0;
}
private function createOrUpdateTouch(touchID:int, phase:String,
globalX:Number, globalY:Number,
pressure:Number=1.0,
width:Number=1.0, height:Number=1.0):Touch
{
var touch:Touch = getCurrentTouch(touchID);
if (touch == null)
{
touch = new Touch(touchID);
addCurrentTouch(touch);
}
touch.globalX = globalX;
touch.globalY = globalY;
touch.phase = phase;
touch.timestamp = _elapsedTime;
touch.pressure = pressure;
touch.width = width;
touch.height = height;
if (phase == TouchPhase.BEGAN)
updateTapCount(touch);
return touch;
}
private function updateTapCount(touch:Touch):void
{
var nearbyTap:Touch = null;
var minSqDist:Number = _multitapDistance * _multitapDistance;
for each (var tap:Touch in _lastTaps)
{
var sqDist:Number = Math.pow(tap.globalX - touch.globalX, 2) +
Math.pow(tap.globalY - touch.globalY, 2);
if (sqDist <= minSqDist)
{
nearbyTap = tap;
break;
}
}
if (nearbyTap)
{
touch.tapCount = nearbyTap.tapCount + 1;
_lastTaps.removeAt(_lastTaps.indexOf(nearbyTap));
}
else
{
touch.tapCount = 1;
}
_lastTaps[_lastTaps.length] = touch.clone(); // avoiding 'push'
}
private function addCurrentTouch(touch:Touch):void
{
for (var i:int=_currentTouches.length-1; i>=0; --i)
if (_currentTouches[i].id == touch.id)
_currentTouches.removeAt(i);
_currentTouches[_currentTouches.length] = touch; // avoiding 'push'
}
private function getCurrentTouch(touchID:int):Touch
{
for each (var touch:Touch in _currentTouches)
if (touch.id == touchID) return touch;
return null;
}
private function containsTouchWithID(touches:Vector.<Touch>, touchID:int):Boolean
{
for each (var touch:Touch in touches)
if (touch.id == touchID) return true;
return false;
}
/** Indicates if multitouch simulation should be activated. When the user presses
* ctrl/cmd (and optionally shift), he'll see a second touch cursor that mimics the first.
* That's an easy way to develop and test multitouch when there's only a mouse available.
*/
public function get simulateMultitouch():Boolean { return _simulateMultitouch; }
public function set simulateMultitouch(value:Boolean):void
{
if (simulateMultitouch == value) return; // no change
_simulateMultitouch = value;
var target:Starling = Starling.current;
if (value && _touchMarker == null)
{
if (Starling.current.contextValid)
createTouchMarker();
else
target.addEventListener(Event.CONTEXT3D_CREATE, createTouchMarker);
}
else if (!value && _touchMarker)
{
_touchMarker.removeFromParent(true);
_touchMarker = null;
}
function createTouchMarker():void
{
target.removeEventListener(Event.CONTEXT3D_CREATE, createTouchMarker);
if (_touchMarker == null)
{
_touchMarker = new TouchMarker();
_touchMarker.visible = false;
_stage.addChild(_touchMarker);
}
}
}
/** The time period (in seconds) in which two touches must occur to be recognized as
* a multitap gesture. */
public function get multitapTime():Number { return _multitapTime; }
public function set multitapTime(value:Number):void { _multitapTime = value; }
/** The distance (in points) describing how close two touches must be to each other to
* be recognized as a multitap gesture. */
public function get multitapDistance():Number { return _multitapDistance; }
public function set multitapDistance(value:Number):void { _multitapDistance = value; }
/** The base object that will be used for hit testing. Per default, this reference points
* to the stage; however, you can limit touch processing to certain parts of your game
* by assigning a different object. */
public function get root():DisplayObject { return _root; }
public function set root(value:DisplayObject):void { _root = value; }
/** The stage object to which the touch events are (per default) dispatched. */
public function get stage():Stage { return _stage; }
/** Returns the number of fingers / touch points that are currently on the stage. */
public function get numCurrentTouches():int { return _currentTouches.length; }
// keyboard handling
private function onKey(event:KeyboardEvent):void
{
if (event.keyCode == 17 || event.keyCode == 15) // ctrl or cmd key
{
var wasCtrlDown:Boolean = _ctrlDown;
_ctrlDown = event.type == KeyboardEvent.KEY_DOWN;
if (_touchMarker && wasCtrlDown != _ctrlDown)
{
_touchMarker.visible = _ctrlDown;
_touchMarker.moveCenter(_stage.stageWidth/2, _stage.stageHeight/2);
var mouseTouch:Touch = getCurrentTouch(0);
var mockedTouch:Touch = getCurrentTouch(1);
if (mouseTouch)
_touchMarker.moveMarker(mouseTouch.globalX, mouseTouch.globalY);
if (wasCtrlDown && mockedTouch && mockedTouch.phase != TouchPhase.ENDED)
{
// end active touch ...
_queue.unshift([1, TouchPhase.ENDED, mockedTouch.globalX, mockedTouch.globalY]);
}
else if (_ctrlDown && mouseTouch)
{
// ... or start new one
if (mouseTouch.phase == TouchPhase.HOVER || mouseTouch.phase == TouchPhase.ENDED)
_queue.unshift([1, TouchPhase.HOVER, _touchMarker.mockX, _touchMarker.mockY]);
else
_queue.unshift([1, TouchPhase.BEGAN, _touchMarker.mockX, _touchMarker.mockY]);
}
}
}
else if (event.keyCode == 16) // shift key
{
_shiftDown = event.type == KeyboardEvent.KEY_DOWN;
}
}
// interruption handling
private function monitorInterruptions(enable:Boolean):void
{
// if the application moves into the background or is interrupted (e.g. through
// an incoming phone call), we need to abort all touches.
try
{
var nativeAppClass:Object = getDefinitionByName("flash.desktop::NativeApplication");
var nativeApp:Object = nativeAppClass["nativeApplication"];
if (enable)
nativeApp.addEventListener("deactivate", onInterruption, false, 0, true);
else
nativeApp.removeEventListener("deactivate", onInterruption);
}
catch (e:Error) {} // we're not running in AIR
}
private function onInterruption(event:Object):void
{
cancelTouches();
}
}
}