Can an overlapping sibling prevent an Event? - apache-flex

Below is the code for a simple Flex actionscript project. A sprite is partially covering a hyperlink. What's happening is that when you hover over the sprite, if you're also hovering over the hyperlink, the hyperlink is activated. I want to prevent that. I want the hyperlink to be activated only when the mouse hovers over it -- but not when the mouse hovers over the sprite which covers it.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.Font;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class SpriteHyperlinkTest extends Sprite
{
private var style : StyleSheet = new StyleSheet();
public function SpriteHyperlinkTest()
{
createOutputTextField();
}
public var output_txt : TextField;
private function createOutputTextField() : void {
// set styles
var hover : Object = new Object();
hover.fontWeight = "bold";
hover.color = "#0000FF";
var link : Object = new Object();
link.fontWeight = "bold";
link.textDecoration = "underline";
link.color = "#555555";
var active : Object = new Object();
active.fontWeight = "bold";
active.color = "#FF0000";
var visited : Object = new Object();
visited.fontWeight = "bold";
visited.color = "#cc0099";
visited.textDecoration = "underline";
style.setStyle("a:link", link);
style.setStyle("a:hover", hover);
style.setStyle("a:active", active);
style.setStyle(".visited", visited);
output_txt = new TextField();
output_txt.backgroundColor = 0xFFFFFF;
output_txt.background = true;
//output_txt.embedFonts = true;
output_txt.wordWrap = true;
output_txt.multiline = true;
output_txt.name = "output_txt";
output_txt.x = 100;
output_txt.y = 100;
output_txt.width = 300;
output_txt.height = 200;
output_txt.htmlText = "<b>sample <a href='http://www.google.com'>hyperlink text</a></b>";
addChild(output_txt);
var mySprite:Sprite = new Sprite();
mySprite.graphics.lineStyle(.5,0x000000);
mySprite.graphics.beginFill(0xff0000, 1);
mySprite.alpha = .7;
mySprite.graphics.drawRect(100, 100, 90, 20);
mySprite.graphics.endFill();
mySprite.useHandCursor = true;
mySprite.mouseChildren = true;
mySprite.buttonMode = true;
mySprite.name = "Sprite1";
this.addChild(mySprite);
output_txt.styleSheet = style;
}
}
}

I've almost found a workaround. It is just amazing that there isn't some object that you can place on top of the textfield in order to prevent its seeing the mouse overing over a hyperlink.
Here, for the record, are some of the things I've tried.
stopPropagation () // didn't work
stopImmediatePropagation(P //didn't work
Because the textfield and the sprite are siblings on the displaylist, I think all of the event propagation stuff is irrelevant. All of that has to do with ancestors and descendants, but what we're dealing with here is neither.
I tried placing another ('cover') textfield on top of the 'target' textfield. I tried setting the cover.visible = false; but that just meant it effectively wasn't there. I tried setting its alpha to .1, but that didn't work either -- just like the covering sprite, it allowed the mouseOver to go through it, so that the hyperlink responded.
I thought about trying to use preventDefault () on the hyperlink but a) I don't know how to reference the hyperlink (it has no ID) and b) the only event that is dispatched from a hyperlink is the TextEvent, and that's when it's clicked. We're not clicking, we're hovering. So I don't know what event to cancel.
The other thing I thought to do was to a kind of a fake 'cancel'. That is, maybe I could set the textformat, or style, of the hyperlink to look like normal text while the mouse is hovering over the sprite. The hyperlink would actually be being activated, but it would look like it isn't being activated because the style would be changed. This is what worked.
But it's only a fake visual workaround...

I'd add a rollOver (or whatever event you're trying to block) event listener to the sprite on top of the hyperlink. In the rollover handler, be sure to call event.stopImmediatePropagation() and that should prevent the hyperlink underneath from receiving the event.

I've never seen that sort of behavior before. Your sample code doesn't even assign events. I would be curious to see how you are assigning the events, because that in itself could be the cause of your problem.

Related

In ActionScript 3.5/Flex, how do I apply a border to whatever UI Component or container the user clicks?

