Hiding a menu on click outside - actionscript-3

I have a menu that pops up and at the same time is hidden on a mouse click event on a button but I would like to be able to hide said menu by clicking outside the menu movieclip and its button. Is it possible to accomplish that in as3?
So far this is the solution I found, adapted and have being trying to work with but still not getting anywhere.
switchbd_btn.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
function onClickHandler(event : MouseEvent) : void {
switch(event.target)
{
case switchbd_btn:
switchbd.gotoAndStop(2);
switchbdIN.start();
switchbd_btn.filters = [ONf];
break;
case stage:
switchbdOUT.start();
switchbd.gotoAndStop(3);
switchbd_btn.filters = [OFf];
break;
}
}
My original working code :-
//////////////////////SWITCH BOARD\\\\\\\\\\\\\\\\\\\\\\\\\\\\
switchbd_btn.addEventListener(MouseEvent.MOUSE_DOWN, ShowswitchBD);
var switchbdIN:Tween = new Tween (switchbd, "x", Strong.easeOut, 1089.05, 277.85, 1, true);
var switchbdOUT:Tween = new Tween (switchbd, "x", Strong.easeOut, 277.85, 1089.05, 1, true);
var ONf:DropShadowFilter = new DropShadowFilter (-1,26,0xCCCCCC,1,2,2,1,3,false,false,false);
var OFf:DropShadowFilter = new DropShadowFilter (6,26,0x000000,1,2,2,1,3,false,false,false);
function ShowswitchBD(e:MouseEvent):void {
if (switchbd.currentFrame != 2)
{
switchbd.gotoAndStop(2);
switchbdIN.start();
switchbd_btn.filters = [ONf];
}
else {
switchbdOUT.start();
switchbd.gotoAndStop(3);
switchbd_btn.filters = [OFf];
}
}
switchbd is the menu movieclip, and switchbd_btn is the button.

Here is a pattern to accomplish what you're asking. This assumes your menu movieClip is called: switchbd and you show/hide it with the button: switchbd_btn.
First, add a click listener to your button and create the handler function:
switchbd_btn.addEventListener(MouseEvent.CLICK, toggleMenu);
function toggleMenu(e:Event):void {
//the x position should be 277.85 when visible
if(switchbd.x <= 278){
hideMenu();
}else{
showMenu();
}
}
Now create the show and hide menu functions:
function showMenu():void {
switchbd.gotoAndStop(2); //or however you 'show' it
switchbdIN.start();
switchbd_btn.filters = [ONf];
//since the menu is visible, listen for global mouse clicks to hide it
stage.addEventListener(MouseEvent.CLICK, hideMenuGlobalClick);
}
function hideMenu():void {
switchbd.gotoAndStop(3);
switchbdOUT.start();
switchbd_btn.filters = [OFf];
//stop listening for global mouse clicks since the menu is hidden now
stage.removeEventListener(MouseEvent.CLICK, hideMenuGlobalClick);
}
function hideMenuGlobalClick(e:Event):void {
var target:DisplayObject = e.target as DisplayObject;
//crawl up the display tree from the item click until there is no parent (stage)
while(target && target.parent){
//if the target is the menu or button, exit this function
if(target == switchbd || target == switcbd_btn){
return;
}
//move on to the next item up the chain
target = target.parent;
}
//if you made it this far, it wasn't the menu that was clicked
hideMenu();
}

Related

AS3 multiple (movieclip buttons) to animate one movieclip

