How can addChild choose the wrong insertion index? - apache-flex

So, In a Flex app I add a new GUI component by creating it and calling parent.addChild(). However in some cases, this causes an error in the bowels of Flex. Turns out, addChild actually does:
return addChildAt(child, numChildren);
In the cases where it breaks, somehow the numChildren is off by one. Leading to this error:
RangeError: Error #2006: The supplied
index is out of bounds. at
flash.display::DisplayObjectContainer/addChildAt()
at
mx.core::Container/addChildAt()
at
mx.core::Container/addChild()
. . at
flash.events::EventDispatcher/dispatchEventFunction()
at
flash.events::EventDispatcher/dispatchEvent()
at
mx.core::UIComponent/dispatchEvent()
at
mx.controls::SWFLoader::contentLoaderInfo_completeEventHandler()
Is this a bug in Flex or in how I am using it? It kind of looks like it could be a threading bug, but since Flex doesn't support threads that is a bit confusing.

I have noticed that it most often occurs when re-parenting a UIComponent that is already on the display list. Are you re-parenting in this situation?

Could it be possible that you are adding a child before the component has been full initialized? Maybe try adding a child after Event.COMPLETE has been broadcast?
It may not support threads but it's still asynchronous...

numChildren doesn't validly reference an existing index in the children array. Arrays in AS3 are indexed starting at 0. This means that the last item in your array as for index numChildren - 1, not numChildren.
try addChildAt(child, numChildren - 1);

OK, like a dope, I was trying to add a child to a container even though it was already there, hence the confusing "wrong insertion index" message.

cf. http://forums.devshed.com/flash-help-38/scroll-pane-scroll-bars-not-working-818174.html - what you need to do is add children to a display object, and then set the source of the scrollpane to the be the display object. Kinda like this...
Code:
var myDisplay : DisplayObject = new DisplayObject();
myDisplay.addChild(myChild1);
myDisplay.addChild(myChild2);
myDisplay.addChild(myChild3);
myDisplay.addChild(myChild4);
ScrollPane.source = myDisplay;
ScrollPane.update();

Related

Assigning anchors dynamically in QML

I am creating 2 elements dynamically in QML and need to assign them each an anchor binding. When I add the anchors.right and anchors.left syntax to the properties object of the createObject() function, the anchors are not working:
current_column_on_left = column_component.createObject(parent_element, {anchors.right: previous_column_on_left.left})
current_column_on_right = column_component.createObject(parent_element, {anchors.left: previous_column_on_left.right})
Qt Creator gives me the annotation/error "Expected token ','". I also tried making them strings and camel case, neither of which worked. However, when I add the property bindings separately using Qt.binding() function, everything works fine:
current_column_on_left.anchors.right = Qt.binding(function(){return previous_column_on_left.left})
current_column_on_right.anchors.left = Qt.binding(function(){return previous_column_on_right.right})
Why is the first code block not working? I would prefer not to have the two extra lines of code if I can avoid it.
The error is because javascript doesn't know what property groups are - only qml does.
This is also invalid logic. In createObject(parent_element, {anchors.left: previous_column_on_left.right}) property previous_column_on_left.right is resolved when qreating the javascript {}-style object, which leaves you bound to a fixed anchor which is never changed. But Qt.binding has a function, and resolves nothing when creating the object, which provides you with the correct binding.

JavaFX binding and null values

