eclipse-efx / efxclipse-rt

Eclipse Public License 1.0
28 stars 29 forks source link

EModelService::hostElement is broken; IPresentationEngine::createGui doesn't work for shared elements #374

Open eliasvasylenko opened 4 years ago

eliasvasylenko commented 4 years ago

Currently the way EModelService::hostElement is implemented can't work due to a problem with how UI elements in a window's shared elements list are handled by the renderer.

I have only tested this with an MPartStack, but I think it will be the same for anything based on my understanding of the problem.

Basically, when you try to render an element which is a shared element, the renderer tries to add it as a normal child of the window.

So for example if it is at index 6 in the shared elements list, and the window currently has fewer than 6 children, we get an IndexOutOfBounds exception when the renderer tries to add the element at the position of the 6th child.

If the index in the shared elements list is lower than the number of children of the window then the element is erroneously added to the list of the window's direct children and rendered in the root of the window.

Here is an example stack trace of the failing case:


org.eclipse.e4.core.di.InjectionException: java.lang.IndexOutOfBoundsException: Index: 6, Size: 1
    at org.eclipse.e4.core.internal.di.ConstructorRequestor.execute(ConstructorRequestor.java:55)
    at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:409)
    at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:333)
    at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:193)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.createFromBundle(ReflectionContributionFactory.java:108)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.doCreate(ReflectionContributionFactory.java:77)
    at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.create(ReflectionContributionFactory.java:59)
    at org.eclipse.fx.ui.workbench.renderers.base.BasePartRenderer.doProcessContent(BasePartRenderer.java:212)
    at org.eclipse.fx.ui.workbench.renderers.base.BasePartRenderer.doProcessContent(BasePartRenderer.java:1)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseRenderer.processContent(BaseRenderer.java:723)
    at org.eclipse.fx.ui.workbench.fx.PartRenderingEngine.createGui(PartRenderingEngine.java:244)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseRenderer.engineCreateWidget(BaseRenderer.java:649)
    at org.eclipse.fx.ui.workbench.renderers.base.BasePlaceholderRenderer.initWidget(BasePlaceholderRenderer.java:130)
    at org.eclipse.fx.ui.workbench.renderers.base.BasePlaceholderRenderer.initWidget(BasePlaceholderRenderer.java:1)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseRenderer.createWidget(BaseRenderer.java:193)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseRenderer.createWidget(BaseRenderer.java:1)
    at org.eclipse.fx.ui.workbench.fx.PartRenderingEngine.createWidget(PartRenderingEngine.java:277)
    at org.eclipse.fx.ui.workbench.fx.PartRenderingEngine.createGui(PartRenderingEngine.java:241)
    at org.eclipse.fx.ui.workbench.fx.PartRenderingEngine.createGui(PartRenderingEngine.java:323)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseRenderer.engineCreateWidget(BaseRenderer.java:629)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseStackRenderer$6.call(BaseStackRenderer.java:299)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseStackRenderer$6.call(BaseStackRenderer.java:1)
    at org.eclipse.fx.ui.workbench.renderers.fx.DefStackRenderer$StackItemImpl.handleSelection(DefStackRenderer.java:518)
    at org.eclipse.fx.ui.workbench.renderers.fx.DefStackRenderer$StackWidgetImpl$4.changed(DefStackRenderer.java:288)
    at org.eclipse.fx.ui.workbench.renderers.fx.DefStackRenderer$StackWidgetImpl$4.changed(DefStackRenderer.java:1)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:105)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:733)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:751)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:672)
    at com.sun.javafx.scene.control.behavior.TabPaneBehavior.selectTab(TabPaneBehavior.java:82)
    at javafx.scene.control.skin.TabPaneSkin$TabHeaderSkin$5.handle(TabPaneSkin.java:1450)
    at javafx.scene.control.skin.TabPaneSkin$TabHeaderSkin$5.handle(TabPaneSkin.java:1428)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3862)
    at javafx.scene.Scene.processMouseEvent(Scene.java:1849)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2590)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:411)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at com.sun.glass.ui.View.notifyMouse(View.java:942)
    at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
    at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: java.lang.IndexOutOfBoundsException: Index: 6, Size: 1
    at java.base/java.util.ArrayList.rangeCheckForAdd(ArrayList.java:787)
    at java.base/java.util.ArrayList.add(ArrayList.java:512)
    at com.sun.javafx.collections.ObservableListWrapper.doAdd(ObservableListWrapper.java:101)
    at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:151)
    at com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:320)
    at org.eclipse.fx.ui.workbench.renderers.fx.DefWindowRenderer$WWindowImpl.addChild(DefWindowRenderer.java:1237)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseWindowRenderer.childRendered(BaseWindowRenderer.java:499)
    at org.eclipse.fx.ui.workbench.renderers.base.BaseWindowRenderer.childRendered(BaseWindowRenderer.java:1)
    at org.eclipse.fx.ui.workbench.fx.PartRenderingEngine.createGui(PartRenderingEngine.java:254)
    at uk.co.saiman.maldi.stage.msapex.MaldiStagePart.<init>(MaldiStagePart.java:57)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
    at org.eclipse.e4.core.internal.di.ConstructorRequestor.execute(ConstructorRequestor.java:43)
    ... 82 more
eliasvasylenko commented 4 years ago

So I've come back to this the following morning, and it appears that something like the following is possible as a fairly straightforward work around:

    // move to front of shared elements list
    window.getSharedElements().remove(sharedElement);
    window.getSharedElements().add(0, sharedElement);

    // make not visible in window
    sharedElement.setVisible(false);

    // create UI and directly get node
    presentationEngine.createGui(sharedElement, null, context);
    var node = (Node) ((WWidget<?>) sharedElement.getWidget()).getWidget();

But it would be nice if hostElement worked directly!

It's also worth noting that nothing is done with the parentWidget parameter in createGui, hence the need to manually cast to WWidget to get the widget node.