Custom ContextMenu is not showing, since Display Object is lying "on Top" - actionscript-3

as a follow up to another question here:
i've build a custom contextmenu item in a flash application and had a problem with it not showing sometimes.
i found out that the issue was that another sprite was lying "on top" of the item with the custom contextmenu.
however, even with the "mouseEnabled" and "mouseChildren" properties set to false for the item "on top" i still cannot get the custom contextmenu to show...
any ideas? thanks!
ps: here is some code to see the problem:
var spr:Sprite=new Sprite();
spr.graphics.beginFill(0xff0000,1);
spr.graphics.drawRect(0,0,100,100);
addChild(spr);
var blocker:Sprite=new Sprite();
blocker.x=50
blocker.y=50
blocker.graphics.beginFill(0x00ff00,1);
blocker.graphics.drawRect(0,0,100,100);
addChild(blocker);
blocker.mouseEnabled=false
blocker.mouseChildren=false
var customContextMenu:ContextMenu = new ContextMenu();
var item:ContextMenuItem = new ContextMenuItem("custom item");
customContextMenu.customItems.push(item);
item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuItemSelectHandler,false,0,true);
spr.contextMenu = customContextMenu;
function menuItemSelectHandler(cem:ContextMenuEvent) {
trace("hello context");
};
when the mouse is over the green rect, the custom contextmenuitem is not shown

The context menu for an object will be displayed only if the user right-clicks directly on that object itself, as far as I know.
I've simplified your problem in this code:
public class Test extends Sprite
{
public function Test()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
var sprite:Sprite = new Sprite();
addChild(sprite);
sprite.graphics.beginFill(0xFF0000);
sprite.graphics.drawRect(0, 0, 200, 200);
sprite.graphics.endFill();
var shape:Shape = new Shape();
addChild(shape);
shape.graphics.beginFill(0x0000FF, .6);
shape.graphics.drawRect(100, 100, 200, 200);
shape.graphics.endFill();
setUpContextMenu(sprite);
}
private function setUpContextMenu(target:InteractiveObject):void
{
var menu:ContextMenu = new ContextMenu();
target.contextMenu = menu;
var item:ContextMenuItem = new ContextMenuItem("About Us");
menu.customItems.push(item);
}
}
When you right-click on the area where the red and blue squares overlap, you don't get the custom context menu.
Here's a possible solution, where I've modified only the setUpContextMenu() function:
private function setUpContextMenu(target:InteractiveObject):void
{
var menu:ContextMenu = new ContextMenu();
this.contextMenu = menu;
var item:ContextMenuItem = new ContextMenuItem("About Us");
var handler:Function = function (event:ContextMenuEvent):void {
// Start with empty menu.
var items:Array = [];
if (event.mouseTarget == target) {
// Directly right-clicked on target. Add custom item.
items = [item];
} else if (event.mouseTarget is DisplayObjectContainer) {
var o:DisplayObjectContainer
= DisplayObjectContainer(event.mouseTarget);
var pt:Point = o.localToGlobal(new Point(o.mouseX, o.mouseY));
var arr:Array = o.getObjectsUnderPoint(pt);
// One of the mouse target's descendants is our target,
// directly under the pointer. Add custom item.
if (arr.indexOf(target) != -1)
items = [item];
}
menu.customItems = items;
};
menu.addEventListener(ContextMenuEvent.MENU_SELECT, handler);
}
Here I'm assigning the context menu to the application itself. On the "menuSelect" event, I customize it based on whether the mouse pointer is somewhere above the red square.

Related

How to get index of a Bitmap Image from bitmapdata(from an Array)?

