How to see if user shift clicked or control clicked or command clicked on Tap Tool javascript callback

Hi,

I am trying to make a tap tool with two different behaviors depending on whether the user is clicking on a point or control + clicking a point.

I would like this so that the user can tap for one type of behavior, but also control + click to open a URL associated with the point.

My example to start with that does not have this feature, it just opens the link when you press the points:

from bokeh.models import CustomJS, TapTool, ColumnDataSource
from bokeh.plotting import figure, show

cds = ColumnDataSource({
    'x': [1, 2, 3],
    'y': ['a', 'b', 'c'],
    'link': ['www.google.com', 'www.bing.com', 'www.cnn.com']
})
plot = figure(width=300, height=300, y_range=['a', 'b', 'c'])
plot.scatter(x='x', y='y', color="#fa9fb5", source=cds,
             marker='triangle_pin', size=15)

code = """
window.open('http://'.concat(cb_data.source.data.link[cb_data.source.inspected.indices]), '_blank')
"""
callback = CustomJS(args=dict(), code=code)
tap_tool = TapTool(behavior='inspect', callback=callback)
plot.add_tools(tap_tool)

show(plot)

I want to adjust the callback code to be something like this:

code = """
console.log(window)
console.log(MouseEvent.ctrlKey)
if (MouseEvent.ctrlKey) {
    window.open('http://'.concat(cb_data.source.data.link[cb_data.source.inspected.indices]), '_blank')
} else {
   // do something different
}
"""

But I am not sure how exactly to know if control key was pressed, since MouseEvent.ctrlKey is undefined.

I’m wondering if I can somehow get to this information via window? Any help is appreciated! Thanks in advance.

Edit: I am using bokeh 2.4.2

This information is not currently available to the CustomJS callback. Perhaps surprisingly in retrospect, no one has ever asked about this before. Please feel free to open a GitHub Issue to request it as a new feature in a future 3.x release.

It would be possible to implement a custom extension that can detect shift- or ctrl-click (but only those two modifiers). I’ll wait for you to express interest in that route before elaborating, however.

Hi Bryan,

I will go ahead and make a feature request! (Edit: Here is the feature request)

For the time being, I came up with a workaround:

from bokeh.models import CustomJS, TapTool, ColumnDataSource, Tabs, Panel, Div
from bokeh.plotting import figure, show

cds = ColumnDataSource({
    'x': [1, 2, 3],
    'y': ['a', 'b', 'c'],
    'link': ['www.google.com', 'www.bing.com', 'www.cnn.com']
})
plot = figure(width=300, height=300, y_range=['a', 'b', 'c'])
plot.scatter(x='x', y='y', color="#fa9fb5", source=cds, marker='triangle_pin', size=15)

code_1 = """
window.onclick = function () {
    window.ctrl_press = event.ctrlKey || event.metaKey
}
"""
callback_1 = CustomJS(code=code_1)

code_2 = """
if (window.ctrl_press) {
    window.open('http://'.concat(cb_data.source.data.link[cb_data.source.inspected.indices]), '_blank')
}
"""
callback_2 = CustomJS(code=code_2)

tap_tool = TapTool(behavior='inspect', callback=callback_2)
plot.add_tools(tap_tool)

panels = [Panel(child=Div(text='Welcome!'), title='Intro'), Panel(child=plot, title='Example')]
tabs = Tabs(tabs=panels, sizing_mode='stretch_width')
tabs.js_on_change('active', callback_1)

show(tabs)

This may be janky… When the user changes tabs, a callback is set to make a property stored in window equal to whether the control or command key was pressed in the window when a click happens. Now, the TapTool callback has access to this property in window.

As for a custom extension, you mentioned it would only be able to detect shift- or ctrl-click. Just to make sure, it could not detect meta-click (command-click)?

Thank you!