From 62d5b45e7f77beca434c29498ac25a7db6e80753 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 20 Dec 2015 20:59:19 +0300 Subject: [PATCH] added base ribbon indicators -- passive done, active still needs work... Signed-off-by: Alex A. Naanou --- ui (gen4)/css/layout.css | 38 +++++ ui (gen4)/css/layout.less | 54 ++++++- ui (gen4)/features/base.js | 16 +- ui (gen4)/features/meta.js | 5 + ui (gen4)/features/ui.js | 298 ++++++++++++++++++++++++++++--------- ui (gen4)/lib/actions.js | 32 +++- ui (gen4)/ribbons.js | 11 +- 7 files changed, 364 insertions(+), 90 deletions(-) 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){