Event control in flex as3 (order of events) - apache-flex

I have a flex application, the display of this application is build with many containers.
I have a FlexEvent.UPDATE_COMPLETE on each of the displayObjects.
What do I want to accomplish?
I want to handle the event only on the top level where it occurred, for instance, if I have a grid and the update occurred in a label somewhere inside, I want to handle the event only on the Grid.
is there a way to accomplish that?
just to emphasize, I don't have any knowledge of the display objects at compile time, only at runtime, the display is being built dynamically to I can't just write the code on the grid, I have to check somehow if the event occurred in a higher level.
I would love some help with this issue, even if it's not code but concept of how to handle this unique issue.
Thanks

Have you considered stopPropagation()/stopImmediatePropagation() on the event, once you handle that event.
Example:
Since your button is in canvas. Your event handler method in canvas would look like this,
function handleEvent(e:FlexEvent):void {
trace("In Canvas's handler");
//do your events...
e.stopPropagation(); //This stops from propagating e to its parent containers, which is an HBOX. The container can be anything at runtime, it doesnt affect the propagation.
}
Try the same example in your other containers too.
Some examples here,
http://livedocs.adobe.com/flex/3/html/help.html?content=events_08.html
http://livedocs.adobe.com/flex/3/langref/flash/events/Event.html#stopPropagation%28%29

Just check for the event.target and ignore if it is not what you're looking for. Or even better: listen for events on top level components and ignore if target and currentTarget doesn't match.
if(event.target != event.currentTarget)
return;
If you can't do this either, check the parents: if parent is your application or the container that holds the top level items, it is a top level item. Based on the structure of your component, it can be anything like
if(event.target.parent == this)
//or
if(event.target.parent == this.theContainer_thatHolds_topLevelItems)
//or
if(event.target.parent is Application)
//or
if(event.target.parent is CustomContainerClassName)

Related

Adobe Scene7 BasicZoomViewer: How to reset zoom

