• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

TextField in MovieClip blocks MouseEvent.CLICK on vector background even with mouseChildren = false

Contributor ,
Aug 27, 2012 Aug 27, 2012

Copy link to clipboard

Copied

I'm trying to set up a custom button in Flash/AS3 but a TextField is causing problems with it no matter what I do. I'm wondering if anyone else has any ideas.

I have a MovieClip called TabSelector. Inside TabSelector there are two layers. The first layer has a TextField called lblLabel. The second layer has a MovieClip named background which is an instance of TabBackground.

TabBackground is a MovieClip containing a single vector shape. It has two frames (representing the "pressed" and "not pressed" states), with the vector a different color in each frame.

To keep the TextField from interfering with click events, I have TabSelector's mouseChildren property set to false. Now, I've done this with about a dozen custom buttons in this project, and it's always worked fine, but for this button, if I click anywhere in the region that the TextField occupies, TabSelector doesn't register a click at all (it still works if I click near the edge where the TextField doesn't reach). I've also tried setting mouseChildren to true and lblLabel.mouseEnabled to false but the same problem occurs.

The only difference between this button and all of the other buttons I've done is this one has a vector background and the others have imported PNG (i.e. bitmap) backgrounds.

Does a TextField cause a hole in any vector graphics behind it if you set its mouseEnabled property to false?

TOPICS
ActionScript

Views

4.7K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

LEGEND , Aug 29, 2012 Aug 29, 2012

It was relevant as you were asking if vectors would act differently than bitmaps, and yes, they do, and there is the proof. That was the point of it. I was also simply trying to illustrate how to nest a shape inside a clip to fix the issue. I did not read that you assigned the listener to the outer most clip however. I still disagree with mouseChildren. Chances are your issue was elsewhere.

Happens to the best of us, especially on complex interfaces. Makes you want to start assigning trace state

...

Votes

Translate

Translate
LEGEND ,
Aug 27, 2012 Aug 27, 2012

Copy link to clipboard

Copied

The text field isn't an input field by any chance?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

No, it's a dynamic TextField with the selectable property set to false. Sorry, I should have said that initially.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

You can apply a listener to a shape but it won't register a MouseClick. You need to put the listener on the MovieClip that contains the shape.

It's also a good idea to only set properties where needed rather than the proverbial carpet bomb technique of mouseChildren=false. That cascades so much it's hard to track. You should just set the selectable property to false on the TextField and listen for the MouseClick event on a valid object that can listen for it.

Your other buttons are using Bitmaps and they can emit a MouseEvent which probably explains why they all work and this doesn't.

Here's an example you can paste in frame 1 on a new AS3 document. I'm applying the listener to the MovieClip that contains nothing but a shape. The listener fires off just fine.

If you comment out the listener on "mc" and uncomment out the listener on "myShape" you'll see even though it compiles, shapes won't dispatch on MouseEvents.

import flash.display.Sprite;

import flash.display.Shape;

import flash.text.TextField;

import flash.events.MouseEvent;

import flash.display.MovieClip;

// add button container

var buttonContainer:Sprite = new Sprite();

addChild(buttonContainer);

// add text, dynamic default, cover 100x50 area,

// background just to verify it exists

var tf:TextField = new TextField();

tf.text = "Example Text Field";

tf.selectable = false;

tf.width = 100;

tf.height = 50;

tf.border = true;

tf.background = true;

buttonContainer.addChild(tf);

// add MovieClip wrapper for shape

var mc:MovieClip = new MovieClip();

buttonContainer.addChild(mc);

// add semi-transparent red rectangle shape same size,

// 100x50, inside a MovieClip over the text

var myShape:Shape = new Shape();

myShape.graphics.beginFill(0x990000,0.5);

myShape.graphics.drawRect(0,0,100,50);

myShape.graphics.endFill();

mc.addChild(myShape);

// listen to the MovieClip for clicks that only

// contains a shape

mc.addEventListener(MouseEvent.CLICK, handleClick);

// if you comment the listener above and uncomment below you'll

// see the shape does not fire off the handler

//myShape.addEventListener(MouseEvent.CLICK, handleClick);

function handleClick(e:MouseEvent):void

{

    trace("Button Clicked, x:" + e.localX + " y:" + e.localY);

}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

"TabBackground is a MovieClip containing a single vector shape"

To reiterate, I have a MovieClip called TabSelector. Inside TabSelector there is a child TextField, and underneath that is a child MovieClip containing a shape. The CLICK event listener is on TabSelector, not on the shape inside the MovieClip that is a child of TabSelector.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

As the code above illustrates there is no issue with setting selectable=false, you don't need to nuke it all with mouseChildren=false and any MovieClip containing a shape (vector-only content) will fire off MouseClick events properly.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

That's fine, but not relevant to my scenario. I have a specific setup, which I've described in the original post, where a MovieClip is not registering a mouse click because there is a TextField inside it. I have mouseChildren set to false because I need to access the TabSelector's properties in the event listener (via event.target), and if mouseChildren is true, the child MC will be the event.target rather than the TabSelector. This problem doesn't occur if the background MC contains a bitmap instead of a shape. However, it's not as simple as "disabling mouseChildren makes the entire button not clickable because there's nothing left to click", because the button is still clickable around the edges that the TextField doesn't cover.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 28, 2012 Aug 28, 2012

Copy link to clipboard

Copied

Ok, I messed around a little more, and I've concluded my FLA is possed by some sort of demon:

I deleted the shape in the "background" MovieClip and replaced it with a PNG bitmap. Now when I debug the project on the computer, two copies of the lblLabel TextField are always visible, one on top of the other, with different font colors. If I change the text on the label in the code, the text of one of the two copies changes, but the other doesn't. I'm not sure if this is related to the MouseEvent problem, but it seems related since it involves the same TextField. I've also found that if I run the app (which is a mobile app) on the iPad, it does not display two copies of the TextField.

Here are all the things I've tried to fix this problem that didn't work

1. First off, there are not two TextFields in the MC in the library. Believe me, I checked about a million times.

2. Delete the TextField and create a new one with the same name

3. Delete the background

4. Delete the TabSelector MovieClip and all of the related libary objects from the Library and recreate them from scratch

5. Delete the embedded font (Myriad Pro Regular) and embed it again

I tried pasting the TabSelector MovieClip into a different FLA. It doesn't have the same overlapping text issue there. I pasted that one back into the primary FLA and it didn't fix it.

I'm completely out of ideas. This makes absolutely no sense at all.

edit: So apparently the problem with the text rendering was because I didn't set this.cacheAsBitmap = true in the constructor... I'm not sure why that never came up before, or why it wasn't a problem in the other FLA I pasted it into... To solve the problem with the button not being clickable where the text was, I ended up moving the button to the top of the display order with setChildIndex... apparently it was behind one, or maybe several, transparent regions of other MovieClips, and for some reason that blocked clicking at the TextField but not the background? I haven't figured out what MovieClip(s) it was behind yet...

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 29, 2012 Aug 29, 2012

Copy link to clipboard

Copied

It was relevant as you were asking if vectors would act differently than bitmaps, and yes, they do, and there is the proof. That was the point of it. I was also simply trying to illustrate how to nest a shape inside a clip to fix the issue. I did not read that you assigned the listener to the outer most clip however. I still disagree with mouseChildren. Chances are your issue was elsewhere.

Happens to the best of us, especially on complex interfaces. Makes you want to start assigning trace statements and click handlers to objects near it to see what's covering it. Chances are it's a TextField gone wild elsewhere. Enabling borders or background on TextFields near it may reveal what is covering it.

Aside that, event.target does give the child but you can always get the first clicked object using event.currentTarget. Keep that in mind. I never use event.target as you never know what object is going to fire back when things contain multiple display items. event.currentTarget will always send back the parent most clip that can respond to your click.

Finally setting cacheAsBitmap on the TextField simply made it a bitmap in memory and therefore would act exactly like any other bitmap, including emitting MouseEvents, unless disabled via mouseChildren=false or mouseEnabled=false.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 29, 2012 Aug 29, 2012

Copy link to clipboard

Copied

Ah, I never knew about currentTarget. That would make life a lot easier. Thank you very much!

Do you happen to know how it affects performance? I would think disabling mouseChildren would also reduce the number of tests Flash has to do during a click, and that event.target would process faster than event.currentTarget if currentTarget has to climb the heirarchy, but sometimes Flash optimizes the opposite of the way I would expect.

edit: I ran a test with two instances (a and b) of the same MovieClip (which has two children, a MovieClip containing a shape [c], and a dynamic TextField [d])

a.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);

