@bokeh/bokehjs
Version:
Interactive, novel data visualization
209 lines • 7.73 kB
JavaScript
import { GestureTool, GestureToolView } from "./gesture_tool";
import { GlyphRenderer } from "../../renderers/glyph_renderer";
import { GraphRenderer } from "../../renderers/graph_renderer";
import { DataRenderer } from "../../renderers/data_renderer";
import { compute_renderers } from "../../util";
import { MenuItem } from "../../ui/menus";
import { SelectionGeometry } from "../../../core/bokeh_events";
import { Signal0 } from "../../../core/signaling";
import { unreachable } from "../../../core/util/assert";
import { uniq } from "../../../core/util/array";
import * as icons from "../../../styles/icons.css";
export class SelectToolView extends GestureToolView {
static __name__ = "SelectToolView";
connect_signals() {
super.connect_signals();
this.model.invert.connect(() => this._invert_selection());
this.model.clear.connect(() => this._clear_selection());
}
get computed_renderers() {
const { renderers } = this.model;
const all_renderers = this.plot_view.model.data_renderers;
return compute_renderers(renderers, all_renderers);
}
_computed_renderers_by_data_source() {
const renderers_by_source = new Map();
for (const r of this.computed_renderers) {
let source;
if (r instanceof GlyphRenderer) {
source = r.data_source;
}
else if (r instanceof GraphRenderer) {
source = r.node_renderer.data_source;
}
else {
continue;
}
const renderers = renderers_by_source.get(source) ?? [];
renderers_by_source.set(source, [...renderers, r]);
}
return renderers_by_source;
}
_clear_overlay() { }
_clear_other_overlays() {
for (const view of this.plot_view.tool_views.values()) {
if (view instanceof SelectToolView && view != this) {
view._clear_overlay();
}
}
}
_clear_selection() {
const { computed_renderers } = this;
const selection_managers = uniq(computed_renderers.map((r) => r.selection_manager));
for (const selection_manager of selection_managers) {
selection_manager.clear();
}
this.plot_view.request_paint(...computed_renderers);
}
_invert_selection() {
const { computed_renderers } = this;
const selection_managers = uniq(computed_renderers.map((r) => r.selection_manager));
for (const selection_manager of selection_managers) {
selection_manager.invert();
}
this.plot_view.request_paint(...computed_renderers);
}
_select_mode(modifiers) {
const { shift, ctrl } = modifiers;
if (!shift && !ctrl) {
return this.model.mode;
}
else if (shift && !ctrl) {
return "append";
}
else if (!shift && ctrl) {
return "intersect";
}
else if (shift && ctrl) {
return "subtract";
}
else {
unreachable();
}
}
_keyup(ev) {
if (!this.model.active) {
return;
}
if (ev.key == "Escape") {
this._clear_selection();
}
}
_emit_selection_event(geometry, final = true) {
const { x_scale, y_scale } = this.plot_view.frame;
const geometry_data = (() => {
switch (geometry.type) {
case "point": {
const { sx, sy } = geometry;
const x = x_scale.invert(sx);
const y = y_scale.invert(sy);
return { ...geometry, x, y };
}
case "span": {
const { sx, sy } = geometry;
const x = x_scale.invert(sx);
const y = y_scale.invert(sy);
return { ...geometry, x, y };
}
case "rect": {
const { sx0, sx1, sy0, sy1 } = geometry;
const [x0, x1] = x_scale.r_invert(sx0, sx1);
const [y0, y1] = y_scale.r_invert(sy0, sy1);
return { ...geometry, x0, y0, x1, y1 };
}
case "poly": {
const { sx, sy } = geometry;
const x = x_scale.v_invert(sx);
const y = y_scale.v_invert(sy);
return { ...geometry, x, y };
}
}
})();
this.plot_view.model.trigger_event(new SelectionGeometry(geometry_data, final));
}
}
export class SelectTool extends GestureTool {
static __name__ = "SelectTool";
invert = new Signal0(this, "invert");
clear = new Signal0(this, "clear");
constructor(attrs) {
super(attrs);
}
static {
this.define(({ List, Ref, Or, Auto }) => ({
renderers: [Or(List(Ref(DataRenderer)), Auto), "auto"],
}));
}
get menu() {
return [
new MenuItem({
icon: `.${icons.tool_icon_replace_mode}`,
label: "Replace mode",
tooltip: "Replace the current selection",
checked: () => this.mode == "replace",
action: () => {
this.mode = "replace";
this.active = true;
},
}),
new MenuItem({
icon: `.${icons.tool_icon_append_mode}`,
label: "Append mode",
tooltip: "Append to the current selection (Shift)",
checked: () => this.mode == "append",
action: () => {
this.mode = "append";
this.active = true;
},
}),
new MenuItem({
icon: `.${icons.tool_icon_intersect_mode}`,
label: "Intersection mode",
tooltip: "Intersect with the current selection (Ctrl)",
checked: () => this.mode == "intersect",
action: () => {
this.mode = "intersect";
this.active = true;
},
}),
new MenuItem({
icon: `.${icons.tool_icon_subtract_mode}`,
label: "Subtraction mode",
tooltip: "Subtract from the current selection (Shift+Ctrl)",
checked: () => this.mode == "subtract",
action: () => {
this.mode = "subtract";
this.active = true;
},
}),
new MenuItem({
icon: `.${icons.tool_icon_xor_mode}`,
label: "XOR mode",
tooltip: "Symmetric difference with the current selection",
checked: () => this.mode == "xor",
action: () => {
this.mode = "xor";
this.active = true;
},
}),
null,
new MenuItem({
icon: `.${icons.tool_icon_invert_selection}`,
label: "Invert selection",
tooltip: "Invert the current selection",
action: () => {
this.invert.emit();
},
}),
new MenuItem({
icon: `.${icons.tool_icon_clear_selection}`,
label: "Clear selection",
tooltip: "Clear the current selection and/or selection overlay (Esc)",
action: () => {
this.clear.emit();
},
}),
];
}
}
//# sourceMappingURL=select_tool.js.map