Skip to content

Commit 325eeb1

Browse files
committed
windowMenu.js: Implement mnemonic accelerators.
This make the extension 'mnemonic-5-4-window-menu@mtwebster' obsolete.
1 parent 4c54812 commit 325eeb1

File tree

1 file changed

+168
-29
lines changed

1 file changed

+168
-29
lines changed

js/ui/windowMenu.js

Lines changed: 168 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,74 @@
11
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
22
/* exported WindowMenuManager */
33

4-
const { GLib, Meta, St, Gtk } = imports.gi;
4+
const { Clutter, GLib, Meta, St, Gtk } = imports.gi;
55

6-
const BoxPointer = imports.ui.boxpointer;
76
const Main = imports.ui.main;
87
const PopupMenu = imports.ui.popupMenu;
9-
const Lang = imports.lang;
108
const CheckBox = imports.ui.checkBox;
119
const RadioButton = imports.ui.radioButton;
1210

13-
var LeftOrnamentedMenuItem = class LeftOrnamentedMenuItem extends PopupMenu.PopupBaseMenuItem {
14-
_init (text, params) {
11+
var BaseMnemonicMenuItem = class BaseMnemonicMenuItem extends PopupMenu.PopupBaseMenuItem {
12+
_init (label, params) {
1513
super._init.call(this, params);
1614

15+
this.mnemonicInit(label);
16+
}
17+
18+
mnemonicInit(label) {
19+
this.origLabel = label;
20+
this.plain = null;
21+
this.mnemonized = null;
22+
this.showing_mnemonic = false;
23+
}
24+
25+
_setLabels() {
26+
let markup = null;
27+
let plain = null;
28+
let mnemonic = undefined;
29+
let title_pieces = this.origLabel.split("_");
30+
31+
if (title_pieces.length === 2) {
32+
this.mnemonic = title_pieces[1][0].toLowerCase();
33+
this.mnemonized = `${title_pieces[0]}<u>${title_pieces[1][0]}</u>${title_pieces[1].substring(1)}`;
34+
this.plain = `${title_pieces[0]}${title_pieces[1]}`;
35+
} else {
36+
this.plain = this.origLabel;
37+
this.mnemonized = this.origLabel;
38+
}
39+
40+
this.label.clutter_text.set_markup(this.plain);
41+
}
42+
43+
toggleMnemonic() {
44+
if (!this.showing_mnemonic) {
45+
this.label.clutter_text.set_markup(this.mnemonized);
46+
this.showing_mnemonic = true;
47+
} else {
48+
this.label.clutter_text.set_markup(this.plain);
49+
this.showing_mnemonic = false;
50+
}
51+
52+
this.label.clutter_text.queue_relayout();
53+
}
54+
}
55+
56+
var MnemonicLeftOrnamentedMenuItem = class MnemonicLeftOrnamentedMenuItem extends BaseMnemonicMenuItem {
57+
_init (label, params) {
58+
super._init.call(this, label, params);
59+
1760
this._ornament = new St.Bin();
1861
this._icon = new St.Icon({ style_class: 'popup-menu-icon', icon_type: St.IconType.SYMBOLIC });
1962

2063
this._ornament.child = this._icon;
2164
this._ornament.child._delegate = this._ornament;
2265
this.addActor(this._ornament, {span: 1});
2366

24-
this.label = new St.Label({ text: text });
67+
this.label = new St.Label();
2568
this.addActor(this.label);
2669
this.actor.label_actor = this.label;
2770

28-
}
29-
30-
setLabel(label) {
31-
this.label.set_text(label);
71+
this._setLabels();
3272
}
3373

3474
setIcon(icon_name) {
@@ -71,6 +111,20 @@ var LeftOrnamentedMenuItem = class LeftOrnamentedMenuItem extends PopupMenu.Popu
71111
}
72112
}
73113

114+
var MnemonicSubMenuMenuItem = class MnemonicSubMenuMenuItem extends PopupMenu.PopupSubMenuMenuItem {
115+
_init (label, params) {
116+
super._init.call(this, label);
117+
BaseMnemonicMenuItem.prototype.mnemonicInit.call(this, label);
118+
119+
// to help align with left side ornaments
120+
let filler = new St.Icon({ style_class: 'popup-menu-icon', icon_type: St.IconType.SYMBOLIC });
121+
this.addActor(filler, { span: 1, position: 0 });
122+
123+
this._setLabels();
124+
}
125+
}
126+
MnemonicSubMenuMenuItem.prototype.toggleMnemonic = BaseMnemonicMenuItem.prototype.toggleMnemonic;
127+
MnemonicSubMenuMenuItem.prototype._setLabels = BaseMnemonicMenuItem.prototype._setLabels;
74128

75129
var WindowMenu = class extends PopupMenu.PopupMenu {
76130
constructor(window, sourceActor) {
@@ -81,17 +135,53 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
81135
Main.uiGroup.add_actor(this.actor);
82136
this.actor.hide();
83137

138+
this.actor.connect('key-press-event', this._windowMenuKeypress.bind(this));
139+
140+
this._items = [];
84141
this._buildMenu(window);
85142
}
86143

144+
_windowMenuKeypress(actor, event) {
145+
if (this._onKeyPressEvent(actor, event) === Clutter.EVENT_STOP) {
146+
return Clutter.EVENT_STOP;
147+
}
148+
149+
if (event.get_key_symbol() === Clutter.KEY_Alt_R || event.get_key_symbol() === Clutter.KEY_Alt_L) {
150+
let items = this._getMenuItems();
151+
152+
for (let item of this._items) {
153+
if (item.mnemonic !== undefined) {
154+
item.toggleMnemonic();
155+
}
156+
}
157+
return Clutter.EVENT_STOP;
158+
}
159+
160+
let items = this._getMenuItems();
161+
for (let item of this._items) {
162+
if (!item.sensitive) {
163+
continue;
164+
}
165+
166+
if (item.mnemonic != undefined && item.mnemonic[0] === String.fromCharCode(event.get_key_symbol()).toLowerCase()) {
167+
item.activate(event);
168+
return Clutter.EVENT_STOP;
169+
}
170+
}
171+
172+
return Clutter.EVENT_PROPAGATE;
173+
}
174+
87175
addAction(to_menu, title, callback) {
88-
let menuItem = new LeftOrnamentedMenuItem(title);
176+
let menuItem = new MnemonicLeftOrnamentedMenuItem(title);
89177
to_menu.addMenuItem(menuItem);
90178

91179
menuItem.connect('activate', (o, event) => {
92180
callback(event);
93181
});
94182

183+
this._items.push(menuItem);
184+
95185
return menuItem;
96186
}
97187

@@ -100,7 +190,9 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
100190

101191
let item;
102192

103-
item = this.addAction(this, _("Minimize"), () => {
193+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
194+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
195+
item = this.addAction(this, _("Mi_nimize"), () => {
104196
window.minimize();
105197
});
106198
item.setIcon("window-minimize-symbolic");
@@ -109,39 +201,51 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
109201
item.setSensitive(false);
110202

111203
if (window.get_maximized()) {
112-
item = this.addAction(this, _("Unmaximize"), () => {
204+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
205+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
206+
item = this.addAction(this, _("Unma_ximize"), () => {
113207
window.unmaximize(Meta.MaximizeFlags.BOTH);
114208
});
115209
} else {
116-
item = this.addAction(this, _("Maximize"), () => {
210+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
211+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
212+
item = this.addAction(this, _("Ma_ximize"), () => {
117213
window.maximize(Meta.MaximizeFlags.BOTH);
118214
});
119215
item.setIcon("window-maximize-symbolic");
120216
}
121217
if (!window.can_maximize())
122218
item.setSensitive(false);
123219

124-
item = this.addAction(this, _("Move"), event => {
220+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
221+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
222+
item = this.addAction(this, _("_Move"), event => {
125223
this._grabAction(window, Meta.GrabOp.KEYBOARD_MOVING, event.get_time());
126224
});
127225
if (!window.allows_move())
128226
item.setSensitive(false);
129227

130-
item = this.addAction(this, _("Resize"), event => {
228+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
229+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
230+
item = this.addAction(this, _("_Resize"), event => {
131231
this._grabAction(window, Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, event.get_time());
132232
});
133233
if (!window.allows_resize())
134234
item.setSensitive(false);
135235

136236
if (!window.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) {
237+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
238+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
137239
this.addAction(this, _("Move Titlebar Onscreen"), () => {
138240
window.shove_titlebar_onscreen();
139241
});
140242
}
141243

142244
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
143245

144-
item = this.addAction(this, _("Always on Top"), () => {
246+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
247+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
248+
item = this.addAction(this, _("Always on _Top"), () => {
145249
if (window.is_above())
146250
window.unmake_above();
147251
else
@@ -161,12 +265,14 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
161265
window.is_on_primary_monitor())) {
162266
let isSticky = window.is_on_all_workspaces();
163267

164-
this.sticky_action = this.addAction(this, _("Always on Visible Workspace"), () => {
165-
log("stick");
268+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
269+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
270+
this.sticky_action = this.addAction(this, _("_Always on Visible Workspace"), () => {
166271
window.stick();
167272
});
168-
this.unsticky_action = this.addAction(this, _("Only on This Workspace"), () => {
169-
log("unstick");
273+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
274+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
275+
this.unsticky_action = this.addAction(this, _("_Only on This Workspace"), () => {
170276
window.unstick();
171277
});
172278
this.sticky_action.setOrnament(PopupMenu.OrnamentType.DOT, isSticky);
@@ -177,17 +283,48 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
177283
this.unsticky_action.setSensitive(false);
178284
}
179285

180-
let ws_sub = new PopupMenu.PopupSubMenuMenuItem(_("Move to Another Workspace"));
181-
let filler = new St.Icon({ style_class: 'popup-menu-icon', icon_type: St.IconType.SYMBOLIC });
182-
ws_sub.addActor(filler, { span: 1, position: 0 });
183-
286+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
287+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
288+
let ws_sub = new MnemonicSubMenuMenuItem(_("Move to Another _Workspace"));
184289
this.addMenuItem(ws_sub);
290+
this._items.push(ws_sub);
185291

186292
let curr_index = window.get_workspace().index();
293+
let used_nums = {};
294+
let name = null;
295+
187296
for (let i = 0; i < global.workspace_manager.get_n_workspaces(); i++) {
188-
let j = i;
189-
let name = Main.workspace_names[i] ? Main.workspace_names[i] : Main._makeDefaultWorkspaceName(i);
190-
item = this.addAction(ws_sub.menu, name, () => window.change_workspace(global.workspace_manager.get_workspace_by_index(j)))
297+
if (used_nums[i + 1]) {
298+
continue;
299+
}
300+
301+
let name = Main.workspace_names[i];
302+
303+
if (name === undefined || name === '') {
304+
name = Main.getWorkspaceName(i);
305+
}
306+
307+
let end = name.substring(name.length - 2).replace("_", "");
308+
let number = parseInt(end);
309+
310+
if (!isNaN(number) && used_nums[number] == undefined) {
311+
if (number == 10) {
312+
name = name.replace("10", "1_0");
313+
used_nums[10] = true;
314+
}
315+
else
316+
if (number < 10) {
317+
name = name.replace(number.toString(), "_" + number.toString());
318+
used_nums[number] = true;
319+
}
320+
}
321+
else
322+
{
323+
name = `${name} (_${i + 1})`
324+
used_nums[i + 1] = true;
325+
}
326+
327+
item = this.addAction(ws_sub.menu, name, () => window.change_workspace(global.workspace_manager.get_workspace_by_index(i)))
191328

192329
if (i == curr_index)
193330
item.setSensitive(false);
@@ -196,7 +333,9 @@ var WindowMenu = class extends PopupMenu.PopupMenu {
196333

197334
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
198335

199-
item = this.addAction(this, _("Close"), event => {
336+
// Translators: If a language-specific mnemonic doesn't make sense, add one after the label itself:
337+
// i.e. <translated>(_n). See https://github.com/linuxmint/muffin/blob/b9a6f3fe43e/po .po files
338+
item = this.addAction(this, _("_Close"), event => {
200339
window.delete(event.get_time());
201340
});
202341
item.setIcon("window-close-symbolic");

0 commit comments

Comments
 (0)