I am wondering if i have an Array that push content that is Bitmap, how do i get index of a specific image when clicked. I tried to use indexOf but no luck, my codes are below.
Thanks for your time!
Code:
//First Part is where i add the URLRequest and add the image into contentHolder then onto Stage
function loadImage():void {
for(var i:int = 5; i < somedata.length; i++){
if(somedata[i]){
var loader:Loader = new Loader();
loader.load(new URLRequest("http://www.rentaid.info/rent/"+somedata[i]));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
}
}
}
function onImageLoaded(e:Event):void {
loadedArray.push(e.target.content as Bitmap);
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
currentY1 += e.currentTarget.loader.content.height +300;
_contentHolder.mouseChildren = false; // ignore children mouseEvents
_contentHolder.mouseEnabled = true; // enable mouse on the object - normally set to true by default
_contentHolder.useHandCursor = true; // add hand cursor on mouse over
_contentHolder.buttonMode = true;
_contentHolder.addChild(loadedArray[i]);
addChild(_contentHolder);
_contentHolder.addEventListener(MouseEvent.CLICK, gotoscene);
}
}
// then the part where i try to get the index
function gotoscene(e:MouseEvent):void {
var index:Number;
index = loadedArray.indexOf(e.target);
trace(index);
}
Edit:
var viewport:Viewport = new Viewport();
viewport.y = 0;
viewport.addChild(_contentHolder);
Your first question has very simple answer:
var image:Bitmap = new Bitmap();
var images:Array = new Array(image);
for (var i:uint = 0; i < images.length; i++) {
// images[i].bitmapData is the original image in your array
// image.bitmapData is searched one
if (images[i].bitmapData == image.bitmapData) {
// found
}
}
But your problem is bigger than this. I see you keep wandering around..
You should add listener to each child, not the content holder as one. I usually don't use Loaders, but get their Bitmaps and wrap them in Sprites or something, that I add into the scene. You should store either this Sprite or your Loader into that array, not the Bitmap. Then add listener to each of them (Sprite or Loader, not Bitmap) and get the target. Depending on what you've stored in the array, you can easily get it as:
function gotoscene(e:MouseEvent):void {
var index:uint = loadedArray(indexOf(e.target));
}
But it's important to store one specific type that will actually be clickable. Don't think about the Bitmap - it's only a graphic representation, and doesn't do much in the code.
**EDIT:
Okay I'm adding the code you need but it's important to understand what you are doing and not just rely on someone else's answer :)
function onImageLoaded(e:Event):void {
var bitmap:Bitmap = e.target.content as Bitmap; // get the Bitmap
var image:Sprite = new Sprite();
image.addChild(bitmap); // wrap it inside new Sprite
// add listener to Sprite!
image.addEventListener(MouseEvent.CLICK, gotoscene);
// gets url of current image (http://website.com/images/image1.jpg)
var url:String = e.target.loaderURL;
// get only the number from that url by replacing or by some other way
// this removes the first part and results in "1.jpg"
var name:String = url.replace("http://website.com/images/image", "");
// this removes the extension and results in number only - 1, 2, 3
// it's important to change this depending on your naming convention
name = name.replace(".jpg", "");
image.name = "button" + name; // results in "button1", "button2", "button3"
// store object, name, or whatever (not really needed in your case, but commonly used)
loadedArray.push(image.name);
image.x = counter * 100; // position so you can see them, at 100, 200, 300, etc.
_contentHolder.addChild(image); // add newly created Sprite to content
}
function gotoscene(e:MouseEvent):void {
var name:String = e.target.name;
// strips down "button" from "button1", and only the number remains,
// which is 1, 2, 3, etc. the number of the scene :)
var scene:uint = name.replace("button", "");
// you're the man now :)
}

AS3. EventListener.Click depends on position

