I am currently working with a drawing tool for a mapping API, and every time I double-click the mouse a map service will perform a measurement and display the length of the line that I am drawing.
I want to mimic this double-click manually by dispatching a MouseEvent.DOUBLE_CLICK, but the map service is not listening to this default Flex event. I suspect that the API has created a custom MapMouseEvent or something like it that is being dispatched when a user double-clicks the mouse.
Is there a way to determine which event is being dispatched when I double-click the mouse?
The API isn't necessarily dispatching its own event. Even if it is, it has to listen for MouseEvents first. One could detect double-clicks using MouseEvent.DOUBLE_CLICK, or by listening for successive MouseEvent.CLICK or MouseEvent.MOUSE_DOWN events. There's no way (just within Flash) to detect a double click without listening for one or more of those events somewhere.
It's also possible that the Map Service IS listening for the DOUBLE_CLICK event, but you're not dispatching the MouseEvent from the right object. For example, if the map service does this:
mapRoot.mapChild.addEventListener(MouseEvent.DOUBLE_CLICK, performMeasurement);
and you do this:
mapRoot.dispatchEvent(new MouseEvent(MouseEvent.DOUBLE_CLICK));
nothing is going to happen.
There are other possibilities as well:
When you actually perform a double-click with the mouse, you cause several events to be dispatched:
MOUSE_DOWN, MOUSE_UP, CLICK, MOUSE_DOWN, MOUSE_UP, CLICK, DOUBLE_CLICK
It's possible that the map service listens for MOUSE_DOWN, and adds the DOUBLE_CLICK listener inside the MOUSE_DOWN handler, like this:
mapRoot.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
private function _mouseDownHandler(event:MouseEvent):void
{
var targ:InteractiveObject = event.target as InteractiveObject;
targ.addEventListener(MouseEvent.DOUBLE_CLICK, _doubleClickHandler);
}
private function _doubleClickHandler(event:MouseEvent):void
{
var targ:InteractiveObject = event.target as InteractiveObject;
targ.removeEventListener(MouseEVent.DOUBLE_CLICK, doubleClickHandler);
performMeasurement();
}
So you may actually need to dispatch a combination of events to trigger the handler.
You can try the following:
Use DisplayObjectContainer.getObjectsUnderPoint() to get an Array of all the objects that could be responding to the click. Then from each of the objects in that array, try dispatching various sequences of MouseEvents till you come up with the right event(s) from the right object.
Take note though, that if
areInaccessibleObjectsUnderPoint() returns true for the point you test at, then there are some objects you won't be able to get access to, and if those are the ones the map service is listening to, then you're out of luck.
public function cbMouseEvents( e:MouseEvent ):void{
trace( e.type )
}
Related
When cloning a button at runtime using Instantiate(), which contained listeners on it's onClick event, the listeners are not present in the clone.
The behaviour can be tested by having a Canvas with a button and this script attached:
void Start () {
var button = transform.GetChild (0);
button.GetComponent<Button> ().onClick.AddListener (new UnityAction(() => Debug.Log("Event triggered!")));
var button2 = Instantiate (button);
button2.SetParent (transform);
}
The cloned button will not print anything to the console when clicked.
Is there a way to clone a GameObject so that it retains event listeners?
Runtime listeners are not persistent and then not serialized. As a result they are not passed on when you clone the button.
Either you'd have to add the method to a script and attach the script to your prefab for it to be serialized along or assign it by code like you do for the first one.
Instantiation. When you call Instantiate() on either a prefab, or a
gameobject that lives in the scene, or on anything else for that
matter (everything that derives from UnityEngine.Object can be
serialized), we serialize the object, then create a new object, and
then we “deserialize” the data onto the new object. (We then run the
same serialization code again in a different variant, where we use it
to report which other UnityEngine.Object’s are being referenced. We
then check for all referenced UnityEngine.Object’s if they are part
of the data being Instantiated(). If the reference is pointing to
something “external” (like a texture) we keep that reference as it
is, if it is pointing to something “internal” (like a child
gameobject), we patch the reference to the corresponding copy).
http://blogs.unity3d.com/2014/06/24/serialization-in-unity/
As for AddListener
This function adds a "non persistent" delegate, which means it will not show up in the inspector, and will be forgotten when you exit play mode in the editor. These differ from "persistent" listeners which you can add during edit-time in the inspector, and which persist between edit and play mode.
https://docs.unity3d.com/ScriptReference/Events.UnityEvent.AddListener.html
For those reasons, I assume the non-persistent is not serialized and then not passed to the cloned object.
I try to make have an EventListener in ItemRenderer but its not working. How to listen to an event inside an ItemRenderer?
----In MainHomeView.mxml----
<fx:Metadata>
[Event(name="myEvent", type="flash.events.Event")]
</fx:Metadata>
protected function btnAdd_clickHandler(event:MouseEvent):void {
var eventObject:Event = new Event("myEvent", true, true);
dispatchEvent(eventObject);
}
----In UserRenderer.mxml (ItemRenderer)---
protected function init(event:FlexEvent):void{ //run in CreationComplete
addEventListener("myEvent", onHandleEvent);
}
protected function onHandleEvent():void {
trace("Event received");
}
This might be a little late, but according to your sample, your ItemRenderer is listening to itself - which is why you'll never get the event. And as for bubbling, remember in both the target and capture phase, things start from the stage and work its way up to dispatching target, then back down again. Since your renderer is a child of the list, it will never receive this event.
If you want the IR to get the event from the list, you'll need a reference to the list - usually owner. In which case, this is owner.addEventListener(). You can also look at the ListData that is assigned to each renderer, and in that composite object is a reference to the list.
Straight from the documentation:
The event target serves as the focal point for how events flow through the display list hierarchy. When an event such as a mouse click or a keypress occurs, Flash Player or the AIR application dispatches an event object into the event flow from the root of the display list. The event object then makes its way through the display list until it reaches the event target, at which point it begins its return trip through the display list. This round-trip journey to the event target is conceptually divided into three phases: the capture phase comprises the journey from the root to the last node before the event target's node, the target phase comprises only the event target node, and the bubbling phase comprises any subsequent nodes encountered on the return trip to the root of the display list
You'll need to be a little careful with this, since the ItemRenderer is part of a ClassFactory creation method each renderer will be assigned this listener - this may or may not be what you want.
I have google and found the solution in gskinner. But by using this method, each item in the ItemRenderer will received an event, so if you have 100 items (in your ItemRenderer) you will rec'd 100 events.
I've just the documentation on the Qt event system and the QEvent class. I'm interested in the behavior of the QObject::event() method. The documentation states:
This virtual function receives events to an object and should return true if the event e was recognized and processed.
What is the expected behavior when false is returned from the event() method? What else is attempted in order to handle the event? Is the event automatically forwarded to the parent object?
Note: I know the source is available, and I do have a copy. I'm ideally looking for some piece of documentation addressing this behavior.
I believe the best practice is to explicitly forward the events to the base-class event method if you do not wish to filter that event type (e.g. return QObject::event(event);) since the event function delegates events to specific handlers (e.g. QWidget::keyPressEvent).
QCoreApplication::notify propogates events based on the return value. On true, it considers the event as consumed and stops. Otherwise, the event is passed to the object's parent. For more information, see Events and Filters and Another Look at Events.
Some Events can be propagated.Event will be propagated to it's parent and it's parent recursively until it is processed. Take a look at this:https://doc.qt.io/archives/qq/qq11-events.html
I need to call FileReference.save() after a web service call has completed, but this method has a restriction: "In Flash Player, you can only call this method successfully in response to a user event (for example, in an event handler for a mouse click or keypress event). Otherwise, calling this method results in Flash Player throwing an Error exception." (from the documentation here)
This restriction is a bit vague. Does it mean that I can only call the FileReference.save() method from within an event handler function that is registered as a listener for certain types of user events? If so then exactly which user events are valid? (Perhaps there's an event that will never be dispatched by user interaction with my application and I could register an event handler function for that event type and make the save() call from within that function?)
My difficulty is that I can't safely call the FileReference.save() method until my web service returns with the data that will be used as the argument of the FileReference.save() method call, so the event that triggers the FileReference.save() call is actually a ResultEvent rather than a user event, and I'm leery of dispatching a new (faux) user event type in order to be able to trigger the FileReference.save() call unless it's definitely a user event that would never be dispatched as a result of actual user interaction with my application.
In a nutshell what I'm doing now is this: I have a function that is registered as a handler for a button click. In this function I make my web service call to fetch data from the server. I also have a result handler function which gets invoked when the web service call completes, and it's in here that I want to call the FileReference.save() method since it's at this point that I know that the data is ready to be saved to a file. But the aforementioned restriction is blocking me from doing this -- I get an error:
Error #2176: Certain actions, such as those that display a pop-up window,
may only be invoked upon user interaction, for example by a mouse click
or button press.
I've tried many things to get around this such as creating a second mouse click event handler function with the FileReference.save() call within and calling it after a timeout interval (to give the web service time to complete), but I keep running into the same error -- maybe that approach doesn't work since the second function isn't registered as an event listener for the event type used as its argument.
I'm new to Flex development so perhaps I'm just not thinking about this in the right way. If anyone can suggest another approach I'd really appreciate it. Thanks in advance for your comments or suggestions.
--James
Adobe does this as a sort of security measure to ensure users are the ones messing with files rather than potentially harmful code. My understanding is that they enforce this by only allowing handlers of (click?) events that originate from UI components to execute the FileReference methods, so generating your own events programmatically will not work, although I have not tried to verify this. Unfortunately the best resolution I've found is to re-work the UI a bit to conform to this constraint. In your particular situation, you could make this a two click process with a button that says something like "Prepare Download", which changes to "Download File" after the web service is complete. This is less than ideal from a user perspective, but I don't think there's much else that can be done unless you can somehow complete your web service call prior to displaying the button that triggers the FileReference.save() call.
After struggling for that for well, a couple hours I found a workaround: you can use both mouseDown AND mouseUp events instead of just click.
For instance:
s:Button
mouseDown="prepare_PDF()"
mouseUp="save_PDF()"
Works fine for me!
Happy coding!
--Thomas
As a workaround I used the ExternalInterface class. I created a javascript function with this code
function downloadFile (url) {
window.open(url);
}
An in AS3 I call
var url = 'www.example.com/downloadfile.php?file_id=xxx';
ExternalInterface.call('downloadAttachmentFile', url);
So with that I transfer the file handling to JS/HTML.
This is a comment on Thomas' answer (I don't have enough XP to comment yet): The mousedown and mouseup workaround works nicely. Just a note that if you make any changes in prepare_PDF() that need 'undoing' in save_PDF(), then its a good idea to call that code on the mouseout event as well, since there might be a case that the user mousedown's on the button, but then moves the mouse away from the button.
This was particularly relevant for my case, in which we increase the size of a watermark on an image when the user clicks the download button (that triggers the .save() call). I reduce the size of the watermark down to normal on the mousedown and mouseout events.
I had this same issue, I chose to use flash.net methods. Call flash.net.navigateToURL(url); from an actionscript or navigateToURL(url); from mxml.
What i do to solve this is to show an alert message with an anonymous function so i don't have to create a button.
Alert.show("Do you wish to download the file?", "Confirm", Alert.OK | Alert.CANCEL, this, function (eventObj:CloseEvent):void {
if (eventObj.detail == Alert.OK) {
fileReference.save(zipOut.byteArray, dateFormater_titulo.format(new Date ()) + ".zip");
}//if
}/*function*/, null, Alert.OK);
So I'm trying to build a tool that will allow me and other users to all open the same .swf, and then I as the Admin user am able to interact with mine while they all see my mouse movements and button clicks etc on theirs.
I'm using BlazeDS to manage this and I'm getting data sent back and forth etc - no difficulties there. The issue I'm running into is this:
In an "Admin" instance, I click a button. I capture that X and Y, then tell Blaze to tell my clients to dispatch a Click event at that X and Y. On my client side, I get that data and dispatch a Click event at that X and Y - but the click is actually caught at the stage level. The click on my client side takes place UNDER all of my buttons and other content - so the whole thing fails.
Does this make sense? Is there a way to tell it to start the click event at the top?
If you are unable to architect the loaded swf's to use a better architecture you could try something a little more hackish to get buttons working.
Have a look at the methods getObjectsUnderPoint and areInaccessibleObjectsUnderPoint of the DisplayObjectContainer. Combined with hasEventListener you should be able to emulate what you want.
Here is some untested pseudo-code:
function detectClick(pt:Point):void
{
var objsUnderPoint:Array = containerStage.getObjectsUnderPoint(pt);
var clickable:Array = [];
for each(dispObj:DisplayObject in objsUnderPoint)
{
if(dispObj.hasEventListener(MouseEvent.CLICK))
{
clickable.push(dispObj);
}
}
if(clickable.length)
{
// sort on depth here
// that might be tricky since you'll be looking at grandchildren
// and not just children but it is doable.
var topMostClickable:DisplayObject = ???
topMostClickable.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));
}
}
areInaccessibleObjectsUnderPoint is important if you think their might be security restrictions (e.g. cross-domain issues) so you can debug if things go wrong.
Also note that you may want to (or need to) fill in more details of the MouseEvent (like the proper target, localX, localyY etc.)
Sounds like you need to set focus to the top most component of the x and y positions before dispatching the click event.
That said, I wonder what the use case is for something like this; as opposed to using a screen sharing tool such as Connect.
This seems like a poor way to implement what you are trying to do. If you "click" in the admin tool you are probably actually triggering some event. Why not trigger that event instead of sending the mouse click?
I'd just keep a map of actions and then when something happens in the Admin interface send the key to the action.
e.g.
In the admin:
myButton.addEventListener(MouseEvent.CLICK, handleClickButton);
function handleClickButton(event:MouseEvent):void
{
doSomeAction();
sendTriggerToClient(MyActions.SOME_TRIGGER);
}
In the client:
var actionMap:Object = {};
actionMap[MyActions.SOME_TRIGGER] = doSomeAction;
function receiveTriggerFromAdmin(trigger:String):void
{
var responseFunc:Function = actionMap[trigger];
responseFunc();
}
I hope that pseudo-code makes sense. Just abstract what happens as a result of a click into a separate function (doSomeAction) and then have the admin send a message before calling that function. In the client wait for any trigger that comes through and map it to the same function.