I am developing a desktop application. I am using ActionScript 3 via Adobe Animate CC. I designed the application, animated the GUI, and started coding. The main functions were successfully coded well, but I have added some additional features which are out of the scope of the application's original goals. All of this just to link the application to a website and some other information, which made me totally crazy because I've spent too much time with very simple if statement LOGIC!
Anyway, I created a menu with three MovieClip buttons. These menu button clicks affect one MovieClip that has a white background that moves with each click. I need there to be one background to show the beautiful effect of easeIn and easeOut animation tweens when clicking each button.
About clicked
Gallery clicked
Contact clicked
To make it easy to understand, I recreated the code with a very simple ball and 3 buttons. If you click first button, the ball moves to the right above the 2nd button. Then the first button should be unclickable, unless another button is clicked. If the second button is clicked, then the ball would move to the right above the third button. Then the second button should be unclickable also, unless another button is clicked. The same thing goes for the third button.
Now, if the first button is clicked again, the animation of the white background should not start from the default position when starting up the application!
It should animated back from its current position to the default position... and so on...
I replaced the white background with a ball for simplicity
This is very easy but I lost it with the eventListeners, eventHandlers, and if statements! :/
I also made this table which studies the cases:
I know my coding technique is not smart enough, but that is because I HATE using classes, packages, project folders... etc..
Even if the code runs too long & repeats, It would be better for me for simplicity, as programming is not my day-job!
Please, any help and quick response would be highly appreciated!
Code:
import flash.ui.Mouse;
import flash.events.MouseEvent;
one.addEventListener(MouseEvent.CLICK, moveToSecondPos);
two.addEventListener(MouseEvent.CLICK, moveToThirdPos);
//three.addEventListener(MouseEvent.CLICK, moveToFirstPos);
var buttonState:Boolean;
one.buttonState = 0;
two.buttonState = 0;
//three.buttonState = 0;
function moveToSecondPos(event:MouseEvent){
if(one.buttonState == 0){
theBall.gotoAndPlay("go1");
one.buttonState = 1;
}
else if(two.buttonState == 1){
theBall.gotoAndPlay("backToOne");
// two.buttonState = 0;
// one.buttonState = 1;
}
else{
//disable About Button
one.removeEventListener(MouseEvent.CLICK, moveToSecondPos);
}
}
function moveToThirdPos(event:MouseEvent){
if((two.buttonState == 0)&&(one.buttonState == 0)){
theBall.gotoAndPlay("goProducts");
two.buttonState = 1;
}
else if(one.buttonState == 1){
theBall.gotoAndPlay("go2");
// two.buttonState = 1;
// one.buttonState = 1;
}
else{
two.removeEventListener(MouseEvent.CLICK, moveToThirdPos);
}
}
//function moveToFirstPos(event:MouseEvent){
// if(three.buttonState == 0){
// theBall.gotoAndPlay("go3");
// three.buttonState = 1;
// }
// else{
// three.removeEventListener(MouseEvent.CLICK, moveToFirstPos);
// }
//}
First, you mentioned you wanted to have the
white background should not start from the default position when starting up the application
For that, you'll need a ShareObject or comparable method of saving and loading data.
Secondly, It appears you may be trying to do some of this with Scenes and Timeline' frames. I highly encourage you not to pursue that.
Below is a solution to the problems you mentioned. Notice that without your project, I had to recreate the scene. You can copy & paste the solution into a new scene and it will compile.
To prevent buttons from being clicked, you can remove the event listener by calling myButton.removeEventListener("click"). Alternatively, you can simply stop the object from responding to mouse events by setting its mouseEnabled property to false.
To animate the box smoothly, you can use the built-in Tween class. Alternatively, I'd direct you Greensock's TweenLite.
Because you appeared to want a series of labels to appear depending on the button clicked, I created an array which stores data specific to each button (btns:Array). By using an organized structure, it's easier to write re-useable code which doesn't depend on explicit functions. Note that there's only one btnListener() function which works for all of your buttons.
Solution
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import fl.motion.Color;
import fl.transitions.*;
import fl.transitions.easing.*;
// the list of buttons we want, along with the descriptions for each
var btns:Object = [
{
"label":"About",
"desc":"Learn more about us",
"btn":null
},
{
"label":"Gallery",
"desc":"See our photos",
"btn":null
},
{
"label":"Contact",
"desc":"Write, call, or message",
"btn":null
}
];
var nav:Sprite; // our navigation bar of buttons
var whiteBox:Sprite; // the "ball" with the content text
// where we save the coordinates of the whiteBox between loads
var settings:Object = SharedObject.getLocal("foo");
init();
function init():void {
// Create our navigation of three buttons
// our container for buttons
nav = new Sprite();
// reference to the last button
var last:Sprite = null;
// loop through the list of buttons
for each (var entry:Object in btns) {
// For each button, create a new button
var btn:Sprite = createButton(entry.label);
entry.btn = btn;
nav.addChild(btn);
// If there was a previous button, place this beside it.
if (last != null) {
btn.x = last.x + last.width + 1;
}
last = btn;
}
// Place the nav onscreen
addChild(nav);
nav.x = stage.stageWidth/2 - nav.width/2;
nav.y = stage.stageHeight - nav.height*2;
// Create the whitebox
whiteBox = new Sprite();
whiteBox.graphics.beginFill(0xFAFAFA);
whiteBox.graphics.drawRect(0, 0, 150, 50);
whiteBox.graphics.endFill();
var txt:TextField = new TextField();
txt.name = "txt";
txt.width = 150;
whiteBox.addChild(txt);
nav.addChild(whiteBox);
// Load the settings from the last run of the program.
if (settings.data.hasOwnProperty("x")) {
whiteBox.x = settings.data.x;
whiteBox.y = settings.data.y;
}
}
function createButton(label:String):Sprite {
// Creates a simple button
// Create the label
var txt:TextField = new TextField();
txt.text = label;
// Create the background
var btn:Sprite = new Sprite();
btn.graphics.beginFill(0xA1A1A1);
btn.graphics.drawRect(0, 0, txt.width + 60, txt.height + 30);
btn.graphics.endFill();
btn.addChild(txt);
btn.name = label;
txt.x = 30;
txt.y = 15;
// Hookup events
btn.addEventListener("mouseOver", btnListener);
btn.addEventListener("mouseOut", btnListener);
btn.addEventListener("click", btnListener);
return btn;
}
function btnListener(e:Event):void {
var btn:Sprite = e.currentTarget as Sprite;
var c:Color = new Color();
var entry:Object;
// Find our button in the list.
for each (entry in btns) {
if (entry.label == btn.name) {
break;
}
}
switch (e.type) {
case "mouseOver":
if (btn.mouseEnabled) {
c.setTint(0x0096ff, 0.5);
btn.transform.colorTransform = c;
}
break;
case "mouseOut":
if (btn.mouseEnabled) {
c.setTint(0x00, 0);
btn.transform.colorTransform = c;
}
break;
case "click":
// Set the text, and position of our whitebox
whiteBox.getChildAt(0)["text"] = entry.desc;
whiteBox.y = -whiteBox.height;
var tween:Tween = new Tween(whiteBox, "x", Regular.easeOut, whiteBox.x, btn.x, 0.35, true);
tween.addEventListener("motionFinish", saveState);
for each (var v:Object in btns) {
if (v.btn == btn) {
// make our button unclickable
btn.mouseEnabled = false;
c.setTint(0xFFFFFF, 0.5);
btn.transform.colorTransform = c;
} else {
// Make the other buttons clickable;
v.btn.mouseEnabled = true;
c.setTint(0x00, 0);
v.btn.transform.colorTransform = c;
}
}
break;
}
}
function saveState(e:Event):void {
settings.data.x = whiteBox.x;
settings.data.y = whiteBox.y;
settings.flush();
}