I am working on a project where I want to add a click listener to the root DisplayObjectContainer, which, if the user clicks on a UIComponent, will add a red border to whatever the user clicked on. Currently, my code to do so looks something like this:
private static var _root:DisplayObjectContainer;
private static var labelStyle:CSSStyleDeclaration;
public static function initialize(root:DisplayObjectContainer):void
{
_root = root;
labelStyle = new CSSStyleDeclaration();
labelStyle.setStyle("borderColor", "red");
labelStyle.setStyle("borderThickness", 4);
labelStyle.setStyle("color", "red");
labelStyle.setStyle("borderStyle", "solid");
StyleManager.setStyleDeclaration(".mystyle", labelStyle, true);
_root.addEventListener(MouseEvent.CLICK, highlightBorder, true, Infinity, true);
}
private static function highlightBorder(event:MouseEvent):void
{
event.stopPropagation();
if(event.target is UIComponent)
{
var tmp:UIComponent = event.target as UIComponent;
tmp.styleDeclaration = labelStyle;
tmp.invalidateProperties();
tmp.invalidateDisplayList();
tmp.validateNow();
}
}
This code is in a .as file, not the .mxml.
Unfortunately, nothing actually happens. The UI component the user clicks on remains distinctly un-bordered. I've tested event.target and am reasonably sure that it does actually point to the UI Component the user clicked on; I also used Alerts to make sure that the if statement was actually executing fully.
Does anyone have any idea why the border is not changing?
Use the setStyle method on the UIComponent. So instead of tmp.styleDeclaration = labelStyle, do something like tmp.setStyle("styleName", "mystyle"), or skip the CSSStyleDeclaration part and do tmp.setStyle("borderColor", "red") and so on directly on the UIComponent.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/core/UIComponent.html#setStyle

Check transparency

I have swf file(900x600) and main part of that file is transparense.
So I want by clicking in swf file know either user clicks on transaprent part of image or not...
I can get mouse coordinates by
event.localX
event.localY
So how to know in clicked part swf is transparent or not?
First of all, be sure, that you have some transparent sprite on background of your swf - otherwise you won't receive event.
Second, do not use pure local coordinates, they can contain local coordinates of another inner object, while you need them from root. For example, I've used stage's coordinates
If you receive mouse event, add mouse event listener to the root of that swf and write following:
var bmd:BitmapData = new BitmapData(1, 1, true, 0);
var pt:Point = new Point();
var m:Matrix = new Matrix();
m.translate(-e.stageX, -e.stageY);
bmd.draw(this, m);
var transparent:Boolean = !bmd.hitTest(pt, 0x00, pt);
trace('color: '+bmd.getPixel32(0,0).toString(16));
trace('is tranparent? ' + transparent);
bmd.dispose();
You can add an event listener on the stage for a mouse click, and check if e.target == stage.
Here is the code:
import flash.events.MouseEvent;
stage.addEventListener(MouseEvent.CLICK, hClick);
function hClick(e : MouseEvent) : void
{
trace(e.target == stage); // true if we click on the transparent zone, false if we hit a symbol on the stage.
}
Not the best or cleanest code but it should work.
This is not tested code I just whipped it up.
private function handleMouseDown(event:MouseEvent):void {
var pt:Point = new Point(event.localX, event.localY);
pt = event.target.globalToLocal(pt);
var tmp:int = int( (new uint( event.target.getPixel32(pt.x,pt.y) ).toString(16)).substr(0,2) );
if( tmp != 0 ){
trace( 'is transparent' )
}
}

How to dynamically load image for drag and drop?

