gwtd3 / gwt-d3

A GWT wrapper library around the d3.js library
Other
131 stars 53 forks source link

Trouble with selecting foreignObjects, workarounds? bug? #90

Closed ThomasWrobel closed 9 years ago

ThomasWrobel commented 10 years ago

a) Great project, really usefull to me as I am working on a open source semantic database that could do with some nice visuals.

b) I wish to use GWT widgets as nodes in my flowchart. I thought I could do this by using foreignObjects in the svg. However, the selectall function cant find them once added;

updateSelection = svg.selectAll("foreignObject").data( showPoints ? points : new ArrayList());

This will always return a empty selection. I think this is a bug in webkit , not gwt-d3, in regards to camelcase selectors;

https://bugs.webkit.org/show_bug.cgi?id=46800 Not sure though.

The workaround I have seen elsewhere is to use; svg.selectAll(document.getElementsByTagName("foreignObject"))

But gwt-d3 doesnt yet support that method of selection.

What does work, however, is ;

D3.selectAll(RootPanel.get().getElement().getElementsByTagName("foreignObject"));

However, that has the wrong parent. Amending new elements using ____.enter().append("foreignObject") Wont work, as that would add it to the docucments root.

So my question is, is there a way to use widgets in gwt-d3 that I have overlooked? Or perhaps a way to select using the RootPanel, but then somehow force the selection to think its parent should be svg? It seems, if it isnt possible yet, purhaps gwt-d3 should support in the future? Mixing gwt widgets with d3 layout would be very powerfull.

Sorry if this query is in the wrong place, as it covered a few issues I didnt know where to put it.

anthonime commented 10 years ago

Hi,

a) thank you

b) I cannot help you very much but here are some of my thoughts:

ThomasWrobel commented 10 years ago

I first use a absolute panel (node_panel)

panelcontainer = D3.select(node_panel.getElement());

Then I select all things with the class "TestNode"

updateSelection = panelcontainer.selectAll("._TestNode_").data( showPoints ? points : new ArrayList<VisualNode>());

Then I use left and top styles to set the position of existing widgets:

updateSelection.style("left", new DatumFunction<String>() { @Override public String apply(Element context, Value d, int index) { return d.<VisualNode> as().x+"px"; } })

Critically, and hack-ish, I then use this to add new widgets on enter;

//Not really a select statement, but rather a way to manual add a new element to the page without effecting anything else.
updateSelection.enter().select(new DatumFunction<Element>() {
                                @Override
                                public Element apply(Element context, Value d, int index) {

                                                                    VisualNode current = d.<VisualNode> as();

                                node_panel.add(current.associatedContainer,(int)current.x,(int)current.y);
                                current.associatedContainer.setStylePrimaryName("_TestNode_");

                                return null;
                                }
                            })

By using gwts node_panel.add() it ensures all the handlers are set up correctly. Setting the style ensures D3 can find the widget again.

And of course;

    `updateSelection.exit().remove();`

This all seems to work. "associatedContainer" is just a gwt widget that is stored with the co-ordinate values. Adding and removing items from the VisualNode list adds and removes widgets from the page. Changing co-ordinates, changes the positon without removing/re-adding. I have only tested this very vaguely though - whether it stands up when used in a big flowchart (like I plan) I dont know.