diff --git a/Packages/ohif-hotkeys/client/classes/HotkeysContext.js b/Packages/ohif-hotkeys/client/classes/HotkeysContext.js index d65b32622..ebbc7a2c8 100644 --- a/Packages/ohif-hotkeys/client/classes/HotkeysContext.js +++ b/Packages/ohif-hotkeys/client/classes/HotkeysContext.js @@ -12,7 +12,10 @@ export class HotkeysContext { Object.keys(definitions).forEach(command => { const hotkey = definitions[command]; this.unregister(command); - this.register(command, hotkey); + if (hotkey) { + this.register(command, hotkey); + } + this.definitions[command] = hotkey; }); } diff --git a/Packages/ohif-hotkeys/client/components/dialog.html b/Packages/ohif-hotkeys/client/components/dialog.html index 18fb11fca..9df5d5ed8 100644 --- a/Packages/ohif-hotkeys/client/components/dialog.html +++ b/Packages/ohif-hotkeys/client/components/dialog.html @@ -1,5 +1,5 @@ diff --git a/Packages/ohif-hotkeys/client/components/dialog.styl b/Packages/ohif-hotkeys/client/components/dialog.styl new file mode 100644 index 000000000..9142edc82 --- /dev/null +++ b/Packages/ohif-hotkeys/client/components/dialog.styl @@ -0,0 +1,3 @@ +.dialog-hotkeys .modal-body + max-height: 80vh + overflow-y: auto diff --git a/Packages/ohif-hotkeys/client/components/form.html b/Packages/ohif-hotkeys/client/components/form.html index ab2fa2de6..e201f730a 100644 --- a/Packages/ohif-hotkeys/client/components/form.html +++ b/Packages/ohif-hotkeys/client/components/form.html @@ -1,5 +1,7 @@ diff --git a/Packages/ohif-hotkeys/client/components/form.js b/Packages/ohif-hotkeys/client/components/form.js index 2c1141220..251a64095 100644 --- a/Packages/ohif-hotkeys/client/components/form.js +++ b/Packages/ohif-hotkeys/client/components/form.js @@ -1,6 +1,8 @@ +import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; import { $ } from 'meteor/jquery'; import { _ } from 'meteor/underscore'; +import { OHIF } from 'meteor/ohif:core'; Template.hotkeysForm.onCreated(() => { const instance = Template.instance(); @@ -38,6 +40,19 @@ Template.hotkeysForm.onCreated(() => { return keysPressedArray; }; + + instance.disallowedCombinations = { + '': [], + ALT: ['SPACE'], + SHIFT: [], + CTRL: ['F4', 'F5', 'F11', 'W', 'R', 'T', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'PAGEDOWN', 'PAGEUP'], + 'CTRL+SHIFT': ['Q', 'W', 'R', 'T', 'P', 'A', 'H', 'V', 'B', 'N'] + }; +}); + +// >>>> REMOVE ME +Meteor.startup(() => { + OHIF.ui.showDialog('hotkeysDialog', { contextName: 'viewer' }); }); Template.hotkeysForm.events({ @@ -52,7 +67,47 @@ Template.hotkeysForm.events({ event.preventDefault(); }, + 'blur .hotkey'(event, instance) { + const $target = $(event.currentTarget); + const combination = $target.val(); + const keys = combination.split('+'); + const lastKey = keys.pop(); + const modifierCombination = keys.join('+'); + const isModifier = ['CTRL', 'ALT', 'SHIFT'].indexOf(lastKey) > -1; + // Clean the input if left with only a modifier key or browser specific command + if (isModifier) { + $target.val(''); + } else if (instance.disallowedCombinations[modifierCombination].indexOf(lastKey) > -1) { + $target.val(''); + // TODO: show warning + $target.focus(); + } + }, + 'keyup .hotkey'(event, instance) { instance.updateInputText(event); } }); + +Template.hotkeysForm.helpers({ + getHotkeyInputInformationList() { + OHIF.context.dep.depend(); + const instance = Template.instance(); + const { contextName } = instance.data; + const hotkeysInputInformation = []; + const hotkeysContext = OHIF.hotkeys.getContext(contextName); + const commandsContext = OHIF.commands.getContext(contextName); + if (!hotkeysContext || !commandsContext) return hotkeysInputInformation; + const hotkeyDefinitions = hotkeysContext.definitions; + _.each(hotkeyDefinitions, (keyCombination, commandName) => { + const commandDefinitions = commandsContext[commandName]; + if (!commandDefinitions) return; + hotkeysInputInformation.push({ + key: commandName, + label: commandDefinitions.name, + value: keyCombination + }); + }); + return hotkeysInputInformation; + } +}); diff --git a/Packages/ohif-hotkeys/client/components/index.js b/Packages/ohif-hotkeys/client/components/index.js index 28804bee3..90bf0451b 100644 --- a/Packages/ohif-hotkeys/client/components/index.js +++ b/Packages/ohif-hotkeys/client/components/index.js @@ -1,4 +1,5 @@ import './dialog.html'; +import './dialog.styl'; import './form.html'; import './form.js'; diff --git a/Packages/ohif-hotkeys/package.js b/Packages/ohif-hotkeys/package.js index 0985263a7..746d6b686 100644 --- a/Packages/ohif-hotkeys/package.js +++ b/Packages/ohif-hotkeys/package.js @@ -15,6 +15,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'templating', + 'stylus', 'reactive-var', 'session', 'iron:router', diff --git a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js index 1f3910fc1..f00f1b877 100644 --- a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js +++ b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js @@ -22,31 +22,54 @@ Meteor.startup(function() { }; OHIF.viewer.defaultHotkeys = { + // Tool hotkeys defaultTool: 'ESC', + zoom: 'Z', + wwwc: 'W', + pan: 'P', angle: 'A', stackScroll: 'S', - pan: 'P', magnify: 'M', - scrollDown: 'DOWN', - scrollUp: 'UP', - nextDisplaySet: 'PAGEDOWN', - previousDisplaySet: 'PAGEUP', - nextPanel: 'RIGHT', - previousPanel: 'LEFT', - invert: 'I', - flipV: 'V', + length: '', + annotate: '', + dragProbe: '', + ellipticalRoi: '', + rectangleRoi: '', + spine: '', + + // Viewport hotkeys flipH: 'H', - wwwc: 'W', - zoom: 'Z', - toggleCinePlay: 'SPACE', + flipV: 'V', rotateR: 'R', rotateL: 'L', + invert: 'I', + zoomIn: '', + zoomOut: '', + zoomToFit: '', + resetViewport: '', + clearTools: '', + + // Viewport navigation hotkeys + scrollDown: 'DOWN', + scrollUp: 'UP', + scrollLastImage: '', + scrollFirstImage: '', + previousDisplaySet: 'PAGEUP', + nextDisplaySet: 'PAGEDOWN', + nextPanel: 'RIGHT', + previousPanel: 'LEFT', + + // Miscellaneous hotkeys toggleOverlayTags: 'SHIFT', - WLPresetSoftTissue: ['NUMPAD1', '1'], - WLPresetLung: ['NUMPAD2', '2'], - WLPresetLiver: ['NUMPAD3', '3'], - WLPresetBone: ['NUMPAD4', '4'], - WLPresetBrain: ['NUMPAD5', '5'] + toggleCinePlay: 'SPACE', + toggleCineDialog: '', + + // Preset hotkeys + WLPresetSoftTissue: '1', + WLPresetLung: '2', + WLPresetLiver: '3', + WLPresetBone: '4', + WLPresetBrain: '5' }; // For now @@ -71,21 +94,27 @@ Meteor.startup(function() { }); }); + // Register the default tool command + OHIF.commands.register(contextName, 'defaultTool', { + name: 'Default Tool', + action: () => toolManager.setActiveTool(toolManager.getDefaultTool()) + }); + // Register the tool switching commands registerToolCommands({ - wwwc: 'Levels', + wwwc: 'Window W/L', zoom: 'Zoom', - angle: 'Angle', - dragProbe: 'Probe', - ellipticalRoi: 'Ellipse', + angle: 'Angle Measurement', + dragProbe: 'Pixel Probe', + ellipticalRoi: 'Elliptical Probe', rectangleRoi: 'Rectangle', magnify: 'Magnify', annotate: 'Annotate', - stackScroll: 'Stack Scroll', + stackScroll: 'Scroll Stack', pan: 'Pan', - length: 'Length', - spine: 'Spine', - wwwcRegion: 'ROI Window' + length: 'Length Measurement', + spine: 'Spine Labels', + wwwcRegion: 'W/L by Region' }); // Functions to register the viewport commands @@ -99,16 +128,16 @@ Meteor.startup(function() { // Register the viewport commands registerViewportCommands({ - zoomIn: 'Zoom in', - zoomOut: 'Zoom out', - zoomToFit: 'Zoom to fit', + zoomIn: 'Zoom In', + zoomOut: 'Zoom Out', + zoomToFit: 'Zoom to Fit', invert: 'Invert', - flipH: 'Flip horizontally', - flipV: 'Flip vertically', - rotateR: 'Rotate right', - rotateL: 'Rotate left', + flipH: 'Flip Horizontally', + flipV: 'Flip Vertically', + rotateR: 'Rotate Right', + rotateL: 'Rotate Left', resetViewport: 'Reset', - clearTools: 'Clear' + clearTools: 'Clear Tools' }); // Functions to register the preset switching commands @@ -122,75 +151,67 @@ Meteor.startup(function() { // Register the preset switching commands registerWLPresetCommands({ - WLPresetSoftTissue: 'SoftTissue', - WLPresetLung: 'Lung', - WLPresetLiver: 'Liver', - WLPresetBone: 'Bone', - WLPresetBrain: 'Brain' + WLPresetSoftTissue: 'W/L Preset: Soft Tissue', + WLPresetLung: 'W/L Preset: Lung', + WLPresetLiver: 'W/L Preset: Liver', + WLPresetBone: 'W/L Preset: Bone', + WLPresetBrain: 'W/L Preset: Brain' }); - // Register scrolling commands - OHIF.commands.set(contextName, { - scrollDown: { - name: 'Scroll down', - action: () => !isActiveViewportEmpty() && switchToImageRelative(1) - }, - scrollUp: { - name: 'Scroll up', - action: () => !isActiveViewportEmpty() && switchToImageRelative(-1) - }, - scrollFirstImage: { - name: 'Scroll to first image', - action: () => !isActiveViewportEmpty() && switchToImageByIndex(0) - }, - scrollLastImage: { - name: 'Scroll to last image', - action: () => !isActiveViewportEmpty() && switchToImageByIndex(-1) - } - }, true); - // Register viewport navigation commands OHIF.commands.set(contextName, { + scrollDown: { + name: 'Scroll Down', + action: () => !isActiveViewportEmpty() && switchToImageRelative(1) + }, + scrollUp: { + name: 'Scroll Up', + action: () => !isActiveViewportEmpty() && switchToImageRelative(-1) + }, + scrollFirstImage: { + name: 'Scroll to First Image', + action: () => !isActiveViewportEmpty() && switchToImageByIndex(0) + }, + scrollLastImage: { + name: 'Scroll to Last Image', + action: () => !isActiveViewportEmpty() && switchToImageByIndex(-1) + }, previousDisplaySet: { - name: 'Previous display set', + name: 'Previous Series', action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(false), disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(false) }, nextDisplaySet: { - name: 'Next display set', + name: 'Next Series', action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(true), disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(true) }, nextPanel: { - name: 'Next panel', + name: 'Next Panel', action: () => panelNavigation.loadNextActivePanel() }, previousPanel: { - name: 'Previous panel', + name: 'Previous Panel', action: () => panelNavigation.loadPreviousActivePanel() } }, true); // Register miscellaneous commands OHIF.commands.set(contextName, { - defaultTool: { - name: 'Default tool', - action: () => toolManager.setActiveTool(toolManager.getDefaultTool()) - }, toggleOverlayTags: { - name: 'Toggle overlay tags', + name: 'Toggle Image Annotations', action() { const $dicomTags = $('.imageViewerViewportOverlay .dicomTag'); $dicomTags.toggle($dicomTags.eq(0).css('display') === 'none'); } }, toggleCinePlay: { - name: 'Play/Pause', + name: 'Play/Pause Cine', action: viewportUtils.toggleCinePlay, disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames }, toggleCineDialog: { - name: 'CINE dialog', + name: 'Show/Hide Cine Controls', action: viewportUtils.toggleCineDialog, disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames }