From a26d03205acec40ee5dea4af6d38c52982d7bbaf Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Wed, 16 Oct 2019 18:26:22 -0700 Subject: [PATCH] tweak bindings to work better with one-button mouse --- src/mol-canvas3d/controls/trackball.ts | 14 +++++++------- src/mol-plugin/behavior/dynamic/camera.ts | 2 +- .../behavior/dynamic/representation.ts | 17 +++++++++++------ .../structure-representation-interaction.ts | 2 +- .../dynamic/volume-streaming/behavior.ts | 2 +- src/mol-util/binding.ts | 19 ++++++++++--------- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/mol-canvas3d/controls/trackball.ts b/src/mol-canvas3d/controls/trackball.ts index 308160784..08e5e43b6 100644 --- a/src/mol-canvas3d/controls/trackball.ts +++ b/src/mol-canvas3d/controls/trackball.ts @@ -21,15 +21,15 @@ const M = ModifiersKeys const Trigger = Binding.Trigger export const DefaultTrackballBindings = { - dragRotate: Binding(Trigger(B.Flag.Primary, M.create()), 'Rotate the 3D scene by dragging using ${trigger}'), - dragRotateZ: Binding(Trigger(B.Flag.Primary, M.create({ shift: true })), 'Rotate the 3D scene around the z-axis by dragging using ${trigger}'), - dragPan: Binding(Trigger(B.Flag.Secondary, M.create()), 'Pan the 3D scene by dragging using ${trigger}'), + dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate the 3D scene by dragging using ${triggers}'), + dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate the 3D scene around the z-axis by dragging using ${triggers}'), + dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan the 3D scene by dragging using ${triggers}'), dragZoom: Binding.Empty, - dragFocus: Binding(Trigger(B.Flag.Forth, M.create()), 'Focus the 3D scene by dragging using ${trigger}'), - dragFocusZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Focus and zoom the 3D scene by dragging using ${trigger}'), + dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus the 3D scene by dragging using ${triggers}'), + dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom the 3D scene by dragging using ${triggers}'), - scrollZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Zoom the 3D scene by scrolling using ${trigger}'), - scrollFocus: Binding(Trigger(B.Flag.Auxilary, M.create({ shift: true })), 'Focus the 3D scene by scrolling using ${trigger}'), + scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom the 3D scene by scrolling using ${triggers}'), + scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Focus the 3D scene by scrolling using ${triggers}'), scrollFocusZoom: Binding.Empty, } diff --git a/src/mol-plugin/behavior/dynamic/camera.ts b/src/mol-plugin/behavior/dynamic/camera.ts index 86ac41e77..517a6d829 100644 --- a/src/mol-plugin/behavior/dynamic/camera.ts +++ b/src/mol-plugin/behavior/dynamic/camera.ts @@ -16,7 +16,7 @@ const M = ModifiersKeys const Trigger = Binding.Trigger const DefaultFocusLociBindings = { - clickCenterFocus: Binding(Trigger(B.Flag.Primary, M.create()), 'Center and focus the clicked element using ${trigger}.'), + clickCenterFocus: Binding([Trigger(B.Flag.Primary, M.create())], 'Center and focus the clicked element using ${triggers}.'), } const FocusLociParams = { minRadius: PD.Numeric(8, { min: 1, max: 50, step: 1 }), diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index a4de734f4..151ab79ad 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -16,7 +16,7 @@ import { StateSelection } from '../../../mol-state'; import { ButtonsType, ModifiersKeys } from '../../../mol-util/input/input-observer'; import { Binding } from '../../../mol-util/binding'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; -import { EmptyLoci } from '../../../mol-model/loci'; +import { EmptyLoci, Loci } from '../../../mol-model/loci'; const B = ButtonsType const M = ModifiersKeys @@ -25,8 +25,8 @@ const Trigger = Binding.Trigger // const DefaultHighlightLociBindings = { - hoverHighlightOnly: Binding(Trigger(B.Flag.None), 'Highlight hovered element using ${trigger}'), - hoverHighlightOnlyExtend: Binding(Trigger(B.Flag.None, M.create({ shift: true })), 'Extend highlight from selected to hovered element along polymer using ${trigger}'), + hoverHighlightOnly: Binding([Trigger(B.Flag.None)], 'Highlight hovered element using ${triggers}'), + hoverHighlightOnlyExtend: Binding([Trigger(B.Flag.None, M.create({ shift: true }))], 'Extend highlight from selected to hovered element along polymer using ${triggers}'), } const HighlightLociParams = { bindings: PD.Value(DefaultHighlightLociBindings, { isHidden: true }), @@ -74,10 +74,11 @@ export const HighlightLoci = PluginBehavior.create({ const DefaultSelectLociBindings = { clickSelect: Binding.Empty, - clickSelectExtend: Binding(Trigger(B.Flag.Primary, M.create({ shift: true })), 'Extend selection to clicked element along polymer using ${trigger}.'), - clickSelectOnly: Binding(Trigger(B.Flag.Secondary, M.create({ control: true })), 'Select only the clicked element using ${trigger}.'), - clickSelectToggle: Binding(Trigger(B.Flag.Primary, M.create({ control: true })), 'Toggle selection of clicked element using ${trigger}.'), + clickSelectExtend: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Extend selection to clicked element along polymer using ${triggers}.'), + clickSelectOnly: Binding([Trigger(B.Flag.Primary, M.create({ alt: true, shift: true }))], 'Select only the clicked element using ${triggers}.'), + clickSelectToggle: Binding([Trigger(B.Flag.Primary, M.create({ alt: true }))], 'Toggle selection of clicked element using ${triggers}.'), clickDeselect: Binding.Empty, + clickDeselectAllOnEmpty: Binding.Empty, } const SelectLociParams = { bindings: PD.Value(DefaultSelectLociBindings, { isHidden: true }), @@ -127,6 +128,10 @@ export const SelectLoci = PluginBehavior.create({ if (Binding.match(this.params.bindings.clickDeselect, buttons, modifiers)) { this.ctx.interactivity.lociSelects.deselect(current) } + + if (Binding.match(this.params.bindings.clickDeselectAllOnEmpty, buttons, modifiers)) { + if (Loci.isEmpty(current.loci)) this.ctx.interactivity.lociSelects.deselect(current) + } }); this.ctx.interactivity.lociSelects.addProvider(this.lociMarkProvider) diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts index 9580f61ac..b6fc45b46 100644 --- a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts +++ b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts @@ -26,7 +26,7 @@ const M = ModifiersKeys const Trigger = Binding.Trigger const DefaultStructureRepresentationInteractionBindings = { - clickInteractionAroundOnly: Binding(Trigger(B.Flag.Secondary, M.create()), 'Show the structure interaction around only the clicked element using ${trigger}.'), + clickInteractionAroundOnly: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Show the structure interaction around only the clicked element using ${triggers}.'), } const StructureRepresentationInteractionParams = { bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }), diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts index d0b2292f9..933ec0b3b 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts @@ -51,7 +51,7 @@ export namespace VolumeStreaming { }; export const DefaultBindings = { - clickVolumeAroundOnly: Binding(Trigger(B.Flag.Secondary, M.create()), 'Show the volume around only the clicked element using ${trigger}.'), + clickVolumeAroundOnly: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Show the volume around only the clicked element using ${triggers}.'), } export function createParams(data?: VolumeServerInfo.Data, defaultView?: ViewTypes, binding?: typeof DefaultBindings) { diff --git a/src/mol-util/binding.ts b/src/mol-util/binding.ts index 7a1061632..b8607fd22 100644 --- a/src/mol-util/binding.ts +++ b/src/mol-util/binding.ts @@ -10,31 +10,32 @@ import { interpolate, stringToWords } from './string'; export { Binding } interface Binding { - trigger: Binding.Trigger + triggers: Binding.Trigger[] description: string } -function Binding(trigger: Binding.Trigger, description = '') { - return Binding.create(trigger, description) +function Binding(triggers: Binding.Trigger[], description = '') { + return Binding.create(triggers, description) } namespace Binding { - export function create(trigger: Trigger, description = ''): Binding { - return { trigger, description } + export function create(triggers: Trigger[], description = ''): Binding { + return { triggers, description } } - export const Empty: Binding = { trigger: {}, description: '' } + export const Empty: Binding = { triggers: [], description: '' } export function isEmpty(binding: Binding) { - return binding.trigger.buttons === undefined && binding.trigger.modifiers === undefined + return binding.triggers.length === 0 || + binding.triggers.every(t => t.buttons === undefined && t.modifiers === undefined) } export function match(binding: Binding, buttons: ButtonsType, modifiers: ModifiersKeys) { - return Trigger.match(binding.trigger, buttons, modifiers) + return binding.triggers.some(t => Trigger.match(t, buttons, modifiers)) } export function format(binding: Binding, name = '') { const help = binding.description || stringToWords(name) - return interpolate(help, { trigger: Trigger.format(binding.trigger) }) + return interpolate(help, { triggers: binding.triggers.map(t => Trigger.format(t)).join(' or ') }) } export interface Trigger { -- GitLab