From 861cef15b8c55006a78bea37f00c462704ee2ad4 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Tue, 22 Aug 2017 16:12:47 +0300 Subject: [PATCH] reworked collections to use the full .data.order... Signed-off-by: Alex A. Naanou --- ui (gen4)/features/base.js | 17 ++- ui (gen4)/features/collections.js | 199 ++++++++++++++++++++++++++---- ui (gen4)/features/meta.js | 7 +- ui (gen4)/imagegrid/data.js | 22 +++- 4 files changed, 206 insertions(+), 39 deletions(-) diff --git a/ui (gen4)/features/base.js b/ui (gen4)/features/base.js index 99a2f350..976eb267 100755 --- a/ui (gen4)/features/base.js +++ b/ui (gen4)/features/base.js @@ -1474,10 +1474,15 @@ module.CropActions = actions.Actions({ Make a crop and use the given data object... NOTE: data must be an instance of data.Data + NOTE: this will overwrite data.tags with this.data.tags .crop(data) - .crop(data, true) -> this + Make a crop and use the given data object but keep data.tags... + .crop(data, false) + -> this + + NOTE: this is used as a basis for all the crop operations, so there is no need to bind to anything but this to handle a crop unless specific action is required for a specific crop @@ -1489,13 +1494,15 @@ module.CropActions = actions.Actions({ function(list, flatten){ list = list || this.data.getImages() - if(this.crop_stack == null){ - this.crop_stack = [] - } + this.crop_stack = this.crop_stack || [] this.crop_stack.push(this.data) if(list instanceof data.Data){ - this.data = list + if(flatten === false){ + list.tags = this.data.tags + } + + this.data = list } else { this.data = this.data.crop(list, flatten) diff --git a/ui (gen4)/features/collections.js b/ui (gen4)/features/collections.js index 7d926953..68e8e3b5 100755 --- a/ui (gen4)/features/collections.js +++ b/ui (gen4)/features/collections.js @@ -24,7 +24,6 @@ var widgets = require('features/ui-widgets') // XXX should collections be in the Crop menu???? // XXX things we need to do to collections: -// - remove images (from collection) ???? // - auto-collections // - tags -- adding/removing images adds/removes tags // - ribbons -- top / bottom / n-m / top+2 / .. @@ -40,8 +39,21 @@ var widgets = require('features/ui-widgets') // locations: // - collection specific stuff (data) to collection path // - global stuff (images, tags, ...) to base index... +// XXX handle tags here??? +// ...keep them global or local to collection??? +// global sounds better... var CollectionActions = actions.Actions({ + // Format: + // { + // : { + // title: <title>, + // gid: <gid>, + // data: <data>, + // ... + // }, + // ... + // } collections: null, get collection(){ @@ -49,7 +61,30 @@ var CollectionActions = actions.Actions({ set collection(value){ this.loadCollection(value) }, + // XXX need a way to prevent multiple loads... + // ...checking if .collection is set to collection is logical but + // may prevent reloading of collections -- i.e. loading a collection + // anew if it is already loaded... + // XXX doc the protocol... loadCollection: ['- Collections/', + core.doc`Load collection... + + This will get collection data and crop into it. + + If .data for a collection is not available this will do nothing, + this enables extending actions to handle the collection in + different ways. + + The extending action if compatible must: + - construct data + - load data via: + this.crop(data) + - when done call: + this.collectionLoaded(collection) + + XXX would be good to have a way to check if loading was done + within this .loadCollection(..) call... + `, function(collection){ if(collection == null || this.collections == null @@ -57,8 +92,33 @@ var CollectionActions = actions.Actions({ return } - this.crop(this.collections[collection].data) + var data = this.collections[collection].data + + data + && this.crop(data) + && this.collectionLoaded(collection) }], + + collectionLoaded: ['- Collections/', + core.doc`This is called by .loadCollection(..) or one of the + overloading actions when collection load is done... + + `, + core.notUserCallable(function(collection){ + // This is the window resize event... + // + // Not for direct use. + this.data.collection = this.location.collection = collection + })], + collectionUnloaded: ['- Collections/', + core.doc`This is called when unloading a collection. + `, + core.notUserCallable(function(collection){ + // This is the window resize event... + // + // Not for direct use. + })], + saveCollection: ['- Collections/', core.doc`Save current state to collection @@ -71,6 +131,7 @@ var CollectionActions = actions.Actions({ -> this `, function(collection, empty){ + var that = this collection = collection || this.collection if(collection == null){ @@ -82,15 +143,15 @@ var CollectionActions = actions.Actions({ collections[collection] = { title: collection, - // XXX we need to trim .order to only the current images??? - data: empty ? - (new this.data.constructor()) - : this.data - .clone() - .removeUnloadedGIDs() - .run(function(){ - this.collection = collection - }), + // NOTE: we do not need to care about tags here as they + // will get overwritten on load... + data: (empty ? + (new this.data.constructor()) + : this.data + .crop()) + .run(function(){ + this.collection = collection + }), } }], newCollection: ['- Collections/', @@ -104,7 +165,8 @@ var CollectionActions = actions.Actions({ return Object.keys(this.collections || {}) .filter(function(c){ return !gid - || that.collections[c].data.order.indexOf(gid) >= 0 }) + || that.collections[c].data.getImage(gid) }) + //|| that.collections[c].data.order.indexOf(gid) >= 0 }) }], collect: ['- Collections/', @@ -196,14 +258,27 @@ var CollectionActions = actions.Actions({ return } + // NOTE: we are not using .data.updateImagePositions(gids, 'hide') + // here because it will remove the gids from everything + // while we need them removed only from ribbons... + var hideGIDs = function(){ + var d = this + gids.forEach(function(gid){ + var i = d.order.indexOf(gid) + Object.keys(d.ribbons).forEach(function(r){ + delete d.ribbons[r][i] + }) + }) + } + if(this.collection == collection){ this.data - .removeGIDs(gids) + .run(hideGIDs) .removeEmptyRibbons() } this.collections[collection].data - .removeGIDs(gids) + .run(hideGIDs) .removeEmptyRibbons() }], @@ -216,15 +291,19 @@ var CollectionActions = actions.Actions({ // manage serialization and loading... // XXX make this reflect the format automatically... load: [function(json){ + var that = this var collections = {} var c = json.collections || {} Object.keys(c).forEach(function(title){ - // XXX make this reflect the format automatically... + var data = data.Data + .fromJSON(c[title].data) + + // XXX make this reflect the format automatically... collections[title] = { title: title, - data: data.Data.fromJSON(c[title].data) + data: data, } }) @@ -237,17 +316,22 @@ var CollectionActions = actions.Actions({ if(collections){ res.collections = {} Object.keys(this.collections).forEach(function(title){ + var data = collections[title].data.dumpJSON() + delete data.tags + // XXX make this reflect the format automatically... res.collections[title] = { title: title, - data: collections[title].data.dumpJSON() + data: data, } }) } } }], clear: [function(){ + this.collectionUnloaded('*') delete this.collections + delete this.location.collection }], }) @@ -272,15 +356,18 @@ module.Collection = core.ImageGridFeatures.Feature({ handlers: [ // maintain the .collection state... - // XXX not yet sure if this is the right way to go... - ['loadCollection', - function(_, collection){ - if(this.collections && collection in this.collections){ - this.data.collection = this.location.collection = collection - } - }], - ['uncrop', + ['uncrop.pre', function(){ + var collection = this.collection + return function(){ + collection != this.data.collection + && this.collectionUnloaded(collection) } + }], + ['collectionLoaded', + function(){ + }], + ['collectionUnloaded', + function(collection){ var collection = this.location.collection = this.data.collection // cleanup... @@ -426,10 +513,11 @@ var UICollectionActions = actions.Actions({ return this.browseCollections(function(title){ this.joinCollect(title) }) })], - // XXX this is not used by metadata yet... + /*/ XXX this is not used by metadata yet... metadataSection: ['- Image/', function(gid, make){ }], + //*/ }) @@ -463,6 +551,13 @@ module.UICollection = core.ImageGridFeatures.Feature({ // - lazy load collections (load list, lazy-load data) // - load directories as collections... // - export collections to directories... + +// XXX lazy load collections... +var FileSystemCollectionActions = actions.Actions({ + +}) + + var FileSystemCollection = module.FileSystemCollection = core.ImageGridFeatures.Feature({ title: '', @@ -470,11 +565,61 @@ module.FileSystemCollection = core.ImageGridFeatures.Feature({ tag: 'fs-collections', depends: [ + 'index-format', 'fs', 'collections', ], - handlers: [], + actions: FileSystemCollectionActions, + + handlers: [ + // XXX maintain changes... + // XXX + [[ + 'collect', + 'joinCollect', + 'uncollect', + + 'saveCollection', + + 'removeCollection', + ], + function(){ + // XXX mark changed collections... + // XXX added/removed collection -> mark collection index as changed... + }], + + // XXX handle removed collections -- move to trash (???) + // ...might be a good idea to add something like index gc API... + ['prepareIndexForWrite', + function(res, _, full){ + var changed = full == true + || res.changes === true + || res.changes.collections + + if(changed && res.raw.collections){ + // select the actual changed collection list... + changed = changed === true ? + Object.keys(res.raw.collections) + : changed + + // collection index... + res.index['collection-index'] = Object.keys(this.collections) + + Object.keys(changed) + // skip the raw field... + .filter(function(k){ return changed.indexOf(k) >= 0 }) + .forEach(function(k){ + // XXX use collection gid... + res.index['collections/' + k] = res.raw.collections[k] + }) + } + }], + ['prepareJSONForLoad', + function(res, json){ + // XXX + }], + ], }) diff --git a/ui (gen4)/features/meta.js b/ui (gen4)/features/meta.js index 52bcc5ab..d81a2b98 100755 --- a/ui (gen4)/features/meta.js +++ b/ui (gen4)/features/meta.js @@ -33,7 +33,6 @@ core.ImageGridFeatures.Feature('viewer-commandline', [ core.ImageGridFeatures.Feature('viewer-minimal', [ 'lifecycle', 'base-full', - 'collections', 'peer', @@ -51,6 +50,12 @@ core.ImageGridFeatures.Feature('viewer-testing', [ 'viewer-commandline', 'viewer-minimal', + 'collections', + + // XXX remove when done testing... + '-fs-collections', + + 'alias', // read-only mode... diff --git a/ui (gen4)/imagegrid/data.js b/ui (gen4)/imagegrid/data.js index 778cad5f..63709c5a 100755 --- a/ui (gen4)/imagegrid/data.js +++ b/ui (gen4)/imagegrid/data.js @@ -1293,9 +1293,11 @@ var DataPrototype = { // // XXX needs more thought.... // do we need to move images by this??? - updateImagePositions: function(from, mode){ + updateImagePositions: function(from, mode, direction){ from = from != null && from.constructor !== Array ? [from] : from + var r = this.getRibbon('current') + this.eachImageList(function(cur, key, set){ set = this[set] @@ -1323,6 +1325,11 @@ var DataPrototype = { } }) + // maintain focus... + if(from.indexOf(this.current) >= 0){ + this.focusImage('r') + } + return this }, @@ -2550,6 +2557,10 @@ var DataPrototype = { return this }, + + + /***************************************** Cleanup and removal ***/ + // Remove empty ribbons... // removeEmptyRibbons: function(){ @@ -2608,6 +2619,8 @@ var DataPrototype = { // Remove GIDs... // // NOTE: this may result in empty ribbons... + // NOTE: to remove gids from lists but keep them in order use: + // .updateImagePositions(gids, 'hide') removeGIDs: function(gids, direction){ var that = this gids = gids || [] @@ -2628,15 +2641,12 @@ var DataPrototype = { // attempt to first get next/prev within the current ribbon... r = r.length > 0 ? r : order - var cur = this.getImage(this.current, direction || 'before', r) + this.current = this.getImage(this.current, direction || 'before', r) || this.getImage(this.current, direction == 'after' ? 'before' : 'after', r) - - this.current = cur } this.order = order - this - .updateImagePositions() + this.updateImagePositions() return this },