184 lines
6.0 KiB
JavaScript
Executable File
184 lines
6.0 KiB
JavaScript
Executable File
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import Adw from 'gi://Adw';
|
|
import Gdk from 'gi://Gdk';
|
|
import GdkPixbuf from 'gi://GdkPixbuf';
|
|
import Gio from 'gi://Gio';
|
|
import GLib from 'gi://GLib';
|
|
import GObject from 'gi://GObject';
|
|
import Gtk from 'gi://Gtk';
|
|
|
|
import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
|
|
|
|
|
export class BackgroundButton extends Gtk.Button {
|
|
#uri;
|
|
|
|
static {
|
|
GObject.registerClass({
|
|
GTypeName: 'BackgroundButton',
|
|
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui',
|
|
InternalChildren: ['filechooser', 'thumbnail'],
|
|
Properties: {
|
|
uri: GObject.ParamSpec.string(
|
|
'uri',
|
|
'URI',
|
|
'URI to the background file',
|
|
GObject.ParamFlags.READWRITE,
|
|
null
|
|
),
|
|
thumbWidth: GObject.ParamSpec.int(
|
|
'thumb-width',
|
|
'Thumbnail width',
|
|
'Width of the displayed thumbnail',
|
|
GObject.ParamFlags.READWRITE,
|
|
0, 600,
|
|
180
|
|
),
|
|
thumbHeight: GObject.ParamSpec.int(
|
|
'thumb-height',
|
|
'Thumbnail height',
|
|
'Height of the displayed thumbnail',
|
|
GObject.ParamFlags.READWRITE,
|
|
0, 600,
|
|
180
|
|
),
|
|
},
|
|
}, this);
|
|
}
|
|
|
|
constructor({ ...params } = {}) {
|
|
super(params);
|
|
this.#setupSize();
|
|
this.#setupDropTarget();
|
|
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() {
|
|
const display = Gdk.Display.get_default();
|
|
const monitor = display.get_monitors().get_item(0);
|
|
if (monitor.width_mm === 0 || monitor.height_mm === 0)
|
|
return;
|
|
if (monitor.width_mm > monitor.height_mm)
|
|
this.thumbHeight *= monitor.height_mm / monitor.width_mm;
|
|
else
|
|
this.thumbWidth *= monitor.width_mm / monitor.height_mm;
|
|
}
|
|
|
|
#setupDropTarget() {
|
|
const dropTarget = Gtk.DropTarget.new(Gio.File.$gtype, Gdk.DragAction.COPY);
|
|
dropTarget.connect('drop', (_target, file, _x, _y) => {
|
|
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
|
if (this.#isContentTypeSupported(contentType)) {
|
|
this.uri = file.get_uri();
|
|
return true;
|
|
} else {
|
|
if (this.root instanceof Adw.PreferencesWindow) {
|
|
this.root.add_toast(new Adw.Toast({
|
|
title: _('This image format is not supported.'),
|
|
timeout: 10,
|
|
}));
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
this.add_controller(dropTarget);
|
|
}
|
|
|
|
#setupFileChooserFilter() {
|
|
this._filechooser.filter = new Gtk.FileFilter();
|
|
this._filechooser.filter.add_pixbuf_formats();
|
|
this._filechooser.filter.add_mime_type('application/xml');
|
|
}
|
|
|
|
#getSupportedContentTypes() {
|
|
return GdkPixbuf.Pixbuf.get_formats().flatMap(format => format.get_mime_types()).concat('application/xml');
|
|
}
|
|
|
|
#isContentTypeSupported(contentType) {
|
|
for (const supportedContentType of this.#getSupportedContentTypes()) {
|
|
if (Gio.content_type_equals(contentType, supportedContentType))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vfunc_mnemonic_activate() {
|
|
this.openFileChooser();
|
|
}
|
|
|
|
openFileChooser() {
|
|
this._filechooser.transient_for = this.get_root();
|
|
this._filechooser.show();
|
|
}
|
|
|
|
onFileChooserResponse(fileChooser, responseId) {
|
|
if (responseId !== Gtk.ResponseType.ACCEPT)
|
|
return;
|
|
this.uri = fileChooser.get_file().get_uri();
|
|
}
|
|
|
|
onClicked(_button) {
|
|
this.openFileChooser();
|
|
}
|
|
|
|
#updateThumbnail() {
|
|
this._thumbnail.paintable = null;
|
|
|
|
if (!this.uri)
|
|
return;
|
|
|
|
const file = Gio.File.new_for_uri(this.uri);
|
|
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
|
|
|
if (!this.#isContentTypeSupported(contentType))
|
|
return;
|
|
|
|
let path;
|
|
if (Gio.content_type_equals(contentType, 'application/xml')) {
|
|
const decoder = new TextDecoder('utf-8');
|
|
const contents = decoder.decode(file.load_contents(null)[1]);
|
|
try {
|
|
path = contents.match(/<file>(.+)<\/file>/m)[1];
|
|
if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0]))
|
|
throw new Error();
|
|
} catch (e) {
|
|
console.error(`No suitable background file found in ${file.get_path()}.\n${e}`);
|
|
return;
|
|
}
|
|
} else {
|
|
path = file.get_path();
|
|
}
|
|
|
|
const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path);
|
|
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, this.thumbWidth, this.thumbHeight);
|
|
pixbuf.scale(
|
|
thumbPixbuf,
|
|
0, 0,
|
|
this.thumbWidth, this.thumbHeight,
|
|
-(pixbuf.width * scale - this.thumbWidth) / 2, -(pixbuf.height * scale - this.thumbHeight) / 2,
|
|
scale, scale,
|
|
GdkPixbuf.InterpType.TILES
|
|
);
|
|
|
|
this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf);
|
|
}
|
|
}
|