added base ribbon indicators -- passive done, active still needs work...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2015-12-20 20:59:19 +03:00
parent ec490e56e9
commit 62d5b45e7f
7 changed files with 364 additions and 90 deletions

View File

@ -274,6 +274,34 @@ stretching in width... */
.ribbon:last-child {
margin-bottom: 0px;
}
.base-ribbon-marker {
position: absolute;
height: 100%;
color: transparent;
font-size: 20pt;
font-weight: bold;
}
.base-ribbon-marker:after {
content: "base ribbon";
display: block;
position: absolute;
width: 300px;
bottom: 0px;
left: 0px;
padding: 5px;
box-sizing: border-box;
z-index: 1000;
color: white;
background: black;
opacity: 0.2;
font-size: 20pt;
font-weight: bold;
transform-origin: bottom left;
transform: rotate(-90deg);
}
.single-image-mode.viewer .base-ribbon-marker {
display: none;
}
/*********************************************************** Image ***/
.marker,
.current-marker,
@ -1405,6 +1433,16 @@ stretching in width... */
font-size: 14px;
opacity: 0.8;
}
.show-passive-base-ribbon-indicator:not(.single-image-mode) .base.ribbon:after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 6px;
top: 101%;
background: yellow;
opacity: 0.8;
}
/*************************************************** Progress bars ***/
progress {
-webkit-appearance: none;

View File

@ -407,6 +407,42 @@ stretching in width... */
margin-bottom: 0px;
}
.base-ribbon-marker {
position: absolute;
height: 100%;
color: transparent;
font-size: 20pt;
font-weight: bold;
}
.base-ribbon-marker:after {
content: "base ribbon";
display: block;
position: absolute;
width: @image-tile-size;
bottom: 0px;
left: 0px;
padding: 5px;
box-sizing: border-box;
z-index: 1000;
color: white;
background: black;
opacity: 0.2;
font-size: 20pt;
font-weight: bold;
transform-origin: bottom left;
transform: rotate(-90deg);
}
.single-image-mode.viewer .base-ribbon-marker {
display: none;
}
/*********************************************************** Image ***/
@ -1105,7 +1141,6 @@ stretching in width... */
display: none;
}
/* these are generic containers for indicators */
.global-mode-indicators,
.context-mode-indicators {
@ -1290,6 +1325,23 @@ stretching in width... */
opacity: 0.8;
}
.show-passive-base-ribbon-indicator:not(.single-image-mode) .base.ribbon:after {
content: "";
position: absolute;
display: block;
width: 100%;
height: @image-tile-size/50;
top: 101%;
//left: -100%;
background: yellow;
opacity: 0.8;
}
/*************************************************** Progress bars ***/

View File