Flash CS4 How do you set variable to the Object Dragging Collison?

I am doing a game for my computer science class and I am working on inventory, dragging an item into it. I do not know how to set the item as whatever the user clicked to drag.
At the moment I hard coded it to the item they are dragging, but in the future I want more items, so a variable set to the item they are dragging would make it work perfectly, but I don't know what it's called to do that.
Here is my code for inventory item dragging
function dragItem (event:MouseEvent):void
{
knife_loot.startDrag();
}
function dropItem (event:MouseEvent):void
{
knife_loot.stopDrag();
if ((knife_loot.hitTestObject(inv_game)) && (inv_game.visible == true))
{
trace("Item dropped in inventory")
trace("")
knife_loot.x = 80
knife_loot.y = 120
}
}
// end of dragging and dropping items
You can start with:
// List the items you want to drag.
var aList:Array = [knife_loot, spoon_loot, fork_loot];
// InteractiveObject is superclass for SimpleButton, Sprite and MovieClip.
// If you're sure what they all are then just use their class instead.
for each (var anItem:InteractiveObject in aList)
{
// Subscribe them all for dragging.
anItem.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
}
public var draggedItem:InteractiveObject;
function onDrag(e:MouseEvent):void
{
// Use e.currentTarget because original MouseEvent e.target
// could be something from deep inside of top object e.currentTarget.
draggedItem = e.currentTarget as InteractiveObject;
draggedItem.startDrag();
// Let's hook drop events.
stage.addEventListener(Event.MOUSE_LEAVE, onDrop);
stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
}
function onDrop(e:Event):void
{
// Unhook drop events.
stage.removeEventListener(Event.MOUSE_LEAVE, onDrop);
stage.removeEventListener(MouseEvent.MOUSE_UP, onDrop);
// Drop the item.
draggedItem.stopDrag();
if ((draggedItem.hitTestObject(inv_game)) && (inv_game.visible == true))
{
trace("Item", draggedItem.name, "was dropped to inventory.");
trace("");
draggedItem.x = 80;
draggedItem.y = 120;
}
// Forget the item.
draggedItem = null;
}

