mirror of
https://github.com/gosticks/PaperWM.git
synced 2026-02-07 01:12:45 +00:00
Creates dropdown menu to let user choose between never, always, or only showing scratch windows. The state is tracked by two booleans,'disable-scratch-in-overview' and 'only-scratch-in-overview'.
756 lines
27 KiB
JavaScript
756 lines
27 KiB
JavaScript
|
|
const Gio = imports.gi.Gio;
|
|
const GLib = imports.gi.GLib;
|
|
const GObject = imports.gi.GObject;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Gdk = imports.gi.Gdk;
|
|
const Mainloop = imports.mainloop;
|
|
|
|
const ExtensionUtils = imports.misc.extensionUtils;
|
|
const Extension = ExtensionUtils.getCurrentExtension();
|
|
const Convenience = Extension.imports.convenience;
|
|
|
|
const WORKSPACE_KEY = 'org.gnome.Shell.Extensions.PaperWM.Workspace';
|
|
const KEYBINDINGS_KEY = 'org.gnome.Shell.Extensions.PaperWM.Keybindings';
|
|
|
|
const wmSettings = new Gio.Settings({ schema_id:
|
|
'org.gnome.desktop.wm.preferences'});
|
|
|
|
let _ = s => s;
|
|
|
|
// TreeStore model
|
|
const COLUMN_ID = 0;
|
|
const COLUMN_INDEX = 1;
|
|
const COLUMN_DESCRIPTION = 2;
|
|
const COLUMN_KEY = 3;
|
|
const COLUMN_MODS = 4;
|
|
const COLUMN_WARNING = 5;
|
|
const COLUMN_RESET = 6;
|
|
const COLUMN_TOOLTIP = 7;
|
|
|
|
const Settings = Extension.imports.settings;
|
|
let getWorkspaceSettings = Settings.getWorkspaceSettings;
|
|
let getNewWorkspaceSettings = Settings.getNewWorkspaceSettings;
|
|
let getWorkspaceSettingsByUUID = Settings.getWorkspaceSettingsByUUID;
|
|
|
|
function range(n) {
|
|
let r = [];
|
|
for (let i = 0; i < n; i++)
|
|
r.push(i);
|
|
return r;
|
|
}
|
|
|
|
function swapArrayElements(array, i, j) {
|
|
let iVal = array[i];
|
|
array[i] = array[j];
|
|
array[j] = iVal;
|
|
return array;
|
|
}
|
|
|
|
function ok(okValue) {
|
|
if(okValue[0]) {
|
|
return okValue[1]
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
class SettingsWidget {
|
|
/**
|
|
selectedWorkspace: index of initially selected workspace in workspace settings tab
|
|
selectedTab: index of initially shown tab
|
|
*/
|
|
constructor(selectedTab=0, selectedWorkspace=0 ) {
|
|
this._settings = Convenience.getSettings();
|
|
|
|
this.builder = Gtk.Builder.new_from_file(Extension.path + '/Settings.ui');
|
|
|
|
this._notebook = this.builder.get_object('paperwm_settings');
|
|
this.widget = this._notebook;
|
|
this._notebook.page = selectedTab;
|
|
|
|
// General
|
|
|
|
let windowGap = this.builder.get_object('window_gap_spin');
|
|
let gap = this._settings.get_int('window-gap');
|
|
windowGap.set_value(gap);
|
|
windowGap.connect('value-changed', () => {
|
|
this._settings.set_int('window-gap', windowGap.get_value());
|
|
});
|
|
|
|
let hMargin = this.builder.get_object('hmargin_spinner');
|
|
hMargin.set_value(this._settings.get_int('horizontal-margin'));
|
|
hMargin.connect('value-changed', () => {
|
|
this._settings.set_int('horizontal-margin', hMargin.get_value());
|
|
});
|
|
|
|
let topMargin = this.builder.get_object('top_margin_spinner');
|
|
topMargin.set_value(this._settings.get_int('vertical-margin'));
|
|
topMargin.connect('value-changed', () => {
|
|
this._settings.set_int('vertical-margin', topMargin.get_value());
|
|
});
|
|
|
|
let bottomMargin = this.builder.get_object('bottom_margin_spinner');
|
|
bottomMargin.set_value(this._settings.get_int('vertical-margin-bottom'));
|
|
bottomMargin.connect('value-changed', () => {
|
|
this._settings.set_int('vertical-margin-bottom', bottomMargin.get_value());
|
|
});
|
|
|
|
let vSens = this.builder.get_object('vertical-sensitivity');
|
|
let hSens = this.builder.get_object('horizontal-sensitivity');
|
|
let [sx, sy] = this._settings.get_value('swipe-sensitivity').deep_unpack();
|
|
hSens.set_value(sx);
|
|
vSens.set_value(sy);
|
|
let sensChanged = () => {
|
|
this._settings.set_value('swipe-sensitivity', new GLib.Variant('ad', [hSens.get_value(), vSens.get_value()]));
|
|
};
|
|
vSens.connect('value-changed', sensChanged);
|
|
hSens.connect('value-changed', sensChanged);
|
|
|
|
|
|
let vFric = this.builder.get_object('vertical-friction');
|
|
let hFric = this.builder.get_object('horizontal-friction');
|
|
let [fx, fy] = this._settings.get_value('swipe-friction').deep_unpack();
|
|
hFric.set_value(fx);
|
|
vFric.set_value(fy);
|
|
let fricChanged = () => {
|
|
this._settings.set_value('swipe-friction', new GLib.Variant('ad', [hFric.get_value(), vFric.get_value()]));
|
|
};
|
|
vFric.connect('value-changed', fricChanged);
|
|
hFric.connect('value-changed', fricChanged);
|
|
|
|
let scratchOverview = this.builder.get_object('scratch-in-overview');
|
|
if (this._settings.get_boolean('only-scratch-in-overview'))
|
|
scratchOverview.set_active_id('only');
|
|
else if (this._settings.get_boolean('disable-scratch-in-overview'))
|
|
scratchOverview.set_active_id('never');
|
|
else
|
|
scratchOverview.set_active_id('always');
|
|
|
|
scratchOverview.connect('changed', (obj) => {
|
|
if (obj.get_active_id() == 'only') {
|
|
this._settings.set_boolean('only-scratch-in-overview', true);
|
|
this._settings.set_boolean('disable-scratch-in-overview', false);
|
|
} else if (obj.get_active_id() == 'never') {
|
|
this._settings.set_boolean('only-scratch-in-overview', false);
|
|
this._settings.set_boolean('disable-scratch-in-overview', true);
|
|
} else {
|
|
this._settings.set_boolean('only-scratch-in-overview', false);
|
|
this._settings.set_boolean('disable-scratch-in-overview', false);
|
|
}
|
|
|
|
});
|
|
|
|
let disableCorner = this.builder.get_object('override-hot-corner');
|
|
disableCorner.state =
|
|
this._settings.get_boolean('override-hot-corner');
|
|
disableCorner.connect('state-set', (obj, state) => {
|
|
this._settings.set_boolean('override-hot-corner',
|
|
state);
|
|
});
|
|
|
|
let topbarFollowFocus = this.builder.get_object('topbar-follow-focus');
|
|
topbarFollowFocus.state =
|
|
this._settings.get_boolean('topbar-follow-focus');
|
|
topbarFollowFocus.connect('state-set', (obj, state) => {
|
|
this._settings.set_boolean('topbar-follow-focus',
|
|
state);
|
|
});
|
|
|
|
|
|
// Workspaces
|
|
|
|
const defaultBackgroundSwitch = this.builder.get_object('use-default-background');
|
|
defaultBackgroundSwitch.state =
|
|
this._settings.get_boolean('use-default-background');
|
|
defaultBackgroundSwitch.connect('state-set', (obj, state) => {
|
|
this._settings.set_boolean('use-default-background',
|
|
state);
|
|
});
|
|
const backgroundPanelButton = this.builder.get_object('gnome-background-panel');
|
|
backgroundPanelButton.connect('clicked', () => {
|
|
GLib.spawn_async(null, ['gnome-control-center', 'background'],
|
|
GLib.get_environ(),
|
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
|
null);
|
|
});
|
|
|
|
let useDefault = this._settings.get_boolean('use-default-background');
|
|
|
|
const workspaceCombo = this.builder.get_object('workspace_combo_text');
|
|
const workspaceStack = this.builder.get_object('workspace_stack');
|
|
|
|
this.workspaceNames = wmSettings.get_strv('workspace-names');
|
|
const nWorkspaces = Settings.workspaceList.get_strv('list').length;
|
|
|
|
// Note: For some reason we can't set the visible child of the workspace
|
|
// stack at construction time.. (!)
|
|
// Ensure the initially selected workspace is added to the stack
|
|
// first as a workaround.
|
|
let wsIndices = range(nWorkspaces);
|
|
let wsSettingsByIndex = wsIndices.map(i => getWorkspaceSettings(i)[1]);
|
|
let wsIndicesSelectedFirst =
|
|
swapArrayElements(wsIndices.slice(), 0, selectedWorkspace);
|
|
|
|
for (let i of wsIndicesSelectedFirst) {
|
|
let view = this.createWorkspacePage(wsSettingsByIndex[i], i);
|
|
workspaceStack.add_named(view, i.toString());
|
|
}
|
|
|
|
for (let i of wsIndices) {
|
|
// Combo box entries in normal workspace index order
|
|
let name = this.getWorkspaceName(wsSettingsByIndex[i], i);
|
|
workspaceCombo.append_text(name);
|
|
}
|
|
|
|
workspaceCombo.connect('changed', () => {
|
|
if (this._updatingName)
|
|
return;
|
|
|
|
let active = workspaceCombo.get_active();
|
|
workspaceStack.set_visible_child_name(active.toString());
|
|
});
|
|
|
|
workspaceCombo.set_active(selectedWorkspace);
|
|
|
|
// Keybindings
|
|
|
|
let settings = Convenience.getSettings(KEYBINDINGS_KEY);
|
|
let box = this.builder.get_object('keybindings');
|
|
box.spacing = 12;
|
|
|
|
let searchEntry = this.builder.get_object('keybinding_search');
|
|
|
|
let windowFrame = new Gtk.Frame({label: _('Windows'),
|
|
label_xalign: 0.5});
|
|
let windows = createKeybindingWidget(settings, searchEntry);
|
|
box.add(windowFrame);
|
|
windowFrame.add(windows);
|
|
|
|
|
|
['new-window', 'close-window', 'switch-next', 'switch-previous',
|
|
'switch-left', 'switch-right', 'switch-up', 'switch-down',
|
|
'switch-first', 'switch-last', 'live-alt-tab', 'live-alt-tab-backward',
|
|
'move-left', 'move-right', 'move-up', 'move-down',
|
|
'slurp-in', 'barf-out', 'center-horizontally',
|
|
'paper-toggle-fullscreen', 'toggle-maximize-width', 'resize-h-inc',
|
|
'resize-h-dec', 'resize-w-inc', 'resize-w-dec', 'cycle-width',
|
|
'cycle-height', 'take-window']
|
|
.forEach(k => {
|
|
addKeybinding(windows.model.child_model, settings, k);
|
|
});
|
|
|
|
annotateKeybindings(windows.model.child_model, settings);
|
|
|
|
let workspaceFrame = new Gtk.Frame({label: _('Workspaces'),
|
|
label_xalign: 0.5});
|
|
let workspaces = createKeybindingWidget(settings, searchEntry);
|
|
box.add(workspaceFrame);
|
|
workspaceFrame.add(workspaces);
|
|
|
|
['previous-workspace', 'previous-workspace-backward',
|
|
'move-previous-workspace', 'move-previous-workspace-backward',
|
|
'switch-up-workspace', 'switch-down-workspace',
|
|
'move-up-workspace', 'move-down-workspace'
|
|
]
|
|
.forEach(k => {
|
|
addKeybinding(workspaces.model.child_model, settings, k);
|
|
});
|
|
|
|
annotateKeybindings(workspaces.model.child_model, settings);
|
|
|
|
let monitorFrame = new Gtk.Frame({label: _('Monitors'),
|
|
label_xalign: 0.5});
|
|
let monitors = createKeybindingWidget(settings, searchEntry);
|
|
box.add(monitorFrame);
|
|
monitorFrame.add(monitors);
|
|
|
|
['switch-monitor-right',
|
|
'switch-monitor-left',
|
|
'switch-monitor-above',
|
|
'switch-monitor-below',
|
|
'move-monitor-right',
|
|
'move-monitor-left',
|
|
'move-monitor-above',
|
|
'move-monitor-below',
|
|
].forEach(k => {
|
|
addKeybinding(monitors.model.child_model, settings, k);
|
|
});
|
|
|
|
annotateKeybindings(monitors.model.child_model, settings);
|
|
|
|
|
|
let scratchFrame = new Gtk.Frame({label: _('Scratch layer'),
|
|
label_xalign: 0.5});
|
|
let scratch = createKeybindingWidget(settings, searchEntry);
|
|
box.add(scratchFrame);
|
|
scratchFrame.add(scratch);
|
|
|
|
['toggle-scratch-layer', 'toggle-scratch', "toggle-scratch-window"]
|
|
.forEach(k => {
|
|
addKeybinding(scratch.model.child_model, settings, k);
|
|
});
|
|
|
|
annotateKeybindings(scratch.model.child_model, settings);
|
|
|
|
searchEntry.connect('changed', () => {
|
|
[windows, workspaces, monitors, scratch].map(tw => tw.model).forEach(m => m.refilter());
|
|
});
|
|
|
|
|
|
// About
|
|
let versionLabel = this.builder.get_object('extension_version');
|
|
let version = Extension.metadata.version.toString();
|
|
versionLabel.set_text(version);
|
|
}
|
|
|
|
createWorkspacePage(settings, index) {
|
|
|
|
let list = new Gtk.Box({
|
|
orientation: Gtk.Orientation.VERTICAL,
|
|
can_focus: false,
|
|
});
|
|
list.add(new Gtk.LevelBar());
|
|
|
|
let nameEntry = new Gtk.Entry();
|
|
let colorButton = new Gtk.ColorButton();
|
|
|
|
let backgroundBox = new Gtk.Box({spacing: 32}); // same spacing as used in glade for default background
|
|
let background = new Gtk.FileChooserButton();
|
|
let clearBackground = new Gtk.Button({label: 'Clear', sensitive: settings.get_string('background') != ''});
|
|
let hideTopBarSwitch = new Gtk.Switch({active: !settings.get_boolean('show-top-bar')});
|
|
backgroundBox.add(background)
|
|
backgroundBox.add(clearBackground)
|
|
|
|
let directoryChooser = new Gtk.FileChooserButton({
|
|
action: Gtk.FileChooserAction.SELECT_FOLDER,
|
|
title: 'Select workspace directory'
|
|
});
|
|
|
|
list.add(createRow('Name', nameEntry));
|
|
list.add(createRow('Color', colorButton));
|
|
list.add(createRow('Background', backgroundBox));
|
|
list.add(createRow('Hide top bar', hideTopBarSwitch));
|
|
list.add(createRow('Directory', directoryChooser));
|
|
|
|
let rgba = new Gdk.RGBA();
|
|
let color = settings.get_string('color');
|
|
let palette = this._settings.get_strv('workspace-colors');
|
|
if (color === '')
|
|
color = palette[index % palette.length];
|
|
|
|
rgba.parse(color);
|
|
colorButton.set_rgba(rgba);
|
|
|
|
let filename = settings.get_string('background');
|
|
if (filename === '')
|
|
background.unselect_all();
|
|
else
|
|
background.set_filename(filename);
|
|
|
|
nameEntry.set_text(this.getWorkspaceName(settings, index));
|
|
|
|
let workspace_combo = this.builder.get_object('workspace_combo_text');
|
|
|
|
nameEntry.connect('changed', () => {
|
|
let active = workspace_combo.get_active();
|
|
let name = nameEntry.get_text();
|
|
|
|
this._updatingName = true;
|
|
workspace_combo.remove(active);
|
|
workspace_combo.insert_text(active, name);
|
|
|
|
workspace_combo.set_active(active);
|
|
this._updatingName = false;
|
|
|
|
settings.set_string('name', name);
|
|
});
|
|
|
|
colorButton.connect('color-set', () => {
|
|
let color = colorButton.get_rgba().to_string();
|
|
settings.set_string('color', color);
|
|
settings.set_string('background', '');
|
|
background.unselect_all();
|
|
});
|
|
|
|
background.connect('file-set', () => {
|
|
let filename = background.get_filename();
|
|
settings.set_string('background', filename);
|
|
clearBackground.sensitive = filename != '';
|
|
});
|
|
|
|
clearBackground.connect('clicked', () => {
|
|
background.unselect_all(); // Note: does not trigger 'file-set'
|
|
settings.reset('background');
|
|
clearBackground.sensitive = settings.get_string('background') != '';
|
|
});
|
|
|
|
hideTopBarSwitch.connect('state-set', (gtkswitch_, state) => {
|
|
settings.set_boolean('show-top-bar', !state);
|
|
});
|
|
|
|
let dir = settings.get_string('directory')
|
|
if (dir === '')
|
|
directoryChooser.unselect_all();
|
|
else
|
|
directoryChooser.set_filename(dir)
|
|
|
|
directoryChooser.connect('file-set', () => {
|
|
let dir = directoryChooser.get_filename();
|
|
settings.set_string('directory', dir);
|
|
});
|
|
|
|
return list;
|
|
}
|
|
|
|
getWorkspaceName(settings, index) {
|
|
let name = settings.get_string('name');
|
|
if (name === '')
|
|
name = this.workspaceNames[index];
|
|
if (name === undefined)
|
|
name = `Workspace ${index + 1}`;
|
|
return name;
|
|
}
|
|
|
|
}
|
|
|
|
function createRow(text, widget, signal, handler) {
|
|
let margin = 12;
|
|
let box = new Gtk.Box({
|
|
margin_start: margin, margin_end: margin,
|
|
margin_top: margin/2, margin_bottom: margin/2,
|
|
orientation: Gtk.Orientation.HORIZONTAL
|
|
});
|
|
let label = new Gtk.Label({
|
|
label: text, hexpand: true, xalign: 0
|
|
});
|
|
|
|
box.add(label);
|
|
box.add(widget);
|
|
|
|
return box;
|
|
}
|
|
|
|
function createKeybindingWidget(settings, searchEntry) {
|
|
let model = new Gtk.TreeStore();
|
|
let filteredModel = new Gtk.TreeModelFilter({child_model: model});
|
|
filteredModel.set_visible_func(
|
|
(model, iter) => {
|
|
let desc = model.get_value(iter, COLUMN_DESCRIPTION);
|
|
|
|
if(ok(model.iter_parent(iter)) || desc === null) {
|
|
return true;
|
|
}
|
|
|
|
let query = searchEntry.get_chars(0, -1).toLowerCase().split(" ");
|
|
let descLc = desc.toLowerCase();
|
|
|
|
return query.every(word => descLc.indexOf(word) > -1);
|
|
}
|
|
);
|
|
|
|
model.set_column_types(
|
|
[
|
|
// GObject.TYPE_BOOLEAN, // COLUMN_VISIBLE
|
|
GObject.TYPE_STRING, // COLUMN_ID
|
|
GObject.TYPE_INT, // COLUMN_INDEX
|
|
GObject.TYPE_STRING, // COLUMN_DESCRIPTION
|
|
GObject.TYPE_INT, // COLUMN_KEY
|
|
GObject.TYPE_INT, // COLUMN_MODS
|
|
GObject.TYPE_BOOLEAN,// COLUMN_WARNING
|
|
GObject.TYPE_BOOLEAN,// COLUMN_RESET
|
|
GObject.TYPE_STRING, // COLUMN_TOOLTIP
|
|
]);
|
|
|
|
let treeView = new Gtk.TreeView();
|
|
treeView.set_enable_search(false);
|
|
treeView.model = filteredModel;
|
|
treeView.headers_visible = false;
|
|
treeView.margin_start = 12;
|
|
treeView.margin_end = 12;
|
|
treeView.search_column = COLUMN_DESCRIPTION;
|
|
treeView.tooltip_column = COLUMN_TOOLTIP;
|
|
|
|
let descriptionRenderer = new Gtk.CellRendererText();
|
|
let descriptionColumn = new Gtk.TreeViewColumn();
|
|
descriptionColumn.expand = true;
|
|
descriptionColumn.pack_start(descriptionRenderer, true);
|
|
descriptionColumn.add_attribute(descriptionRenderer, "text", COLUMN_DESCRIPTION);
|
|
|
|
treeView.append_column(descriptionColumn);
|
|
|
|
let warningRenderer = new Gtk.CellRendererPixbuf();
|
|
warningRenderer.mode = Gtk.CellRendererMode.INERT;
|
|
warningRenderer.stock_id = 'gtk-dialog-warning';
|
|
let warningColumn = new Gtk.TreeViewColumn();
|
|
warningColumn.pack_start(warningRenderer, true);
|
|
warningColumn.add_attribute(warningRenderer, "visible", COLUMN_WARNING);
|
|
|
|
treeView.append_column(warningColumn);
|
|
|
|
let accelRenderer = new Gtk.CellRendererAccel();
|
|
accelRenderer.accel_mode = Gtk.CellRendererAccelMode.GTK;
|
|
accelRenderer.editable = true;
|
|
|
|
accelRenderer.connect("accel-edited",
|
|
(accelRenderer, path, key, mods, hwCode) => {
|
|
let iter = ok(filteredModel.get_iter_from_string(path));
|
|
if(!iter)
|
|
return;
|
|
|
|
iter = filteredModel.convert_iter_to_child_iter(iter);
|
|
|
|
// Update the UI.
|
|
model.set(iter, [COLUMN_KEY, COLUMN_MODS], [key, mods]);
|
|
|
|
// Update the stored setting.
|
|
let id = model.get_value(iter, COLUMN_ID);
|
|
let index = model.get_value(iter, COLUMN_INDEX);
|
|
let accelString = Gtk.accelerator_name(key, mods);
|
|
|
|
let accels = settings.get_strv(id);
|
|
|
|
if (index === -1) {
|
|
accels.push(accelString);
|
|
} else {
|
|
accels[index] = accelString
|
|
}
|
|
settings.set_strv(id, accels);
|
|
|
|
let newEmptyRow = null, parent;
|
|
if (index === -1) {
|
|
model.set_value(iter, COLUMN_INDEX, accels.length-1);
|
|
model.set_value(iter, COLUMN_DESCRIPTION, "...");
|
|
|
|
let parent = ok(model.iter_parent(iter));
|
|
newEmptyRow = model.insert_after(parent, iter);
|
|
} else if (index === 0 && !model.iter_has_child(iter)) {
|
|
newEmptyRow = model.insert(iter, -1);
|
|
}
|
|
|
|
if (newEmptyRow) {
|
|
model.set(newEmptyRow, ...transpose([
|
|
[COLUMN_ID, id],
|
|
[COLUMN_INDEX, -1],
|
|
[COLUMN_DESCRIPTION, "New binding"],
|
|
[COLUMN_KEY, 0],
|
|
[COLUMN_MODS, 0],
|
|
]));
|
|
}
|
|
|
|
annotateKeybindings(model, settings);
|
|
});
|
|
|
|
accelRenderer.connect("accel-cleared",
|
|
(accelRenderer, path) => {
|
|
let iter = ok(filteredModel.get_iter_from_string(path));
|
|
if(!iter)
|
|
return;
|
|
|
|
iter = filteredModel.convert_iter_to_child_iter(iter);
|
|
|
|
let index = model.get_value(iter, COLUMN_INDEX);
|
|
|
|
// Update the UI.
|
|
model.set(iter, [COLUMN_KEY, COLUMN_MODS], [0, 0]);
|
|
|
|
if (index === -1) {
|
|
// Clearing the empty row
|
|
return;
|
|
}
|
|
|
|
let id = model.get_value(iter, COLUMN_ID);
|
|
let accels = settings.get_strv(id);
|
|
accels.splice(index, 1);
|
|
|
|
let parent, nextSibling;
|
|
// Simply rebuild the model for this action
|
|
if (index === 0) {
|
|
parent = iter.copy();
|
|
} else {
|
|
parent = ok(model.iter_parent(iter));
|
|
}
|
|
nextSibling = parent.copy();
|
|
|
|
if(!model.iter_next(nextSibling))
|
|
nextSibling = null;
|
|
|
|
model.remove(parent);
|
|
|
|
// Update the stored setting.
|
|
settings.set_strv(id, accels);
|
|
|
|
let recreated = addKeybinding(model, settings, id, nextSibling);
|
|
let selection = treeView.get_selection();
|
|
selection.select_iter(recreated);
|
|
|
|
annotateKeybindings(model, settings);
|
|
});
|
|
|
|
let accelColumn = new Gtk.TreeViewColumn();
|
|
accelColumn.pack_end(accelRenderer, false);
|
|
accelColumn.add_attribute(accelRenderer, "accel-key", COLUMN_KEY);
|
|
accelColumn.add_attribute(accelRenderer, "accel-mods", COLUMN_MODS);
|
|
|
|
treeView.append_column(accelColumn);
|
|
|
|
let resetRenderer = new Gtk.CellRendererToggle();
|
|
resetRenderer.mode = Gtk.CellRendererMode.ACTIVATABLE;
|
|
let resetColumn = new Gtk.TreeViewColumn();
|
|
resetColumn.clickable = true;
|
|
resetColumn.pack_start(resetRenderer, true);
|
|
resetColumn.add_attribute(resetRenderer, "visible", COLUMN_RESET);
|
|
|
|
resetRenderer.connect('toggled', (renderer, path) => {
|
|
let iter = ok(filteredModel.get_iter_from_string(path));
|
|
if(!iter)
|
|
return;
|
|
iter = filteredModel.convert_iter_to_child_iter(iter);
|
|
|
|
let id = model.get_value(iter, COLUMN_ID);
|
|
if (settings.get_user_value(id)) {
|
|
settings.reset(id);
|
|
model.set_value(iter, COLUMN_RESET, false);
|
|
}
|
|
|
|
let parent = ok(model.iter_parent(iter)) || iter.copy();
|
|
let nextSibling = parent.copy();
|
|
if(!model.iter_next(nextSibling))
|
|
nextSibling = null;
|
|
|
|
model.remove(parent);
|
|
|
|
let recreated = addKeybinding(model, settings, id, nextSibling);
|
|
let selection = treeView.get_selection();
|
|
selection.select_iter(recreated);
|
|
|
|
annotateKeybindings(model, settings);
|
|
});
|
|
|
|
treeView.append_column(resetColumn);
|
|
|
|
return treeView;
|
|
}
|
|
|
|
function parseAccelerator(accelerator) {
|
|
let key, mods;
|
|
if (accelerator.match(/Above_Tab/)) {
|
|
let keymap = Gdk.Keymap.get_default();
|
|
let entries = keymap.get_entries_for_keycode(49)[1];
|
|
let keyval = keymap.lookup_key(entries[0]);
|
|
let name = Gtk.accelerator_name(keyval, 0);
|
|
accelerator = accelerator.replace('Above_Tab', name);
|
|
}
|
|
|
|
[key, mods] = Gtk.accelerator_parse(accelerator);
|
|
return [key, mods];
|
|
}
|
|
|
|
function transpose(colValPairs) {
|
|
let colKeys = [], values = [];
|
|
colValPairs.forEach(([k, v]) => {
|
|
colKeys.push(k); values.push(v);
|
|
})
|
|
return [colKeys, values];
|
|
}
|
|
|
|
function addKeybinding(model, settings, id, position=null) {
|
|
let accels = settings.get_strv(id);
|
|
|
|
let schema = settings.settings_schema;
|
|
let schemaKey = schema.get_key(id);
|
|
let description = _(schemaKey.get_summary());
|
|
|
|
let accelerator = accels.length > 0 ? accels[0] : null;
|
|
// Add a row for the keybinding.
|
|
let [key, mods] = accelerator ? parseAccelerator(accelerator) : [0, 0];
|
|
let row = model.insert_before(null, position);
|
|
model.set(row, ...transpose([
|
|
[COLUMN_ID, id],
|
|
[COLUMN_INDEX, 0],
|
|
[COLUMN_DESCRIPTION, description],
|
|
[COLUMN_KEY, key],
|
|
[COLUMN_MODS, mods],
|
|
]));
|
|
|
|
|
|
|
|
// Add one subrow for each additional keybinding
|
|
accels.slice(1).forEach((accelerator, i) => {
|
|
let [key, mods] = parseAccelerator(accelerator);
|
|
let subrow = model.insert(row, 0);
|
|
model.set(subrow, ...transpose([
|
|
[COLUMN_ID, id],
|
|
[COLUMN_INDEX, i+1],
|
|
[COLUMN_DESCRIPTION, "..."],
|
|
[COLUMN_KEY, key],
|
|
[COLUMN_MODS, mods],
|
|
]));
|
|
});
|
|
|
|
if (accels.length !== 0) {
|
|
// Add an empty row used for adding new bindings
|
|
let emptyRow = model.append(row);
|
|
model.set(emptyRow, ...transpose([
|
|
[COLUMN_ID, id],
|
|
[COLUMN_INDEX, -1],
|
|
[COLUMN_DESCRIPTION, "New binding"],
|
|
[COLUMN_KEY, 0],
|
|
[COLUMN_MODS, 0],
|
|
]));
|
|
}
|
|
|
|
return row;
|
|
}
|
|
|
|
function annotateKeybindings(model, settings) {
|
|
let conflicts = Settings.findConflicts();
|
|
let warning = (id, c) => {
|
|
return conflicts.filter(({name, combo}) => name === id && combo === c);
|
|
};
|
|
|
|
model.foreach((model, path, iter) => {
|
|
let id = model.get_value(iter, COLUMN_ID);
|
|
if (model.iter_depth(iter) === 0) {
|
|
let reset = settings.get_user_value(id) ? true : false;
|
|
model.set_value(iter, COLUMN_RESET, reset);
|
|
}
|
|
|
|
let accels = settings.get_strv(id);
|
|
let index = model.get_value(iter, COLUMN_INDEX);
|
|
if (index === -1 || accels.length === 0)
|
|
return;
|
|
let combo = Settings.keystrToKeycombo(accels[index]);
|
|
|
|
let conflict = warning(id, combo);
|
|
let tooltip = null;
|
|
if (conflict.length > 0) {
|
|
let keystr = Settings.keycomboToKeystr(combo);
|
|
tooltip = `${keystr} overrides ${conflict[0].conflicts} in ${conflict[0].settings.path}`;
|
|
|
|
model.set_value(iter, COLUMN_TOOLTIP,
|
|
GLib.markup_escape_text(tooltip, -1));
|
|
model.set_value(iter, COLUMN_WARNING, true);
|
|
} else {
|
|
model.set_value(iter, COLUMN_WARNING, false);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
}
|
|
|
|
function buildPrefsWidget() {
|
|
let selectedWorkspace = GLib.getenv("PAPERWM_PREFS_SELECTED_WORKSPACE");
|
|
let selectedTab = 0;
|
|
if (selectedWorkspace) {
|
|
selectedTab = 1;
|
|
}
|
|
let settings = new SettingsWidget(selectedTab, selectedWorkspace || 0);
|
|
let widget = settings.widget;
|
|
widget.show_all();
|
|
return widget;
|
|
}
|