bacfdbbb03
ES6 finally adds standard class syntax to the language, so we can replace our custom Lang.Class framework with the new syntax. Any classes that inherit from GObject will need special treatment, so limit the port to regular javascript classes for now. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/361
200 lines
6.1 KiB
JavaScript
200 lines
6.1 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Atk = imports.gi.Atk;
|
|
const GLib = imports.gi.GLib;
|
|
const GObject = imports.gi.GObject;
|
|
const Gio = imports.gi.Gio;
|
|
const Shell = imports.gi.Shell;
|
|
const ShellMenu = imports.gi.ShellMenu;
|
|
const St = imports.gi.St;
|
|
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
function stripMnemonics(label) {
|
|
if (!label)
|
|
return '';
|
|
|
|
// remove all underscores that are not followed by another underscore
|
|
return label.replace(/_([^_])/, '$1');
|
|
}
|
|
|
|
function _insertItem(menu, trackerItem, position) {
|
|
let mapper;
|
|
|
|
if (trackerItem.get_is_separator())
|
|
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
|
|
else if (trackerItem.get_has_submenu())
|
|
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
|
|
else
|
|
mapper = new RemoteMenuItemMapper(trackerItem);
|
|
|
|
let item = mapper.menuItem;
|
|
menu.addMenuItem(item, position);
|
|
}
|
|
|
|
function _removeItem(menu, position) {
|
|
let items = menu._getMenuItems();
|
|
items[position].destroy();
|
|
}
|
|
|
|
var RemoteMenuSeparatorItemMapper = class {
|
|
constructor(trackerItem) {
|
|
this._trackerItem = trackerItem;
|
|
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
|
|
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
|
this._updateLabel();
|
|
|
|
this.menuItem.connect('destroy', () => {
|
|
trackerItem.run_dispose();
|
|
});
|
|
}
|
|
|
|
_updateLabel() {
|
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
|
}
|
|
};
|
|
|
|
var RequestSubMenu = class extends PopupMenu.PopupSubMenuMenuItem {
|
|
constructor() {
|
|
super('');
|
|
this._requestOpen = false;
|
|
}
|
|
|
|
_setOpenState(open) {
|
|
this.emit('request-open', open);
|
|
this._requestOpen = open;
|
|
}
|
|
|
|
_getOpenState() {
|
|
return this._requestOpen;
|
|
}
|
|
};
|
|
|
|
var RemoteMenuSubmenuItemMapper = class {
|
|
constructor(trackerItem) {
|
|
this._trackerItem = trackerItem;
|
|
this.menuItem = new RequestSubMenu();
|
|
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
|
this._updateLabel();
|
|
|
|
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
|
|
_insertItem.bind(null, this.menuItem.menu),
|
|
_removeItem.bind(null, this.menuItem.menu));
|
|
|
|
this.menuItem.connect('request-open', (menu, open) => {
|
|
this._trackerItem.request_submenu_shown(open);
|
|
});
|
|
|
|
this._trackerItem.connect('notify::submenu-shown', () => {
|
|
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
|
|
});
|
|
|
|
this.menuItem.connect('destroy', () => {
|
|
trackerItem.run_dispose();
|
|
});
|
|
}
|
|
|
|
destroy() {
|
|
this._tracker.destroy();
|
|
}
|
|
|
|
_updateLabel() {
|
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
|
}
|
|
};
|
|
|
|
var RemoteMenuItemMapper = class {
|
|
constructor(trackerItem) {
|
|
this._trackerItem = trackerItem;
|
|
|
|
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
|
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
|
this.menuItem.actor.add_child(this._icon);
|
|
|
|
this._label = new St.Label();
|
|
this.menuItem.actor.add_child(this._label);
|
|
this.menuItem.actor.label_actor = this._label;
|
|
|
|
this.menuItem.connect('activate', () => {
|
|
this._trackerItem.activated();
|
|
});
|
|
|
|
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
|
|
|
this._trackerItem.connect('notify::icon', this._updateIcon.bind(this));
|
|
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
|
this._trackerItem.connect('notify::sensitive', this._updateSensitivity.bind(this));
|
|
this._trackerItem.connect('notify::role', this._updateRole.bind(this));
|
|
this._trackerItem.connect('notify::toggled', this._updateDecoration.bind(this));
|
|
|
|
this._updateIcon();
|
|
this._updateLabel();
|
|
this._updateSensitivity();
|
|
this._updateRole();
|
|
|
|
this.menuItem.connect('destroy', () => {
|
|
trackerItem.run_dispose();
|
|
});
|
|
}
|
|
|
|
_updateIcon() {
|
|
this._icon.gicon = this._trackerItem.icon;
|
|
this._icon.visible = (this._icon.gicon != null);
|
|
}
|
|
|
|
_updateLabel() {
|
|
this._label.text = stripMnemonics(this._trackerItem.label);
|
|
}
|
|
|
|
_updateSensitivity() {
|
|
this.menuItem.setSensitive(this._trackerItem.sensitive);
|
|
}
|
|
|
|
_updateDecoration() {
|
|
let ornamentForRole = {};
|
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
|
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
|
|
|
|
let ornament = PopupMenu.Ornament.NONE;
|
|
if (this._trackerItem.toggled)
|
|
ornament = ornamentForRole[this._trackerItem.role];
|
|
|
|
this.menuItem.setOrnament(ornament);
|
|
}
|
|
|
|
_updateRole() {
|
|
let a11yRoles = {};
|
|
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
|
|
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
|
|
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
|
|
|
|
let a11yRole = a11yRoles[this._trackerItem.role];
|
|
this.menuItem.actor.accessible_role = a11yRole;
|
|
|
|
this._updateDecoration();
|
|
}
|
|
};
|
|
|
|
var RemoteMenu = class extends PopupMenu.PopupMenu {
|
|
constructor(sourceActor, model, actionGroup) {
|
|
super(sourceActor, 0.0, St.Side.TOP);
|
|
|
|
this._model = model;
|
|
this._actionGroup = actionGroup;
|
|
this._tracker = Shell.MenuTracker.new(this._actionGroup,
|
|
this._model,
|
|
null, /* action namespace */
|
|
_insertItem.bind(null, this),
|
|
_removeItem.bind(null, this));
|
|
}
|
|
|
|
get actionGroup() {
|
|
return this._actionGroup;
|
|
}
|
|
|
|
destroy() {
|
|
this._tracker.destroy();
|
|
super.destroy();
|
|
}
|
|
};
|