improve images again

* Files.Images does the same thing, with just a new accessor (getSorted).
* Files.Image is splited and the new subclass manage gicons.
This commit is contained in:
abakkk 2020-09-12 19:54:10 +02:00
parent 24682db69c
commit 464aae77c6
2 changed files with 122 additions and 86 deletions

206
files.js
View File

@ -73,18 +73,13 @@ Object.keys(ThemedIconNames).forEach(key => {
}); });
}); });
// wrapper around an image file // wrapper around an image file. If not subclassed, it is used with drawing files (.json) and it takes { displayName, contentType, base64, hash } as params.
var Image = new Lang.Class({ var Image = new Lang.Class({
Name: 'DrawOnYourScreenImage', Name: 'DrawOnYourScreenImage',
_init: function(params) { _init: function(params) {
for (let key in params) for (let key in params)
this[key] = params[key]; this[key] = params[key];
if (this.info) {
this.displayName = this.info.get_display_name();
this.contentType = this.info.get_content_type();
}
}, },
toString: function() { toString: function() {
@ -100,47 +95,9 @@ var Image = new Lang.Class({
}; };
}, },
get thumbnailFile() {
if (!this._thumbnailFile) {
if (this.info.has_attribute('thumbnail::path') && this.info.get_attribute_boolean('thumbnail::is-valid')) {
let thumbnailPath = this.info.get_attribute_as_string('thumbnail::path');
this._thumbnailFile = Gio.File.new_for_path(thumbnailPath);
}
}
return this._thumbnailFile || null;
},
// only called from menu or area so this.file exists
get gicon() {
if (!this._gicon)
this._gicon = new Gio.FileIcon({ file: this.thumbnailFile || this.file });
return this._gicon;
},
// use only thumbnails in menu (memory)
get thumbnailGicon() {
if (this.contentType != 'image/svg+xml' && !this.thumbnailFile)
return null;
return this.gicon;
},
get bytes() { get bytes() {
if (!this._bytes) { if (!this._bytes)
if (this.file) this._bytes = new GLib.Bytes(GLib.base64_decode(this.base64));
try {
// load_bytes available in GLib 2.56+
this._bytes = this.file.load_bytes(null)[0];
} catch(e) {
let [, contents] = this.file.load_contents(null);
if (contents instanceof Uint8Array)
this._bytes = ByteArray.toGBytes(contents);
else
this._bytes = contents.toGBytes();
}
else
this._bytes = new GLib.Bytes(GLib.base64_decode(this.base64));
}
return this._bytes; return this._bytes;
}, },
@ -188,7 +145,61 @@ var Image = new Lang.Class({
} }
}); });
// Get images with getPrevious, getNext, or by iterating over it. // Add a gicon generator to Image. It is used with image files and it takes { file, info } as params.
const ImageWithGicon = new Lang.Class({
Name: 'DrawOnYourScreenImageWithGicon',
Extends: Image,
get displayName() {
return this.info.get_display_name();
},
get contentType() {
return this.info.get_content_type();
},
get thumbnailFile() {
if (!this._thumbnailFile) {
if (this.info.has_attribute('thumbnail::path') && this.info.get_attribute_boolean('thumbnail::is-valid')) {
let thumbnailPath = this.info.get_attribute_as_string('thumbnail::path');
this._thumbnailFile = Gio.File.new_for_path(thumbnailPath);
}
}
return this._thumbnailFile || null;
},
get gicon() {
if (!this._gicon)
this._gicon = new Gio.FileIcon({ file: this.thumbnailFile || this.file });
return this._gicon;
},
// use only thumbnails in menu (memory)
get thumbnailGicon() {
if (this.contentType != 'image/svg+xml' && !this.thumbnailFile)
return null;
return this.gicon;
},
get bytes() {
if (!this._bytes) {
try {
// load_bytes available in GLib 2.56+
this._bytes = this.file.load_bytes(null)[0];
} catch(e) {
let [, contents] = this.file.load_contents(null);
if (contents instanceof Uint8Array)
this._bytes = ByteArray.toGBytes(contents);
else
this._bytes = contents.toGBytes();
}
}
return this._bytes;
}
});
// Access images with getPrevious, getNext, getSorted or by iterating over it.
var Images = { var Images = {
_images: [], _images: [],
_clipboardImages: [], _clipboardImages: [],
@ -198,57 +209,84 @@ var Images = {
return this._clipboardImages.some(image => image.file.equal(file)); return this._clipboardImages.some(image => image.file.equal(file));
}, },
_getImages: function() { // Firstly iterate over the extension directory that contains Example.svg,
// secondly iterate over the directory that was configured by the user in prefs,
// finally iterate over the images pasted from the clipboard.
[Symbol.iterator]: function() {
if (this._upToDate) if (this._upToDate)
return this._images; return this._images.concat(this._clipboardImages)[Symbol.iterator]();
let images = []; this._upToDate = true;
let userLocation = Me.drawingSettings.get_string('image-location') || DEFAULT_USER_IMAGE_LOCATION; let oldImages = this._images;
let userDirectory = Gio.File.new_for_commandline_arg(userLocation); let newImages = this._images = [];
let clipboardImagesContains = this._clipboardImagesContains.bind(this);
let clipboardIterator = this._clipboardImages[Symbol.iterator]();
[EXAMPLE_IMAGE_DIRECTORY, userDirectory].forEach(directory => { return {
let enumerator; getExampleEnumerator: function() {
try { try {
enumerator = directory.enumerate_children('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null); return EXAMPLE_IMAGE_DIRECTORY.enumerate_children('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) { } catch(e) {
return; return this.getUserEnumerator();
} }
},
let info = enumerator.next_file(null); getUserEnumerator: function() {
while (info) { try {
let file = enumerator.get_child(info); let userLocation = Me.drawingSettings.get_string('image-location') || DEFAULT_USER_IMAGE_LOCATION;
let userDirectory = Gio.File.new_for_commandline_arg(userLocation);
return userDirectory.enumerate_children('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
return null;
}
},
if (info.get_content_type().indexOf('image') == 0 && !this._clipboardImagesContains(file)) { get enumerator() {
let index = this._images.findIndex(image => image.file.equal(file)); if (this._enumerator === undefined)
if (index != -1) this._enumerator = this.getExampleEnumerator();
images.push(this._images[index]); else if (this._enumerator && this._enumerator.get_container().equal(EXAMPLE_IMAGE_DIRECTORY) && this._enumerator.is_closed())
else this._enumerator = this.getUserEnumerator();
images.push(new Image({ file, info })); else if (this._enumerator && this._enumerator.is_closed())
this._enumerator = null;
return this._enumerator;
},
next: function() {
if (!this.enumerator)
return clipboardIterator.next();
let info = this.enumerator.next_file(null);
if (!info) {
this.enumerator.close(null);
return this.next();
} }
info = enumerator.next_file(null); let file = this.enumerator.get_child(info);
}
enumerator.close(null);
});
this._images = images.concat(this._clipboardImages) if (info.get_content_type().indexOf('image') == 0 && !clipboardImagesContains(file)) {
.sort((a, b) => a.toString().localeCompare(b.toString())); let image = oldImages.find(image => image.file.equal(file)) || new ImageWithGicon({ file, info });
this._upToDate = true; newImages.push(image);
return this._images; return { value: image, done: false };
} else {
return this.next();
}
}
};
}, },
[Symbol.iterator]: function() { getSorted: function() {
return this._getImages()[Symbol.iterator](); return [...this].sort((a, b) => a.toString().localeCompare(b.toString()));
}, },
getNext: function(currentImage) { getNext: function(currentImage) {
let images = this._getImages(); let images = this.getSorted();
let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : 0; let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : -1;
return images[index == images.length - 1 ? 0 : index + 1] || null; return images[index == images.length - 1 ? 0 : index + 1] || null;
}, },
getPrevious: function(currentImage) { getPrevious: function(currentImage) {
let images = this._getImages(); let images = this.getSorted();
let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : 0; let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : 0;
return images[index <= 0 ? images.length - 1 : index - 1] || null; return images[index <= 0 ? images.length - 1 : index - 1] || null;
}, },
@ -271,7 +309,7 @@ var Images = {
.filter(file => file.query_exists(null)) .filter(file => file.query_exists(null))
.map(file => [file, file.query_info('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null)]) .map(file => [file, file.query_info('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null)])
.filter(pair => pair[1].get_content_type().indexOf('image') == 0) .filter(pair => pair[1].get_content_type().indexOf('image') == 0)
.map(pair => new Image({ file: pair[0], info: pair[1] })); .map(pair => new ImageWithGicon({ file: pair[0], info: pair[1] }));
// Prevent duplicated // Prevent duplicated
images.filter(image => !this._clipboardImagesContains(image.file)) images.filter(image => !this._clipboardImagesContains(image.file))
@ -279,10 +317,8 @@ var Images = {
if (images.length) { if (images.length) {
this.reset(); this.reset();
let allImages = this._getImages();
let lastFile = images[images.length - 1].file; let lastFile = images[images.length - 1].file;
let index = allImages.findIndex(image => image.file.equal(lastFile)); callback(this._clipboardImages.find(image => image.file.equal(lastFile)));
callback(allImages[index]);
} }
}); });
} }

View File

@ -539,7 +539,7 @@ var DrawingMenu = new Lang.Class({
item.menu.openOld = item.menu.open; item.menu.openOld = item.menu.open;
item.menu.open = (animate) => { item.menu.open = (animate) => {
if (!item.menu.isOpen && item.menu.isEmpty()) { if (!item.menu.isOpen && item.menu.isEmpty()) {
[...Files.Images].forEach(image => { Files.Images.getSorted().forEach(image => {
let subItem = item.menu.addAction(image.toString(), () => { let subItem = item.menu.addAction(image.toString(), () => {
item.label.set_text(image.toString()); item.label.set_text(image.toString());
this.area.currentImage = image; this.area.currentImage = image;