reworked keyboard to support better searching for keys + added capslock state support...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-04-22 06:20:12 +03:00
parent c64fe92afe
commit b2c9a5cb34
5 changed files with 211 additions and 32 deletions

View File

@ -1482,6 +1482,23 @@ stretching in width... */
opacity: 1; opacity: 1;
} }
/* Capslock indicator.. */
.overlay-info .capslock-state {
font-size: small;
font-style: italic;
margin: 0px 10pt;
}
.overlay-info .capslock-state:not(.on) {
opacity: 0.2;
}
.overlay-info .capslock-state.on {
color: yellow;
opacity: 0.6;
}
.overlay-info .capslock-state:hover {
opacity: 1
}
/*************************************************** Global status ***/ /*************************************************** Global status ***/

View File

@ -271,16 +271,20 @@ module.GLOBAL_KEYBOARD = {
shift_End: 'lastRibbon', shift_End: 'lastRibbon',
Up: 'prevRibbon', Up: 'prevRibbon',
caps_shift_Up: 'prevRibbon',
Down: 'nextRibbon', Down: 'nextRibbon',
caps_shift_Down: 'nextRibbon',
// shifting... // shifting...
shift_Up: 'shiftImageUp', shift_Up: 'shiftImageUp',
caps_Up: 'shiftImageUp',
alt_shift_Up: 'travelImageUp', alt_shift_Up: 'travelImageUp',
ctrl_shift_Up: 'shiftImageUpNewRibbon', ctrl_shift_Up: 'shiftImageUpNewRibbon',
ctrl_Up: 'shiftMarkedUp', ctrl_Up: 'shiftMarkedUp',
shift_Down: 'shiftImageDown', shift_Down: 'shiftImageDown',
caps_Down: 'shiftImageDown',
alt_shift_Down: 'travelImageDown', alt_shift_Down: 'travelImageDown',
ctrl_shift_Down: 'shiftImageDownNewRibbon', ctrl_shift_Down: 'shiftImageDownNewRibbon',
ctrl_Down: 'shiftMarkedDown', ctrl_Down: 'shiftMarkedDown',

View File

@ -64,7 +64,6 @@ core.ImageGridFeatures.Feature('viewer-testing', [
'ui-single-image', 'ui-single-image',
//'ui-partial-ribbons', //'ui-partial-ribbons',
// XXX this is still experimental but seems to already work faster...
'ui-partial-ribbons-2', 'ui-partial-ribbons-2',
'marks', 'marks',

View File

@ -62,6 +62,7 @@ var StatusBarActions = actions.Actions({
// separates left/right aligned elements... // separates left/right aligned elements...
'---', '---',
'edit-mode',
'mark', 'mark',
'bookmark', 'bookmark',
], ],
@ -90,7 +91,39 @@ var StatusBarActions = actions.Actions({
}, },
__statusbar_elements__: { __statusbar_elements__: {
/* item template...
item: function(item){
// cleanup...
if(item == null){
// XXX
return
}
// setup the item DOM...
if(typeof(item) == typeof('str')){
var type = item
item = $('<span>')
.addClass('item-example')
.attr('type', item)
.text('example')
// get stuff from the item...
} else {
var type = item.attr('type')
}
// update the item...
// XXX
return item
},
*/
index: function(item, gid, img){ index: function(item, gid, img){
// cleanup...
if(item == null){
return
}
var that = this var that = this
gid = gid || this.current gid = gid || this.current
@ -188,6 +221,11 @@ var StatusBarActions = actions.Actions({
return item return item
}, },
ribbon: function(item, gid, img){ ribbon: function(item, gid, img){
// cleanup...
if(item == null){
return
}
var that = this var that = this
// get ribbon number... // get ribbon number...
@ -248,6 +286,11 @@ var StatusBarActions = actions.Actions({
return item return item
}, },
changes: function(item, gid, img){ changes: function(item, gid, img){
// cleanup...
if(item == null){
return
}
if(typeof(item) == typeof('str')){ if(typeof(item) == typeof('str')){
item = $('<span>') item = $('<span>')
.addClass('changes') .addClass('changes')
@ -260,6 +303,11 @@ var StatusBarActions = actions.Actions({
}, },
// XXX handle path correctly... // XXX handle path correctly...
gid: function(item, gid, img){ gid: function(item, gid, img){
// cleanup...
if(item == null){
return
}
var that = this var that = this
gid = gid || this.current gid = gid || this.current
img = img || (this.images && gid in this.images && this.images[gid]) img = img || (this.images && gid in this.images && this.images[gid])
@ -309,8 +357,54 @@ var StatusBarActions = actions.Actions({
return item return item
}, },
path: 'gid', path: 'gid',
'edit-mode': function(item){
// cleanup...
if(item == null){
this.__edit_mode_indicator_update
&& this.off('keyPress', this.__edit_mode_indicator_update)
delete this.__edit_mode_indicator_update
return
}
var update = this.__edit_mode_indicator_update = this.__edit_mode_indicator_update
|| (function(){
var caps = this.keyboard.capslock
caps = typeof(event) != 'undefined' && event.getModifierState ?
event.getModifierState('CapsLock')
: caps
item
.attr('info', 'Edit mode '
+ (caps ? 'on' : 'off')
+ ' (Click to update / Press CapsLock to toggle)')
[caps ? 'addClass' : 'removeClass']('on')
}).bind(this)
if(typeof(item) == typeof('str')){
var type = item
item = $('<span>')
.addClass('capslock-state expanding-text')
.append($('<span class="shown">')
.text('E'))
.append($('<span class="hidden">')
.text('Edit mode'))
.click(update)
// XXX need a way to cleanly unhandle this...
this.on('keyPress', update)
}
update()
return item
},
// XXX show menu in the appropriate corner... // XXX show menu in the appropriate corner...
mark: function(item, gid, img){ mark: function(item, gid, img){
// cleanup...
if(item == null){
return
}
gid = gid || this.current gid = gid || this.current
var that = this var that = this
@ -398,8 +492,50 @@ var StatusBarActions = actions.Actions({
var that = this var that = this
this.config['status-bar-mode'] = state this.config['status-bar-mode'] = state
var _getHandler = function(key){
var elems = that.__statusbar_elements__ || {}
var base_elems = StatusBarActions.__statusbar_elements__ || {}
var handler = elems[key] || base_elems[key]
if(handler == null){
return
}
// handle aliases...
var seen = []
while(typeof(handler) == typeof('str')){
seen.push(handler)
var handler = elems[handler] || base_elems[handler]
// check for loops...
if(seen.indexOf(handler) >= 0){
console.error('state indicator alias loop detected at:', key)
handler = null
break
}
}
return handler
}
// destroy... // destroy...
if(state == 'none'){ if(state == 'none'){
// notify items that they are removed...
bar.children()
.each(function(i, item){
item = $(item)
var type = item.attr('type')
if(type == null){
return
}
var handler = _getHandler(type)
if(handler != null){
handler.call(that, null)
}
})
bar.empty() bar.empty()
// build/update... // build/update...
@ -407,32 +543,6 @@ var StatusBarActions = actions.Actions({
gid = gid || this.current gid = gid || this.current
var img = this.images && this.images[gid] var img = this.images && this.images[gid]
var _getHandler = function(key){
var elems = that.__statusbar_elements__ || {}
var base_elems = StatusBarActions.__statusbar_elements__ || {}
var handler = elems[key] || base_elems[key]
if(handler == null){
return
}
// handle aliases...
var seen = []
while(typeof(handler) == typeof('str')){
seen.push(handler)
var handler = elems[handler] || base_elems[handler]
// check for loops...
if(seen.indexOf(handler) >= 0){
console.error('state indicator alias loop detected at:', key)
handler = null
break
}
}
return handler
}
// build... // build...
if(bar.children().length <= 0){ if(bar.children().length <= 0){
var items = this.config['status-bar-items'].slice() var items = this.config['status-bar-items'].slice()

View File

@ -14,7 +14,8 @@ var object = require('lib/object')
/*********************************************************************/ /*********************************************************************/
var MODIFIERS = var MODIFIERS =
module.MODIFIERS = [ 'ctrl', 'meta', 'alt', 'shift' ] //module.MODIFIERS = [ 'ctrl', 'meta', 'alt', 'shift' ]
module.MODIFIERS = [ 'caps', 'ctrl', 'meta', 'alt', 'shift' ]
var KEY_SEPARATORS = var KEY_SEPARATORS =
@ -204,12 +205,17 @@ var event2key =
module.event2key = module.event2key =
function event2key(evt){ function event2key(evt){
evt = evt || event evt = evt || event
// NOTE: we do not care about the jQuery wrapper here...
evt = evt.originalEvent || evt
var key = [] var key = []
evt.ctrlKey && key.push('ctrl') evt.ctrlKey && key.push('ctrl')
evt.altKey && key.push('alt') evt.altKey && key.push('alt')
evt.metaKey && key.push('meta') evt.metaKey && key.push('meta')
evt.shiftKey && key.push('shift') evt.shiftKey && key.push('shift')
evt.getModifierState
&& evt.getModifierState('CapsLock')
&& key.push('caps')
var k = code2key(evt.keyCode) var k = code2key(evt.keyCode)
@ -760,8 +766,7 @@ var KeyboardPrototype = {
mode = '*' mode = '*'
} }
var joinKeys = function(key, shift_key){
var genKeys = function(key, shift_key){
// match candidates... // match candidates...
return key_separators return key_separators
// full key... // full key...
@ -772,6 +777,40 @@ var KeyboardPrototype = {
.map(function(s){ return shift_key.join(s) }) .map(function(s){ return shift_key.join(s) })
: []) : [])
.unique() } .unique() }
var normalize = this.normalizeKey
var keyCombinations = function(key, shift_key){
if(key.length <= 1){
return key
}
var _combinations = function(level, res){
var next = []
level
.map(function(elem){
var c = normalize(elem)
c = typeof(c) == typeof('str') ? c : c.join('+++')
res.indexOf(c) < 0
&& res.push(c)
&& elem
//.reverse()
.slice(0, -1)
.map(function(_, i){
var s = elem.slice()
s.splice(i, 1)
s.length > 0
//&& next.push(s.reverse())
&& next.push(s)
})
})
next.length > 0
&& _combinations(next, res)
return res
}
return _combinations(shift_key ? [key, shift_key] : [key], [])
// XXX is there a better way???
//.map(function(e){ return e.split(/\+\+\+/g).concat(key.slice(-1)) })
.map(function(e){ return joinKeys(e.split(/\+\+\+/g)) })
.reduce(function(a, b){ return a.concat(b) }, [])
}
var walkAliases = function(bindings, handler, modifiers){ var walkAliases = function(bindings, handler, modifiers){
var seen = [] var seen = []
var modifiers = modifiers || [] var modifiers = modifiers || []
@ -803,7 +842,8 @@ var KeyboardPrototype = {
var shift_key = this.shifted(key) var shift_key = this.shifted(key)
// match candidates... // match candidates...
var keys = genKeys(key, shift_key).unique() //var keys = joinKeys(key, shift_key).unique()
var keys = keyCombinations(key, shift_key)
// get modes... // get modes...
var modes = mode == '*' ? Object.keys(keyboard) var modes = mode == '*' ? Object.keys(keyboard)
@ -817,7 +857,8 @@ var KeyboardPrototype = {
var k = key.slice(-1)[0] var k = key.slice(-1)[0]
var c = this.key2code(k) var c = this.key2code(k)
var mod = genKeys(key.slice(0, -1).concat('')) //var mod = joinKeys(key.slice(0, -1).concat(''))
var mod = keyCombinations(key.slice(0, -1).concat(''))
var drop = mode == 'test' || mode == '?' var drop = mode == 'test' || mode == '?'
for(var i=0; i < modes.length; i++){ for(var i=0; i < modes.length; i++){
@ -983,6 +1024,11 @@ KeyboardWithCSSModes.prototype.__proto__ = Keyboard.prototype
// // used directly... // // used directly...
// handler('ctrl_C', function(k){ console.log('Not bound:', k) }) // handler('ctrl_C', function(k){ console.log('Not bound:', k) })
// //
// NOTE: the handler will also set the .capslock attribute on the
// keyboard object and update it on each key press...
// NOTE: if .capslock is false means that either it is not on or
// undetectable...
// NOTE: before any key is pressed the .capslock is set to undefined
var makeKeyboardHandler = var makeKeyboardHandler =
module.makeKeyboardHandler = module.makeKeyboardHandler =
function makeKeyboardHandler(keyboard, unhandled, actions){ function makeKeyboardHandler(keyboard, unhandled, actions){
@ -991,6 +1037,7 @@ function makeKeyboardHandler(keyboard, unhandled, actions){
keyboard keyboard
//: Keyboard(keyboard, checkGlobalMode) //: Keyboard(keyboard, checkGlobalMode)
: Keyboard(keyboard) : Keyboard(keyboard)
kb.capslock = undefined
return function(key, no_match){ return function(key, no_match){
no_match = no_match || unhandled no_match = no_match || unhandled
@ -1002,6 +1049,8 @@ function makeKeyboardHandler(keyboard, unhandled, actions){
if(typeof(key) != typeof('str')){ if(typeof(key) != typeof('str')){
evt = key evt = key
key = kb.event2key(evt) key = kb.event2key(evt)
kb.capslock = key.indexOf('caps') >= 0
} }
var handlers = kb.handler('test', key) var handlers = kb.handler('test', key)