Skip to content

Conversation

@mattpap
Copy link
Contributor

@mattpap mattpap commented Nov 17, 2023

This This PR adds various new and improves existings "floating" UI elements, like dialogs, panels (e.g. floating toolbars), drop panes, drawers (side bars), alerts, tooltips, context menus, etc. and refines methods for attaching them can to other UI elements and renderers (e.g. annotations, frames).

An example of multiple dialogs, which support moving and resize (with constraints), z-order management, various controls (like minimization/maximization, collapsing, pinning) and contents which can be HTML, UI elements or layouts:
image

@mattpap mattpap added this to the 3.4 milestone Nov 17, 2023
@codecov
Copy link

codecov bot commented Dec 7, 2023

Codecov Report

Attention: 13 lines in your changes are missing coverage. Please review.

Comparison is base (b46bf47) 92.59% compared to head (279092e) 92.58%.

Additional details and impacted files
@@              Coverage Diff               @@
##           branch-3.4   #13538      +/-   ##
==============================================
- Coverage       92.59%   92.58%   -0.02%     
==============================================
  Files             323      325       +2     
  Lines           20590    20723     +133     
==============================================
+ Hits            19065    19186     +121     
- Misses           1525     1537      +12     

@mattpap
Copy link
Contributor Author

mattpap commented Jan 30, 2024

Attribution is now implemented as a panel positioned with respect to the frame (using a symbolic coordinate) and allows multiple non-overlapping entries:
image

@mattpap
Copy link
Contributor Author

mattpap commented Jan 30, 2024

examples/basic/ui/inline_data_entry.py shows how to dynamically position a panel in data space:

Screencast.from.30.01.2024.02.58.20.webm

@mattpap
Copy link
Contributor Author

mattpap commented Jan 30, 2024

examples/basic/ui/selection_custom_toolbar.py shows how to attach a panel with a layout to a renderer (selection overlay):

Screencast.from.30.01.2024.11.35.33.webm

@mattpap
Copy link
Contributor Author

mattpap commented Jan 30, 2024

