appDisplay: Replace AppIconMenu

We are finally ready to share the app menu between the top bar and
context menus in the dash/app grid. Notably that means that it is
now possible to quit an app from its context menu.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2875

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1948>
This commit is contained in:
Florian Müllner 2021-08-12 02:02:12 +02:00 committed by Marge Bot
parent 414ed9128f
commit b496c9f956
2 changed files with 11 additions and 179 deletions

View File

@ -108,8 +108,7 @@ $app_grid_fg_color: #fff;
} }
// right-click app menu // right-click app menu
.app-menu, .app-menu {
.app-well-menu {
max-width: 27.25em; max-width: 27.25em;
} }

View File

@ -3,9 +3,9 @@
const { Clutter, Gio, GLib, GObject, Graphene, Meta, const { Clutter, Gio, GLib, GObject, Graphene, Meta,
Pango, Shell, St } = imports.gi; Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const AppFavorites = imports.ui.appFavorites; const AppFavorites = imports.ui.appFavorites;
const { AppMenu } = imports.ui.appMenu;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const GrabHelper = imports.ui.grabHelper; const GrabHelper = imports.ui.grabHelper;
@ -53,8 +53,6 @@ const DELAYED_MOVE_TIMEOUT = 200;
const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x000000cc); const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x000000cc);
const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000055); const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000055);
let discreteGpuAvailable = false;
var SidePages = { var SidePages = {
NONE: 0, NONE: 0,
PREVIOUS: 1 << 0, PREVIOUS: 1 << 0,
@ -1453,20 +1451,6 @@ class AppDisplay extends BaseAppView {
this._viewIsReady = false; this._viewIsReady = false;
Main.queueDeferredWork(this._redisplayWorkId); Main.queueDeferredWork(this._redisplayWorkId);
}); });
this._switcherooNotifyId = global.connect('notify::switcheroo-control',
() => this._updateDiscreteGpuAvailable());
this._updateDiscreteGpuAvailable();
}
_updateDiscreteGpuAvailable() {
this._switcherooProxy = global.get_switcheroo_control();
if (this._switcherooProxy) {
let prop = this._switcherooProxy.get_cached_property('HasDualGpu');
discreteGpuAvailable = prop?.unpack() ?? false;
} else {
discreteGpuAvailable = false;
}
} }
_onDestroy() { _onDestroy() {
@ -3248,10 +3232,11 @@ var AppIcon = GObject.registerClass({
this.fake_release(); this.fake_release();
if (!this._menu) { if (!this._menu) {
this._menu = new AppIconMenu(this, side); this._menu = new AppMenu(this, side, {
this._menu.connect('activate-window', (menu, window) => { favoritesSection: true,
this.activateWindow(window); showSingleWindows: true,
}); });
this._menu.setApp(this.app);
this._menu.connect('open-state-changed', (menu, isPoppedUp) => { this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
if (!isPoppedUp) if (!isPoppedUp)
this._onMenuPoppedDown(); this._onMenuPoppedDown();
@ -3263,26 +3248,23 @@ var AppIcon = GObject.registerClass({
Main.overview.disconnect(id); Main.overview.disconnect(id);
}); });
// We want to keep the item hovered while the menu is up
this._menu.blockSourceEvents = true;
Main.uiGroup.add_actor(this._menu.actor);
this._menuManager.addMenu(this._menu); this._menuManager.addMenu(this._menu);
} }
this.emit('menu-state-changed', true); this.emit('menu-state-changed', true);
this.set_hover(true); this.set_hover(true);
this._menu.popup(); this._menu.open(BoxPointer.PopupAnimation.FULL);
this._menuManager.ignoreRelease(); this._menuManager.ignoreRelease();
this.emit('sync-tooltip'); this.emit('sync-tooltip');
return false; return false;
} }
activateWindow(metaWindow) {
if (metaWindow)
Main.activateWindow(metaWindow);
else
Main.overview.hide();
}
_onMenuPoppedDown() { _onMenuPoppedDown() {
this.sync_hover(); this.sync_hover();
this.emit('menu-state-changed', false); this.emit('menu-state-changed', false);
@ -3410,155 +3392,6 @@ var AppIcon = GObject.registerClass({
} }
}); });
var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
constructor(source, side) {
if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
if (side === St.Side.LEFT)
side = St.Side.RIGHT;
else if (side === St.Side.RIGHT)
side = St.Side.LEFT;
}
super(source, 0.5, side);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
this._source = source;
this._parentalControlsManager = ParentalControlsManager.getDefault();
this.actor.add_style_class_name('app-well-menu');
Main.uiGroup.add_actor(this.actor);
}
_rebuildMenu() {
this.removeAll();
let windows = this._source.app.get_windows().filter(
w => !w.skip_taskbar);
if (windows.length > 0) {
this.addMenuItem(
/* Translators: This is the heading of a list of open windows */
new PopupMenu.PopupSeparatorMenuItem(_('Open Windows')));
}
windows.forEach(window => {
let title = window.title
? window.title : this._source.app.get_name();
let item = this._appendMenuItem(title);
item.connect('activate', () => {
this.emit('activate-window', window);
});
});
if (!this._source.app.is_window_backed()) {
this._appendSeparator();
let appInfo = this._source.app.get_app_info();
let actions = appInfo.list_actions();
if (this._source.app.can_open_new_window() &&
!actions.includes('new-window')) {
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._newWindowMenuItem.connect('activate', () => {
this._source.animateLaunch();
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
});
this._appendSeparator();
}
if (discreteGpuAvailable &&
this._source.app.state == Shell.AppState.STOPPED) {
const appPrefersNonDefaultGPU = appInfo.get_boolean('PrefersNonDefaultGPU');
const gpuPref = appPrefersNonDefaultGPU
? Shell.AppLaunchGpu.DEFAULT
: Shell.AppLaunchGpu.DISCRETE;
this._onGpuMenuItem = this._appendMenuItem(appPrefersNonDefaultGPU
? _('Launch using Integrated Graphics Card')
: _('Launch using Discrete Graphics Card'));
this._onGpuMenuItem.connect('activate', () => {
this._source.animateLaunch();
this._source.app.launch(0, -1, gpuPref);
this.emit('activate-window', null);
});
}
for (let i = 0; i < actions.length; i++) {
let action = actions[i];
let item = this._appendMenuItem(appInfo.get_action_name(action));
item.connect('activate', (emitter, event) => {
if (action == 'new-window')
this._source.animateLaunch();
this._source.app.launch_action(action, event.get_time(), -1);
this.emit('activate-window', null);
});
}
let canFavorite = global.settings.is_writable('favorite-apps') &&
this._parentalControlsManager.shouldShowApp(this._source.app.app_info);
if (canFavorite) {
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
if (isFavorite) {
let item = this._appendMenuItem(_("Remove from Favorites"));
item.connect('activate', () => {
let favs = AppFavorites.getAppFavorites();
favs.removeFavorite(this._source.app.get_id());
});
} else {
let item = this._appendMenuItem(_("Add to Favorites"));
item.connect('activate', () => {
let favs = AppFavorites.getAppFavorites();
favs.addFavorite(this._source.app.get_id());
});
}
}
if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) {
this._appendSeparator();
this.addAction(_('Show Details'), async () => {
let id = this._source.app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']);
const bus = await Gio.DBus.get(Gio.BusType.SESSION, null);
bus.call(
'org.gnome.Software',
'/org/gnome/Software',
'org.gtk.Actions', 'Activate',
new GLib.Variant.new(
'(sava{sv})', ['details', [args], null]),
null, 0, -1, null);
Main.overview.hide();
});
}
}
}
_appendSeparator() {
let separator = new PopupMenu.PopupSeparatorMenuItem();
this.addMenuItem(separator);
}
_appendMenuItem(labelText) {
// FIXME: app-well-menu-item style
let item = new PopupMenu.PopupMenuItem(labelText);
this.addMenuItem(item);
return item;
}
popup(_activatingButton) {
this._rebuildMenu();
this.open(BoxPointer.PopupAnimation.FULL);
}
};
Signals.addSignalMethods(AppIconMenu.prototype);
var SystemActionIcon = GObject.registerClass( var SystemActionIcon = GObject.registerClass(
class SystemActionIcon extends Search.GridSearchResult { class SystemActionIcon extends Search.GridSearchResult {
activate() { activate() {