b.mouseChildren = false;

//b.c.mouseEnabled = false;

//b.d.mouseEnabled = false;

b.addEventListener(MouseEvent.CLICK, onClick2, false, 0, true);

function onClick(event:MouseEvent):void {

          var time:int = getTimer();

          for (var i:int = 0; i < 5000000; i++) {

                    var n:String = event.currentTarget.name;

          }

          trace(getTimer() - time);

}

function onClick2(event:MouseEvent):void {

          var time:int = getTimer();

          for (var i:int = 0; i < 5000000; i++) {

                    var n:String = event.target.name;

          }

          trace(getTimer() - time);

}


I found that the currentTarget test on a averaged 1000 milliseconds, and the target test on b averaged 980 milliseconds. If I uncommented the two mouseEnabled lines, b averaged 960 milliseconds. So if you ever need a 2-4% increase in speed while accessing a property of an event target several million times, there you go

(or you could use a reference variable instead of calling event.target/event.currentTarget each time and save 300 milliseconds...)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Aug 29, 2012 Aug 29, 2012

Copy link to clipboard

Copied

LATEST

If you're directly coding a new 3d openGL engine saving that performance might make sense but I wouldn't kill yourself with it . Todays systems will have little to no noticable difference outside epic scale interfaces and intense vector/3d processing.

As for a bit of time savings you can set the "useCapture" argument to true in addEventListener() to stop the event during the capture phase and stop the propagation of itself or all other events overall with either stopPropagation() or stopImmediatePropagation() respectively inside the listener function.

Try those in your tests and see if you can shed a higher number. An event and listener that only cared about the fastest click response would look something like this:

a.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true, 0, true);

b.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler2, false, 0, true);

function mouseDownHandler(e:MouseEvent):void

{

     // only care about the mouse event, stop all other events and this event further

     e.stopImmediatePropagation();

     //  your code

}

function mouseDownHandler(e:MouseEvent):void

{

     //  your code

}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines