// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- /* exported init, buildPrefsWidget */ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; try { // eslint-disable-next-line no-unused-expressions imports.misc.extensionUtils; } catch (e) { const resource = Gio.Resource.load( '/usr/share/gnome-shell/org.gnome.Extensions.src.gresource'); resource._register(); imports.searchPath.push('resource:///org/gnome/Extensions/js'); const gtkVersion = GLib.getenv('FORCE_GTK_VERSION') || '4.0'; imports.gi.versions.Gtk = gtkVersion; imports.gi.versions.Gdk = gtkVersion; } const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const Gettext = imports.gettext.domain( Me ? Me.metadata['gettext-domain'] : 'AppIndicatorExtension'); const _ = Gettext.gettext; function init() { ExtensionUtils.initTranslations(); } const AppIndicatorPreferences = GObject.registerClass( class AppIndicatorPreferences extends Gtk.Box { _init() { super._init({ orientation: Gtk.Orientation.VERTICAL, spacing: 30 }); this._settings = Me ? ExtensionUtils.getSettings() : new Gio.Settings({ schema_id: 'org.gnome.shell.extensions.appindicator' }); let label = null; let widget = null; this.preferences_vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 8, margin_start: 30, margin_end: 30, margin_top: 30, margin_bottom: 30 }); this.custom_icons_vbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); // Icon opacity this.opacity_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Opacity (min: 0, max: 255)'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.SpinButton({ halign: Gtk.Align.END }); widget.set_sensitive(true); widget.set_range(0, 255); widget.set_value(this._settings.get_int('icon-opacity')); widget.set_increments(1, 2); widget.connect('value-changed', w => { this._settings.set_int('icon-opacity', w.get_value_as_int()); }); if (imports.gi.versions.Gtk === '4.0') { this.opacity_hbox.append(label); this.opacity_hbox.append(widget); } else { this.opacity_hbox.pack_start(label, true, true, 0); this.opacity_hbox.pack_start(widget, false, false, 0); } // Icon saturation this.saturation_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Desaturation (min: 0.0, max: 1.0)'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.SpinButton({ halign: Gtk.Align.END, digits: 1 }); widget.set_sensitive(true); widget.set_range(0.0, 1.0); widget.set_value(this._settings.get_double('icon-saturation')); widget.set_increments(0.1, 0.2); widget.connect('value-changed', w => { this._settings.set_double('icon-saturation', w.get_value()); }); if (imports.gi.versions.Gtk === '4.0') { this.saturation_hbox.append(label); this.saturation_hbox.append(widget); } else { this.saturation_hbox.pack_start(label, true, true, 0); this.saturation_hbox.pack_start(widget, false, false, 0); } // Icon brightness this.brightness_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Brightness (min: -1.0, max: 1.0)'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.SpinButton({ halign: Gtk.Align.END, digits: 1 }); widget.set_sensitive(true); widget.set_range(-1.0, 1.0); widget.set_value(this._settings.get_double('icon-brightness')); widget.set_increments(0.1, 0.2); widget.connect('value-changed', w => { this._settings.set_double('icon-brightness', w.get_value()); }); if (imports.gi.versions.Gtk === '4.0') { this.brightness_hbox.append(label); this.brightness_hbox.append(widget); } else { this.brightness_hbox.pack_start(label, true, true, 0); this.brightness_hbox.pack_start(widget, false, false, 0); } // Icon contrast this.contrast_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Contrast (min: -1.0, max: 1.0)'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.SpinButton({ halign: Gtk.Align.END, digits: 1 }); widget.set_sensitive(true); widget.set_range(-1.0, 1.0); widget.set_value(this._settings.get_double('icon-contrast')); widget.set_increments(0.1, 0.2); widget.connect('value-changed', w => { this._settings.set_double('icon-contrast', w.get_value()); }); if (imports.gi.versions.Gtk === '4.0') { this.contrast_hbox.append(label); this.contrast_hbox.append(widget); } else { this.contrast_hbox.pack_start(label, true, true, 0); this.contrast_hbox.pack_start(widget, false, false, 0); } // Icon size this.icon_size_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Icon size (min: 0, max: 96)'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.SpinButton({ halign: Gtk.Align.END }); widget.set_sensitive(true); widget.set_range(0, 96); widget.set_value(this._settings.get_int('icon-size')); widget.set_increments(1, 2); widget.connect('value-changed', w => { this._settings.set_int('icon-size', w.get_value_as_int()); }); if (imports.gi.versions.Gtk === '4.0') { this.icon_size_hbox.append(label); this.icon_size_hbox.append(widget); } else { this.icon_size_hbox.pack_start(label, true, true, 0); this.icon_size_hbox.pack_start(widget, false, false, 0); } // Tray position in panel this.tray_position_hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10, margin_start: 10, margin_end: 10, margin_top: 10, margin_bottom: 10 }); label = new Gtk.Label({ label: _('Tray horizontal alignment'), hexpand: true, halign: Gtk.Align.START, }); widget = new Gtk.ComboBoxText(); widget.append('center', _('Center')); widget.append('left', _('Left')); widget.append('right', _('Right')); this._settings.bind('tray-pos', widget, 'active-id', Gio.SettingsBindFlags.DEFAULT); if (imports.gi.versions.Gtk === '4.0') { this.tray_position_hbox.append(label); this.tray_position_hbox.append(widget); this.preferences_vbox.append(this.opacity_hbox); this.preferences_vbox.append(this.saturation_hbox); this.preferences_vbox.append(this.brightness_hbox); this.preferences_vbox.append(this.contrast_hbox); this.preferences_vbox.append(this.icon_size_hbox); this.preferences_vbox.append(this.tray_position_hbox); } else { this.tray_position_hbox.pack_start(label, true, true, 0); this.tray_position_hbox.pack_start(widget, false, false, 0); this.preferences_vbox.pack_start(this.opacity_hbox, true, false, 0); this.preferences_vbox.pack_start(this.saturation_hbox, true, false, 0); this.preferences_vbox.pack_start(this.brightness_hbox, true, false, 0); this.preferences_vbox.pack_start(this.contrast_hbox, true, false, 0); this.preferences_vbox.pack_start(this.icon_size_hbox, true, false, 0); this.preferences_vbox.pack_start(this.tray_position_hbox, true, false, 0); } // Custom icons section const customListStore = new Gtk.ListStore(); customListStore.set_column_types([ GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, ]); const customInitArray = this._settings.get_value('custom-icons').deep_unpack(); customInitArray.forEach(pair => { customListStore.set(customListStore.append(), [0, 1, 2], pair); }); customListStore.append(); const customTreeView = new Gtk.TreeView({ model: customListStore, hexpand: true, vexpand: true, }); const customTitles = [ _('Indicator ID'), _('Icon Name'), _('Attention Icon Name'), ]; const indicatorIdColumn = new Gtk.TreeViewColumn({ title: customTitles[0], sizing: Gtk.TreeViewColumnSizing.AUTOSIZE, }); const customIconColumn = new Gtk.TreeViewColumn({ title: customTitles[1], sizing: Gtk.TreeViewColumnSizing.AUTOSIZE, }); const customAttentionIconColumn = new Gtk.TreeViewColumn({ title: customTitles[2], sizing: Gtk.TreeViewColumnSizing.AUTOSIZE, }); const cellrenderer = new Gtk.CellRendererText({ editable: true }); indicatorIdColumn.pack_start(cellrenderer, true); customIconColumn.pack_start(cellrenderer, true); customAttentionIconColumn.pack_start(cellrenderer, true); indicatorIdColumn.add_attribute(cellrenderer, 'text', 0); customIconColumn.add_attribute(cellrenderer, 'text', 1); customAttentionIconColumn.add_attribute(cellrenderer, 'text', 2); customTreeView.insert_column(indicatorIdColumn, 0); customTreeView.insert_column(customIconColumn, 1); customTreeView.insert_column(customAttentionIconColumn, 2); customTreeView.set_grid_lines(Gtk.TreeViewGridLines.BOTH); if (imports.gi.versions.Gtk === '4.0') this.custom_icons_vbox.append(customTreeView); else this.custom_icons_vbox.pack_start(customTreeView, false, false, 0); cellrenderer.connect('edited', (w, path, text) => { this.selection = customTreeView.get_selection(); const title = customTreeView.get_cursor()[1].get_title(); const columnIndex = customTitles.indexOf(title); const selection = this.selection.get_selected(); const iter = selection[2]; const text2 = customListStore.get_value(iter, columnIndex ? 0 : 1); customListStore.set(iter, [columnIndex], [text]); const storeLength = customListStore.iter_n_children(null); const customIconArray = []; for (let i = 0; i < storeLength; i++) { const returnIter = customListStore.iter_nth_child(null, i); const [success, iterList] = returnIter; if (!success) break; if (iterList) { const id = customListStore.get_value(iterList, 0); const customIcon = customListStore.get_value(iterList, 1); const customAttentionIcon = customListStore.get_value(iterList, 2); if (id && customIcon) customIconArray.push([id, customIcon, customAttentionIcon || '']); } else { break; } } this._settings.set_value('custom-icons', new GLib.Variant( 'a(sss)', customIconArray)); if (storeLength === 1 && (text || text2)) customListStore.append(); if (storeLength > 1) { if ((!text && !text2) && (storeLength - 1 > path)) customListStore.remove(iter); if ((text || text2) && storeLength - 1 <= path) customListStore.append(); } }); this.notebook = new Gtk.Notebook(); this.notebook.append_page(this.preferences_vbox, new Gtk.Label({ label: _('Preferences') })); this.notebook.append_page(this.custom_icons_vbox, new Gtk.Label({ label: _('Custom Icons') })); if (imports.gi.versions.Gtk === '4.0') this.append(this.notebook); else this.add(this.notebook); } }); function buildPrefsWidget() { let widget = new AppIndicatorPreferences(); if (widget.show_all) widget.show_all(); return widget; } if (!Me) { GLib.setenv('GSETTINGS_SCHEMA_DIR', './schemas', true); Gtk.init(null); const loop = GLib.MainLoop.new(null, false); const win = new Gtk.Window(); if (win.set_child) { win.set_child(buildPrefsWidget()); win.connect('close-request', () => loop.quit()); } else { win.add(buildPrefsWidget()); win.connect('delete-event', () => loop.quit()); } win.present(); loop.run(); }