I was wondering how to bind values where the source of the bind could be null.
I have a property:
private ObjectProperty<Operation> operation = new SimpleObjectProperty<>(null);
I also have a text field:
#FXML
private Text txtCurrentOperation;
I would like to bind the textProperty of the field to the value of the operation object.
My first thought was to use FluentAPI with its when/then/otherwise construct, but it is eagerly evaluated so the solution:
Bindings.when(operation.isNotNull())
.then("null")
.otherwise(operation.get().getName()));
will throw a NPE, because the parameter of otherwise is evaluated no matter what the result of the when.
My next idea was to use lambda somehow:
txtCurrentOperation.textProperty().bind(() ->
new SimpleStringProperty(
operation.isNotNull().get() ? "Null" : operation.get().getName()
));
But the bind has no lambda enabled solution. (Later I realized that it couldn't have, becasue the real work goes backward: the change of the binded object (operation) will trigger the update of the binder (the field text property).)
Some articles I found suggested to use an "extremal" value for the property instead of null. But Operation is a complex and heavy weight component so it is not trivial to construct an artifical instance to represent null. Even more, this seems to me boilercode, something the binding mechanism is designed to help eliminating.
My next try was to logically swap the binding direction and add listener to the operation property and let it update the field programatically. It works and rather simple as long as the need of update only depends the operation object instances:
operation.addListener((e) -> {
txtCurrentOperation.setText(operation.isNull().get() ?
"Null" : operation.get().getName());
});
operation.set(oper);
It is relatively simple, but doesn't work: it throws "A bound value cannot be set." exception and I don't see why is the text property of the control regarded as bound.
I ran out of ideas. After much searching, I still cannot solve the simple problem to update a text field differently based on whether the source is null or not.
This seems so simple and everyday problem, that I am sure I missed the solution.
If a 3rd party library is an option, check out EasyBind. Try something like this:
EasyBind.select(operation)
.selectObject(Operation::nameProperty)
.orElse("null");
There's also a JavaFX JIRA issue for the type of functionality provided by EasyBind. If you don't want to use a 3rd party library, try Bindings.select:
Bindings.when(operation.isNotNull())
.then("null")
.otherwise(Bindings.select(operation, "name"));
Be aware the null checking in Bindings.select isn't super efficient. There's a JIRA issue for it.
Just in case if somebody using not Java itself but Kotlin.
It is a good idea to use wonderful tornadofx library.
There you can just use operation.select{it.name}. Although, this feature seems not to be documented yet, so it took some time to discover it.

Qt QGraphicsScene::drawItems subsitute?

For QGraphicsScene::drawItems the reference says:
Reimplement this function to provide custom painting of all items for the scene; gaining complete control over how each item is drawn.
But this function is marked as obsolete.
Is there any new equivalent method?
QGraphicsView::paintEvent() now calls
d->scene->d_func()->drawItems()
which means the method is part of class QGraphicsScenePrivate which you cannot override afaik.
If you need to change the way your items are drawn, first try to think of another way (i.e. a solution which does not require stepping into the drawItems() method). If you can't find any solution like that, your only chance is reactivating the pre-4.6-behaviour by setting
QGraphicsView::setOptimizationFlag( QGraphicsView::IndirectPainting )

Flex: select tree node right after the dataProvider is been assigned / updated / replace

i have a Flex tree control and im trying to select a tree node 3 levels down right after the dataProvider is assigned with a collection object like the following.
basically treeItem1, treeItem2, treeItem3 are the nodes in the tree and treeitem3 is a child of treeItem2 which is a child of treeItem1. Assume these treeItem(1,2,3) are referenced correctly from the collection items.
my problem is that if i wait for the whole component to load completely then select the nodes, it open/select/scrolltoIndex correctly. However, if i were to select the node right after the dataProvider is assigned, then it doesn't even open or select (basically the this.treeService.selectedItem is always null).
can anyone point out what i did wrong? is there anything needs to happen after the dataProvider is assigned?
thanks
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
I have used the updateComplete event to know when a component (such as a DataGroup or List) has completed rendering after performing a simple task (such as updating the dataProvider reference). Of course, you have to be careful and remove listening to updateComplete because it can run a lot, unless you have a need for it to run.
Something like:
//...some function...
this.treeService.addEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
//...rest of some function...
private function onTreeUpdateComplete(event:FlexEvent):void {
this.treeService.removeEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
}
I'm not positive your experiencing the same issue but I seem to have the same type of problem with using the advanced data grid, it appears in these cases where the dataprovider is acceptable as multiple types, the components do some extra work in the background to wrap things up into something Hierarchical (HierarchicalData or HierarchicalCollectionView) and in doing so the dataprovider setter call is not synchronous (so it will return before actually having assigned the internal property storing the dataprovider). I've used callLater in this case with moderate success, callLater is generally a bad practice but basically adds a function to a list of functions to call once background processing is done, so this is assuming that something in the dataprovider setter called UIComponent.suspendBackgroundProcessing() and that it will subsequently call UIComponent.resumeBackgroundProcessing() and then it will execute the list of functions added by using callLater. Alternatively you could use setTimeout(someFunction,1000).
These are both "hacks" the real solution is to dig into the framework code and see what it's really doing when you tell it to set the dataprovider. Wherever you see that it actually has set the dataprovider you could extend that class and dispatch an event that you could listen for to run the function to do the selections after this point.
If anyone has a better solution please by all means correct me (I would love to have a better answer than this)

FLEX: how can I get the width of my linkButton object?

How can I get the width of my LinkButton object ?
myLinkButton = new LinkButton();
myLinkButton.label = "blabla";
myLinkButton.setStyle("fontSize", 24);
myContainer.addChild(myLinkButton);
trace (myContainer.width); //this doesn't work because I haven't directly set the attribute
thanks
First, what does that trace() show? Is it null or undefined or NaN or simply a wrong value?
Then, there are several ways I can think of how you could get around this problem:
Try using getBounds() or getRect(). These methods return a Rectangle object working as the DisplayObject's bounding box (including all coordinates and dimensions). Sometimes Flex behaves a bit weird and returns wrong/off results for the coordinates or dimensions of objects.
Try experimenting with validateSize() and/or measuredWidth. Perhaps you're trying to access the width property too soon so that Flex cannot do the measuring/layouting in time.
Similar idea: what happens if you use myContainer.callLater(trace, [myContainer.width]); (assuming your myContainer inherits from UIComponent)? If you do get a valid result using callLater() but not when accessing width directly then Flex just hasn't had a chance to layout and update the container.
You could also try using this method, which creates a Bitmap from the object and returns the Bitmap's height/width. This is especially useful if you have components with visible = false in your container, because Flex doesn't handle invisible components well in that regard.
Finally, you could try accessing $width in the mx_internal namespace and check that property's value. However, using mx_internal is sort of a very ugly hack because these properties and methods weren't meant for external use and are subject to change any time (so your component could stop working when a new version is released) - so use with caution.

Resources