[gnome-shell-extensions] auto-move: Overhaul preference dialog
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell-extensions] auto-move: Overhaul preference dialog
- Date: Sun, 3 May 2020 17:53:20 +0000 (UTC)
commit c87cfc822a19fd75b444e733f0c9b51b3bc4c300
Author: Florian Müllner <fmuellner gnome org>
Date: Fri May 1 16:33:41 2020 +0200
auto-move: Overhaul preference dialog
auto-move uses the same outdated UI pattern as workspace-indicator did
until commit 90d3c5c51d2, imposing the same problems for a future GTK4
port.
So replace treeview and toolbar with an editable list like we did for
the other extension.
https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/121
extensions/auto-move-windows/prefs.js | 446 +++++++++++++++++-----------------
1 file changed, 229 insertions(+), 217 deletions(-)
---
diff --git a/extensions/auto-move-windows/prefs.js b/extensions/auto-move-windows/prefs.js
index 84f98ae..1324451 100644
--- a/extensions/auto-move-windows/prefs.js
+++ b/extensions/auto-move-windows/prefs.js
@@ -2,11 +2,10 @@
// Start apps on custom workspaces
/* exported init buildPrefsWidget */
-const { Gio, GObject, Gtk } = imports.gi;
+const { Gio, GLib, GObject, Gtk, Pango } = imports.gi;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
-const N_ = e => e;
const ExtensionUtils = imports.misc.extensionUtils;
@@ -14,269 +13,282 @@ const SETTINGS_KEY = 'application-list';
const WORKSPACE_MAX = 36; // compiled in limit of mutter
-const Columns = {
- APPINFO: 0,
- DISPLAY_NAME: 1,
- ICON: 2,
- WORKSPACE: 3,
- ADJUSTMENT: 4,
-};
+const AutoMoveSettingsWidget = GObject.registerClass(
+class AutoMoveSettingsWidget extends Gtk.ScrolledWindow {
+ _init() {
+ super._init({
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ });
+
+ const box = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ halign: Gtk.Align.CENTER,
+ spacing: 12,
+ margin_top: 36,
+ margin_bottom: 36,
+ margin_start: 36,
+ margin_end: 36,
+ });
+ this.add(box);
-const Widget = GObject.registerClass(
-class Widget extends Gtk.Grid {
- _init(params) {
- super._init(params);
- this.set_orientation(Gtk.Orientation.VERTICAL);
+ box.add(new Gtk.Label({
+ label: '<b>%s</b>'.format(_('Workspace Rules')),
+ use_markup: true,
+ halign: Gtk.Align.START,
+ }));
- this._settings = ExtensionUtils.getSettings();
- this._settings.connect('changed', this._refresh.bind(this));
- this._changedPermitted = false;
+ this._list = new Gtk.ListBox({
+ selection_mode: Gtk.SelectionMode.NONE,
+ valign: Gtk.Align.START,
+ });
+ this._list.set_header_func(this._updateHeader.bind(this));
+ box.add(this._list);
- this._store = new Gtk.ListStore();
- this._store.set_column_types([
- Gio.AppInfo,
- GObject.TYPE_STRING,
- Gio.Icon,
- GObject.TYPE_INT,
- Gtk.Adjustment,
- ]);
+ const context = this._list.get_style_context();
+ const cssProvider = new Gtk.CssProvider();
+ cssProvider.load_from_data(
+ 'list { min-width: 30em; }');
- let scrolled = new Gtk.ScrolledWindow({ shadow_type: Gtk.ShadowType.IN });
- scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
- this.add(scrolled);
+ context.add_provider(cssProvider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ context.add_class('frame');
+ this._list.add(new NewRuleRow());
- this._treeView = new Gtk.TreeView({
- model: this._store,
- hexpand: true,
- vexpand: true,
- });
- this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this._list.insert_action_group('rules', this._actionGroup);
- let appColumn = new Gtk.TreeViewColumn({
- expand: true,
- sort_column_id: Columns.DISPLAY_NAME,
- title: _('Application'),
- });
- let iconRenderer = new Gtk.CellRendererPixbuf();
- appColumn.pack_start(iconRenderer, false);
- appColumn.add_attribute(iconRenderer, 'gicon', Columns.ICON);
- let nameRenderer = new Gtk.CellRendererText();
- appColumn.pack_start(nameRenderer, true);
- appColumn.add_attribute(nameRenderer, 'text', Columns.DISPLAY_NAME);
- this._treeView.append_column(appColumn);
-
- let workspaceColumn = new Gtk.TreeViewColumn({
- title: _('Workspace'),
- sort_column_id: Columns.WORKSPACE,
+ let action;
+ action = new Gio.SimpleAction({ name: 'add' });
+ action.connect('activate', this._onAddActivated.bind(this));
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({
+ name: 'remove',
+ parameter_type: new GLib.VariantType('s'),
});
- let workspaceRenderer = new Gtk.CellRendererSpin({ editable: true });
- workspaceRenderer.connect('edited', this._workspaceEdited.bind(this));
- workspaceColumn.pack_start(workspaceRenderer, true);
- workspaceColumn.add_attribute(workspaceRenderer, 'adjustment', Columns.ADJUSTMENT);
- workspaceColumn.add_attribute(workspaceRenderer, 'text', Columns.WORKSPACE);
- this._treeView.append_column(workspaceColumn);
-
- scrolled.add(this._treeView);
-
- let toolbar = new Gtk.Toolbar({ icon_size: Gtk.IconSize.SMALL_TOOLBAR });
- toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
- this.add(toolbar);
-
- let newButton = new Gtk.ToolButton({
- icon_name: 'bookmark-new-symbolic',
- label: _('Add Rule'),
- is_important: true,
+ action.connect('activate', this._onRemoveActivated.bind(this));
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({ name: 'update' });
+ action.connect('activate', () => {
+ this._settings.set_strv(SETTINGS_KEY,
+ this._getRuleRows().map(row => `${row.id}:${row.value}`));
});
- newButton.connect('clicked', this._createNew.bind(this));
- toolbar.add(newButton);
+ this._actionGroup.add_action(action);
+ this._updateAction = action;
- let delButton = new Gtk.ToolButton({ icon_name: 'edit-delete-symbolic' });
- delButton.connect('clicked', this._deleteSelected.bind(this));
- toolbar.add(delButton);
+ this._settings = ExtensionUtils.getSettings();
+ this._changedId = this._settings.connect('changed',
+ this._sync.bind(this));
+ this._sync();
- let selection = this._treeView.get_selection();
- selection.connect('changed', () => {
- delButton.sensitive = selection.count_selected_rows() > 0;
- });
- delButton.sensitive = selection.count_selected_rows() > 0;
+ this.connect('destroy', () => this._settings.run_dispose());
- this._changedPermitted = true;
- this._refresh();
+ this.show_all();
}
- _createNew() {
- let dialog = new Gtk.Dialog({
- title: _('Create new matching rule'),
- transient_for: this.get_toplevel(),
- use_header_bar: true,
- modal: true,
- });
- dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
- let addButton = dialog.add_button(_('Add'), Gtk.ResponseType.OK);
- dialog.set_default_response(Gtk.ResponseType.OK);
-
- let grid = new Gtk.Grid({
- column_spacing: 10,
- row_spacing: 15,
- margin: 10,
- });
- dialog._appChooser = new Gtk.AppChooserWidget({ show_all: true });
- dialog._appChooser.connect('application-selected', (w, appInfo) => {
- addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
- });
- let appInfo = dialog._appChooser.get_app_info();
- addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
-
- grid.attach(dialog._appChooser, 0, 0, 2, 1);
- grid.attach(new Gtk.Label({
- label: _('Workspace'),
- halign: Gtk.Align.END,
- }), 0, 1, 1, 1);
- let adjustment = new Gtk.Adjustment({
- lower: 1,
- upper: WORKSPACE_MAX,
- step_increment: 1,
- });
- dialog._spin = new Gtk.SpinButton({
- adjustment,
- snap_to_ticks: true,
- });
- dialog._spin.set_value(1);
- grid.attach(dialog._spin, 1, 1, 1, 1);
- dialog.get_content_area().add(grid);
-
+ _onAddActivated() {
+ const dialog = new NewRuleDialog(this.get_toplevel());
dialog.connect('response', (dlg, id) => {
- if (id !== Gtk.ResponseType.OK) {
- dialog.destroy();
- return;
+ const appInfo = id === Gtk.ResponseType.OK
+ ? dialog.get_widget().get_app_info() : null;
+ if (appInfo) {
+ this._settings.set_strv(SETTINGS_KEY, [
+ ...this._settings.get_strv(SETTINGS_KEY),
+ `${appInfo.get_id()}:1`,
+ ]);
}
+ dialog.destroy();
+ });
+ }
- appInfo = dialog._appChooser.get_app_info();
- if (!appInfo)
- return;
- let index = Math.floor(dialog._spin.value);
- if (isNaN(index) || index < 0)
- index = 1;
-
- this._changedPermitted = false;
- this._appendItem(appInfo.get_id(), index);
- this._changedPermitted = true;
+ _onRemoveActivated(action, param) {
+ const removed = param.deepUnpack();
+ this._settings.set_strv(SETTINGS_KEY,
+ this._settings.get_strv(SETTINGS_KEY).filter(entry => {
+ const [id] = entry.split(':');
+ return id !== removed;
+ }));
+ }
- this._appendRow(appInfo, index);
+ _getRuleRows() {
+ return this._list.get_children().filter(row => !!row.id);
+ }
- dialog.destroy();
+ _sync() {
+ const oldRules = this._getRuleRows();
+ const newRules = this._settings.get_strv(SETTINGS_KEY).map(entry => {
+ const [id, value] = entry.split(':');
+ return { id, value };
});
- dialog.show_all();
- }
- _deleteSelected() {
- let [any, model_, iter] = this._treeView.get_selection().get_selected();
+ this._settings.block_signal_handler(this._changedId);
+ this._updateAction.enabled = false;
- if (any) {
- let appInfo = this._store.get_value(iter, Columns.APPINFO);
+ newRules.forEach(({ id, value }, index) => {
+ const row = oldRules.find(r => r.id === id);
+ const appInfo = row
+ ? null : Gio.DesktopAppInfo.new(id);
- this._changedPermitted = false;
- this._removeItem(appInfo.get_id());
- this._changedPermitted = true;
- this._store.remove(iter);
- }
- }
+ if (row)
+ row.set({ value });
+ else if (appInfo)
+ this._list.insert(new RuleRow(appInfo, value), index);
+ });
+
+ const removed = oldRules.filter(
+ ({ id }) => !newRules.find(r => r.id === id));
+ removed.forEach(r => r.destroy());
- _workspaceEdited(renderer, pathString, text) {
- let index = parseInt(text);
- if (isNaN(index) || index < 0)
- index = 1;
- let path = Gtk.TreePath.new_from_string(pathString);
- let [model_, iter] = this._store.get_iter(path);
- let appInfo = this._store.get_value(iter, Columns.APPINFO);
-
- this._changedPermitted = false;
- this._changeItem(appInfo.get_id(), index);
- this._store.set_value(iter, Columns.WORKSPACE, index);
- this._changedPermitted = true;
+ this._settings.unblock_signal_handler(this._changedId);
+ this._updateAction.enabled = true;
}
- _refresh() {
- if (!this._changedPermitted)
- // Ignore this notification, model is being modified outside
+ _updateHeader(row, before) {
+ if (!before || row.get_header())
return;
+ row.set_header(new Gtk.Separator());
+ }
+});
- this._store.clear();
+const RuleRow = GObject.registerClass({
+ Properties: {
+ 'id': GObject.ParamSpec.string(
+ 'id', 'id', 'id',
+ GObject.ParamFlags.READABLE,
+ ''),
+ 'value': GObject.ParamSpec.uint(
+ 'value', 'value', 'value',
+ GObject.ParamFlags.READWRITE,
+ 1, WORKSPACE_MAX, 1),
+ },
+}, class RuleRow extends Gtk.ListBoxRow {
+ _init(appInfo, value) {
+ super._init({
+ activatable: false,
+ value,
+ });
+ this._appInfo = appInfo;
+
+ const box = new Gtk.Box({
+ spacing: 6,
+ margin_top: 6,
+ margin_bottom: 6,
+ margin_start: 6,
+ margin_end: 6,
+ });
- let currentItems = this._settings.get_strv(SETTINGS_KEY);
- let validItems = [];
- for (let i = 0; i < currentItems.length; i++) {
- let [id, index] = currentItems[i].split(':');
- let appInfo = Gio.DesktopAppInfo.new(id);
- if (!appInfo)
- continue;
- validItems.push(currentItems[i]);
+ const icon = new Gtk.Image({
+ gicon: appInfo.get_icon(),
+ pixel_size: 32,
+ });
+ icon.get_style_context().add_class('icon-dropshadow');
+ box.add(icon);
- this._appendRow(appInfo, parseInt(index));
- }
+ const label = new Gtk.Label({
+ label: appInfo.get_display_name(),
+ halign: Gtk.Align.START,
+ hexpand: true,
+ max_width_chars: 20,
+ ellipsize: Pango.EllipsizeMode.END,
+ });
+ box.add(label);
+
+ const spinButton = new Gtk.SpinButton({
+ adjustment: new Gtk.Adjustment({
+ lower: 1,
+ upper: WORKSPACE_MAX,
+ step_increment: 1,
+ }),
+ snap_to_ticks: true,
+ margin_end: 6,
+ });
+ this.bind_property('value',
+ spinButton, 'value',
+ GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
+ box.add(spinButton);
+
+ const button = new Gtk.Button({
+ action_name: 'rules.remove',
+ action_target: new GLib.Variant('s', this.id),
+ image: new Gtk.Image({
+ icon_name: 'edit-delete-symbolic',
+ pixel_size: 16,
+ }),
+ });
+ box.add(button);
- if (validItems.length !== currentItems.length) // some items were filtered out
- this._settings.set_strv(SETTINGS_KEY, validItems);
- }
+ this.add(box);
- _appendRow(appInfo, workspace) {
- let iter = this._store.append();
- let icon = appInfo.get_icon();
- let displayName = appInfo.get_display_name();
- let adj = new Gtk.Adjustment({
- lower: 1,
- upper: WORKSPACE_MAX,
- step_increment: 1,
- value: workspace,
+ this.connect('notify::value', () => {
+ const actionGroup = this.get_action_group('rules');
+ actionGroup.activate_action('update', null);
});
- let { APPINFO, ICON, DISPLAY_NAME, WORKSPACE, ADJUSTMENT } = Columns;
- this._store.set(iter,
- [APPINFO, ICON, DISPLAY_NAME, WORKSPACE, ADJUSTMENT],
- [appInfo, icon, displayName, workspace, adj]);
+
+ this.show_all();
}
- _checkId(id) {
- let items = this._settings.get_strv(SETTINGS_KEY);
- return !items.some(i => i.startsWith(`${id}:`));
+ get id() {
+ return this._appInfo.get_id();
}
+});
- _appendItem(id, workspace) {
- let currentItems = this._settings.get_strv(SETTINGS_KEY);
- currentItems.push(`${id}:${workspace}`);
- this._settings.set_strv(SETTINGS_KEY, currentItems);
+const NewRuleRow = GObject.registerClass(
+class NewRuleRow extends Gtk.ListBoxRow {
+ _init() {
+ super._init({
+ action_name: 'rules.add',
+ });
+ this.get_accessible().set_name(_('Add Rule'));
+
+ this.add(new Gtk.Image({
+ icon_name: 'list-add-symbolic',
+ pixel_size: 16,
+ margin_top: 12,
+ margin_bottom: 12,
+ margin_start: 12,
+ margin_end: 12,
+ }));
+
+ this.show_all();
}
+});
- _removeItem(id) {
- let currentItems = this._settings.get_strv(SETTINGS_KEY);
- let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
+const NewRuleDialog = GObject.registerClass(
+class NewRuleDialog extends Gtk.AppChooserDialog {
+ _init(parent) {
+ super._init({
+ transient_for: parent,
+ modal: true,
+ });
- if (index < 0)
- return;
- currentItems.splice(index, 1);
- this._settings.set_strv(SETTINGS_KEY, currentItems);
- }
+ this._settings = ExtensionUtils.getSettings();
+
+ this.get_widget().set({
+ show_all: true,
+ show_other: true, // hide more button
+ });
- _changeItem(id, workspace) {
- let currentItems = this._settings.get_strv(SETTINGS_KEY);
- let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
+ this.get_widget().connect('application-selected',
+ this._updateSensitivity.bind(this));
+ this._updateSensitivity();
- if (index < 0)
- currentItems.push(`${id}:${workspace}`);
- else
- currentItems[index] = `${id}:${workspace}`;
- this._settings.set_strv(SETTINGS_KEY, currentItems);
+ this.show();
}
-});
+ _updateSensitivity() {
+ const rules = this._settings.get_strv(SETTINGS_KEY);
+ const appInfo = this.get_widget().get_app_info();
+ this.set_response_sensitive(Gtk.ResponseType.OK,
+ appInfo && !rules.some(i => i.startsWith(appInfo.get_id())));
+ }
+});
function init() {
ExtensionUtils.initTranslations();
}
function buildPrefsWidget() {
- let widget = new Widget({ margin: 12 });
- widget.show_all();
-
- return widget;
+ return new AutoMoveSettingsWidget();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]