import numpy as np
from bokeh.models import CustomJS, Div, TapTool
from bokeh.plotting import figure, row, show
N = 26 * 26
x, y = np.mgrid[0:101:4, 0:101:4].reshape((2, N))
radii = np.random.random(N) * 0.4 + 1.7
colors = np.array([(r, g, 150) for r, g in zip(50+2*x, 30+2*y)], dtype="uint8")
TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,save"
p = figure(title="Tappy Scatter", tools=TOOLS)
cr = p.circle(x[:N//2], y[:N//2], radius=radii[:N//2],
              fill_color=colors[:N//2], fill_alpha=0.6, line_color=None)
rr = p.rect(x[N//2:], y[N//2:], width=3, height=3,
            fill_color=colors[N//2:], fill_alpha=0.6, line_color=None)
div = Div(stylesheets=[":host { white-space: pre; }"])
tap_tool = TapTool(
    description="Tap (requires Ctrl modifier key)",
    renderers=[cr],
    behavior="inspect",
    modifiers="ctrl",
    callback=CustomJS(
        args=dict(div=div),
        code="""
            div.text = `${div.text}\nInspected circle #${cb_data.source.inspected.indices}`
        """,
    ),
)
p.add_tools(tap_tool)
tap_tool = TapTool(
    description="Tap (shows modifiers used)",
    renderers=[rr],
    behavior="inspect",
    callback=CustomJS(
        args=dict(div=div),
        code="""
            const {shift, ctrl, alt} = cb_data.event.modifiers
            const modifiers = `shift = ${shift}, ctrl = ${ctrl}, alt = ${alt}`
            div.text = `${div.text}\nInspected rect #${cb_data.source.inspected.indices} using ${modifiers}`
        """,
    ),
)
p.add_tools(tap_tool)
show(row(p, div))