Adding inputs to define hotkeys

This commit is contained in:
Bruno Alves de Faria 2017-04-28 08:50:41 -03:00 committed by Erik Ziegler
parent bbb1cf0789
commit 20f274962b
8 changed files with 160 additions and 74 deletions

View File

@ -12,7 +12,10 @@ export class HotkeysContext {
Object.keys(definitions).forEach(command => { Object.keys(definitions).forEach(command => {
const hotkey = definitions[command]; const hotkey = definitions[command];
this.unregister(command); this.unregister(command);
this.register(command, hotkey); if (hotkey) {
this.register(command, hotkey);
}
this.definitions[command] = hotkey; this.definitions[command] = hotkey;
}); });
} }

View File

@ -1,5 +1,5 @@
<template name="hotkeysDialog"> <template name="hotkeysDialog">
{{#dialogSimple title='Keyboard Shortcuts'}} {{#dialogSimple (extend this dialogClass='dialog-hotkeys' title='Keyboard Shortcuts')}}
{{>hotkeysForm}} {{>hotkeysForm this}}
{{/dialogSimple}} {{/dialogSimple}}
</template> </template>

View File

@ -0,0 +1,3 @@
.dialog-hotkeys .modal-body
max-height: 80vh
overflow-y: auto

View File

@ -1,5 +1,7 @@
<template name="hotkeysForm"> <template name="hotkeysForm">
{{#form class='form-themed'}} {{#form (extend this class='form-themed')}}
{{>inputText class='hotkey' label='Window W/L'}} {{#each hotkeyInputInformation in getHotkeyInputInformationList}}
{{>inputText (extend hotkeyInputInformation class='hotkey')}}
{{/each}}
{{/form}} {{/form}}
</template> </template>

View File

@ -1,6 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { $ } from 'meteor/jquery'; import { $ } from 'meteor/jquery';
import { _ } from 'meteor/underscore'; import { _ } from 'meteor/underscore';
import { OHIF } from 'meteor/ohif:core';
Template.hotkeysForm.onCreated(() => { Template.hotkeysForm.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
@ -38,6 +40,19 @@ Template.hotkeysForm.onCreated(() => {
return keysPressedArray; 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({ Template.hotkeysForm.events({
@ -52,7 +67,47 @@ Template.hotkeysForm.events({
event.preventDefault(); 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) { 'keyup .hotkey'(event, instance) {
instance.updateInputText(event); 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;
}
});

View File

@ -1,4 +1,5 @@
import './dialog.html'; import './dialog.html';
import './dialog.styl';
import './form.html'; import './form.html';
import './form.js'; import './form.js';

View File

@ -15,6 +15,7 @@ Package.onUse(function(api) {
api.use([ api.use([
'ecmascript', 'ecmascript',
'templating', 'templating',
'stylus',
'reactive-var', 'reactive-var',
'session', 'session',
'iron:router', 'iron:router',

View File

@ -22,31 +22,54 @@ Meteor.startup(function() {
}; };
OHIF.viewer.defaultHotkeys = { OHIF.viewer.defaultHotkeys = {
// Tool hotkeys
defaultTool: 'ESC', defaultTool: 'ESC',
zoom: 'Z',
wwwc: 'W',
pan: 'P',
angle: 'A', angle: 'A',
stackScroll: 'S', stackScroll: 'S',
pan: 'P',
magnify: 'M', magnify: 'M',
scrollDown: 'DOWN', length: '',
scrollUp: 'UP', annotate: '',
nextDisplaySet: 'PAGEDOWN', dragProbe: '',
previousDisplaySet: 'PAGEUP', ellipticalRoi: '',
nextPanel: 'RIGHT', rectangleRoi: '',
previousPanel: 'LEFT', spine: '',
invert: 'I',
flipV: 'V', // Viewport hotkeys
flipH: 'H', flipH: 'H',
wwwc: 'W', flipV: 'V',
zoom: 'Z',
toggleCinePlay: 'SPACE',
rotateR: 'R', rotateR: 'R',
rotateL: 'L', 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', toggleOverlayTags: 'SHIFT',
WLPresetSoftTissue: ['NUMPAD1', '1'], toggleCinePlay: 'SPACE',
WLPresetLung: ['NUMPAD2', '2'], toggleCineDialog: '',
WLPresetLiver: ['NUMPAD3', '3'],
WLPresetBone: ['NUMPAD4', '4'], // Preset hotkeys
WLPresetBrain: ['NUMPAD5', '5'] WLPresetSoftTissue: '1',
WLPresetLung: '2',
WLPresetLiver: '3',
WLPresetBone: '4',
WLPresetBrain: '5'
}; };
// For now // 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 // Register the tool switching commands
registerToolCommands({ registerToolCommands({
wwwc: 'Levels', wwwc: 'Window W/L',
zoom: 'Zoom', zoom: 'Zoom',
angle: 'Angle', angle: 'Angle Measurement',
dragProbe: 'Probe', dragProbe: 'Pixel Probe',
ellipticalRoi: 'Ellipse', ellipticalRoi: 'Elliptical Probe',
rectangleRoi: 'Rectangle', rectangleRoi: 'Rectangle',
magnify: 'Magnify', magnify: 'Magnify',
annotate: 'Annotate', annotate: 'Annotate',
stackScroll: 'Stack Scroll', stackScroll: 'Scroll Stack',
pan: 'Pan', pan: 'Pan',
length: 'Length', length: 'Length Measurement',
spine: 'Spine', spine: 'Spine Labels',
wwwcRegion: 'ROI Window' wwwcRegion: 'W/L by Region'
}); });
// Functions to register the viewport commands // Functions to register the viewport commands
@ -99,16 +128,16 @@ Meteor.startup(function() {
// Register the viewport commands // Register the viewport commands
registerViewportCommands({ registerViewportCommands({
zoomIn: 'Zoom in', zoomIn: 'Zoom In',
zoomOut: 'Zoom out', zoomOut: 'Zoom Out',
zoomToFit: 'Zoom to fit', zoomToFit: 'Zoom to Fit',
invert: 'Invert', invert: 'Invert',
flipH: 'Flip horizontally', flipH: 'Flip Horizontally',
flipV: 'Flip vertically', flipV: 'Flip Vertically',
rotateR: 'Rotate right', rotateR: 'Rotate Right',
rotateL: 'Rotate left', rotateL: 'Rotate Left',
resetViewport: 'Reset', resetViewport: 'Reset',
clearTools: 'Clear' clearTools: 'Clear Tools'
}); });
// Functions to register the preset switching commands // Functions to register the preset switching commands
@ -122,75 +151,67 @@ Meteor.startup(function() {
// Register the preset switching commands // Register the preset switching commands
registerWLPresetCommands({ registerWLPresetCommands({
WLPresetSoftTissue: 'SoftTissue', WLPresetSoftTissue: 'W/L Preset: Soft Tissue',
WLPresetLung: 'Lung', WLPresetLung: 'W/L Preset: Lung',
WLPresetLiver: 'Liver', WLPresetLiver: 'W/L Preset: Liver',
WLPresetBone: 'Bone', WLPresetBone: 'W/L Preset: Bone',
WLPresetBrain: 'Brain' 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 // Register viewport navigation commands
OHIF.commands.set(contextName, { 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: { previousDisplaySet: {
name: 'Previous display set', name: 'Previous Series',
action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(false), action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(false),
disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(false) disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(false)
}, },
nextDisplaySet: { nextDisplaySet: {
name: 'Next display set', name: 'Next Series',
action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(true), action: () => OHIF.viewerbase.layoutManager.moveDisplaySets(true),
disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(true) disabled: () => !OHIF.viewerbase.layoutManager.canMoveDisplaySets(true)
}, },
nextPanel: { nextPanel: {
name: 'Next panel', name: 'Next Panel',
action: () => panelNavigation.loadNextActivePanel() action: () => panelNavigation.loadNextActivePanel()
}, },
previousPanel: { previousPanel: {
name: 'Previous panel', name: 'Previous Panel',
action: () => panelNavigation.loadPreviousActivePanel() action: () => panelNavigation.loadPreviousActivePanel()
} }
}, true); }, true);
// Register miscellaneous commands // Register miscellaneous commands
OHIF.commands.set(contextName, { OHIF.commands.set(contextName, {
defaultTool: {
name: 'Default tool',
action: () => toolManager.setActiveTool(toolManager.getDefaultTool())
},
toggleOverlayTags: { toggleOverlayTags: {
name: 'Toggle overlay tags', name: 'Toggle Image Annotations',
action() { action() {
const $dicomTags = $('.imageViewerViewportOverlay .dicomTag'); const $dicomTags = $('.imageViewerViewportOverlay .dicomTag');
$dicomTags.toggle($dicomTags.eq(0).css('display') === 'none'); $dicomTags.toggle($dicomTags.eq(0).css('display') === 'none');
} }
}, },
toggleCinePlay: { toggleCinePlay: {
name: 'Play/Pause', name: 'Play/Pause Cine',
action: viewportUtils.toggleCinePlay, action: viewportUtils.toggleCinePlay,
disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames
}, },
toggleCineDialog: { toggleCineDialog: {
name: 'CINE dialog', name: 'Show/Hide Cine Controls',
action: viewportUtils.toggleCineDialog, action: viewportUtils.toggleCineDialog,
disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames
} }