diff --git a/src/mol-canvas3d/controls/trackball.ts b/src/mol-canvas3d/controls/trackball.ts index 3081607849a33a590362a13ae4b6b1c987ee0e44..08e5e43b60afe002863b354deaa764b99f901bd1 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 86ac41e7711c19eb07565901ed5a267dba9cfaaf..517a6d8294ff359533ae4c0e04e34d98353d23a6 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 a4de734f4221f0018687ff617e135de524165b4e..151ab79adfe56c5708e9f1155066622d53eba16d 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 9580f61ace8f815f2c6ea62e08b3efff67cb0709..b6fc45b46e925eb31b2c74692a93ab6a680bf96b 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 d0b2292f931211a7cacc0de9331449edb19cbb30..933ec0b3b4c75084adbf2a0982ebc671b5150cc9 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 7a10616325a5540e251d74c9efc4f7a0cf92dbb6..b8607fd22ac5784f2570a892fb9d8006605d9ed0 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 {