Merge pull request #179 from paperwm/workspace-menu

Modernize the workspace menu
This commit is contained in:
Tor Hedin Brønner
2019-10-21 13:26:23 +02:00
committed by GitHub
4 changed files with 174 additions and 104 deletions

View File

@@ -115,55 +115,13 @@ function cycleWorkspaceSettings(binding = "<Super>q") {
var Settings = Extension.imports.settings;
var Utils = Extension.imports.utils;
function rotated(list, dir=1) {
return [].concat(
list.slice(dir),
list.slice(0, dir)
);
}
function cycle(mw, dir=1) {
let n = global.workspace_manager.get_n_workspaces();
let N = Settings.workspaceList.get_strv('list').length;
let space = Tiling.spaces.selectedSpace;
let wsI = space.workspace.index();
// 2 6 7 8 <-- indices
// x a b c <-- settings
// a b c x <-- rotated settings
let uuids = Settings.workspaceList.get_strv('list');
// Work on tuples of [uuid, settings] since we need to uuid association
// in the last step
let settings = uuids.map(
uuid => [uuid, Settings.getWorkspaceSettingsByUUID(uuid)]
);
settings.sort((a, b) => a[1].get_int('index') - b[1].get_int('index'));
let unbound = settings.slice(n);
let strip = [settings[wsI]].concat(unbound);
strip = rotated(strip, dir);
let nextSettings = strip[0];
unbound = strip.slice(1);
nextSettings[1].set_int('index', wsI);
space.setSettings(nextSettings); // ASSUMPTION: ok that two settings have same index here
// Re-assign unbound indices:
for (let i = n; i < N; i++) {
unbound[i-n][1].set_int('index', i);
}
}
Keybindings.bindkey(
binding, "next-space-setting",
mw => cycle(mw, -1), { activeInNavigator: true }
mw => Tiling.cycleWorkspaceSettings(-1), { activeInNavigator: true }
);
Keybindings.bindkey(
"<Shift>"+binding, "prev-space-setting",
mw => cycle(mw, 1), { activeInNavigator: true }
mw => Tiling.cycleWorkspaceSettings(1), { activeInNavigator: true }
);
}

View File

