diff --git a/ui (gen4)/css/layout.css b/ui (gen4)/css/layout.css
index ef26b629..2ecc7e91 100644
--- a/ui (gen4)/css/layout.css
+++ b/ui (gen4)/css/layout.css
@@ -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;
diff --git a/ui (gen4)/css/layout.less b/ui (gen4)/css/layout.less
index d3998541..7a167e0f 100755
--- a/ui (gen4)/css/layout.less
+++ b/ui (gen4)/css/layout.less
@@ -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 ***/
diff --git a/ui (gen4)/features/base.js b/ui (gen4)/features/base.js
index d105575d..ff1e4528 100755
--- a/ui (gen4)/features/base.js
+++ b/ui (gen4)/features/base.js
@@ -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')],
+
})
diff --git a/ui (gen4)/features/meta.js b/ui (gen4)/features/meta.js
index 01148c3c..a602d471 100755
--- a/ui (gen4)/features/meta.js
+++ b/ui (gen4)/features/meta.js
@@ -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',
diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js
index 1e343171..2f3deb07 100755
--- a/ui (gen4)/features/ui.js
+++ b/ui (gen4)/features/ui.js
@@ -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 = $('
')
+ .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 $('
')
+ .addClass('state-indicator-container ' + type || '')
+}
+
+var makeStateIndicatorItem = function(container, type, text){
+ var item = $('
')
+ .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
diff --git a/ui (gen4)/lib/actions.js b/ui (gen4)/lib/actions.js
index e26a7436..35fcfe07 100755
--- a/ui (gen4)/lib/actions.js
+++ b/ui (gen4)/lib/actions.js
@@ -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')
//
//
.actions
// -> list of action names
+//
+// .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')
// }
// }]
diff --git a/ui (gen4)/ribbons.js b/ui (gen4)/ribbons.js
index 9a56b250..b805ea40 100755
--- a/ui (gen4)/ribbons.js
+++ b/ui (gen4)/ribbons.js
@@ -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){