Repository: bokeh/bokeh

Issue:
Add support for spans to grid layout
This is a follow up on issue #8084. Technically speaking this is a regression since the old layout. This is needed to restore functionality of `examples/app/spectrogram`, etc.


Discussion:
Just out of curiousity, how big a lift do you think this is?
I'm not sure, because I need to figure out how to measure track sizes (under all supported sizing policies), implement new internal data structures that will allow spanning layouts across grid cells, adjust axis alignment algorithm, etc. Definitively it's a bigger chunk of work.
Is there anything simple but dumb (and unobtrusive for later) that could be done to cover the 80% case? i.e. could we set min border for top and bottom to be the max value of every plot in a row? And similarly min border left of every plot in a column? I.e. punt on nested cases for now until you can work out a general solution, but do enough to get basic and common cases looking more like they used to?

It will be hard to argue this is not a breaking change. The other alternative is to push 1.1 back which I am fine with personally, but I am not sure how that would impact @philippjfr @jlstevens @jbednar Perhaps they could use 1.1 rc builds in client work for now?
>It will be hard to argue this is not a breaking change. The other alternative is to push 1.1 back which I am fine with personally, but I am not sure how that would impact @philippjfr @jlstevens @jbednar Perhaps they could use 1.1 rc builds in client work for now?

We are at least a 1.5 weeks away from being ready to push out new releases for our stuff so I'd be happy to postpone for that long. Does that seem like sufficient time? A new dev/rc release would be sufficient for the time being.
>adjust axis alignment algorithm

Will that intersect at all with the axis alignment issue for fixed inner dimensions or is that a whole other chunk of work?
> Will that intersect at all with the axis alignment issue for fixed inner dimensions (...)

To some extent yes, depending on the layout. I will keep that issue in mind fixing this one.

Resolving patch:
diff --git a/bokeh/core/enums.py b/bokeh/core/enums.py
--- a/bokeh/core/enums.py
+++ b/bokeh/core/enums.py
@@ -211,6 +211,9 @@ def enumeration(*values, **kwargs):
 
     return type(str("Enumeration"), (Enumeration,), attrs)()
 