I'm doing a button on AS3 made out of a sprite wich (just a simple square). When I add the event listener so that it acts as a button it works but depending on button.x, so when I put the button where I want the button stops working.
Thanks
public function pintaInterficieTrad(){
while(numChildren != 0) removeChildAt(0);
var idioma = new TextField();
idioma.text=traductor.*[numTrad].*;
idioma.width=200;
idioma.selectable=false;
idioma.setTextFormat(format);
idioma.x=20;
idioma.y=20;
addChild(idioma);
var trad = new Sprite();
trad.graphics.lineStyle(5,0x00ff00);
trad.graphics.beginFill(0x000000);
trad.graphics.drawRect(300,20,150,70);
addChild(trad);
var textTrad = new TextField();
if(numTrad==0) {
textTrad.text="Traduir";
}else{
textTrad.text="Traducir";
}
textTrad.width=200;
textTrad.selectable=false;
textTrad.setTextFormat(format);
textTrad.x=270;
textTrad.y=40;
addChild(textTrad);
var getBack = new Sprite();
getBack.graphics.lineStyle(5,0x00ff00);
getBack.graphics.beginFill(0x000000);
getBack.graphics.drawRect(500,20,150,70);
addChild(getBack);
var textgetBack = new TextField();
if(numTrad==0) {
textgetBack.text="Tornar";
}else{
textgetBack.text="Volver";
}
textgetBack.width=200;
textgetBack.selectable=false;
textgetBack.setTextFormat(format);
textgetBack.x=470;
textgetBack.y=40;
addChild(textgetBack);
trad.addEventListener(MouseEvent.CLICK,traduirBtn);
getBack.addEventListener(MouseEvent.CLICK,tornarBtn);
var userBox = new Sprite();
userBox.graphics.lineStyle(2,0x00ff00);
userBox.graphics.beginFill(0xffffff);
userBox.graphics.drawRect(40,130,610,160);
addChild(userBox);
var tradBox = new Sprite();
tradBox.graphics.lineStyle(2,0x00ff00);
tradBox.graphics.beginFill(0xffffff);
tradBox.graphics.drawRect(40,320,610,160);
addChild(tradBox);
var formatTxt = new TextFormat();
formatTxt.color=0x000000;
formatTxt.size=14;
var textUser = new TextField();
var textTraduit = new TextField();
textUser.defaultTextFormat=formatTxt;
textUser.text = textUsuari;
textUser.width = 600;
textUser.height = 150;
textUser.x=45;
textUser.y=130;
addChild(textUser);
textTraduit.text = traduccio;
textTraduit.setTextFormat(formatTxt);
textTraduit.width = 600;
textTraduit.height = 150;
textTraduit.x=45;
textTraduit.y=325;
addChild(textTraduit);
}
public function traduirBtn(e){
while(numChildren != 0) removeChildAt(0);
tradueix();
pintaInterficieTrad();
}
public function tornarBtn(e){
while(numChildren != 0) removeChildAt(0);
pintaMenu();
}
}
If I put the squares on x=0 they do what they're suposed to do...
seems like the button you're talking about is trad. You're addChilding it quite early in your code, which means that antoher displayobject could get above it at the same position. When you click at that position, the click event will only get send to the top most element, so try adding the elements you want people to interact with as the last (buttons, text field inputs, etc.)
Your problem is in the fact that you are adding the text over the button and the text is being clicked, not the button.
Add the text to the button itself and your click event will work.
Also verify that no other component is covering the button. Even if transparent, like a textbox.

Actionscript 3: mouse_over & mouse_out causing Error #1009?

When I mouse over the buttonSprite, it adds displaySprite to the stage, and when I mouse out buttonSprite, the displaySprite is removed.
My problem is that when I swiftly mouse over and out the buttonSprite several times, the displaySprite is not be removed and an error message(Error #1009) is displayed. Even I type "displaySprite = null", it's still not working. Any suggestions? Thanks
var buttonSprite:Sprite = new Sprite();
addChild(buttonSprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OVER, overSprite);
var displaySprite:Sprite;
function overSprite(e:MouseEvent):void{
displaySprite = new Sprite();
addChild(displaySprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OUT, outSprite);
}
function outSprite(e:MouseEvent):void{
removeChild(displaySprite);
displaySprite = null;
}
There is no guarantee that the events will fire in order.
In your case you do not have to instantiate the displaySprite multiple times.
Just don't null it out and the object will be there.
var buttonSprite:Sprite = new Sprite();
addChild(buttonSprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OVER, overSprite);
//you only need to create it once.
var displaySprite:Sprite = new Sprite();
function overSprite(e:MouseEvent):void{
addChild(displaySprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OUT, outSprite);
}
function outSprite(e:MouseEvent):void{
removeChild(displaySprite);
}
How about this?
var buttonSprite:Sprite = new Sprite();
addChild(buttonSprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OVER, overSprite);
buttonSprite.addEventListener(MouseEvent.MOUSE_OUT, outSprite);
var displaySprite:Sprite;
addChild(displaySprite);
displaySprite.visible=false;
function overSprite(e:MouseEvent):void
{
displaySprite.visible = true;
}
function outSprite(e:MouseEvent):void
{
displaySprite.visible = false;
}
The problem is MouseEvent.MOUSE_OVER is dispatched multiple times when you mouse over a display object. What you should try is MouseEvent.ROLL_OVER and MouseEvent.ROLL_OUT those two events are dispatched once.
Something along the lines of :
var buttonSprite:Sprite = new Sprite();
addChild(buttonSprite);
buttonSprite.addEventListener(MouseEvent.ROLL_OVER, overSprite);
buttonSprite.addEventListener(MouseEvent.ROLL_OUT, outSprite);
var displaySprite:Sprite;
function overSprite(e:MouseEvent):void
{
if(!displaySprite)
{
displaySprite = new Sprite();
addChild(displaySprite);
}
}
function outSprite(e:MouseEvent):void{
if(displaySprite)
{
removeChild(displaySprite);
displaySprite = null;
}
}

as3 moving image from one mc to another

With the code below I created some imgMcA and some imgMcB then I loaded images into imgMcA ones. ImgMcBs have no image at that moment. So if one of imgMcA is clicked the image should be transferred to one of the empty imgMcBs (may be randomly) and if imgmcB is clicked later the image should move back to its imgMcA back. I could not find out how I can accomplish this.
Thanks in advance
function imageList(mcname, img, index){
var imgMcA:MovieClip=new MovieClip();
imgMcA.graphics.beginFill(0x000000);
imgMcA.graphics.drawRect(0,0,imgWidth,imgHeight);
imgMcA.graphics.endFill();
imgMcA.name=lemma;
imgMcA.addEventListener(MouseEvent.CLICK, moveImage);
var imgMcB:MovieClip=new MovieClip();
imgMcB.graphics.beginFill(0x000000);
imgMcB.graphics.drawRect(0,0,imgWidth,imgHeight);
imgMcB.graphics.endFill();
imgMcB.name=index;
addChild(imgMcB);
var imgLoader:Loader = new Loader();
imgLoader.load(new URLRequest(img));
imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, changeProperties);
imgLoader.mouseEnabled=false;
imgMcA.addChild(imgLoader);
}
function moveImage(evnt:MouseEvent){
}
You could linked mcA & mcB by adding them to the same parent.
function createBlock():void
{
var imgLoader:Loader = new Loader();
//add the loading code here...
var mcA:Sprite = new Sprite();
var mcB:Sprite = new Sprite();
// add Click event listeners for both Mcs here...
var parent:Sprite = new Sprite();
//add Children
parent.addChild(mcA);
parent.addChild(mcB);
addChild(parent);
}
function mouseClickHandler(event:MouseEvent)
{
var dispatcher:Sprite = event.currentTarget as Sprite;
//if you added a Loader to it
if( dispatcher.numChildren > 0)
{
//retrieve the image
var img:Loader = dispatcher.getChildAt(0);
//identify the parent
var parent:Sprite = dispatcher.parent;
var index:int = parent.getChildIndex(dispatcher);
//identify the receiving MC
//of course this only works with two children!!!
if(index > 0)
var receiver:Sprite = parent.getChildAt(0) as Sprite;
else
receiver = parent.getChildAt(1) as Sprite;
//add the image to the other MC
receiver.addChild(img);
}
}
The rest is not too complicated to achieve. You will need to use a Boolean and add a TextField. If the TextField contains text, set the Boolean to true.
It may be worth looking at Classes or you could use an Object as a container for the MovieClips, the TextField and the Boolean, although a Class will give you more flexibility...
With a Class, you wouldn't have to iterate in order to find what does what. Your Click listener would look something like this:
private function mouseClickHandler(event:MouseEvent)
{
receiver.addChild( image );
if( hasText)
imageReturn();
}

TextField breaking MOUSE_OVER after moving it