I am implementing drag and drop from a DataGrid onto a List in a Flex 3 AIR application. I would like to have the drag image be a photo (jpg) referenced by a String field in the data grid item, named 'imagePath'. I'm having trouble getting the image to show up during dragging. I have triple checked that it is not because of an invalid path to the image. I have tried Image's source() and load() methods in every way I can think of. I am calling this method 'dragCurrentToList(event)' on a mouseDown event.
private function dragCurrentToList(event:MouseEvent):void
{
var current:Object = event.currentTarget.selectedItem;
var dragImg:Image = new Image();
dragImg.load(current.imagePath);
dragImg.width = 100;
dragImg.width = 100;
var dsource:DragSource = new DragSource();
dsource.addData(current, 'record');
DragManager.doDrag(event.currentTarget as DataGrid, dsource, event, dragImg);
}
This works perfectly if I set the image source to the following bindable variable but I don't want to hardcode the image name.
[Bindable]
[Embed(source='assets/icons/default.jpg')]
public var dragIcon:Class;
...
dragImg.source = dragIcon
...
In your dragCurrentToList method, why are you loading the image instead of just specifying the source attribute as the URL to the image?
http://livedocs.adobe.com/flex/3/langref/mx/controls/SWFLoader.html#source
private function dragCurrentToList(event:MouseEvent):void
{
var current:Object = event.currentTarget.selectedItem;
var dragImg:Image = new Image();
dragImg.source = current.imagePath;
dragImg.width = 100;
dragImg.width = 100;
var dsource:DragSource = new DragSource();
dsource.addData(current, 'record');
DragManager.doDrag(event.currentTarget as DataGrid, dsource, event, dragImg);
}
Also make sure you are responding to the dragStart event. ( http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#event:dragStart ). And I believe instead of accessing the DragManager class; you should simply modify the dragSource property of the dragStart event.

AS3: Textfield active state

I would like to create some text on the stage that does not respond to mouse activity. I've found that after adding a piece of text to a class I am using to draw, it covers up some of the mousing area of my class and disables the user from being able to roll over the other graphical elements...
Is there something like:
var t:TextField = new TextField();
t.text = "hello";
t.active = false; //i know that this is wrong, but I haven't found an equivalent method
Thanks,
jml
Use this
t.selectable = false;

How do I implement custom drag functionality in a Flex list control?

Flex has built in drag-n-drop for list controls, and allows you to override this. But they don't cover this in examples. The built-in functionality automatically drags the list-item, if you want to override this you find the handlers are being set up on the list itself.
What I specifically want to do, is my TileList shows small thumbnails of items I can drag onto a large Canvas. As I drag an item from the list, the drag proxy should be a different image.
So, I followed the technique suggested and it only works if I explicitly set the width/height on the proxy Image. Why?
It's not obvious until you've tried it =) I struggled with the same thing just a few weeks ago. This was my solution:
The list:
<List>
<mouseDown>onListMouseDown(event)</mouseDown>
</Tree>
The mouse down handler:
private function onMouseDown( event : MouseEvent ) : void {
var list : List = List(event.currentTarget);
// the data of the clicked row, change the name of the class to your own
var item : MyDataType = MyDataType(list.selectedItem);
var source : DragSource = new DragSource();
// MyAwsomeDragFormat is the key that you will retrieve the data by in the
// component that handles the drop
source.addData(item, "MyAwsomeDragFormat");
// this is the component that will be shown as the drag proxy image
var dragView : UIComponent = new Image();
// set the source of the image to a bigger version here
dragView.source = getABiggerImage(item);
// get hold of the renderer of the clicked row, to use as the drag initiator
var rowRenderer : UIComponent = UIComponent(list.indexToItemRenderer(list.selectedIndex));
DragManager.doDrag(
rowRenderer,
source,
event,
dragView
);
}
That will start the drag when the user clicks an item in the list. Notice that I don't set dragEnabled and the other drag-related properties on the list since I handle all that myself.
It can be useful to add this to the beginning of the event handler:
if ( event.target is ScrollThumb || event.target is Button ) {
return;
}
Just to short circuit if the user clicks somewhere in the scrollbar. It's not very elegant but it does the job.
I found a simpler answer here. That example extends a DataGrid control, but you can do the same with a List control. In my case, I use an image source instead of Class:
public class CustomDragList extends List {
[Bindable]
public var dragProxyImageSource:Object;
override protected function get dragImage():IUIComponent {
var image:Image = new Image();
image.width = 50;
image.height = 50;
image.source = dragProxyImageSource;
image.owner = this;
return image;
}
}
Then use that custom list like this:
<control:CustomDragList
allowMultipleSelection="true"
dragEnabled="true"
dragProxyImageSource="{someImageSource}"
dragStart="onDragStart(event)"/>
Where 'someImageSource' can be anything you'd normally use for an image source (embedded, linked, etc.)

Resources