On my screen are tiles generated from an array. I have a mouse roll over function called rollover, that adds a movieclip that highlights the edge of the tiles that I am currently on. I want it so that once I click a tile, the roll over function doesn't work until another button is clicked. I tried putting removeEventListener for the roll over function in the click function, doesn't seem to work. How would I go about this if possible?
I will post more information if needed.
function rollover(event:MouseEvent)
{
var tileHover = true;
if (tileHover == true){
(event.currentTarget as Tile).outline.gotoAndStop("hover");
}
if(tileHover == false){
(event.currentTarget as Tile).outline.gotoAndStop("blank");
}
}
Below is the mouseclick function
function mouseclick(event:MouseEvent)
{
tileHover = false;
if (tileHover == false){
tile_MC.removeEventListener(MouseEvent.ROLL_OVER, rollover)
}
}
See below. You set a property and immediately check what the value of that property is. It will always be true because you just set it as true.
var tileHover = true;
if (tileHover == true){
(event.currentTarget as Tile).outline.gotoAndStop("hover");
}
Also, don't forget your data types.
I think you need to have (event.currentTarget as Tile).outline.gotoAndStop("blank"); in mouseclick.
Also, I assume tilehover is some global variable used for tracking the hover state. Explicitly setting it true/false in the handler is just for debugging purposes!!
Related
I have a dropdown menu that lets you select an item to be placed on the stage. The item is drag and droppable so I use event.currentTarget.startDrag(); to start the drag. Ok, everything works fine so far.
However, I also need to be able to rotate the item while it is being dragged (using the spacebar).
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
function myKeyDown(e:KeyboardEvent):void{
if (e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
WHATGOESHERE?.gotoAndStop(rotate);
}
If I hardcode in an instance name of an object everything works fine - so the rotate function is working properly. The problem is, how can I reference event.currentTarget from the startDrag function while inside of the keyDown event?
My first thought was to set event.currentTarget to a variable and then calling the variable from within the keyDown event. However, targetHold = event.currentTarget; does not seem to record the instance name of the object being clicked...
public var targetHold:Object = new Object;
function ClickToDrag(event:MouseEvent):void {
event.currentTarget.startDrag();
targetHold = event.currentTarget;
trace ("targetHold " + targetHold);
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
function myKeyDown(e:KeyboardEvent):void{
if (e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
targetHold.gotoAndStop(rotate); //does not work
}
}
function ReleaseToDrop(event:MouseEvent):void {
event.currentTarget.stopDrag();
}
As you click the object, it should have focus. If you register the listener for the KeyboardEvent on the object and not on the stage, it will be .currentTarget.
Here's an example of what I have in mind. Right after starting the drag, add the listener to the same object instead of the stage.
event.currentTarget.startDrag();
event.currentTarget.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
The proper way of doing this would be to define all the functionality in a class. Within a self contained class, you would not need any .currentTarget.
Here is how I would do this: (well, actually I'd follow #null's advice and encapsulate it in a sub class that all your dragable objects would extend, but that is a little broad so this will do)
public var targetHold:MovieClip; //don't make a new object, just create the empty var
public function YourConstructor(){
//your other constructor code
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown); //don't add the listener in the click function
}
private function clickToDrag(event:MouseEvent):void {
if(targetHold) ReleaseToDrop(null); //safeguard in case flash lost the focus during the mouse up
targetHold = event.currentTarget as MovieClip; //assign the current target. Might as well cast it as MovieClip and get code completion benefits
targetHold.startDrag();
trace ("targetHold " + targetHold);
}
private function myKeyDown(e:KeyboardEvent):void{
//check if target hold exists
if (targetHold != null && e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
targetHold.gotoAndStop(rotate);
}
}
private function ReleaseToDrop(event:MouseEvent):void {
if(targetHold) targetHold.stopDrag();
targetHold = null;
}
I am having trouble with the controls showing up for my transform tool. When I click my image I get the bounding box (to scale or rotate the image), but when I hover over the corner I do not get the cursor to transform it.
I am using these files:
TransformTool.as
TransformToolControl.as
TransformToolCursor.as
This is my code to call the transform tool:
var tool:TransformTool = new TransformTool();
addChild(tool);
And this later on to make the tool show up when the image is clicked and make the tool disappear when the stage is clicked:
tmpImage.addEventListener(MouseEvent.CLICK, select);
function select(e:MouseEvent):void {
tool.target = e.currentTarget as Sprite;
stage.addEventListener(MouseEvent.MOUSE_DOWN, deselect);
}
function deselect(e:MouseEvent):void {
tool.target = null;
tmpImage.addEventListener(MouseEvent.CLICK, select);
}
My image selection for the bounding box to appear and disappear work perfectly. All my code works as expected.... except the actual controls on the bounding box. Please help!
Edit
The concept is the user can click an image from a menu and drag a new instance of that image to the stage. Then the user can click the new instance and should be able to rotate or scale it. Then they can click off the image to make the bounding box disappear. (They can add as many images to the stage that they want).
Here is some code that shows the basic click, create new instance, and drag process I have implemented.
//sb1 is the menu area that contains a group of images
//hill is one of the images the user can add to the stage
sb1.hill.addEventListener(MouseEvent.MOUSE_DOWN, createCopy);
var i:int=0;
var tmpImage:Sprite; //to store which image is being dragged currently
function createCopy(e:MouseEvent):void {
tmpImage = new Hill_mc();
tmpImage.name = "hillChild"+(i++); //increment every copy
container.addChild(tmpImage);
tmpImage.x = mouseX-470;
tmpImage.y = mouseY-270;
tmpImage.startDrag();
tmpImage.addEventListener(MouseEvent.MOUSE_DOWN, onDown); //add the mouse down to this new object
stage.addEventListener(MouseEvent.MOUSE_UP, onUp); //since the mouse is currently down, we need to listen for mouse up to tell the current copy to stop dragging
}
//this will be called when click a copy
function onDown(e:MouseEvent):void {
tmpImage = Sprite(e.currentTarget); //get a reference to the one that was clicked, so we know which object to stop dragging on the global mouse up.
container.addEventListener(MouseEvent.MOUSE_UP, onUp); //listen for the mouse up
tmpImage.startDrag();
}
function onUp(e:MouseEvent):void {
container.removeEventListener(MouseEvent.MOUSE_UP,onUp);
if (tmpImage.hitTestObject(thesubmenu1)) {
container.removeChild(tmpImage);
}
else {
tmpImage.stopDrag();
}
tmpImage.addEventListener(MouseEvent.CLICK, select);
}
function select(e:MouseEvent):void {
tool.target = e.currentTarget as Sprite;
tmpImage.addEventListener(MouseEvent.MOUSE_DOWN, deselect);
}
function deselect(e:MouseEvent):void {
tool.target = null;
tmpImage.addEventListener(MouseEvent.CLICK, select);
}
EDIT
I found this code and placed it in my TransformTool.as. I feel like it's so close and that there must be something called incorrectly because I get an error for a null object reference:
public function select(event:Event):void {
// the selected object will either be the
// event target or current target. The current
// target is checked first followed by target.
// The parent of the target must match the
// parent of the tool to be selected this way.
if (event.currentTarget != this
&& event.currentTarget.parent == parent){
setTarget(event.currentTarget as DisplayObject, event);
}else if (event.target != this
&& event.target.parent == parent){
setTarget(event.target as DisplayObject, event);
}
}
/**
* Helper selection handler for deselecting target objects. Set this
* handler as the listener for an event that would cause the
* deselection of a target object.
* It is not required that you use this event handler. It is only a
* helper function that can optionally be used to help ease
* development.
*/
public function deselect(event:Event):void {
if (_target != null && event.eventPhase == EventPhase.AT_TARGET){
setTarget(null, null);
}
}
You give too little information to determine what exactly is wrong.
However, there is a very good sample code that does exactly what you want here :
http://www.senocular.com/demo/TransformToolAS3/TransformTool.html
(Click on the link at the bottom of the image.)
I am sure that you are going to make it with this.
EDIT :
Try to use the built-in handlers. I would do something like this :
Instead of this :
tmpImage.addEventListener(MouseEvent.CLICK, select);
function select(e:MouseEvent):void {
tool.target = e.currentTarget as Sprite;
stage.addEventListener(MouseEvent.MOUSE_DOWN, deselect);
}
function deselect(e:MouseEvent):void {
tool.target = null;
tmpImage.addEventListener(MouseEvent.CLICK, select);
}
Do this :
tmpImage.addEventListener(MouseEvent.MOUSE_DOWN, tool.select);
stage.addEventListener(MouseEvent.MOUSE_DOWN, tool.deselect);
EDIT :
If you do not have the handlers, I am not sure :) but I would recommand removing event listeners in each method as they might be interfering with each other.
tmpImage.addEventListener(MouseEvent.CLICK, select);
function select(e:MouseEvent):void {
tmpImage.removeEventListener(MouseEvent.CLICK, select);
tool.target = e.currentTarget as Sprite;
stage.addEventListener(MouseEvent.MOUSE_DOWN, deselect);
}
function deselect(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_DOWN, deselect);
tool.target = null;
tmpImage.addEventListener(MouseEvent.CLICK, select);
}
I have a function that recognizes the ESC key being pressed. At which point, I want to stop dragging all items.
I"ve tried this.stopDrag() but it wont override the MOUSE_DOWN event.
It there a way to force it to "drop" the item being dragged?
Thanks
stage.addEventListener(KeyboardEvent.KEY_DOWN, escapeKeyDown);
function escapeKeyDown(event : KeyboardEvent):void {
if (event.keyCode == 27) {
trace("ESC");
this.stopDrag();
}
}
Make a global array of all your dragging DisplayObjects:
static var CURRENT_DRAGGING_ITEMS:Array = [];
Then whenever you call startDrag on anything, add it to the array.
function onMouseDown(event:MouseEvent):void
{
event.target.startDrag();
CURRENT_DRAGGING_ITEMS.push(event.target);
}
Then when you hit ESC, just loop through the array, calling stopDrag on all items, and removing them from the array.
function escapeKeyDown(event:KeyboardEvent):void
{
event.target.stopDrag();
var targetIndex:uint = CURRENT_DRAGGING_ITEMS.indexOf(event.target);
CURRENT_DRAGGING_ITEMS.splice(targetIndex, 1);
}
Make sure you also remove the dragging item from the array when you call stopDrag on it from anywhere else.
I would like to focus on the end of a TextField when the up arrow is pressed. I'm using:
txt.setSelection(txt.text.length,txt.text.length);
This works great for any key except the up arrow. I believe that the up arrow automatically sets selection to the beginning of a TextField when it is in focus. How can I override this default behaviour?
I wanted to change the behavior of the home key, this is how I did it :
(The following code should essentially disable the HOME key but can be modified to make it do anything)
// Create two variables two remember the TextField's selection
// so that it can be restored later. These varaibles correspong
// to TextField.selectionBeginIndex and TextField.selectionEndIndex
var overrideSelectionBeginIndex:int = -1;
var overrideSelectionEndIndex:int;
// Create a KEY_DOWN listener to intercept the event ->
// (Assuming that you have a TextField named 'input')
input.addEventListener(KeyboardEvent.KEY_DOWN, event_inputKeyDown, false, 0, true);
function event_inputKeyDown(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.HOME){
if(overrideSelectionBeginIndex == -1){
stage.addEventListener(Event.RENDER, event_inputOverrideKeyDown, false, 0, true);
stage.invalidate();
}
// At this point the variables 'overrideSelectionBeginIndex'
// and 'overrideSelectionEndIndex' could be set to whatever
// you want but for this example they just store the
// input's selection before the home key changes it.
overrideSelectionBeginIndex = input.selectionBeginIndex;
overrideSelectionEndIndex = input.selectionEndIndex;
}
}
// Create a function that will be called after the key is
// pressed to override it's behavior
function event_inputOverrideKeyDown(event:Event):void{
// Restore the selection
input.setSelection(overrideSelectionBeginIndex, overrideSelectionEndIndex);
// Clean up
stage.removeEventListener(Event.RENDER, event_inputOverrideKeyDown);
overrideSelectionBeginIndex = -1;
overrideSelectionEndIndex = -1;
}
there is the Prevent Default (livedocs) function you can apply to the action if it is cancelable (which i presume it would be) otherwise you can try and catch it with stopPropagation instead:
This hasnt been tested, but should look something like:
function buttonPress(ev:KeyboardEvent):void{
txt.setSelection(txt.text.length,txt.text.length);
ev.preventDefault();
}
is it not possible to have both CLICK and DOUBLE_CLICK on the same display object? i'm trying to have both for the stage where double clicking the stage adds a new object and clicking once on the stage deselects a selected object.
it appears that DOUBLE_CLICK will execute both itself as well as the first CLICK functions in the path toward DOUBLE CLICK (mouse down, mouse up, click, mouse down, mouse up, double click).
in other languages i've programmed with there was a built-in timers that set the two apart. is this not available in AS3?
UPDATE
here's some code. essentially what i would like is have one or the other, not both with double click
stage.doubleClickEnabled = true;
stage.addEventListener(MouseEvent.DOUBLE_CLICK, twoClicks, false, 0, true);
stage.addEventListener(MouseEvent.CLICK, oneClick, false, 0, true);
function oneClick(evt:MouseEvent):void
{
trace("One CLICK");
}
function twoClicks(evt:MouseEvent):void
{
trace("Two CLICKS");
}
//oneClick trace = "One CLICK"
//twoClicks trace = "One CLICK Two CLICKS" (instead of just Two CLICKS)
Well, you could use setTimeout and clearTimeout.
It'd look something like this:
const var DOUBLE_CLICK_SPEED:int = 10;
var mouseTimeout;
function handleClick(evt:MouseEvent):void {
if (mouseTimeout != undefined) {
twoClicks();
clearTimeout(mouseTimeout);
mouseTimeout = undefined;
} else {
function handleSingleClick():void {
oneClick();
mouseTimeout = undefined;
}
mouseTimeout = setTimeout(handleSingleClick, DOUBLE_CLICK_SPEED);
}
}
function oneClick(evt:MouseEvent):void {
trace("One CLICK");
}
function twoClicks(evt:MouseEvent):void {
trace("Two CLICKS");
}
stage.addEventListener(MouseEvent.CLICK, handleClick, false, 0, true);
Did you set .doubleClickEnabled to true?
You should also take a look here.
Great answer Wallacoloo - thanks for that. I have just implemented your solution and refined a few points, so I'd thought I'd put it here for future reference (and of course the benefit of the overflow community!). Firstly, I couldn't test for undefined on the uint returned by setTimeout, so I replaced the undefined conditional with an == 0 conditional. Secondly, I wanted to commit the logic of a single click instantaneously (just makes for a more pleasant user interface), so I've done a bit of reshuffling:
if (mouseTimeout != 0) {
// clicked within the timeout, handle as double click
// rollback single click logic
rollbackSingleClickHandler(e);
// commit double click logic
dblClickHandler(e);
clearTimeOut(mouseTimeout);
mouseTimeout = 0;
} else {
// first click of a click sequence
// commit single click logic
singleClickHandler(e);
function clearTime():void {
mouseTimeout = 0;
}
// register a timeout for a potential double click
mouseTimeout = setTimeout(clearTime, DOUBLE_CLICK_SPEED);
}