2021-08-11 13:40:52 -04:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2023-07-10 05:53:00 -04:00
|
|
|
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Shell from 'gi://Shell';
|
|
|
|
import St from 'gi://St';
|
|
|
|
|
|
|
|
import * as AppFavorites from './appFavorites.js';
|
|
|
|
import * as Main from './main.js';
|
|
|
|
import * as ParentalControlsManager from '../misc/parentalControlsManager.js';
|
|
|
|
import * as PopupMenu from './popupMenu.js';
|
|
|
|
|
|
|
|
export class AppMenu extends PopupMenu.PopupMenu {
|
2021-08-11 13:40:52 -04:00
|
|
|
/**
|
|
|
|
* @param {Clutter.Actor} sourceActor - actor the menu is attached to
|
2021-08-11 16:53:54 -04:00
|
|
|
* @param {St.Side} side - arrow side
|
2021-08-12 15:25:49 -04:00
|
|
|
* @param {object} params - options
|
2021-08-11 17:50:50 -04:00
|
|
|
* @param {bool} params.favoritesSection - show items to add/remove favorite
|
2021-08-12 15:25:49 -04:00
|
|
|
* @param {bool} params.showSingleWindow - show window section for a single window
|
2021-08-11 13:40:52 -04:00
|
|
|
*/
|
2021-08-12 15:25:49 -04:00
|
|
|
constructor(sourceActor, side = St.Side.TOP, params = {}) {
|
2021-08-11 16:53:54 -04:00
|
|
|
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(sourceActor, 0.5, side);
|
2021-08-11 13:40:52 -04:00
|
|
|
|
|
|
|
this.actor.add_style_class_name('app-menu');
|
|
|
|
|
2021-08-12 15:25:49 -04:00
|
|
|
const {
|
2021-08-11 17:50:50 -04:00
|
|
|
favoritesSection = false,
|
2021-08-12 15:25:49 -04:00
|
|
|
showSingleWindows = false,
|
|
|
|
} = params;
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
this._app = null;
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
2021-08-11 17:50:50 -04:00
|
|
|
this._parentalControlsManager = ParentalControlsManager.getDefault();
|
|
|
|
this._appFavorites = AppFavorites.getAppFavorites();
|
|
|
|
this._enableFavorites = favoritesSection;
|
2021-08-12 15:25:49 -04:00
|
|
|
this._showSingleWindows = showSingleWindows;
|
2021-08-11 13:40:52 -04:00
|
|
|
|
|
|
|
this._windowsChangedId = 0;
|
2021-08-11 20:16:59 -04:00
|
|
|
this._updateWindowsLaterId = 0;
|
2021-08-11 13:40:52 -04:00
|
|
|
|
|
|
|
/* 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'), () => {
|
2021-08-11 17:54:30 -04:00
|
|
|
this._animateLaunch();
|
2021-08-11 13:40:52 -04:00
|
|
|
this._app.open_new_window(-1);
|
2021-08-11 16:54:11 -04:00
|
|
|
Main.overview.hide();
|
2021-08-11 13:40:52 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
this._actionSection = new PopupMenu.PopupMenuSection();
|
|
|
|
this.addMenuItem(this._actionSection);
|
|
|
|
|
2021-08-11 19:31:35 -04:00
|
|
|
this._onGpuMenuItem = this.addAction('', () => {
|
|
|
|
this._animateLaunch();
|
|
|
|
this._app.launch(0, -1, this._getNonDefaultLaunchGpu());
|
|
|
|
Main.overview.hide();
|
|
|
|
});
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
|
2021-08-11 17:50:50 -04:00
|
|
|
this._toggleFavoriteItem = this.addAction('', () => {
|
|
|
|
const appId = this._app.get_id();
|
|
|
|
if (this._appFavorites.isFavorite(appId))
|
|
|
|
this._appFavorites.removeFavorite(appId);
|
|
|
|
else
|
|
|
|
this._appFavorites.addFavorite(appId);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
|
2022-12-15 17:52:29 -05:00
|
|
|
this._detailsItem = this.addAction(_('App Details'), async () => {
|
2021-08-11 13:40:52 -04:00
|
|
|
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);
|
2022-06-07 15:22:54 -04:00
|
|
|
Main.overview.hide();
|
2021-08-11 13:40:52 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
|
2021-08-11 20:47:23 -04:00
|
|
|
this._quitItem =
|
|
|
|
this.addAction(_('Quit'), () => this._app.request_quit());
|
2021-08-11 13:40:52 -04:00
|
|
|
|
2021-08-15 18:36:59 -04:00
|
|
|
this._appSystem.connectObject(
|
|
|
|
'installed-changed', () => this._updateDetailsVisibility(),
|
|
|
|
'app-state-changed', this._onAppStateChanged.bind(this),
|
|
|
|
this.actor);
|
|
|
|
|
|
|
|
this._parentalControlsManager.connectObject(
|
|
|
|
'app-filter-changed', () => this._updateFavoriteItem(), this.actor);
|
|
|
|
|
|
|
|
this._appFavorites.connectObject(
|
|
|
|
'changed', () => this._updateFavoriteItem(), this.actor);
|
|
|
|
|
|
|
|
global.settings.connectObject(
|
|
|
|
'writable-changed::favorite-apps', () => this._updateFavoriteItem(),
|
|
|
|
this.actor);
|
|
|
|
|
|
|
|
global.connectObject(
|
|
|
|
'notify::switcheroo-control', () => this._updateGpuItem(),
|
|
|
|
this.actor);
|
|
|
|
|
2021-08-11 20:47:23 -04:00
|
|
|
this._updateQuitItem();
|
2021-08-11 17:50:50 -04:00
|
|
|
this._updateFavoriteItem();
|
2021-08-11 19:31:35 -04:00
|
|
|
this._updateGpuItem();
|
2021-08-11 13:40:52 -04:00
|
|
|
this._updateDetailsVisibility();
|
|
|
|
}
|
|
|
|
|
2021-08-11 20:47:23 -04:00
|
|
|
_onAppStateChanged(sys, app) {
|
|
|
|
if (this._app !== app)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._updateQuitItem();
|
2021-08-11 21:49:37 -04:00
|
|
|
this._updateNewWindowItem();
|
2021-08-11 19:31:35 -04:00
|
|
|
this._updateGpuItem();
|
2021-08-11 20:47:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateQuitItem() {
|
|
|
|
this._quitItem.visible = this._app?.state === Shell.AppState.RUNNING;
|
|
|
|
}
|
|
|
|
|
2021-08-11 21:49:37 -04:00
|
|
|
_updateNewWindowItem() {
|
|
|
|
const actions = this._app?.appInfo?.list_actions() ?? [];
|
|
|
|
this._newWindowItem.visible =
|
|
|
|
this._app?.can_open_new_window() && !actions.includes('new-window');
|
|
|
|
}
|
|
|
|
|
2021-08-11 17:50:50 -04:00
|
|
|
_updateFavoriteItem() {
|
|
|
|
const appInfo = this._app?.app_info;
|
|
|
|
const canFavorite = appInfo &&
|
|
|
|
this._enableFavorites &&
|
|
|
|
global.settings.is_writable('favorite-apps') &&
|
|
|
|
this._parentalControlsManager.shouldShowApp(appInfo);
|
|
|
|
|
|
|
|
this._toggleFavoriteItem.visible = canFavorite;
|
|
|
|
|
|
|
|
if (!canFavorite)
|
|
|
|
return;
|
|
|
|
|
2023-08-06 18:40:20 -04:00
|
|
|
const {id} = this._app;
|
2021-08-11 17:50:50 -04:00
|
|
|
this._toggleFavoriteItem.label.text = this._appFavorites.isFavorite(id)
|
2022-01-29 12:07:25 -05:00
|
|
|
? _('Unpin')
|
|
|
|
: _('Pin to Dash');
|
2021-08-11 17:50:50 -04:00
|
|
|
}
|
|
|
|
|
2021-08-11 19:31:35 -04:00
|
|
|
_updateGpuItem() {
|
|
|
|
const proxy = global.get_switcheroo_control();
|
|
|
|
const hasDualGpu = proxy?.get_cached_property('HasDualGpu')?.unpack();
|
|
|
|
|
|
|
|
const showItem =
|
|
|
|
this._app?.state === Shell.AppState.STOPPED && hasDualGpu;
|
|
|
|
|
|
|
|
this._onGpuMenuItem.visible = showItem;
|
|
|
|
|
|
|
|
if (!showItem)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const launchGpu = this._getNonDefaultLaunchGpu();
|
|
|
|
this._onGpuMenuItem.label.text = launchGpu === Shell.AppLaunchGpu.DEFAULT
|
|
|
|
? _('Launch using Integrated Graphics Card')
|
|
|
|
: _('Launch using Discrete Graphics Card');
|
|
|
|
}
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
_updateDetailsVisibility() {
|
|
|
|
const sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
|
|
|
|
this._detailsItem.visible = sw !== null;
|
|
|
|
}
|
|
|
|
|
2021-08-11 17:54:30 -04:00
|
|
|
_animateLaunch() {
|
|
|
|
if (this.sourceActor.animateLaunch)
|
|
|
|
this.sourceActor.animateLaunch();
|
|
|
|
}
|
|
|
|
|
2021-08-11 19:31:35 -04:00
|
|
|
_getNonDefaultLaunchGpu() {
|
|
|
|
return this._app.appInfo.get_boolean('PrefersNonDefaultGPU')
|
|
|
|
? Shell.AppLaunchGpu.DEFAULT
|
|
|
|
: Shell.AppLaunchGpu.DISCRETE;
|
|
|
|
}
|
|
|
|
|
2021-08-11 18:24:02 -04:00
|
|
|
/** */
|
|
|
|
destroy() {
|
|
|
|
super.destroy();
|
|
|
|
|
|
|
|
this.setApp(null);
|
|
|
|
}
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
|
2021-08-15 18:36:59 -04:00
|
|
|
this._app?.disconnectObject(this);
|
2021-08-11 13:40:52 -04:00
|
|
|
|
|
|
|
this._app = app;
|
|
|
|
|
2021-08-15 18:36:59 -04:00
|
|
|
this._app?.connectObject('windows-changed',
|
|
|
|
() => this._queueUpdateWindowsSection(), this);
|
2021-08-11 13:40:52 -04:00
|
|
|
|
|
|
|
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 => {
|
2021-08-11 17:54:30 -04:00
|
|
|
if (action === 'new-window')
|
|
|
|
this._animateLaunch();
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
this._app.launch_action(action, event.get_time(), -1);
|
2021-08-11 16:54:11 -04:00
|
|
|
Main.overview.hide();
|
2021-08-11 13:40:52 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-08-11 20:47:23 -04:00
|
|
|
this._updateQuitItem();
|
2021-08-11 21:49:37 -04:00
|
|
|
this._updateNewWindowItem();
|
2021-08-11 17:50:50 -04:00
|
|
|
this._updateFavoriteItem();
|
2021-08-11 19:31:35 -04:00
|
|
|
this._updateGpuItem();
|
2021-08-11 13:40:52 -04:00
|
|
|
}
|
|
|
|
|
2021-08-11 20:16:59 -04:00
|
|
|
_queueUpdateWindowsSection() {
|
|
|
|
if (this._updateWindowsLaterId)
|
|
|
|
return;
|
|
|
|
|
2022-09-07 14:23:38 -04:00
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
this._updateWindowsLaterId = laters.add(
|
2021-08-11 20:16:59 -04:00
|
|
|
Meta.LaterType.BEFORE_REDRAW, () => {
|
|
|
|
this._updateWindowsSection();
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
_updateWindowsSection() {
|
2022-09-07 14:23:38 -04:00
|
|
|
if (this._updateWindowsLaterId) {
|
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
laters.remove(this._updateWindowsLaterId);
|
|
|
|
}
|
2021-08-11 20:16:59 -04:00
|
|
|
this._updateWindowsLaterId = 0;
|
|
|
|
|
2021-08-11 13:40:52 -04:00
|
|
|
this._windowSection.removeAll();
|
|
|
|
this._openWindowsHeader.hide();
|
|
|
|
|
|
|
|
if (!this._app)
|
|
|
|
return;
|
|
|
|
|
2021-08-12 15:25:49 -04:00
|
|
|
const minWindows = this._showSingleWindows ? 1 : 2;
|
2021-08-11 20:34:12 -04:00
|
|
|
const windows = this._app.get_windows().filter(w => !w.skip_taskbar);
|
2021-08-12 15:25:49 -04:00
|
|
|
if (windows.length < minWindows)
|
2021-08-11 13:40:52 -04:00
|
|
|
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());
|
|
|
|
});
|
2021-08-15 18:36:59 -04:00
|
|
|
window.connectObject('notify::title', () => {
|
2021-08-11 13:40:52 -04:00
|
|
|
item.label.text = window.title || this._app.get_name();
|
2021-08-15 18:36:59 -04:00
|
|
|
}, item);
|
2021-08-11 13:40:52 -04:00
|
|
|
});
|
|
|
|
}
|
2023-07-10 05:53:00 -04:00
|
|
|
}
|