@@ -2891,6 +2891,49 @@ function sortWindows(space, windows) {
.map(c => c.meta_window);
}
function rotated(list, dir=1) {
return [].concat(
list.slice(dir),
list.slice(0, dir)
);
}
function cycleWorkspaceSettings(dir=1) {
let n = workspaceManager.get_n_workspaces();
let N = Settings.workspaceList.get_strv('list').length;
let space = spaces.selectedSpace;
let wsI = space.workspace.index();
// 2 6 7 8 <-- indices
// x a b c <-- settings
// a b c x <-- rotated settings
let uuids = Settings.workspaceList.get_strv('list');
// Work on tuples of [uuid, settings] since we need to uuid association
// in the last step
let settings = uuids.map(
uuid => [uuid, Settings.getWorkspaceSettingsByUUID(uuid)]
);
settings.sort((a, b) => a[1].get_int('index') - b[1].get_int('index'));
let unbound = settings.slice(n);
let strip = [settings[wsI]].concat(unbound);
strip = rotated(strip, dir);
let nextSettings = strip[0];
unbound = strip.slice(1);
nextSettings[1].set_int('index', wsI);
space.setSettings(nextSettings); // ASSUMPTION: ok that two settings have same index here
// Re-assign unbound indices:
for (let i = n; i < N; i++) {
unbound[i-n][1].set_int('index', i);
}
return space;
}
// Backward compatibility
function defwinprop(...args) {

186
topbar.js
View File

@@ -43,27 +43,67 @@ var colors = [
'#46A046', '#267726', '#ffffff', '#000000'
];
class PopupMenuEntry {
constructor (text, label) {
this.actor = new St.Entry({hint_text: text});
this.entry = this.actor;
this.actor.set_style('margin: 4px 0 4px 0');
// 3.32 crashes when an entry with text is first shown...
let id = this.entry.clutter_text.connect('key-focus-in', () => {
this.entry.clutter_text.disconnect(id);
this.entry.text = this.entry.hint_text;
var PopupMenuEntryHelper = function constructor(text) {
this.label = new St.Entry({
text,
// While not a search entry, this looks much better
style_class:'search-entry',
name: 'workspace-name-entry',
track_hover: true,
reactive: true,
can_focus: true
});
this.actor.add(this.label, {expand: true});
this.actor.label_actor = this.label;
this.label.clutter_text.connect('activate', this.emit.bind(this, 'activate'));
}
this.button = new St.Button({label,
style_class: 'modal-dialog-button button'});
this.actor.set_secondary_icon(this.button);
var PopupMenuEntry;
// 3.32 uses `class` to define `PopupBaseMenuItem`, but doesn't use
// registerClass, breaking our somewhat lame registerClass polyfill.
if (Utils.version[1] === 32) {
PopupMenuEntry = class PopupMenuEntry extends PopupMenu.PopupBaseMenuItem {
constructor(text) {
super({
activate: false,
reactive: true,
hover: false,
can_focus: false
});
this.entry.clutter_text.set_activatable(true);
this.entry.clutter_text.connect('activate', () => {
this.button.emit('clicked', null);
});
}
PopupMenuEntryHelper.call(this, text);
}
activate(event) {
this.label.grab_key_focus();
}
_onKeyFocusIn(actor) {
this.activate();
}
};
} else {
PopupMenuEntry = Utils.registerClass(
class PopupMenuEntry extends PopupMenu.PopupBaseMenuItem {
_init(text) {
super._init({
activate: false,
reactive: true,
hover: false,
can_focus: false
});
PopupMenuEntryHelper.call(this, text);
}
activate(event) {
this.label.grab_key_focus();
}
_onKeyFocusIn(actor) {
this.activate();
}
});
}
class Color {
@@ -128,8 +168,13 @@ class WorkspaceMenu extends PanelMenu.Button {
this.actor.name = 'workspace-button';
let scale = display.get_monitor_scale(Main.layoutManager.primaryIndex);
this._label = new St.Label({
y_align: Clutter.ActorAlign.CENTER });
y_align: Clutter.ActorAlign.CENTER,
// Avoid moving the menu on short names
// TODO: update on scale changes
min_width: 60*scale
});
this.setName(Meta.prefs_get_workspace_name(workspaceManager.get_active_workspace_index()));
@@ -140,41 +185,59 @@ class WorkspaceMenu extends PanelMenu.Button {
'switch-workspace',
this.workspaceSwitched.bind(this));
this.entry = new PopupMenuEntry(this._label.text, 'Set name');
let clicked = () => {
let name = this.entry.entry.text;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(_('Workspace Settings')));
this.entry = new PopupMenuEntry(this._label.text);
this.menu.addMenuItem(this.entry);
let changed = () => {
let name = this.entry.label.text;
let space = Tiling.spaces.spaceOf(workspaceManager.get_active_workspace());
space.settings.set_string('name', name);
this.setName(name);
};
this.signals.connect(this.entry.button, 'clicked',
clicked.bind(this.entry));
this.signals.connect(this.entry.label.clutter_text, 'text-changed',
changed);
// let clicked = () => {
// let name = this.entry.entry.text;
// let space = Tiling.spaces.spaceOf(workspaceManager.get_active_workspace());
// space.settings.set_string('name', name);
// this.setName(name);
// };
// this.signals.connect(this.entry.button, 'clicked',
// clicked.bind(this.entry));
let space = Tiling.spaces.spaceOf(workspaceManager.get_active_workspace());
// let space = Tiling.spaces.spaceOf(workspaceManager.get_active_workspace());
// this.entry.actor.text = space.name;
// this.colors.entry.actor.text = space.color;
this.colors = new ColorEntry(space.color);
// this.colors = new ColorEntry(space.color);
this._contentBox = new St.BoxLayout({vertical: true});
this._contentBox.layout_manager.spacing = 10;
this._contentBox.set_style('margin: 10px 20px;');
this._contentBox.add_actor(this.entry.actor);
this._contentBox.add_actor(this.colors.actor);
this.menu.box.add_actor(this._contentBox);
// this._contentBox = new St.BoxLayout({vertical: true});
// this._contentBox.layout_manager.spacing = 10;
// this._contentBox.set_style('margin: 10px 20px;');
// this._contentBox.add_actor(this.entry.actor);
// this._contentBox.add_actor(this.colors.actor);
// this.menu.box.add_actor(this._contentBox);
this._zenItem = new PopupMenu.PopupSwitchMenuItem('show top bar', true);
this._zenItem = new PopupMenu.PopupSwitchMenuItem('Hide top bar', false);
this.menu.addMenuItem(this._zenItem);
this._zenItem.connect('toggled', item => {
Tiling.spaces.selectedSpace.settings.set_boolean('show-top-bar', item.state);
Tiling.spaces.selectedSpace.settings.set_boolean('show-top-bar', !item.state);
});
this.prefsIcon = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
accessible_name: 'workspace preference',
style_class: 'system-menu-action' });
this.prefsIcon.child = new St.Icon({ icon_name: 'gtk-preferences' });
function createButton(icon_name, accessible_name) {
return new St.Button({reactive: true,
can_focus: true,
track_hover: true,
accessible_name,
style_class: 'system-menu-action',
child: new St.Icon({icon_name})
});
}
this.prefsIcon = createButton('gtk-preferences', 'workspace preference');
this.prevIcon = createButton('go-previous-symbolic', 'previous workspace setting');
this.nextIcon = createButton('go-next-symbolic', 'next workspace setting');
this.prefsIcon.connect('clicked', () => {
this.menu.close(true);
@@ -188,10 +251,28 @@ class WorkspaceMenu extends PanelMenu.Button {
}
});
this.menu.box.add(this.prefsIcon, { expand: true, x_fill: false });
this.nextIcon.connect('clicked', () => {
let space = Tiling.cycleWorkspaceSettings(-1);
this.entry.label.text = space.name;
menu.nextIcon.grab_key_focus();
});
this.prevIcon.connect('clicked', () => {
let space = Tiling.cycleWorkspaceSettings(1);
this.entry.label.text = space.name;
menu.prevIcon.grab_key_focus();
});
this.entry.actor.width = this.colors.actor.width;
this.colors.entry.actor.width = this.colors.actor.width;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.iconBox = new St.BoxLayout();
this.menu.box.add(this.iconBox);
this.iconBox.add(this.prevIcon, { expand: true, x_fill: false });
this.iconBox.add(this.prefsIcon, { expand: true, x_fill: false });
this.iconBox.add(this.nextIcon, { expand: true, x_fill: false });
// this.entry.actor.width = this.colors.actor.width;
// this.colors.entry.actor.width = this.colors.actor.width;
this.state = "NORMAL";
}
@@ -372,27 +453,16 @@ class WorkspaceMenu extends PanelMenu.Button {
return Clutter.EVENT_PROPAGATE;
}
// WorkspaceMenu.prototype._onOpenStateChanged = function
_onOpenStateChanged(menu, open) {
if (!open)
return;
// FIXME: 3.32: An StEntry cannot have `text` when shown the first time,
// as it will crash the shell. We handle this by setting the text when
// the entry first gains focus, only then can we start updating the text
// property safely.
let space = Tiling.spaces.spaceOf(workspaceManager.get_active_workspace());
if (this.entry.actor.text != '') {
this.entry.actor.text = space.name;
} else {
this.entry.actor.hint_text = space.name;
}
if (this.entry.actor.text != '') {
this.colors.entry.actor.text = space.color;
} else {
this.colors.actor.hint_text = space.name;
}
this.entry.label.text = space.name;
GLib.idle_add(GLib.PRIORITY_DEFAULT, this.entry.activate.bind(this.entry));
this._zenItem._switch.setToggleState(space.showTopBar);
this._zenItem._switch.setToggleState(!space.showTopBar);
}
workspaceSwitched(wm, fromIndex, toIndex) {

View File

@@ -19,9 +19,8 @@ var version = imports.misc.config.PACKAGE_VERSION.split('.').map(Number);
if (version[0] >= 3 && version[1] > 30) {
registerClass = GObject.registerClass;
} else {
registerClass = (x => x);
registerClass = (x, y) => y ? y : x;
}
}
var debug_all = false; // Turn off by default