Background button: Create thumbnail asynchronously

This commit is contained in:
Romain Vigier
2022-10-29 11:57:49 +02:00
parent e0957eaa14
commit d7dce86fb7
2 changed files with 39 additions and 25 deletions
+1 -8
View File
@@ -11,14 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<class name="image-button"/> <class name="image-button"/>
</style> </style>
<child> <child>
<object class="GtkPicture"> <object class="GtkPicture" id="thumbnail">
<binding name="paintable">
<closure function="getThumbnail" type="GdkPaintable">
<lookup name="uri">BackgroundButton</lookup>
<lookup name="thumb-width">BackgroundButton</lookup>
<lookup name="thumb-height">BackgroundButton</lookup>
</closure>
</binding>
<property name="can-shrink">False</property> <property name="can-shrink">False</property>
<property name="width-request" bind-source="BackgroundButton" bind-property="thumb-width" bind-flags="sync-create"/> <property name="width-request" bind-source="BackgroundButton" bind-property="thumb-width" bind-flags="sync-create"/>
<property name="height-request" bind-source="BackgroundButton" bind-property="thumb-height" bind-flags="sync-create"/> <property name="height-request" bind-source="BackgroundButton" bind-property="thumb-height" bind-flags="sync-create"/>
+38 -17
View File
@@ -11,7 +11,7 @@ const _ = extensionUtils.gettext;
var BackgroundButton = GObject.registerClass({ var BackgroundButton = GObject.registerClass({
GTypeName: 'BackgroundButton', GTypeName: 'BackgroundButton',
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui', Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui',
InternalChildren: ['filechooser'], InternalChildren: ['filechooser', 'thumbnail'],
Properties: { Properties: {
uri: GObject.ParamSpec.string( uri: GObject.ParamSpec.string(
'uri', 'uri',
@@ -20,7 +20,7 @@ var BackgroundButton = GObject.registerClass({
GObject.ParamFlags.READWRITE, GObject.ParamFlags.READWRITE,
null null
), ),
thumb_width: GObject.ParamSpec.int( thumbWidth: GObject.ParamSpec.int(
'thumb-width', 'thumb-width',
'Thumbnail width', 'Thumbnail width',
'Width of the displayed thumbnail', 'Width of the displayed thumbnail',
@@ -28,7 +28,7 @@ var BackgroundButton = GObject.registerClass({
0, 600, 0, 600,
180 180
), ),
thumb_height: GObject.ParamSpec.int( thumbHeight: GObject.ParamSpec.int(
'thumb-height', 'thumb-height',
'Thumbnail height', 'Thumbnail height',
'Height of the displayed thumbnail', 'Height of the displayed thumbnail',
@@ -38,6 +38,8 @@ var BackgroundButton = GObject.registerClass({
), ),
}, },
}, class BackgroundButton extends Gtk.Button { }, class BackgroundButton extends Gtk.Button {
#uri;
constructor(props = {}) { constructor(props = {}) {
super(props); super(props);
this.#setupSize(); this.#setupSize();
@@ -45,15 +47,30 @@ var BackgroundButton = GObject.registerClass({
this.#setupFileChooserFilter(); this.#setupFileChooserFilter();
} }
get uri() {
return this.#uri || null;
}
set uri(uri) {
if (uri === this.#uri)
return;
this.#uri = uri;
this.notify('uri');
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
this.#updateThumbnail();
return GLib.SOURCE_REMOVE;
});
}
#setupSize() { #setupSize() {
const display = Gdk.Display.get_default(); const display = Gdk.Display.get_default();
const monitor = display.get_monitors().get_item(0); const monitor = display.get_monitors().get_item(0);
if (monitor.width_mm === 0 || monitor.height_mm === 0) if (monitor.width_mm === 0 || monitor.height_mm === 0)
return; return;
if (monitor.width_mm > monitor.height_mm) if (monitor.width_mm > monitor.height_mm)
this.thumb_height *= monitor.height_mm / monitor.width_mm; this.thumbHeight *= monitor.height_mm / monitor.width_mm;
else else
this.thumb_width *= monitor.width_mm / monitor.height_mm; this.thumbWidth *= monitor.width_mm / monitor.height_mm;
} }
#setupDropTarget() { #setupDropTarget() {
@@ -113,15 +130,17 @@ var BackgroundButton = GObject.registerClass({
this.openFileChooser(); this.openFileChooser();
} }
getThumbnail(_widget, uri, width, height) { #updateThumbnail() {
if (!uri) this._thumbnail.paintable = null;
return null;
const file = Gio.File.new_for_uri(uri); if (!this.uri)
return;
const file = Gio.File.new_for_uri(this.uri);
const contentType = Gio.content_type_guess(file.get_basename(), null)[0]; const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
if (!this.#isContentTypeSupported(contentType)) if (!this.#isContentTypeSupported(contentType))
return null; return;
let path; let path;
if (Gio.content_type_equals(contentType, 'application/xml')) { if (Gio.content_type_equals(contentType, 'application/xml')) {
@@ -129,26 +148,28 @@ var BackgroundButton = GObject.registerClass({
const contents = decoder.decode(file.load_contents(null)[1]); const contents = decoder.decode(file.load_contents(null)[1]);
try { try {
path = contents.match(/<file>(.+)<\/file>/m)[1]; path = contents.match(/<file>(.+)<\/file>/m)[1];
if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0]))
throw new Error();
} catch (e) { } catch (e) {
console.error(e); console.error(`[${Me.metadata.name}] No suitable background file found in ${file.get_path()}`);
return null; return;
} }
} else { } else {
path = file.get_path(); path = file.get_path();
} }
const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path); const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path);
const scale = pixbuf.width / pixbuf.height > width / height ? height / pixbuf.height : width / pixbuf.width; const scale = pixbuf.width / pixbuf.height > this.thumbWidth / this.thumbHeight ? this.thumbHeight / pixbuf.height : this.thumbWidth / pixbuf.width;
const thumbPixbuf = GdkPixbuf.Pixbuf.new(pixbuf.colorspace, pixbuf.has_alpha, pixbuf.bits_per_sample, width, height); const thumbPixbuf = GdkPixbuf.Pixbuf.new(pixbuf.colorspace, pixbuf.has_alpha, pixbuf.bits_per_sample, this.thumbWidth, this.thumbHeight);
pixbuf.scale( pixbuf.scale(
thumbPixbuf, thumbPixbuf,
0, 0, 0, 0,
width, height, this.thumbWidth, this.thumbHeight,
-(pixbuf.width * scale - width) / 2, -(pixbuf.height * scale - height) / 2, -(pixbuf.width * scale - this.thumbWidth) / 2, -(pixbuf.height * scale - this.thumbHeight) / 2,
scale, scale, scale, scale,
GdkPixbuf.InterpType.TILES GdkPixbuf.InterpType.TILES
); );
return Gdk.Texture.new_for_pixbuf(thumbPixbuf); this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf);
} }
}); });