Question
I'm working with Adobe Scene7 BasicZoomViewer and I'm looking for a way to tell the ZoomViewer to reset the zoom so that the user is no longer zoomed in on an image but instead will show the default "zoom" level.
What I've found
The closest thing I found to what I need is this reset property ZoomView.reset which "Resets the viewport when the frame (image) changes. If set to 0 it preserves the current viewport with the best possible fit while preserving the aspect ratio of the newly set image".
This looks close to something I need but it states that it will reset or preserve the aspect ratio when a new image has been inserted but I am not inserting new images.
Demo from Adobe
There is a button on the image that the API inserts into the page that resets the zoom level. Adobe provides a demo page that shows what I'm working with. If you look at the bottom left, the right-most button is the reset button. When clicked, it has to make some kind of API call and I need to figure out which one it is.
Edit
I've been able to find a minified version of the BasicZoomViewer and I am currently attempting to make sense of the code.
There is an event listener placed on the "Zoom Reset Button" that just simply calls a reset() method on line 274 in the uglified version of the file. Currently, I am trying to make sense of the file and figure out how to access this method.
c.zoomResetButton.addEventListener("click", function () {
c.zoomView.zoomReset()
});
I will be answering my own question. If someone finds a better way please feel free to answer as well.
tldr;
Create a variable to hold the instance of your s7viewers.BasicZoomViewer() and inside of that you can access the event handlers and much more.
Example of calling the reset zoom handler
// instantiate the s7viewers class and save it in a variable
var s7BasicZoomViewer = new s7viewers.BasicZoomViewer({
containerId: 's7viewer',
params: {
asset: assetUrl,
serverurl: serverUrl
})
// example of how to call the "zoomReset()" method
s7BasicZoomViewer.zoomResetButton.component.events.click[0].handler()
Explanation
After digging through the minified code that was uglified I found an event listener on the s7zoomresetbutton DOM class name, or perhaps it's watching for the ID of that DOM element which is the same ID as the container div for your S7 BasicZoom Viewer plus some added text to make this ID unique. For example, if the container div is s7viewer then the reset zoom button will have an ID of s7viewer_zoomresetbutton.
Now, going through the code I found this event listener which let me know there must be some way to call the zoomReset() method.
c.zoomResetButton.addEventListener("click", function () {
c.zoomView.zoomReset()
});
In the code above, the value of c is this or in other words it's the instance of your S7 BasicViewerZoom and in my case I have multiple depending on how many images I need to zoom on.
When instantiating the s7viewers class you can then reference that instance later and access the event handlers on each button and other properties and methods.
From there it was just looking through the object returned from the instance and calling the handler for the reset button.

Why JavaFX properties have not getListeners() methods

I am using JavaFX properties since JavaFX 8.0 , they seem to miss something appropriate i think . Let's say i add a change listener to a JavaFX Property :
DoubleProperty doubleProp = new SimpleDoubleProperty(1);
doubleProp.addListener((observable,oldValue,newValue)->{ ...code here }));
and i want to add 3 more including and InvalidationListener
Why to create an instances of ChangeListenerJavaDoc or InvalidationListenerJavaDoc?
Adding lines of code like :
ChangeListener<? super Number> listener = (observable , oldValue , newValue) -> {
if (newValue.intValue() == 1)
//..... code
};
all over the place isn't clear.
Why methods like these doesn't exist:
doubleProp.getChangeListeners().clearAll();
or
doubleProp.getChangeListeners().remove(doubleProp.getChangeListeners().get(0));
or
doubleProp.remove(doubleProp.getChangeListeners().get(0));
or
doubleProp.getInvalidationListeners().get(0));
Does JavaFX 9.0 has methods like these ? Is what i want a bad design ? I need to know the above :).
Allowing access to a collection of all listeners on a property would be a bad design as it would break encapsulation for controls (or other objects) that used those properties. (Specifically, a control would likely want to expose a property while registering listeners on that property and keeping those listeners private. If the property exposed the listeners, this would become impossible.)
Just as one example, if StringProperty exposed a ObservableList<ChangeListener<String>> getListeners() method, then the API would make the following possible:
Label label = new Label("Some text");
label.textProperty().getListeners().clear();
This would completely break the label. The label's skin registers a listener with the label's textProperty(), that ensures the skin updates if the text is changed. If you remove this listener, which is the only possible result of executing the code above, then the label's skin would not know it had to resize or even display new text when you called label.setText(...).
If you need to register a listener that you might need to remove, you just need to retain a reference to it. The overhead in code is minimal, i.e. there is not much difference between
label.textProperty().addListener((obs, oldText, newText) -> { /* code */ });
and
ChangeListener<String> listener = (obs, oldText, newText) -> {/* code */} ;
label.textProperty().addListener(listener);
#James_D, your explanation about what can go wrong when a listener is carelessly removed is good. However, this is really a developer problem, not design. Indeed, it is bad design for any 'add/remove' pattern not to also include access to whatever has been added.
For example, it may be useful to remove ("suspend") all the listeners temporarily in order to perform a process that changes the value (indeed several times) where the value at the end of the process is the same as the start; when the process is finished the listeners are added back ("restored"). While the 'reference' solution works for listeners you know about when designing a control, it doesn't help for listeners added by a developer using your control - these can't be referenced because you don't know what they will be. The 'suspend/restore' technique deals with this, it's up to the developer to use it properly and in the right situation.
JavaFX's property enhancements (object properties, bindings etc) are a huge improvement over Swing, like moving from a covered wagon to a Bugatti. However, the omission of a 'getListeners()' feature is a drawback.

Flex: Lose Component Focus