@philippjfr, @droumis, @hoxbro, this is tentatively ready for initial feedback. At this time I would suggest looking into examples/basic/ui/*.py, which show use cases requested in issue #13291 and more; some of which I demonstrated in screencasts above.

),
)
show_plot.js_on_click(OpenDialog(dialog=dialog))
close_dialog.js_on_click(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it not symmetric with OpenDialog?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we we are good with this kind of API, then I will fill in the missing parts. Though it's also good to show how to do things manually, if an API is missing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be no CloseDialog equivalent, correct?

Running this example with Bokeh serve (and updating the code with show -> curdoc().add_root, I can't get the close button to work and get the log:

Uncaught (in promise) Error: cannot find a view for Dialog(p1046)
    get_one http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:221
    default blob:http://localhost:5006/4e8e6ce5-4530-4159-ae07-e228cfae3dcf:1
    execute http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:480
    execute http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:216
    _process_event http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:217
    trigger http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:166
    trigger_event http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:217
    click http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:57
    render http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:47
    render http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:47
    render http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:573
    render_to http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:573
    render http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:486
    _toggle http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:486
    connect_signals http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:486
    n http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:221
    emit http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:181
    emit http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:181
    _setv http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:180
    setv http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:180
    open http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:486
    execute http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:485
    execute http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:216
    _process_event http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:217
    trigger http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:166
    trigger_event http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:217
    click http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:57
    render http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:47
    render http://localhost:5006/static/js/bokeh-widgets.min.js?v=8945a2a59d802de388cad0ff943167d1f705d69f095c16e44e908e94b6dd70ceba9b48c6ab0601f0d6b1c593988520f8ab2ce507540022d08cc8888dccb244d8:47
    render_to http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:573
    s http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:220
    s http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:220
    add_document_standalone http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:220
    add_document_from_session http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:232
    k http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:164
    embed_items http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:164
    embed_document http://localhost:5006/dialog:38
    fn http://localhost:5006/dialog:41
    fn http://localhost:5006/dialog:57
    safely http://localhost:5006/static/js/bokeh.min.js?v=09b616ff6e78e0bc7aa27d971d6bd254ee707011ace76821753ce252849fa5443f279823051efd10b5ca74b0a9a148a1306310dbde3a3dcae097acca168ae1bf:240
    fn http://localhost:5006/dialog:33
    EventListener.handleEvent* http://localhost:5006/dialog:61
    <anonymous> http://localhost:5006/dialog:62
bokeh.min.js:221:4067

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error: cannot find a view for Dialog(p1046)

This actually doesn't work at all. This is because OpenDialog (callbacks in general) doesn't have a view, so it doesn't participate in the mechanism that allows to resolve views via the global view manager. Given the intent behind OpenDialog and this example is to have a non-root, detached/global dialog, then I need to add a mechanism to register such "free" views.

clear = Button(label="Clear", **common)

toolbar = Panel(
position=box_select.overlay.nodes.bottom_left,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can I read about the nodes object? Does this resolve on render or does nodes.bottom_left represent some actual concrete value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to write documentation for nodes. It resolves during geometry/positioning computations, so still technically on render/paint. I'm in the process of separating those two phases, but it won't be done here. Computed values are only available in bokehjs. It may be possible to expose those values in a similar way to how computed layouts are exposed, e.g. in plots.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think syncing it is probably overkill, was just trying to understand how it works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered adding support for reporting computed values. For performance reasons, that would be done via a subscription based mechanism similar to how JS event callbacks work.

@hoxbro
Copy link
Contributor

hoxbro commented Jan 30, 2024

Some comments about the dialog box icons (they are subjective 🙃 ). Names left-to-right: pin, collapse, minimize, maximize, close.
image

Pin

  1. Maybe update the text from "Pin" to "Pin to top". I was a bit confused that I could still move and resize it even though I had pinned it.
  2. I don't know how feasible it is, but it could only show up when there is a need for it when you have two dialog boxes or more.
  3. The icons show the action that will happen when you click on it, not the current state. This works fine for the collapse, minimize, maximize, and close icons, which gives you feedback immediately, but the pin icon makes it hard to know if you are pinned or not without hovering over it. Maybe make the toolbar a different color, like darker grey?

Minimize

  1. Looks very much like the download button on a figure. Maybe just have it as a single line at the bottom?
  2. When minimized, maybe only show the restore (opposite of minimized) and close icons. Or even just the close icon and make it expand when clicking on everything else.

position=Node(target="frame", symbol="bottom_left"),
anchor="bottom_left",
css_variables={
"--max-width": Node(target="frame", symbol="width"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but does this have an effect on the output? If I comment out this and max-width in the stylesheet, I see no change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see this working here (attribution must not overflow the frame; frame is a pseudo renderer, so it doesn't have intrinsic support for managing overflow like CSS based UIs do):

Screencast.from.31.01.2024.13.34.12.webm

@mattpap
Copy link
Contributor Author

mattpap commented Feb 5, 2024

I finalized support for context menus, which completes the features I indented for this PR.

Screencast.from.05.02.2024.15.09.53.webm

@mattpap
Copy link
Contributor Author

mattpap commented Feb 5, 2024

I will finish docstrings and some rudimentary testing later today, so that we can start testing this downstream. I will continue polishing things up and adding more tests in future PRs.

@mattpap
Copy link
Contributor Author

mattpap commented Feb 5, 2024

examples/basic/ui/annotation_menu.py with some custom styling in the sub-menu:
image

@mattpap mattpap merged commit fb83377 into branch-3.4 Feb 15, 2024
@mattpap mattpap deleted the mattpap/13291_ui branch February 15, 2024 12:40
Chiemezuo pushed a commit to Chiemezuo/bokeh that referenced this pull request Aug 27, 2024
* Add more icons

* Add hover tool's tooltip to plot's event layer

* Make Dialog movable and resizable

* Set dialog's title in examiner tool

* Add OpenDialog and ToggleVisibility callbacks

* Update defaults' baseline

* Robustify application of CSS styles

* Correctly configure pointer capture

* Move box shadow to the inner element

* Use a style sheet for Dialog positioning

* Implement stacking order for dialogs

* Implement dependency between dialog's states

* Robustify initialization and destruction

* Collapse/Restore dialog when scrolling on title

* Add Panel UI element

* Generalize symbolic Node API

* Add support for z-index pinning to Dialog

* Allow CSS variables in CSS preprocessor

* Don't duplicate testing effort in test_enums.py

* Improve Literal type support in Enum

* Allow adding additional UI elements

* Allow Tooltip to handle UI elements

* Add support for canvas positioning of UIs

* Add support for context menus to UIs/renderers

* Add support for data positioning

* Temporarily add CartesianFrame.resolve_symbol()

* Include PlotView's canvas in elements

* Finalize various types of node resolution

* Make implicit node resolution work

* Rename Panel -> SidePanel

* Formalize support for plot attributions

* Fix issues with resolution of coordinates/nodes

* Add examples/basic/ui/dialog.py

* Add examples/basic/ui/custom_attribution.py

* Update tests/unit/bokeh/models/test_annotations.py

* Reposition Panel when its positioning node changes

* Add examples/basic/ui/inline_data_entry.py

* Add examples/basic/ui/selection_custom_toolbar.py

* Fix code quality issues

* Move ViewManager to its own module

* Allow to manage non-root parent-less views

* Make Dialog's close action configurable

* Fix base class of bokeh.models.dom.HTML

* Add CloseDialog callback along OpenDialog

* Allow AbstractButton.label to have HTML contents

* Improve examples/basic/ui/dialog.py

* Add support for a dialog minimization area

* Add more icons and update the baseline

* Increase size allowance for bokeh.min.js

* Use Coordinate instead of Node in Tooltip

* Replace .bring-to-front() with a CSS variable

* Robustify LayoutDOMView.update_children()

* Properly rebuild toolbar when location changes

* Don't reference HTML if DOMNode is used

* Don't require root-layout in render_to()

* Use appropriate dialog close action

* Finalize support for context menus

* Add examples/basic/ui/annotation_menu.py

* Make ui.icons.Icon a UIElement

* Move ui.{panes->panels}.Panel

* Simplify definition of CompositeRenderer.elements

* Add CompositeRenderer.renderers property

* Update docstrings of bokeh.models.ui.*

* Improve menu naming convention and add docstrings

* Implement integration/examples/color_scatter.ts

* Update test_annotations.py

* Add rudimentary integration tests for UI elements

* Use consistently pointer events instead of mouse events

* Allow to programmatically display context menus

* Rename DictLike -> Dict type

* Update docstrings and properties' help

* Don't use deprecated class properties

* Update cross integration tests' baselines
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

5 participants