Repository: bokeh/bokeh

Issue:
legend title
I was wondering if an option can be added to bokeh to add titles to legends?
This is similar to a question that was asked here:
[Any way to create a legend title in Bokeh?](https://stackoverflow.com/questions/38155099/any-way-to-create-a-legend-title-in-bokeh
)

Something consistent with the bokeh syntax like:
```
p.legend.title = 'Legend Title'
```







Discussion:
This capability does not yet exist, but has recently been brought up by a few different folks (ping @jlstevens @philippjfr) So I've slated it for `0.12.8` However, it should not be a lot of work, so we'd be happy to help anyone that can work on it sooner than we are able to get to it. 
Yes, I'd love to get this functionality into bokeh but I probably won't find the time in the near future.
I'm marking this as part of layout work, because it would a nice opportunity to bring layout to annotations.
> I'm marking this as part of layout work, because it would a nice opportunity to bring layout to annotations.

That seems like overkill? Drawing a title above the existing legend seems like a trivial thing to do, what need is there to complicate it by bringing in layout?
Sure, in isolation this is definitively an overkill, and if someone wants to just add this feature then go ahead. However, I would like to have a broader look at how we layout annotations, because having a title on a legend shouldn't be a special case, but should nicely flow from canvas layout. I have some thoughts on this subject based on failings with my recent tutorial.
@mattpap, did this end up being included in layout work?
Not yet, but it's work in progress.
Awesome news. Congrats on the layout PR, I know what a lift layout is. 

To clarify: does the layout PR include a framework for annotation layout and the work of adding legend titles is the piece that isn't available yet; or is annotation layout still to be included?

Resolving patch:
diff --git a/bokeh/models/annotations.py b/bokeh/models/annotations.py
--- a/bokeh/models/annotations.py
+++ b/bokeh/models/annotations.py
@@ -183,6 +183,22 @@ class Legend(Annotation):
     when they are drawn.
     """)
 
+    title = String(help="""
+    The title text to render.
+    """)
+
+    title_props = Include(TextProps, help="""
+    The %s values for the title text.
+    """)
+
+    title_text_font_size = Override(default={'value': "10pt"})
+
+    title_text_font_style = Override(default="italic")
+
+    title_standoff = Int(5, help="""
+    The distance (in pixels) to separate the title from the legend.
+    """)
+
     border_props = Include(LineProps, help="""
     The %s for the legend border outline.
     """)
diff --git a/examples/models/file/legends.py b/examples/models/file/legends.py
--- a/examples/models/file/legends.py
+++ b/examples/models/file/legends.py
@@ -51,6 +51,7 @@ def add_legend(location, orientation, side):
         items=[("line", [line]), ("circle", [circle])],
         location=location, orientation=orientation,
         border_line_color="black",
+        title='Example Title'
     )
     plot.add_layout(legend, side)
 
diff --git a/examples/plotting/file/legend.py b/examples/plotting/file/legend.py
--- a/examples/plotting/file/legend.py
+++ b/examples/plotting/file/legend.py
@@ -14,6 +14,8 @@
 p1.circle(x, 2*y, legend="2*sin(x)", color="orange")
 p1.circle(x, 3*y, legend="3*sin(x)", color="green")
 
+p1.legend.title = 'Example Title'
+
 p2 = figure(title="Another Legend Example", tools=TOOLS)
 
 p2.circle(x, y, legend="sin(x)")


Test patch:
diff --git a/bokeh/models/tests/test_annotations.py b/bokeh/models/tests/test_annotations.py
--- a/bokeh/models/tests/test_annotations.py
+++ b/bokeh/models/tests/test_annotations.py
@@ -53,6 +53,9 @@
 def test_Legend():
     legend = Legend()
     assert legend.location == 'top_right'
+    assert legend.orientation == 'vertical'
+    assert legend.title is None
+    assert legend.title_standoff == 5
     assert legend.label_standoff == 5
     assert legend.label_height == 20
     assert legend.label_width == 20
@@ -69,6 +72,8 @@ def test_Legend():
         "visible",
         "location",
         "orientation",
+        "title",
+        "title_standoff",
         "label_standoff",
         "label_height",
         "label_width",
@@ -81,6 +86,7 @@ def test_Legend():
         "level",
         "click_policy"],
         prefix('label_', TEXT),
+        prefix('title_', TEXT),
         prefix('border_', LINE),
         prefix('background_', FILL),
         prefix('inactive_', FILL))
diff --git a/tests/baselines/examples/models/file/legends b/tests/baselines/examples/models/file/legends
--- a/tests/baselines/examples/models/file/legends
+++ b/tests/baselines/examples/models/file/legends
@@ -1,9 +1,9 @@
 Plot bbox=[0, 0, 1000, 600]
-  Legend bbox=[127, 0, 746, 60]
-  LinearAxis bbox=[127, 516, 746, 24]
-  Legend bbox=[127, 540, 746, 60]
-  LinearAxis bbox=[96, 60, 31, 456]
-  Legend bbox=[0, 60, 96, 456]
-  LinearAxis bbox=[873, 60, 31, 456]
-  Legend bbox=[904, 60, 96, 456]
-  Title bbox=[127, 0, 746, 0]
+  Legend bbox=[152, 0, 696, 81]
+  LinearAxis bbox=[152, 495, 696, 24]
+  Legend bbox=[152, 519, 696, 81]
+  LinearAxis bbox=[121, 81, 31, 414]
+  Legend bbox=[0, 81, 121, 414]
+  LinearAxis bbox=[848, 81, 31, 414]
+  Legend bbox=[879, 81, 121, 414]
+  Title bbox=[152, 0, 696, 0]