+#: Alignment (vertical or horizontal) of a child item
+Align = enumeration("start", "center", "end")
+
 #: Specify an anchor position on a box/frame
 Anchor = enumeration(
     "top_left",    "top_center",    "top_right",
@@ -352,9 +355,6 @@ def enumeration(*values, **kwargs):
 #: Specify how a format string for a tooltip field should be interpreted
 TooltipFieldFormatter = enumeration("numeral", "datetime", "printf")
 
-#: Grid track (row/column) alignment (vertical/horizontal) of child items
-TrackAlign = enumeration("start", "center", "end")
-
 #: Grid track (row/column) sizing policies
 TrackPolicy = enumeration("auto", "min", "max", "flex", "fixed")
 
diff --git a/bokeh/models/layouts.py b/bokeh/models/layouts.py
--- a/bokeh/models/layouts.py
+++ b/bokeh/models/layouts.py
@@ -25,7 +25,7 @@
 # External imports
 
 # Bokeh imports
-from ..core.enums import SizingMode, SizingPolicy, Location, TrackAlign
+from ..core.enums import Align, SizingMode, SizingPolicy, Location
 from ..core.has_props import abstract
 from ..core.properties import (Bool, Auto, Enum, Int, NonNegativeInt, Float,
     Instance, List, Seq, Tuple, Dict, String, Either, Struct, Color)
@@ -220,6 +220,14 @@ class LayoutDOM(Model):
 
     """)
 
+    align = Either(Enum(Align), Tuple(Enum(Align), Enum(Align)), default="start", help="""
+    The alignment point within the parent container.
+
+    This property is useful only if this component is a child element of a layout
+    (e.g. a grid). Self alignment can be overridden by the parent container (e.g.
+    grid track align).
+    """)
+
     background = Color(default=None, help="""
     Background color of the component.
     """)
@@ -279,23 +287,27 @@ class Spacer(LayoutDOM):
 
 QuickTrackSizing = Either(Enum("auto", "min", "max"), Int)
 
+TrackAlign = Either(Auto, Enum(Align))
+
 RowSizing = Either(
     QuickTrackSizing,
-    Struct(policy=Enum("auto", "min", "max"), align=Enum(TrackAlign)),
-    Struct(policy=Enum("fixed"), height=Int, align=Enum(TrackAlign)),
-    Struct(policy=Enum("flex"), factor=Float, align=Enum(TrackAlign)))
+    Struct(policy=Enum("auto", "min", "max"), align=TrackAlign),
+    Struct(policy=Enum("fixed"), height=Int, align=TrackAlign),
+    Struct(policy=Enum("flex"), factor=Float, align=TrackAlign))
 
 ColSizing = Either(
     QuickTrackSizing,
-    Struct(policy=Enum("auto", "min", "max"), align=Enum(TrackAlign)),
-    Struct(policy=Enum("fixed"), width=Int, align=Enum(TrackAlign)),
-    Struct(policy=Enum("flex"), factor=Float, align=Enum(TrackAlign)))
+    Struct(policy=Enum("auto", "min", "max"), align=TrackAlign),
+    Struct(policy=Enum("fixed"), width=Int, align=TrackAlign),
+    Struct(policy=Enum("flex"), factor=Float, align=TrackAlign))
 
 IntOrString = Either(Int, String) # XXX: work around issue #8166
 
 class GridBox(LayoutDOM):
 
-    children = List(Tuple(Instance(LayoutDOM), Int, Int), default=[], help="""
+    children = List(Either(
+        Tuple(Instance(LayoutDOM), Int, Int),
+        Tuple(Instance(LayoutDOM), Int, Int, Int, Int)), default=[], help="""
     A list of children with their associated position in the grid (row, column).
     """)
 
diff --git a/examples/app/spectrogram/main.py b/examples/app/spectrogram/main.py
--- a/examples/app/spectrogram/main.py
+++ b/examples/app/spectrogram/main.py
@@ -4,8 +4,8 @@
 import numpy as np
 
 from bokeh.io import curdoc
-from bokeh.layouts import row, column
-from bokeh.models import ColumnDataSource, Slider, Div
+from bokeh.layouts import row #, column
+from bokeh.models import ColumnDataSource, Slider, Div, GridBox
 from bokeh.plotting import figure
 
 import audio
@@ -27,7 +27,7 @@
 
 waterfall_renderer = WaterfallRenderer(palette=PALETTE, num_grams=NUM_GRAMS,
                                        gram_length=GRAM_LENGTH, tile_width=TILE_WIDTH)
-waterfall_plot = figure(plot_width=990, plot_height=300, min_border_left=80,
+waterfall_plot = figure(plot_width=1000, plot_height=300,
                         x_range=[0, NUM_GRAMS], y_range=[0, MAX_FREQ_KHZ], **PLOTARGS)
 waterfall_plot.grid.grid_line_color = None
 waterfall_plot.background_fill_color = "#024768"
@@ -102,7 +102,13 @@ def update():
 
 controls = row(gain, freq)
 
-plots = column(waterfall_plot, row(column(signal_plot, spectrum_plot), eq))
+#plots = column(waterfall_plot, row(column(signal_plot, spectrum_plot), eq))
+plots = GridBox(children=[
+    (waterfall_plot, 0, 0, 1, 2),
+    (signal_plot,    1, 0, 1, 1),
+    (spectrum_plot,  2, 0, 1, 1),
+    (eq,             1, 1, 2, 1),
+])
 
 curdoc().add_root(desc)
 curdoc().add_root(controls)


Test patch:
diff --git a/bokeh/core/tests/test_enums.py b/bokeh/core/tests/test_enums.py
--- a/bokeh/core/tests/test_enums.py
+++ b/bokeh/core/tests/test_enums.py
@@ -224,6 +224,7 @@ def test_VerticalLocation(self):
 # any changes to contents of bce.py easily trackable here
 def test_enums_contents():
     assert [x for x in dir(bce) if x[0].isupper()] == [
+        'Align',
         'Anchor',
         'AngleUnits',
         'ButtonType',
@@ -268,7 +269,6 @@ def test_enums_contents():
         'TickLabelOrientation',
         'TooltipAttachment',
         'TooltipFieldFormatter',
-        'TrackAlign',
         'TrackPolicy',
         'VerticalAlign',
         'VerticalLocation',