@ -259,6 +259,8 @@ actions.Actions({
// 'visual' - focus visually closest to current image
//
// NOTE: default mode is set in .config.ribbon-focus-mode
// NOTE: this explicitly does nothing if mode is unrecognised, this
// is done to add support for other custom modes...
focusRibbon: ['- Navigate/Focus Ribbon',
function(target, mode){
var data = this.data
@ -287,7 +289,7 @@ actions.Actions({
} else if(mode == 'first' || mode == 'last'){
var t = data.getImage(mode, r)
// unknown mode...
// unknown mode -- do nothing...
} else {
return
}
@ -388,12 +390,6 @@ actions.Actions({
this.nextImage(c[Math.min.apply(null, Object.keys(c))])
}],
// XXX should these be here???
prevTagged: ['- Navigate/Previous image tagged with tag',
makeTagWalker('prev')],
nextTagged: ['- Navigate/Next image tagged with tag',
makeTagWalker('next')],
firstRibbon: ['Navigate/First ribbon',
function(){ this.focusRibbon('first') }],
lastRibbon: ['Navigate/Last ribbon',
@ -662,6 +658,12 @@ module.TagsActions = actions.Actions({
this.data.tagsFromImages(images, mode)
}
}],
prevTagged: ['- Navigate/Previous image tagged with tag',
makeTagWalker('prev')],
nextTagged: ['- Navigate/Next image tagged with tag',
makeTagWalker('next')],
})

View File

@ -69,6 +69,8 @@ core.ImageGridFeatures.Feature('viewer-testing', [
// NOTE: only one of these can be set...
'ui-current-image-indicator-hide-on-fast-screen-nav',
//'ui-current-image-indicator-hide-on-screen-nav',
//'ui-base-ribbon-indicator',
'ui-passive-base-ribbon-indicator',
'ui-image-state-indicator',
'ui-global-state-indicator',
'ui-url-history',
@ -76,11 +78,14 @@ core.ImageGridFeatures.Feature('viewer-testing', [
'ui-browse-actions',
'ui-widget-test',
// ui control...
'ui-clickable',
//'ui-direct-control-jquery',
'ui-direct-control-gsap',
// experimental and optional features...
//'auto-single-image',
//'auto-ribbon',
// XXX not yet fully tested...
'system-journal',

View File

@ -468,6 +468,30 @@ actions.Actions({
}
}
}],
focusRibbon: [
function(target, mode){
mode = mode || this.config['ribbon-focus-mode']
var c = this.data.getRibbonOrder()
var i = this.data.getRibbonOrder(target)
// NOTE: we are not changing the direction here based on
// this.direction as swap will confuse the user...
var direction = c < i ? 'before' : 'after'
if(mode == 'visual'){
var ribbons = this.ribbons
var r = this.data.getRibbon(target)
var t = ribbons.getImageByPosition('current', r)
if(t.length > 1){
t = t.eq(direction == 'before' ? 0 : 1)
}
t = ribbons.getElemGID(t)
this.focusImage(t, r)
}
}],
setBaseRibbon: [
function(target){
var r = this.data.getRibbon(target)
@ -701,10 +725,12 @@ module.Viewer = core.ImageGridFeatures.Feature({
function(){
var that = this
// load themes from config...
if(this.config.theme){
this.toggleTheme(this.config.theme)
}
// center viewer on resize events...
if(!this.__viewer_resize){
this.__viewer_resize = function(){
if(that.__centering_on_resize){
@ -728,33 +754,6 @@ module.Viewer = core.ImageGridFeatures.Feature({
delete that.__viewer_resize
}
}],
// add support for visual mode...
// XXX 'visual' mode fails in single-image-mode....
['focusRibbon',
function(res, target, mode){
mode = mode || this.config['ribbon-focus-mode']
var c = this.data.getRibbonOrder()
var i = this.data.getRibbonOrder(r)
// NOTE: we are not changing the direction here based on
// this.direction as swap will confuse the user...
var direction = c < i ? 'before' : 'after'
if(mode == 'visual'){
var ribbons = this.ribbons
var r = this.data.getRibbon(target)
var t = ribbons.getImageByPosition('current', r)
if(t.length > 1){
t = t.eq(direction == 'before' ? 0 : 1)
}
t = ribbons.getElemGID(t)
this.focusImage(t, r)
}
}],
],
})
@ -2350,6 +2349,125 @@ module.CurrentImageIndicatorHideOnScreenNav = core.ImageGridFeatures.Feature({
//---------------------------------------------------------------------
// XXX this should:
// - float to the left of a ribbon if image #1 is fully visible (working)
// - float at left of viewer if image #1 is off screen...
// - float on the same level as the base ribbon...
// XXX make this an action...
var updateBaseRibbonIndicator = function(img){
var scale = this.ribbons.getScale()
var base = this.ribbons.getRibbon('base')
img = this.ribbons.getImage(img)
var m = base.find('.base-ribbon-marker')
if(base.length == 0){
return
}
if(m.length == 0){
m = this.ribbons.viewer.find('.base-ribbon-marker')
// make the indicator...
if(m.length == 0){
m = $('<div>')
.addClass('base-ribbon-marker')
.text('base ribbon')
}
m.prependTo(base)
}
// XXX this is wrong -- need to calculate the offset after the move and not now...
if(base.offset().left < 0){
m.css('left', (img.position().left + img.width()/2 - this.ribbons.viewer.width()/2) / scale)
} else {
m.css('left', '')
}
}
var BaseRibbonIndicator =
module.BaseRibbonIndicator = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-base-ribbon-indicator',
depends: ['ui'],
handlers: [
// move marker to current image...
['focusImage.pre',
function(target){
updateBaseRibbonIndicator.call(this, target)
}],
// prevent animations when focusing ribbons...
['focusRibbon.pre setBaseRibbon',
function(){
updateBaseRibbonIndicator.call(this)
/*
this.ribbons.preventTransitions(m)
return function(){
this.ribbons.restoreTransitions(m)
}
*/
}],
]
})
var PassiveBaseRibbonIndicator =
module.PassiveBaseRibbonIndicator = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-passive-base-ribbon-indicator',
depends: ['ui'],
config: {
'ui-show-passive-base-ribbon-indicator': true,
},
actions: actions.Actions({
togglePassiveBaseRibbonIndicator: ['Interface/Toggle passive base ribbon indicator',
CSSClassToggler(
function(){ return this.ribbons.viewer },
'show-passive-base-ribbon-indicator',
function(state){
this.config['ui-show-passive-base-ribbon-indicator'] = state == 'on' }) ],
}),
handlers: [
['start',
function(){
this.togglePassiveBaseRibbonIndicator(
this.config['ui-show-passive-base-ribbon-indicator'] ?
'on' : 'off')
}]
],
})
//---------------------------------------------------------------------
// XXX add setup / teardown...
// XXX might be a good idea to merge this with single image mode...
var makeStateIndicator = function(type){
return $('<div>')
.addClass('state-indicator-container ' + type || '')
}
var makeStateIndicatorItem = function(container, type, text){
var item = $('<div>')
.addClass('item '+ type || '')
.attr('text', text)
this.ribbons.viewer.find('.state-indicator-container.'+container)
.append(item)
return item
}
// XXX
var ImageStateIndicator =
@ -2358,7 +2476,38 @@ module.ImageStateIndicator = core.ImageGridFeatures.Feature({
doc: '',
tag: 'ui-image-state-indicator',
depends: ['ui'],
depends: [
'ui',
'ui-single-image-view',
],
actions: actions.Actions({
updateStateIndicators: ['- Interface/',
function(){
// make/get indicator containers...
var image = this.ribbons.viewer.find('.state-indicator-container.image-info')
if(image.length == 0){
image = makeStateIndicator('.image-info')
.appendTo(this.ribbons.viewer)
}
var global = this.ribbons.viewer.find('.state-indicator-container.global-info')
if(global.length == 0){
global = makeStateIndicator('.global-info')
.appendTo(this.ribbons.viewer)
}
// XXX specific status...
// XXX
}],
}),
handlers: [
['focusImage',
function(){
this.updateStateIndicators()
}]
],
})
@ -2372,14 +2521,17 @@ module.GlobalStateIndicator = core.ImageGridFeatures.Feature({
doc: '',
tag: 'ui-global-state-indicator',
depends: ['ui'],
depends: [
'ui'
//'ui-single-image-view',
],
})
//---------------------------------------------------------------------
// XXX experimental...
// ...not sure if this is the right way to go...
// XXX need to get the minimal size and not the width as results will
// depend on viewer format...
@ -2415,28 +2567,35 @@ module.AutoSingleImage = core.ImageGridFeatures.Feature({
],
})
var AutoRibbon =
module.AutoRibbon = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'auto-ribbon',
handlers: [
['nextRibbon prevRibbon',
function(){
this.toggleSingleImage('?') == 'on'
&& this.toggleSingleImage('off') }],
],
})
//---------------------------------------------------------------------
// XXX add tap/click to focus...
// XXX add pinch-zoom...
// XXX add vertical scroll...
// XXX disable drag in single image mode unless image is larger than the screen...
// XXX BUG: current image indicator gets shown in random places...
var DirectControljQ =
module.DirectControljQ = core.ImageGridFeatures.Feature({
// XXX add setup/taredown...
var Clickable =
module.Clickable = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-direct-control-jquery',
tag: 'ui-clickable',
depends: [
'ui',
// this is only used to trigger reoad...
//'ui-partial-ribbons',
],
// XXX add setup/taredown...
handlers: [
// setup click targets...
// XXX click only if we did not drag...
@ -2468,7 +2627,30 @@ module.DirectControljQ = core.ImageGridFeatures.Feature({
})
}
}],
],
})
// XXX add tap/click to focus...
// XXX add pinch-zoom...
// XXX add vertical scroll...
// XXX disable drag in single image mode unless image is larger than the screen...
// XXX BUG: current image indicator gets shown in random places...
var DirectControljQ =
module.DirectControljQ = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-direct-control-jquery',
exclusive: ['ui-direct-control'],
depends: [
'ui',
// this is only used to trigger reoad...
//'ui-partial-ribbons',
],
// XXX add setup/taredown...
handlers: [
// setup ribbon dragging...
// XXX this is really sloooooow...
// XXX hide current image indicator as soon as the image is not visible...
@ -2517,13 +2699,14 @@ module.DirectControljQ = core.ImageGridFeatures.Feature({
// XXX disable drag in single image mode unless image is larger than the screen...
// XXX do not use this for production -- GSAp has bad license...
// XXX do not use this for production -- GSAp has a bad license...
var DirectControlGSAP =
module.DirectControlGSAP = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-direct-control-gsap',
exclusive: ['ui-direct-control'],
depends: [
'ui',
// this is only used to trigger reoad...
@ -2532,37 +2715,6 @@ module.DirectControlGSAP = core.ImageGridFeatures.Feature({
// XXX add setup/taredown...
handlers: [
// setup click targets...
// XXX click only if we did not drag...
['updateImage',
function(res, gid){
var that = this
var img = this.ribbons.getImage(gid)
// set the clicker only once...
if(!img.prop('clickable')){
var x, y
img
.prop('clickable', true)
.on('mousedown touchstart', function(){
x = event.clientX
y = event.clientY
t = Date.now()
})
.on('mouseup touchend', function(){
if(x != null
&& Math.max(
Math.abs(x - event.clientX),
Math.abs(y - event.clientY)) < 5){
// this will prevent double clicks...
x = null
y = null
that.focusImage(that.ribbons.getElemGID($(this)))
}
})
}
}],
// setup ribbon dragging...
// XXX fast but uses messes up positioning...
// ...setting type: 'left' will fix this but make things

View File

@ -20,7 +20,7 @@ var object = require('lib/object')
//
// Goals:
// - provide a unified mechanism to define and manage user API's for
// use in UI-hooks, keyboard mappings, scripting, ...
// use in UI-hooks, keyboard mappings, scripting, ... etc.
// - a means to generate configuration UI's
// - a means to generate documentation
//
@ -34,14 +34,31 @@ var object = require('lib/object')
// - the action handlers are bound relative to it (._action_handlers)
//
// Action
//
// + pre + pre + + post + post +
// Action event handler: o-------x o-------x
// v ^
// Actions o-------x o-------x
// v ^
// Root Action o-------x
//
// - a method, created by Action(..),
// - calls all the shadowed actions in the inheritance chain in
// sequence implicitly,
// - calls all the shadowed/overloaded actions in the inheritance
// chain in sequence implicitly,
// NOTE: there is no way to prevent an action in the chain from
// running, this is by design, i.e. no way to full shadow.
// - returns the action set (for call chaining),
// running, this is by design, i.e. no way to fully shadow.
// - actions that do not shadow anything are called root actions.
// - returns the action set by default (for call chaining),
// - the base/root action can return any value.
// NOTE: if undefined is returned, it will be replaced by the
// action context/action set.
// NOTE: there is no distinction between root and other actions
// other than that root action's return values are not
// ignored.
// - can consist of two parts: the first is called before the
// shadowed action (pre-callback) and the second after (post-callback).
// - post-callback has access to the return value and can modify it
// but not replace it.
// - can be bound to, a-la an event, calling the handlers when it is
// called,
//
@ -70,6 +87,9 @@ var object = require('lib/object')
//
// <action-set>.actions
// -> list of action names
//
// <action-set>.length
// -> number of actions
//
//
// 2) Event-like callbacks for actions (MetaActions, Action)
@ -91,7 +111,7 @@ var object = require('lib/object')
// var O = Actions(X, {
// m: [function(){
// console.log('pre')
// return function(){
// return function(res){
// console.log('post')
// }
// }]

View File

@ -436,7 +436,12 @@ var RibbonsPrototype = {
var w = that.getVisibleImageSize('width', null, img)
// skip images not fully shown in viewer...
if(L > l || l+w > L+W){
// NOTE: we explicitly leave partial images here so as to
// include at least two.
// This is done so as to include at least a couple
// of images at large magnifications when nothing
// other than the current image fully fit...
if(L > l+w || l > L+W){
return
}
@ -463,8 +468,8 @@ var RibbonsPrototype = {
// we have two images that are about the same distance from
// target...
// NOTE: this is a one-dimentional filter so the can not be more
// than two hits...
// NOTE: this is a one-dimentional filter so there can not be
// more than two hits...
// NOTE: delta is used ONLY if position is either 'center',
// 'current' or an jQuery object...
if(b && (a >= 0) != (b >= 0) && Math.abs(a + b) < delta){