refactored the status bar...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2016-02-18 04:16:55 +03:00
parent 9085954d04
commit 4c87161b29
6 changed files with 242 additions and 393 deletions

View File

@ -1173,7 +1173,7 @@ stretching in width... */
-ms-user-select: auto; -ms-user-select: auto;
user-select: auto; user-select: auto;
} }
.overlay-info .float-right { .overlay-info .spacer ~ * {
float: right; float: right;
} }
.overlay-info .secondary { .overlay-info .secondary {

View File

@ -1034,7 +1034,7 @@ stretching in width... */
.overlay-info:hover { .overlay-info:hover {
.user-select(auto); .user-select(auto);
} }
.overlay-info .float-right { .overlay-info .spacer~* {
float: right; float: right;
} }
.overlay-info .secondary { .overlay-info .secondary {

View File

@ -2740,6 +2740,7 @@ var DataWithTagsPrototype = {
getTags: function(gids){ getTags: function(gids){
gids = arguments.length > 1 ? [].slice.call(arguments) : gids gids = arguments.length > 1 ? [].slice.call(arguments) : gids
gids = gids == null || gids == 'current' ? this.getImage() : gids gids = gids == null || gids == 'current' ? this.getImage() : gids
gids = gids == null ? [] : gids
gids = gids.constructor !== Array ? [gids] : gids gids = gids.constructor !== Array ? [gids] : gids
if(this.tags == null){ if(this.tags == null){

View File

@ -103,8 +103,7 @@ core.ImageGridFeatures.Feature('viewer-testing', [
//'ui-current-image-indicator-hide-on-screen-nav', //'ui-current-image-indicator-hide-on-screen-nav',
//'ui-base-ribbon-indicator', //'ui-base-ribbon-indicator',
'ui-passive-base-ribbon-indicator', 'ui-passive-base-ribbon-indicator',
'ui-image-state-indicator', 'ui-status-bar',
'ui-global-state-indicator',
'ui-url-history', 'ui-url-history',
'ui-browse-actions', 'ui-browse-actions',

View File

@ -85,17 +85,18 @@ var makeStateIndicatorItem = function(container, type, text){
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// XXX Add status messages and log... var StatusBarActions = actions.Actions({
var ImageStateIndicatorActions = actions.Actions({
config: { config: {
// XXX might be a good idea to add custom components API... 'status-bar-mode': 'minimal',
'global-state-indicator-elements': [ 'status-bar-modes': [
// Q: should index be here or to the right??? 'none',
'minimal',
'full'
],
'status-bar-items': [
'index', 'index',
//'path',
'gid', 'gid',
// XXX is this the right place for this??? //'path',
'info',
// separates left/right aligned elements... // separates left/right aligned elements...
'---', '---',
@ -104,52 +105,26 @@ var ImageStateIndicatorActions = actions.Actions({
'bookmark', 'bookmark',
], ],
'global-state-indicator-elements-full-only': [ // XXX not sure about this...
'status-bar-full-only': [
'gid', 'gid',
'path',
], ],
'global-state-indicator-modes': [
'none',
'minimal',
'full',
],
'global-state-indicator-mode': null,
}, },
// Status indicator handlers... __statusbar_elements__: {
// index: function(item, gid, img){
// Format:
// {
// <key>: <handler>,
// <alias>: <key> | <handler>,
// ...
// }
//
// Supported actions:
// - make
// - update
// - remove
//
// NOTE: built-in handlers can be overloaded by user.
// NOTE: alias loops are ignored.
//
// XXX make this visible to the user???
// XXX is this too complex???
__state_indicator_elements__: {
// XXX make position editable...
// - edit position on click
// - goto position on enter/blur (blur with value)
// - cancel on esc/blur (blur with no value)
// - treat index depending on mode (global/ribbon)
// XXX might be a good idea to auto-reset global index to ribbon...
index: function(action, container, elem, gid){
var that = this var that = this
// construct... gid = gid || this.current
if(action == 'make'){
return $('<span>').addClass(elem) // make an element...
// XXX might be a good idea to make this an input... if(typeof(item) == typeof('str')){
var type = item
item = $('<span>')
.addClass(type)
.append($('<span>') .append($('<span>')
.addClass('position') .addClass('position')
.attr('info', 'Image position (click to edit image position)')
.click(function(){ .click(function(){
// XXX enter edit mode -> select contents... // XXX enter edit mode -> select contents...
// XXX might be a good idea to live select // XXX might be a good idea to live select
@ -157,314 +132,269 @@ var ImageStateIndicatorActions = actions.Actions({
//$(this) //$(this)
// .prop('contenteditable', true) // .prop('contenteditable', true)
// XXX // XXX
})
// XXX STUB...
.on('mouseover', function(){
that.showInfo('Image position (click to edit image position)')
})
// XXX STUB...
.on('mouseout', function(){
that.hideInfo()
})) }))
.append($('<span>') .append($('<span>')
.addClass('length') .addClass('length')
.attr('info', 'Image position (click to toggle ribbon/global)')
// toggle index state... // toggle index state...
.click(function(){ .click(function(){
$(this).parent() $(this).parent()
.toggleClass('global') .toggleClass('global')
that.updateStateIndicators() that.updateStatusBar()
})
// XXX STUB...
.on('mouseover', function(){
that.showInfo('Image position (click to toggle ribbon/global)')
})
// XXX STUB...
.on('mouseout', function(){
that.hideInfo()
})) }))
// update... } else {
} else if(action == 'update'){ var type = item.attr('type')
gid = gid || this.current
var c = container.find('.'+elem)
// global index...
if(c.hasClass('global')){
c.find('.position')
.text(this.data.getImageOrder(gid)+1)
c.find('.length')
.text('/'+ this.data.length)
// ribbon index...
} else {
c.find('.position')
.text(this.data.getImageOrder('ribbon', gid)+1)
c.find('.length')
.text('/'+ this.data.getImages(gid).len)
}
// remove...
} else if(action == 'remove'){
container.find('.'+elem).remove()
} }
// update...
// global index...
if(item.hasClass('global')){
item.find('.position')
.text(this.data.getImageOrder(gid)+1)
item.find('.length')
.text('/'+ this.data.length)
// ribbon index...
} else {
item.find('.position')
.text(this.data.getImageOrder('ribbon', gid)+1)
item.find('.length')
.text('/'+ this.data.getImages(gid).len)
}
return item
}, },
// XXX handle path correctly... // XXX handle path correctly...
gid: function(action, container, elem, gid){ gid: function(item, gid, img){
// construct... var that = this
if(action == 'make'){ gid = gid || this.current
return $('<span>') img = img || (this.images && gid in this.images && this.images[gid])
.addClass(elem + ' expanding-text ')
// make an element...
if(typeof(item) == typeof('str')){
var type = item
item = $('<span>')
.addClass(type + ' expanding-text ')
.attr('info', type == 'gid' ? 'Image GID'
: type == 'path'? 'Image filename/path'
: '')
.append($('<span class="shown">')) .append($('<span class="shown">'))
.append($('<span class="hidden">')) .append($('<span class="hidden">'))
// update... } else {
} else if(action == 'update'){ var type = item.attr('type')
// gid..
if(elem == 'gid'){
var txt = gid.slice(-6)
var text = gid
// path...
// XXX
} else if(elem == 'path'){
var img = this.images && gid in this.images && this.images[gid]
var text = img && img.path || '---'
var txt = text
}
container.find('.'+elem+' .shown').text(txt)
container.find('.'+elem+' .hidden').text(text)
// remove...
} else if(action == 'remove'){
container.find('.'+elem).remove()
} }
// update...
var txt = ''
var text = ''
// gid..
if(type == 'gid'){
txt = gid ? gid.slice(-6) : '-'
text = gid || '-'
// path...
// XXX
} else if(type == 'path'){
text = img && img.path || '---'
txt = text
}
item.find('.shown').text(txt)
item.find('.hidden').text(text)
return item
}, },
path: 'gid', path: 'gid',
mark: function(action, container, elem, gid){ mark: function(item, gid, img){
// construct...
if(action == 'make'){
var that = this
return $('<span>').addClass(elem+'ed')
.click(function(){
that['toggle'+elem.capitalize()]()
})
// XXX STUB...
.on('mouseover', function(){
that.showInfo('Image '
+(elem == 'mark' ? 'selection' : 'bookmark')
+' status (click to toggle)')
})
// XXX STUB...
.on('mouseout', function(){
that.hideInfo()
})
// update...
} else if(action == 'update'){
// NOTE: we are not using .toggleMark('?') and friends
// here to avoid recursion as we might be handling
// them here...
// ...this also simpler than handling '?' and other
// special toggler args in the handler...
var tags = this.data.getTags(gid)
var tag = elem == 'mark' ? 'selected' : 'bookmark'
container.find('.'+elem+'ed')[
tags.indexOf(tag) < 0 ?
'removeClass'
: 'addClass']('on')
// remove...
} else if(action == 'remove'){
container.find('.'+elem+'ed').remove()
}
},
bookmark: 'mark',
// XXX STUB
// XXX need to style this in an appropriate way...
// ...might not be a good spot for this...
// XXX might be a good idea to make the info global, e.g. show
// info for anything that either has or is nested in an
// element that has an info attr...
info: function(action, container, elem, gid){
// construct...
if(action == 'make'){
return $('<span>')
.addClass('info')
.hide()
// remove...
} else if(action == 'remove'){
container.find('.info').remove()
}
},
},
// XXX should this be a toggler???
updateStateIndicators: ['- Interface/',
function(gid){
gid = gid || this.current gid = gid || this.current
var that = this var that = this
var _getHandlers = function(){ if(typeof(item) == typeof('str')){
return Object.keys(that.__state_indicator_elements__ || {}) var type = item
.concat(Object.keys(ImageStateIndicatorActions.__state_indicator_elements__ || {})) item = $('<span>')
.unique() .addClass(type + 'ed')
} .attr('info', 'Image '
var _getHandler = function(key){ +(type == 'mark' ? 'selection' : 'bookmark')
var handler = (that.__state_indicator_elements__ || {})[key] +' status (click to toggle)')
|| (ImageStateIndicatorActions.__state_indicator_elements__ || {})[key] .click(function(){
that['toggle'+type.capitalize()]()
})
if(handler == null){ } else {
return var type = item.attr('type')
}
// handle aliases...
var seen = []
while(typeof(handler) == typeof('str')){
seen.push(handler)
var handler = (that.__state_indicator_elements__ || {})[handler]
|| (ImageStateIndicatorActions.__state_indicator_elements__ || {})[handler]
// check for loops...
if(seen.indexOf(handler) >= 0){
console.error('state indicator alias loop detected at:', key)
handler = null
}
}
return handler
} }
var global = this.ribbons.viewer.find('.state-indicator-container.global-info') // NOTE: we are not using .toggleMark('?') and friends
if(global.length == 0){ // here to avoid recursion as we might be handling
//global = makeStateIndicator('global-info') // them here...
global = makeStateIndicator('global-info overlay-info') // ...this also simpler than handling '?' and other
// special toggler args in the handler...
var tags = this.data.getTags(gid)
var tag = type == 'mark' ? 'selected' : 'bookmark'
item[tags.indexOf(tag) < 0 ?
'removeClass'
: 'addClass']('on')
var align = '' return item
var order = this.config['global-state-indicator-elements'].slice() },
bookmark: 'mark',
},
var i = order.indexOf('---') // NOTE: to reset the status bar cycle through 'none' mode to
// rearrange the tail section... // reconstruct all the items.
// NOTE: this is here as we need to push the floated toggleStatusBar: ['Interface/Toggle status bar modes',
// right items in reverse order...
if(i >= 0){
order = order.concat(order.splice(i+1, order.length).reverse())
}
order.forEach(function(elem){
var full_only = that.config['global-state-indicator-elements-full-only'].indexOf(elem) >= 0
var res = $()
// spacer...
if(elem == '---'){
align = 'float-right'
// handlers...
} else {
var handler = _getHandler(elem)
// do the call...
if(handler != null){
res = handler.call(that, 'make', global, elem, gid)
}
}
// append the actual element...
res.length > 0 && res
.addClass(align +' '+ (full_only && 'full-only'))
.appendTo(global)
})
// add and init in the correct state...
global.appendTo(this.ribbons.viewer)
if(this.config['global-state-indicator-mode']){
this.toggleStateIndicator(this.config['global-state-indicator-mode'])
}
}
if(!gid){
return
}
// populate the info...
_getHandlers().forEach(function(key){
_getHandler(key).call(that, 'update', global, key, gid)
})
}],
toggleStateIndicator: ['Interface/Toggle state indicator modes',
toggler.CSSClassToggler( toggler.CSSClassToggler(
// XXX change class...
function(){ function(){
return this.ribbons.viewer.find('.state-indicator-container.global-info') }, var bar = this.ribbons.viewer.find('.state-indicator-container.global-info')
function(){ return this.config['global-state-indicator-modes'] }, if(bar.length == 0){
function(state){ this.config['global-state-indicator-mode'] = state }) ], bar = makeStateIndicator('global-info overlay-info')
.on('mouseover', function(){
var t = $(event.target)
var info = t.attr('info')
|| t.parents('.overlay-info, [info]').first().attr('info')
if(info){
bar.find('.info').text(info)
}
})
.on('mouseout', function(){
bar.find('.info').empty()
})
.appendTo(this.ribbons.viewer)
}
return bar
},
function(){ return this.config['status-bar-modes'] },
// XXX check if we will be getting gid reliably...
function(state, bar, gid){
var that = this
this.config['status-bar-mode'] = state
// XXX Should these be a separate class??? // destroy...
showInfo: ['- Interface/', if(state == 'none'){
function(text){ bar.empty()
this.ribbons.viewer.find('.state-indicator-container.global-info .info')
.text(text) // build/update...
.stop() } else {
.css('opacity', 1) gid = gid || this.current
.show() var img = this.images && this.images[gid]
}],
hideInfo: ['- Interface/', var _getHandler = function(key){
function(){ var elems = that.__statusbar_elements__ || {}
this.ribbons.viewer.find('.state-indicator-container.global-info .info') var base_elems = StatusBarActions.__statusbar_elements__ || {}
.fadeOut()
}] 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...
if(bar.children().length <= 0){
var items = this.config['status-bar-items'].slice()
// rearrange the tail section...
// NOTE: this is here as we need to push the floated
// right items in reverse order...
var i = items.indexOf('---')
items = i >= 0 ?
items.concat(items.splice(i+1, items.length).reverse())
: items
items.forEach(function(item){
// spacer...
if(item == '---'){
//item = $('<span class="spacer">')
item = $('<span class="spacer info">')
// info items...
} else {
var handler = _getHandler(item)
var type = item
item = (handler ?
handler.call(that, item, gid, img)
: $('<span>'))
.attr('type', item)
if('status-bar-full-only' in that.config
&& that.config['status-bar-full-only'].indexOf(type) >= 0){
item.addClass('full-only')
}
}
bar.append(item)
})
// update...
} else {
var items = 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, item, gid, img)
}
})
}
}
})],
updateStatusBar: ['Interface/Update satus bar',
function(){ this.toggleStatusBar('!') }]
}) })
// XXX an alternative approach: var StatusBar =
// - global status area module.StatusBar = core.ImageGridFeatures.Feature({
// - status bar for local status
// - as in gen3
// - add image status
//
// General item format:
// - minimal state - only short version / icon is shown
// - when not active a disabled state/icon is shown
//
// - expanded state - status bar sows expanded state (only?)
// - title/help shown above
// - floating text, transparent bg
// - same align as item
//
// XXX Q: can title bar be used instead of global state indication???
// ...especially if we are indicating only crop...
// XXX add styling:
// - element spacing
// - tip text
// - avoid multi-line
// XXX rename to status bar???
var ImageStateIndicator =
module.ImageStateIndicator = core.ImageGridFeatures.Feature({
title: '', title: '',
doc: '', doc: '',
tag: 'ui-image-state-indicator', tag: 'ui-status-bar',
depends: [ depends: [
'ui', 'ui',
'ui-single-image-view',
], ],
actions: ImageStateIndicatorActions, actions: StatusBarActions,
handlers: [ handlers: [
['start', ['start',
function(){ function(){
if(this.config['global-state-indicator-mode']){ if(this.config['status-bar-mode']){
this.toggleStateIndicator(this.config['global-state-indicator-mode']) this.toggleStatusBar(this.config['status-bar-mode'])
} }
}], }],
['focusImage', ['focusImage',
function(){ function(){
this.updateStateIndicators() this.updateStatusBar()
}], }],
[[ [[
'tag', 'tag',
@ -476,7 +406,7 @@ module.ImageStateIndicator = core.ImageGridFeatures.Feature({
&& (gids.indexOf('current') >= 0 && (gids.indexOf('current') >= 0
|| gids.indexOf(this.current) >= 0) || gids.indexOf(this.current) >= 0)
|| this.data.getImage(gids) == this.current){ || this.data.getImage(gids) == this.current){
this.updateStateIndicators() this.updateStatusBar()
} }
}], }],
], ],
@ -484,87 +414,6 @@ module.ImageStateIndicator = core.ImageGridFeatures.Feature({
//---------------------------------------------------------------------
/*
var InfoIndicatorActions = actions.Actions({
config: {
// XXX might be a good idea to add custom components API...
'global-state-indicator-elements': [
// Q: should index be here or to the right???
'index',
//'path',
'gid',
// XXX is this the right place for this???
'info',
// separates left/right aligned elements...
'---',
'mark',
'bookmark',
],
},
__state_indicator_elements__: {
// XXX STUB
// XXX need to style this in an appropriate way...
// ...might not be a good spot for this...
// XXX might be a good idea to make the info global, e.g. show
// info for anything that either has or is nested in an
// element that has an info attr...
info: function(action, container, elem, gid){
// construct...
if(action == 'make'){
return $('<span>')
.addClass('info')
.hide()
// remove...
} else if(action == 'remove'){
container.find('.info').remove()
}
},
},
// XXX Should these be a separate class???
showInfo: ['- Interface/',
function(text){
this.ribbons.viewer.find('.state-indicator-container.global-info .info')
.text(text)
.stop()
.css('opacity', 1)
.show()
}],
hideInfo: ['- Interface/',
function(){
this.ribbons.viewer.find('.state-indicator-container.global-info .info')
.fadeOut()
}]
})
InfoIndicatorActions
.__state_indicator_elements__.__proto__
= ImageStateIndicatorActions.__state_indicator_elements__
var InfoIndicator =
module.InfoIndicator = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-info-indicator',
depends: [
'ui',
'ui-image-state-indicator',
],
actions: InfoIndicatorActions,
})
*/
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// XXX // XXX

View File

@ -219,7 +219,7 @@ module.GLOBAL_KEYBOARD = {
}, },
I: { I: {
default: 'showMetadata', default: 'showMetadata',
shift: 'toggleStateIndicator', shift: 'toggleStatusBar',
ctrl: 'toggleMark!: "ribbon"', ctrl: 'toggleMark!: "ribbon"',
'ctrl+shift': 'showMetadata: "current" "full"', 'ctrl+shift': 'showMetadata: "current" "full"',