I have a (hopefully) quick question. I've got some stepper boxes. Though really this could apply to any interactive component. I want the selected box to lose focus when when I click anywhere else ( stage included ). Is there an easy way to do this? I can't seem to find an effective way to make it lose focus.
In case anyone else finds their way here seeking a solution to this problem, here is the answer:
private function onGlobalMouseUp(event : MouseEvent) : void {
var fm:FocusManager = new FocusManager(stage);
//Since Flash Player can set focus on subcomponents as well as on components themselves,
//findFocusManagerComponent is used to find the component that either has focus or contains the subcomponent
//that has focus. If, for example, a TextField contained within a TextArea component has focus, findFocusManagerComponent
//will return the TextArea component, and not the TextField. This being the case, we can definitely determine
//whether the target object of the MouseUp event is a component (or is part of a component). If the target
//object is NOT a component (nor contained within one), then we clear all component focus.
if(fm.findFocusManagerComponent(event.target as InteractiveObject) is UIComponent){
//target of MouseUp is either a UIComponent, or is contained within a UIComponent, so do nothing.
}else{
//target of MouseUp is neither a UIComponent, nor is it contained within a UIComponent, so set component focus to null.
fm.setFocus(null);
}
}
So here's the solution I've come up with that works very well. I have a function called add() which has been assigned to applicationComplete. In that function I include:
this.skin.addEventListener( MouseEvent.MOUSE_UP, loseFocus );
Which calls:
private function loseFocus( e : MouseEvent ) : void
{
if ( e.eventPhase == EventPhase.AT_TARGET )
{
this.focusManager.deactivate();
}
}
Simple enough, and does what I was looking for. "Phase" filter is necessary to keep other components from registering the clicks.
As an important note: this.skin needs to be the event target. The stage is never exposed to the mouse in a Flex application.
Example Application Code
If someone has a better solution, please suggest one!
Perhaps you should check out FocusManager.hideFocus().
Maybe tie it into the focusOut event of your UIComponent.
Flex API

Adding dynamic DisplayObjects in Flex

I am adding DisplayObjects to a Canvas using
ContentContainer.addChild(c);
Where ContentContainer is my Canvas object and c is the DisplayObject I have created at run-time
Some of these DisplayObjects also have children of there own which are added at run-time prior to the DisplayObject being added to the Canvas
I then need to iterate over all the children of ContentContainer but the first time I do this, it says that ContentContainer has no children (ie, ContentContainer.numChildren = 0). If I do it again tho then it is fine and returns the correct number of children.
Is there something I need to call to get ContentContainer to recalculate how many children it has?
As Michael noted it would be helpful to see the code but you might want to look into the Event overview and About the creation policy sections in the docs - http://livedocs.adobe.com/flex/3/html/help.html?content=containers_intro_3.html
Specifically the childAdd event might be what you want to listen for before you iterate over it:
add Dispatched by a component after the component has been added to its container and the parent and the child are in a consistent state. This event is dispatched after the container has dispatched the childAdd event and all changes that need to be made as result of the addition have happened.
=Ryan
ryan#adobe.com
I did a similar task with a callLater in order to wait till after a dragdrop completed before recalculating some tasks. This might work for you.
public function myFunct():void{
//do your adding
callLater(
function():void{
//do your loop
}
)
}

Flex and fake Mxml initialisation without actually showing the component, (more insise)

I have a TitleWindow mxml class wich has several components, and listeners.
On its creationComplete and init state i add some listeners which listen for events on its gui.
This TitleWindow is only shown when the user click on a "button", i made TitleWindow a singleton with the following code:
public static function getInstance():MyWindow
{
if ( MyWindow.singleton )
{
return MyWindow.singleton;
}
else{
MyWindow.singleton = new MyWindow();
return MyWindow.singleton;
}
}
I needed a singleton because the user will call this window several times as much as he wants and i only need one.
The problem is the following on some special external events i need to "modify" some listeners (remove listeners and add new ones) on a button from MyWindow, before it was even shown once.
I still have MyWindow.getInstance() in memory when my application starts up.
However adding /removing listeners does not seem to have any effect if he actual rendering of the components did not happen, event when using the following code on app startup.
myWindow= MyWindow.getInstance();
myWindow.initialize();
Not suprisingly if i "show" ('render') the myWindow at least once then the events modifications on the myWindow instance works perfectly.
How can i fake the complete initialisation of this component without showing it on startup ?
Thanks !
Which sort of a container holds your button? If you are using a Multiple View Container you can try setting the creationPolicy to all. Single View Containers create all their children in one go and you shouldn't face this problem.
From Flex 3.0 docs I could retrieve this:
The default creation policy for all containers, except the Application container, is the policy of the parent container. The default policy for the Application container is auto.
This looks like the cause for all your troubles.
Update: I did not mention this earlier, since I thought this was to be expected :) Setting the creationPolicy to all makes your application load more slowly. So, read up on Ordered Creation -- this technique helps you to choose if the controls are displayed all in one go (which is the default behavior, after all of the controls have been created) or step-by-step, as and when they are created.

Resources