From 856d212aed369eebce0c6ef236a5ab0af6cf304c Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Mon, 16 May 2016 00:56:15 +0300 Subject: [PATCH] cleanup and refinement of ribbon pan handler (still not too happy with it)... Signed-off-by: Alex A. Naanou --- ui (gen4)/features/ui-status.js | 5 ++ ui (gen4)/features/ui.js | 89 +++++++++++++++++++++++++------- ui (gen4)/lib/actions.js | 90 +++++++++++++++++++++++---------- ui (gen4)/ribbons.js | 5 +- 4 files changed, 142 insertions(+), 47 deletions(-) diff --git a/ui (gen4)/features/ui-status.js b/ui (gen4)/features/ui-status.js index 77332a37..7fa71c0f 100755 --- a/ui (gen4)/features/ui-status.js +++ b/ui (gen4)/features/ui-status.js @@ -500,6 +500,11 @@ module.StatusBar = core.ImageGridFeatures.Feature({ } }], + ['ribbonPanning.post', + function(_, gid){ + gid == this.data.getRibbon() && this.updateStatusBar() + }], + // Workspace... ['saveWorkspace', core.makeWorkspaceConfigWriter( diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js index d3fb0749..8f2d9346 100755 --- a/ui (gen4)/features/ui.js +++ b/ui (gen4)/features/ui.js @@ -1800,8 +1800,25 @@ var ControlActions = actions.Actions({ 'focus-central-image': 'silent', 'ribbon-pan-threshold': 30, + 'control-in-progress-timeout': 100, }, + // Ribbon pan "event"... + // + // Protocol: + // - pre phase is called when pan is started. + // - post phase is called when pan is finished. + // + // This is not intended to be called by user, instead it is + // internally called by the pan handler. + // + // NOTE: more than one ribbon can be panned at once. + ribbonPanning: ['- Interface/', + core.notUserCallable(function(gid){ + // This is ribbon pan event... + // + // Not for direct use. + })], // XXX this is really slow on IE... toggleRibbonPanHandling: ['Interface/Toggle ribbon pan handling', @@ -1817,6 +1834,9 @@ var ControlActions = actions.Actions({ // XXX var that = this var r = this.ribbons.getRibbon(target) + var rgid = this.ribbons.getElemGID(r) + var data = false + var post_handlers // setup dragging... if(r.length > 0 && !r.hasClass('draggable')){ @@ -1835,17 +1855,16 @@ var ControlActions = actions.Actions({ //evt.stopPropagation() // XXX stop all previous animations... - //r.velocity("stop") + r.velocity("stop") var d = that.ribbons.dom - var s = that.scale var g = evt.gesture - - var data = r.data('drag-data') + var s = that.scale // we just started... if(!data){ that.__control_in_progress = (that.__control_in_progress || 0) + 1 + post_handlers = that.ribbonPanning.pre(that, [rgid]) // hide and remove current image indicator... // NOTE: it will be reconstructed on @@ -1860,20 +1879,21 @@ var ControlActions = actions.Actions({ }) // store initial position... - var data = { - left: d.getOffset(this).left, + data = { + //left: d.getOffset(this).left, + left: $(this).transform('x'), pointers: g.pointers.length, } - r.data('drag-data', data) } // do the actual move... - d.setOffset(this, data.left + (g.deltaX / s)) + //d.setOffset(this, data.left + (g.deltaX / s)) + r.transform({x: data.left + (g.deltaX / s)}) /* XXX this seems to offer no speed advantages * vs. .setOffset(..) but does not play * well with .updateRibbon(..) - $(this) + r .velocity('stop') .velocity({ translateX: data.left + (g.deltaX / s), @@ -1896,19 +1916,44 @@ var ControlActions = actions.Actions({ // when done... if(g.isFinal){ - r.removeData('drag-data') - // XXX this seems to have trouble with off-screen images... var central = that.ribbons.getImageByPosition('center', r) + // check if central if off screen, if yes, + // nudge it into user-accessible area... + // + // we are fully off screen -- focus first/last image... + if(central == null){ + var gid = that.data.getImage( + r.offset().left < 0 ? -1 : 0, rgid) + + that.centerImage(gid) + central = that.ribbons.getImage(gid) + + // partly out the left -- show last image... + } else if(central.offset().left < 0){ + r.transform({ + x: r.transform('x') - (central.offset().left / s) + }) + + // partly out the right -- show first image... + } else if(central.offset().left + (central.width()*s) + > that.ribbons.viewer.width()){ + r.transform({ + x: r.transform('x') + + (that.ribbons.viewer.width() + - (central.offset().left + + central.width()*s)) / s + }) + } + // load stuff if needed... that.updateRibbon(central) + /* // XXX add inertia.... - /* XXX - console.log('!!!!', g.velocityX) r.velocity({ - translateX: (data.left + g.deltaX + (g.velocityX * 10)) +'px' + translateX: (data.left + ((g.deltaX + (g.velocityX * 10)) / s)) +'px' }, 'easeInSine') */ @@ -1916,21 +1961,27 @@ var ControlActions = actions.Actions({ if(that.config['focus-central-image'] == 'silent'){ var gid = that.ribbons.getElemGID(central) - // XXX is this the right way to do this??? - that.data.focusImage(gid) - that.ribbons.focusImage(gid) + that.data.focusImage(gid, rgid) + that.ribbons.focusImage(a.current) // focus central image in a normal manner... } else if(that.config['focus-central-image']){ - that.focusImage(that.ribbons.getElemGID(central)) + var gid = that.ribbons.getElemGID(central) + + that.data.focusImage(gid, rgid) + that.focusImage() } + data = false + + that.ribbonPanning.post(that, post_handlers) + setTimeout(function(){ that.__control_in_progress -= 1 if(that.__control_in_progress <= 0){ delete that.__control_in_progress } - }, 50) + }, that.config['control-in-progress-timeout'] || 100) } }) } diff --git a/ui (gen4)/lib/actions.js b/ui (gen4)/lib/actions.js index 0f8e2309..8eeb3573 100755 --- a/ui (gen4)/lib/actions.js +++ b/ui (gen4)/lib/actions.js @@ -358,6 +358,9 @@ function Action(name, doc, ldoc, func){ meth.func = func + // make introspection be a bit better... + meth.toString = func.toString.bind(func) + return meth } // this will make action instances behave like real functions... @@ -370,24 +373,60 @@ Action.prototype.chainApply = function(context, inner, args){ var res = context var outer = this.name + var data = this.pre(context, args) + + // call the inner action/function if preset.... + if(inner){ + //res = inner instanceof Function ? + inner instanceof Function ? + inner.call(context, args) + : inner instanceof Array && inner.length > 0 ? + context[inner.pop()].chainCall(context, inner, args) + : typeof(inner) == typeof('str') ? + context[inner].chainCall(context, null, args) + : null + } + + return this.post(context, data) +} +Action.prototype.chainCall = function(context, inner){ + return this.chainApply(context, inner, args2array(arguments).slice(2)) +} + +// The pre/post stage runners... +// +// .pre(context, args) +// -> data +// +// .post(context, data) +// -> result +// +Action.prototype.pre = function(context, args){ + args = args || [] + + var res = context + var outer = this.name + var getHandlers = context.getHandlers || MetaActions.getHandlers var isToggler = context.isToggler || MetaActions.isToggler - // get .__call__(..) wrapper handler list... - var call_wrapper = outer != '__call__' && inner != '__call__' ? - getHandlers.call(context, '__call__') - : [] - // get the handler list... var handlers = getHandlers.call(context, outer) // special case: toggler -- do not handle special args... + // XXX should this be here??? if(isToggler.call(context, outer) && args.length == 1 && (args[0] == '?' || args[0] == '??')){ - return handlers.slice(-1)[0].pre.apply(context, args) + return { + result: handlers.slice(-1)[0].pre.apply(context, args), + } } + var call_wrapper = outer != '__call__' ? + getHandlers.call(context, '__call__') + : [] + // wrapper handlers: pre phase... call_wrapper = call_wrapper .map(function(a){ @@ -430,25 +469,29 @@ Action.prototype.chainApply = function(context, inner, args){ return a }) - // call the inner action/function if preset.... - if(inner){ - //res = inner instanceof Function ? - inner instanceof Function ? - inner.call(context, args) - : inner instanceof Array && inner.length > 0 ? - context[inner.pop()].chainCall(context, inner, args) - : typeof(inner) == typeof('str') ? - context[inner].chainCall(context, null, args) - : null - } - - // return this if nothing specific is returned... + // return context if nothing specific is returned... res = res === undefined ? context : res + + return { + arguments: args, + + wrapper: call_wrapper, + handlers: handlers, + + result: res, + } +} +Action.prototype.post = function(context, data){ + var res = data.result || context + + var args = data.arguments || [] // the post handlers get the result as the first argument... args.splice(0, 0, res) + var outer = this.name + // handlers: post phase... - handlers + data.handlers && data.handlers // NOTE: post handlers are called LIFO -- last defined last... .reverse() .forEach(function(a){ @@ -459,7 +502,7 @@ Action.prototype.chainApply = function(context, inner, args){ }) // wrapper handlers: post phase... - call_wrapper + data.wrapper && data.wrapper // NOTE: post handlers are called LIFO -- last defined last... .reverse() .forEach(function(a){ @@ -469,13 +512,8 @@ Action.prototype.chainApply = function(context, inner, args){ : a.post.call(context, res, outer, args.slice(1))) }) - // action result... return res } -Action.prototype.chainCall = function(context, inner){ - return this.chainApply(context, inner, args2array(arguments).slice(2)) -} - // A base action-set object... // diff --git a/ui (gen4)/ribbons.js b/ui (gen4)/ribbons.js index f240adb2..0bef7949 100755 --- a/ui (gen4)/ribbons.js +++ b/ui (gen4)/ribbons.js @@ -1027,7 +1027,7 @@ var RibbonsPrototype = { // sort images by distance... .sort(function(a, b){ return Math.abs(a[0]) - Math.abs(b[0]) }) - var a = res[0][0] + var a = res[0] ? res[0][0] : null var b = res[1] ? res[1][0] : null // we have two images that are about the same distance from @@ -1041,7 +1041,8 @@ var RibbonsPrototype = { // a single hit... } else { - return res[0][1] + // NOTE: if no image is on screen this will get nothing... + return res[0] ? res[0][1] : null } },