appMenu: Split out from panel
There is a big overlap between the app menu in the top bar and the context menu of app icons. It makes sense to unify the two both from a design- and from a code perspective, so split out the more modern one into a separate module as basis for a shared class. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1948>
This commit is contained in:
parent
5e90e8d385
commit
1550976c51
@ -40,6 +40,7 @@
|
|||||||
<file>ui/animation.js</file>
|
<file>ui/animation.js</file>
|
||||||
<file>ui/appDisplay.js</file>
|
<file>ui/appDisplay.js</file>
|
||||||
<file>ui/appFavorites.js</file>
|
<file>ui/appFavorites.js</file>
|
||||||
|
<file>ui/appMenu.js</file>
|
||||||
<file>ui/audioDeviceSelection.js</file>
|
<file>ui/audioDeviceSelection.js</file>
|
||||||
<file>ui/backgroundMenu.js</file>
|
<file>ui/backgroundMenu.js</file>
|
||||||
<file>ui/background.js</file>
|
<file>ui/background.js</file>
|
||||||
|
137
js/ui/appMenu.js
Normal file
137
js/ui/appMenu.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
/* exported AppMenu */
|
||||||
|
const { Gio, GLib, Shell, St } = imports.gi;
|
||||||
|
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
var AppMenu = class AppMenu extends PopupMenu.PopupMenu {
|
||||||
|
/**
|
||||||
|
* @param {Clutter.Actor} sourceActor - actor the menu is attached to
|
||||||
|
*/
|
||||||
|
constructor(sourceActor) {
|
||||||
|
super(sourceActor, 0.5, St.Side.TOP);
|
||||||
|
|
||||||
|
this.actor.add_style_class_name('app-menu');
|
||||||
|
|
||||||
|
this._app = null;
|
||||||
|
this._appSystem = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
|
this._windowsChangedId = 0;
|
||||||
|
|
||||||
|
/* Translators: This is the heading of a list of open windows */
|
||||||
|
this._openWindowsHeader = new PopupMenu.PopupSeparatorMenuItem(_('Open Windows'));
|
||||||
|
this.addMenuItem(this._openWindowsHeader);
|
||||||
|
|
||||||
|
this._windowSection = new PopupMenu.PopupMenuSection();
|
||||||
|
this.addMenuItem(this._windowSection);
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
this._newWindowItem = this.addAction(_('New Window'), () => {
|
||||||
|
this._app.open_new_window(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
this._actionSection = new PopupMenu.PopupMenuSection();
|
||||||
|
this.addMenuItem(this._actionSection);
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
this._detailsItem = this.addAction(_('Show Details'), async () => {
|
||||||
|
const id = this._app.get_id();
|
||||||
|
const 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('(sava{sv})', ['details', [args], null]),
|
||||||
|
null, 0, -1, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
this.addAction(_('Quit'), () => this._app.request_quit());
|
||||||
|
|
||||||
|
this._appSystem.connect('installed-changed',
|
||||||
|
() => this._updateDetailsVisibility());
|
||||||
|
this._updateDetailsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateDetailsVisibility() {
|
||||||
|
const sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
|
||||||
|
this._detailsItem.visible = sw !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {bool} - true if the menu is empty
|
||||||
|
*/
|
||||||
|
isEmpty() {
|
||||||
|
if (!this._app)
|
||||||
|
return true;
|
||||||
|
return super.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Shell.App} app - the app the menu represents
|
||||||
|
*/
|
||||||
|
setApp(app) {
|
||||||
|
if (this._app === app)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._windowsChangedId)
|
||||||
|
this._app.disconnect(this._windowsChangedId);
|
||||||
|
this._windowsChangedId = 0;
|
||||||
|
|
||||||
|
this._app = app;
|
||||||
|
|
||||||
|
if (app) {
|
||||||
|
this._windowsChangedId = app.connect('windows-changed', () => {
|
||||||
|
this._updateWindowsSection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateWindowsSection();
|
||||||
|
|
||||||
|
const appInfo = app?.app_info;
|
||||||
|
const actions = appInfo?.list_actions() ?? [];
|
||||||
|
|
||||||
|
this._actionSection.removeAll();
|
||||||
|
actions.forEach(action => {
|
||||||
|
const label = appInfo.get_action_name(action);
|
||||||
|
this._actionSection.addAction(label, event => {
|
||||||
|
this._app.launch_action(action, event.get_time(), -1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this._newWindowItem.visible =
|
||||||
|
app && app.can_open_new_window() && !actions.includes('new-window');
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateWindowsSection() {
|
||||||
|
this._windowSection.removeAll();
|
||||||
|
this._openWindowsHeader.hide();
|
||||||
|
|
||||||
|
if (!this._app)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const windows = this._app.get_windows();
|
||||||
|
if (windows.length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._openWindowsHeader.show();
|
||||||
|
|
||||||
|
windows.forEach(window => {
|
||||||
|
const title = window.title || this._app.get_name();
|
||||||
|
const item = this._windowSection.addAction(title, event => {
|
||||||
|
Main.activateWindow(window, event.get_time());
|
||||||
|
});
|
||||||
|
const id = window.connect('notify::title', () => {
|
||||||
|
item.label.text = window.title || this._app.get_name();
|
||||||
|
});
|
||||||
|
item.connect('destroy', () => window.disconnect(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
128
js/ui/panel.js
128
js/ui/panel.js
@ -1,10 +1,11 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
/* exported Panel */
|
/* exported Panel */
|
||||||
|
|
||||||
const { Atk, Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
|
const { Atk, Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
|
||||||
const Cairo = imports.cairo;
|
const Cairo = imports.cairo;
|
||||||
|
|
||||||
const Animation = imports.ui.animation;
|
const Animation = imports.ui.animation;
|
||||||
|
const { AppMenu } = imports.ui.appMenu;
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
@ -18,131 +19,6 @@ var APP_MENU_ICON_MARGIN = 0;
|
|||||||
|
|
||||||
var BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
var BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
class AppMenu extends PopupMenu.PopupMenu {
|
|
||||||
constructor(sourceActor) {
|
|
||||||
super(sourceActor, 0.5, St.Side.TOP);
|
|
||||||
|
|
||||||
this.actor.add_style_class_name('app-menu');
|
|
||||||
|
|
||||||
this._app = null;
|
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
|
||||||
|
|
||||||
this._windowsChangedId = 0;
|
|
||||||
|
|
||||||
/* Translators: This is the heading of a list of open windows */
|
|
||||||
this._openWindowsHeader = new PopupMenu.PopupSeparatorMenuItem(_('Open Windows'));
|
|
||||||
this.addMenuItem(this._openWindowsHeader);
|
|
||||||
|
|
||||||
this._windowSection = new PopupMenu.PopupMenuSection();
|
|
||||||
this.addMenuItem(this._windowSection);
|
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
|
|
||||||
this._newWindowItem = this.addAction(_("New Window"), () => {
|
|
||||||
this._app.open_new_window(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
|
|
||||||
this._actionSection = new PopupMenu.PopupMenuSection();
|
|
||||||
this.addMenuItem(this._actionSection);
|
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
|
|
||||||
this._detailsItem = this.addAction(_('Show Details'), async () => {
|
|
||||||
let id = this._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('(sava{sv})', ['details', [args], null]),
|
|
||||||
null, 0, -1, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
|
|
||||||
this.addAction(_("Quit"), () => {
|
|
||||||
this._app.request_quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
this._appSystem.connect('installed-changed', () => {
|
|
||||||
this._updateDetailsVisibility();
|
|
||||||
});
|
|
||||||
this._updateDetailsVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateDetailsVisibility() {
|
|
||||||
let sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
|
|
||||||
this._detailsItem.visible = sw != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isEmpty() {
|
|
||||||
if (!this._app)
|
|
||||||
return true;
|
|
||||||
return super.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
setApp(app) {
|
|
||||||
if (this._app == app)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._windowsChangedId)
|
|
||||||
this._app.disconnect(this._windowsChangedId);
|
|
||||||
this._windowsChangedId = 0;
|
|
||||||
|
|
||||||
this._app = app;
|
|
||||||
|
|
||||||
if (app) {
|
|
||||||
this._windowsChangedId = app.connect('windows-changed', () => {
|
|
||||||
this._updateWindowsSection();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateWindowsSection();
|
|
||||||
|
|
||||||
const appInfo = app?.app_info;
|
|
||||||
const actions = appInfo?.list_actions() ?? [];
|
|
||||||
|
|
||||||
this._actionSection.removeAll();
|
|
||||||
actions.forEach(action => {
|
|
||||||
let label = appInfo.get_action_name(action);
|
|
||||||
this._actionSection.addAction(label, event => {
|
|
||||||
this._app.launch_action(action, event.get_time(), -1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this._newWindowItem.visible =
|
|
||||||
app && app.can_open_new_window() && !actions.includes('new-window');
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateWindowsSection() {
|
|
||||||
this._windowSection.removeAll();
|
|
||||||
this._openWindowsHeader.hide();
|
|
||||||
|
|
||||||
if (!this._app)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let windows = this._app.get_windows();
|
|
||||||
if (windows.length < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._openWindowsHeader.show();
|
|
||||||
|
|
||||||
windows.forEach(window => {
|
|
||||||
let title = window.title || this._app.get_name();
|
|
||||||
let item = this._windowSection.addAction(title, event => {
|
|
||||||
Main.activateWindow(window, event.get_time());
|
|
||||||
});
|
|
||||||
let id = window.connect('notify::title', () => {
|
|
||||||
item.label.text = window.title || this._app.get_name();
|
|
||||||
});
|
|
||||||
item.connect('destroy', () => window.disconnect(id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppMenuButton:
|
* AppMenuButton:
|
||||||
*
|
*
|
||||||
|
@ -15,6 +15,7 @@ js/portalHelper/main.js
|
|||||||
js/ui/accessDialog.js
|
js/ui/accessDialog.js
|
||||||
js/ui/appDisplay.js
|
js/ui/appDisplay.js
|
||||||
js/ui/appFavorites.js
|
js/ui/appFavorites.js
|
||||||
|
js/ui/appMenu.js
|
||||||
js/ui/audioDeviceSelection.js
|
js/ui/audioDeviceSelection.js
|
||||||
js/ui/backgroundMenu.js
|
js/ui/backgroundMenu.js
|
||||||
js/ui/calendar.js
|
js/ui/calendar.js
|
||||||
|
Loading…
Reference in New Issue
Block a user