Action Script: Call corresponding functions on button click

I have been trying to add buttons dynamically and then assign event listeners to them. For which my code is,
var i=0;
var step=50;
for each (var child:XML in courseXML.footer.tray.elements())
{
var thumbClip:thumb = new thumb();
//set name
thumbClip.name = "mc_thumb" + (i + 1);
trace("thumbClipname:: "+thumbClip.name);
//set the x and y values
thumbClip.x = 620 + (i * step);
thumbClip.y = 560;
//attach the newly created instance to the container
addChild(thumbClip);
trace("thumbClip:: "+thumbClip);
//attach icon image from xml path
thumbClip.thumbLoader.source = child.icon;
trace("thumbClip.thumbLoader.source: "+thumbClip.thumbLoader.source);
//add listeners
trace("Node name "+ child.localName());
if(child.localName().toLowerCase() == "feedback");
{ trace("gotoFeedback..");
thumbClip.addEventListener(MouseEvent.CLICK, gotoFeedback);
}
if(child.localName().toLowerCase() == "resources");
{ trace("gotoResources..");
thumbClip.addEventListener(MouseEvent.CLICK, gotoResources);
}
if(child.localName().toLowerCase() == "glossary");
{ trace("gotoGlossary..");
thumbClip.addEventListener(MouseEvent.CLICK, gotoGlossary);
}
if(child.localName().toLowerCase() == "discussion");
{ trace("gotoDiscussion..");
thumbClip.addEventListener(MouseEvent.CLICK, gotoDiscussion);
}
thumbClip.buttonMode = true;
i++;
}
So, Finally 4 buttons are displayed on stage and when I click on any button all the four functions gotofeedback, gotoResources, gotoGlossary, gotoDiscussion are called. How do I call corresponding functions on button click? Thanks!
The four functions are:
function gotoFeedback(e: MouseEvent):void
{
trace("gotofeedback fn called..");
}
function gotoResources(e: MouseEvent):void
{
trace("gotoResources fn called..");
}
function gotoGlossary(e: MouseEvent):void
{
trace("gotoGlossary fn called..");
}
function gotoDiscussion(e: MouseEvent):void
{
trace("gotoDiscussion fn called..");
}
You need to add the event listeners to the child buttons, rather than to thumbClip. The way you have your code now, whenever the mouse is clicked anywhere inside thumbClip all the the listeners are called (which is what you are seeing).

Flash Senocular Transform Tool - controls are not showing on the tool

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);
}

Click event outside MovieClip in AS3

Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.