rte-antares-rpackage / manipulateWidget

Add More Interactivity to htmlwidgets
128 stars 12 forks source link

Manipulating combineWidgets objects #48

Open dmurdoch opened 6 years ago

dmurdoch commented 6 years ago

The rgl package commonly has several widgets in a display that need to communicate with each other. Recently I've added crosstalk support, so this will become more common.

Older rgl code used magrittr pipes to glue together widgets in an htmltools::tagList. I've been experimenting with using combineWidgets instead of a tagList, and this looks very promising, because things are automatically resized to fit the available space. (Issue #46 was related to this.).

I've been working out a simple model. If w1, w2, and w3 are functions producing widgets, then w1() %>% w2() %>% w3() should end up something like combineWidgets(w1(), w2(), w3()), except that the 2nd and 3rd widgets will see what came before, so they can set up communications.

This requires a few hacks. I'm hoping that some of these things could be supported so I don't need the hacks.

  1. w1() %>% w2() is going to produce a combineWidgets object (call it c12) which will be passed into w3(). That would be okay, but it makes w3's job tricky to see the earlier ones, and it gets worse if there's a longer pipeline. Currently I'm grabbing the c12$widgets element and assuming it's basically a list containing unmodified copies of the original widgets, then constructing a new combined widget by putting those two widgets together with w3() in a single call using do.call(combineWidgets, c(c12$widgets, list(w3(), ncol = 1))). It would be nice if there were an appendWidget function to allow this in a less hacky way.

  2. Another problem is that not all widgets are the same size. Some (like rglwidget() or leaflet()) are pretty big, while others (like crosstalk::filter_slider) have fairly small heights. I'd like to have the sizing happen somewhat automatically (with the possibility for a user to override it), so I need to construct a rowsize vector for the combineWidgets call. I've been grabbing the old rowsize (e.g. c12$params$rowsize in the example above) and adding one more entry to it in the new combineWidgets call. Perhaps the hypothetical appendWidget function could do something similar.

  3. Finally, I'm having trouble guessing at what size to put into the new rowsize vector. Mostly those sizes are determined in Javascript once the target location of the widgets is known, but it would be really nice to have a function that could guess what would happen, based on a default target location, and the sizingPolicy of the widget.

FrancoisGuillem commented 6 years ago

For point 2, have you tried to put the filter_slider in the header parameter ?

combineWidgets(header = filter_slider(...), ...)

Parameters header, footer, leftCol and rightCol can be used to insert arbitrary html elements around the charts.

dmurdoch commented 6 years ago

I haven't tried that. It would probably display nicely, but I'm hoping for a fairly general solution, e.g. allowing the filter_slider to be w2() in my original example.

I guess appendWidget could work by setting the footer or rightCol elements in a new object, e.g. appendWidget(w3(), c12) could be done as combineWidget(c12, footer = w3()). This strikes me as ending up with a more complicated result, but maybe it would work better. I'll do some experiments.

dmurdoch commented 6 years ago

After a little bit of experimentation:

  1. I can use the filter_slider as a header or footer, but only with a new patch to the code in combineWidgets to get its dependencies. (The new patch is part of PR#47 now.) The spacing looks fine.

  2. I can't put an htmlwidget into the header or footer, only HTML tags or text. filter_slider works because it isn't a widget, but something like

    library(leaflet) library(manipulateWidget) combineWidgets(leaflet(), footer = leaflet())

produces this image in the RStudio viewer:

screen shot

i.e. the second widget is displayed as text.