I'm having a really weird problem with the MOUSE_OVER event. I'm building dynamic tabs representing mp3 songs containing textfields with info and a dynamic image for the cover art. I am trying to get a simple MOUSE_OVER working over the whole tab, such that you can select the next song to play.
I am using a Sprite with alpha 0 that overlays my whole tab (incl. the textFields) as a Listener for MOUSE_OVER and _OUT... I've checked by setting the alpha to something visible and it indeed covers my tab and follows it around as I move it (just making sure I'm not moving the tab without moving the hotspot). Also, I only create it once my cover art is loaded, ensuring that it will cover that too.
Now, when the tab is in the top position, everything is dandy. As soon as I move the tab to make space for the next tab, the textFields break my roll behaviour... just like that noob mistake of overlaying a sprite over the one that you're listening for MouseEvents on. But... the roll area is still on top of the field, I've set selectable and mouseEnabled to false on the textFields... nothing.
It is as if the mere fact of moving the whole tab now puts the textField on top of everything in my tab (whereas visually, it's still in its expected layer).
I'm using pixel fonts but tried it with system fonts, same thing... at my wits end here.
public function Tab(tune:Tune) {
_tune = tune;
mainSprite = new Sprite();
addChild(mainSprite);
drawBorder();
createFormat();
placeArtist();
placeTitle();
placeAlbum();
coverArt();
}
private function placeButton():void {
_button = new Sprite();
_button.graphics.beginFill(0xFF000,0);
_button.graphics.drawRect(0,0,229,40);
_button.graphics.endFill();
_button.addEventListener(MouseEvent.MOUSE_OVER, mouseListener);
_button.addEventListener(MouseEvent.MOUSE_OUT, mouseListener);
_button.buttonMode = true;
mainSprite.addChild(_button);
}
private function mouseListener(event:MouseEvent):void {
switch(event.type){
case MouseEvent.MOUSE_OVER :
hilite(true);
break;
case MouseEvent.MOUSE_OUT :
hilite(false);
break;
}
}
private function createFormat():void {
_format = new TextFormat();
_format.font = "FFF Neostandard";
_format.size = 8;
_format.color = 0xFFFFFF;
}
private function placeArtist():void {
var artist : TextField = new TextField();
artist.selectable = false;
artist.defaultTextFormat = _format;
artist.x = 41;
artist.y = 3;
artist.width = 135;
artist.text = _tune.artist;
artist.mouseEnabled = false;
mainSprite.addChild(artist);
}
private function placeTitle():void {
var title : TextField = new TextField();
title.selectable = false;
title.defaultTextFormat = _format;
title.x = 41;
title.y = 14;
title.width = 135;
title.text = _tune.title;
title.mouseEnabled = false;
mainSprite.addChild(title);
}
private function placeAlbum():void {
var album : TextField = new TextField();
album.selectable = false;
album.defaultTextFormat = _format;
album.x = 41;
album.y = 25;
album.width = 135;
album.text = _tune.album;
album.mouseEnabled = false;
mainSprite.addChild(album);
}
private function drawBorder():void {
_border = new Sprite();
_border.graphics.lineStyle(1, 0x545454);
_border.graphics.drawRect (0,0,229,40);
mainSprite.addChild(_border);
}
private function coverArt():void {
_image = new Sprite();
var imageLoader : Loader = new Loader();
_loaderInfo = imageLoader.contentLoaderInfo;
_loaderInfo.addEventListener(Event.COMPLETE, coverLoaded)
var image:URLRequest = new URLRequest(_tune.coverArt);
imageLoader.load(image);
_image.x = 1.5;
_image.y = 2;
_image.addChild(imageLoader);
}
private function coverLoaded(event:Event):void {
_loaderInfo.removeEventListener(Event.COMPLETE, coverLoaded);
var scaling : Number = IMAGE_SIZE / _image.width;
_image.scaleX = scaling;
_image.scaleY = scaling;
mainSprite.addChild (_image);
placeButton();
}
public function hilite(state:Boolean):void{
var col : ColorTransform = new ColorTransform();
if(state){
col.color = 0xFFFFFF;
} else {
col.color = 0x545454;
}
_border.transform.colorTransform = col;
}
Fixed it. What was happening was that I didn't set the height of the textfield. That was overshooting the tab and hence lying over the previously instantiated tab, blocking MouseOver... don't even ask.