Repository: bokeh/bokeh

Issue:
Adding User Defined Layouts
Currently extending `HBox` and `VBox` doesn't work because those classes do not appear in `main.coffee`.  Any reason why `widgets` as a whole are not registered in `main.coffee`?



Discussion:
They are in the separate `widgets.coffee` that generates `bokeh-widgets.js` AFAIK. ping @mattpap 

@brendancol, you register new widgets in `bokehjs/src/coffee/widget/main.coffee`, the same as you would do with core bokeh classes in `common/base`.

@mattpap So my issue was that to extend those widgets as User Defined Models, I think they need to be registered in `coffee/main.coffee`.  Seems like either the UDM logic needs to also check `widgets/main.coffee` OR widgets also need to be in `coffee/main.coffee`. As a start, I wanted to include `widgets/layout` in `coffee/main` so users could extend Layout.

User defined models don't need any manual registration.

In any case, @brendancol please show the code and then I can tell you more where the issue is.

@mattpap, yeah something like this:

```
from bokeh.properties import Dict, String
from bokeh.models.widgets import BaseBox

class StyleableBox(BaseBox):
    __implementation__ = '''
        _ = require "underscore"
        build_views = require "common/build_views"
        ContinuumView = require "common/continuum_view"
        BaseBox = require "widget/basebox"

        class StyleableBoxView extends ContinuumView
          tag: "div"
          attributes:
            class: "bk-vbox"

          initialize: (options) ->
            super(options)
            @views = {}
            @render()
            @listenTo(@model, 'change', @render)

          render: () ->
            children = @model.children()
            build_views(@views, children)
            for own key, val of @views
              val.$el.detach()
            @$el.empty()

            css_props = @mget("css_properties")
            if css_props? then @$el.css(css_props)

            for child in children
              @$el.append(@views[child.id].$el)
            return @

        class StyleableBox extends BaseBox.Model
          type: "StyleableBox"
          default_view: StyleableBoxView

          defaults: ->
            return _.extend {}, super(), {
              children: []
              css_properties: {}
            }

          children: () ->
            return @get('children')

        module.exports =
          Model: StyleableBox
          View: StyleableBoxView
        '''
    css_properties = Dict(String, String, default=None)
```

So this is a quick example of a CSS Styleable Box which extends `BaseBox`.  Since `widget/basebox` is not in `coffee/main.coffee` this will not work.

So, it's not going to work right now because wrong `require()` is used in the module generated for the custom model.


Resolving patch:
diff --git a/examples/glyphs/custom.py b/examples/glyphs/custom.py
--- a/examples/glyphs/custom.py
+++ b/examples/glyphs/custom.py
@@ -7,6 +7,7 @@
 from bokeh.models.callbacks import Callback
 from bokeh.models.glyphs import Circle
 from bokeh.models import Plot, DataRange1d, LinearAxis, ColumnDataSource, PanTool, WheelZoomTool, TapTool
+from bokeh.models.widgets import HBox
 from bokeh.resources import INLINE
 
 class Popup(Callback):
@@ -39,6 +40,29 @@ class Popup extends HasProperties
     which will be formatted with data from the data source.
     """)
 
+class MyHBox(HBox):
+
+    __implementation__ = """
+_ = require "underscore"
+build_views = require "common/build_views"
+ContinuumView = require "common/continuum_view"
+HBox = require "widget/hbox"
+
+class MyHBoxView extends HBox.View
+  render: () ->
+    super()
+    @$el.css({border: "5px solid black"})
+
+class MyHBox extends HBox.Model
+  type: "MyHBox"
+  default_view: MyHBoxView
+
+module.exports = {
+  Model: MyHBox
+  View: MyHBoxView
+}
+"""
+
 source = ColumnDataSource(
     data = dict(
         x = [1, 2, 3, 4, 4,   5, 5],
@@ -62,7 +86,7 @@ class Popup extends HasProperties
 plot.add_tools(PanTool(), WheelZoomTool(), tap)
 
 doc = Document()
-doc.add_root(plot)
+doc.add_root(MyHBox(children=[plot]))
 
 if __name__ == "__main__":
     filename = "custom.html"


Test patch: