diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index 6d7940f06..6dc01b924 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -40,6 +40,7 @@ ui/animation.js ui/appDisplay.js ui/appFavorites.js + ui/appMenu.js ui/audioDeviceSelection.js ui/backgroundMenu.js ui/background.js diff --git a/js/ui/appMenu.js b/js/ui/appMenu.js new file mode 100644 index 000000000..ae6aa16a0 --- /dev/null +++ b/js/ui/appMenu.js @@ -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)); + }); + } +}; diff --git a/js/ui/panel.js b/js/ui/panel.js index c039112d7..89b082ad0 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -1,10 +1,11 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* 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 Animation = imports.ui.animation; +const { AppMenu } = imports.ui.appMenu; const Config = imports.misc.config; const CtrlAltTab = imports.ui.ctrlAltTab; const DND = imports.ui.dnd; @@ -18,131 +19,6 @@ var APP_MENU_ICON_MARGIN = 0; 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: * diff --git a/po/POTFILES.in b/po/POTFILES.in index 8e763c8f2..6340bff17 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,6 +15,7 @@ js/portalHelper/main.js js/ui/accessDialog.js js/ui/appDisplay.js js/ui/appFavorites.js +js/ui/appMenu.js js/ui/audioDeviceSelection.js js/ui/backgroundMenu.js js/ui/calendar.js