2024-07-08 22:46:35 +02:00

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);
}
}