a0fde0eed5
Ported from Sam Hewett's Merge-Request. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2308#8714a3530d73823716c0f8334aceaabeaccd01b7 The CHECK ornament used in powerprofile and volume control is not very pleasant looking, specially if system font is changed. Replace CHECK and DOT unicode ornament with revelant icons in popupMenu. Solves issue: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6055 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2767>
264 lines
8.4 KiB
JavaScript
264 lines
8.4 KiB
JavaScript
/* exported Indicator */
|
|
const {Clutter, Gio, GLib, GObject, Shell, St} = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
const Util = imports.misc.util;
|
|
|
|
const {Spinner} = imports.ui.animation;
|
|
const {QuickToggle, SystemIndicator} = imports.ui.quickSettings;
|
|
const {loadInterfaceXML} = imports.misc.dbusUtils;
|
|
|
|
const DBUS_NAME = 'org.freedesktop.background.Monitor';
|
|
const DBUS_OBJECT_PATH = '/org/freedesktop/background/monitor';
|
|
|
|
const SPINNER_TIMEOUT = 5; // seconds
|
|
|
|
const BackgroundMonitorIface = loadInterfaceXML('org.freedesktop.background.Monitor');
|
|
const BackgroundMonitorProxy = Gio.DBusProxy.makeProxyWrapper(BackgroundMonitorIface);
|
|
|
|
Gio._promisify(Gio.DBusConnection.prototype, 'call');
|
|
|
|
var BackgroundAppMenuItem = GObject.registerClass({
|
|
Properties: {
|
|
'app': GObject.ParamSpec.object('app', '', '',
|
|
GObject.ParamFlags.READWRITE,
|
|
Shell.App),
|
|
'instance': GObject.ParamSpec.int64('instance', '', '',
|
|
GObject.ParamFlags.READWRITE,
|
|
-1, GLib.MAXINT64_BIGINT, -1),
|
|
'message': GObject.ParamSpec.string('message', '', '',
|
|
GObject.ParamFlags.READWRITE,
|
|
null),
|
|
},
|
|
}, class BackgroundAppMenuItem extends PopupMenu.PopupImageMenuItem {
|
|
_init(app, params = {}) {
|
|
const message = params.message;
|
|
delete params.message;
|
|
|
|
const instance = params.instance;
|
|
delete params.instance;
|
|
|
|
super._init(app.get_name(), app.get_icon(), {
|
|
activate: false,
|
|
reactive: false,
|
|
...params,
|
|
});
|
|
|
|
this.set({message, instance});
|
|
|
|
this.add_style_class_name('background-app-item');
|
|
this.label.add_style_class_name('title');
|
|
|
|
this.app = app;
|
|
|
|
const box = new St.BoxLayout({
|
|
vertical: true,
|
|
x_expand: true,
|
|
x_align: Clutter.ActorAlign.START,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this.add_child(box);
|
|
|
|
this.remove_child(this.label);
|
|
box.add_child(this.label);
|
|
|
|
const messageLabel = new St.Label({
|
|
y_expand: true,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
style_class: 'subtitle',
|
|
});
|
|
box.add_child(messageLabel);
|
|
|
|
this.bind_property('message',
|
|
messageLabel, 'text', GObject.BindingFlags.SYNC_CREATE);
|
|
this.bind_property_full('message',
|
|
messageLabel, 'visible', GObject.BindingFlags.SYNC_CREATE,
|
|
(bind, source) => [true, source !== null],
|
|
null);
|
|
|
|
this.set_child_above_sibling(this._ornamentIcon, null);
|
|
|
|
this._spinner = new Spinner(16, {hideOnStop: true});
|
|
this._spinner.add_style_class_name('spinner');
|
|
this.add_child(this._spinner);
|
|
|
|
const closeButton = new St.Button({
|
|
iconName: 'window-close-symbolic',
|
|
styleClass: 'close-button',
|
|
x_expand: true,
|
|
y_expand: false,
|
|
x_align: Clutter.ActorAlign.END,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this.add_child(closeButton);
|
|
|
|
this._spinner.bind_property('visible',
|
|
closeButton, 'visible',
|
|
GObject.BindingFlags.INVERT_BOOLEAN);
|
|
|
|
closeButton.connect('clicked', () => this._quitApp().catch(logError));
|
|
|
|
this.connect('destroy', () => this._onDestroy());
|
|
}
|
|
|
|
_onDestroy() {
|
|
if (this._spinnerTimeoutId)
|
|
GLib.source_remove(this._spinnerTimeoutId);
|
|
delete this._spinnerTimeoutId;
|
|
}
|
|
|
|
async _quitApp() {
|
|
const appId = this.app.get_id().replace(/\.desktop$/, '');
|
|
|
|
this._spinner.play();
|
|
this._spinnerTimeoutId =
|
|
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, SPINNER_TIMEOUT,
|
|
() => {
|
|
// Assume the quit request has failed, stop the spinner
|
|
this._spinner.stop();
|
|
delete this._spinnerTimeoutId;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
|
|
try {
|
|
await Gio.DBus.session.call(
|
|
appId,
|
|
`/${appId.replaceAll('.', '/')}`,
|
|
'org.freedesktop.Application',
|
|
'ActivateAction',
|
|
new GLib.Variant('(sava{sv})', ['quit', [], {}]),
|
|
null,
|
|
Gio.DBusCallFlags.NONE,
|
|
-1,
|
|
null);
|
|
} catch (_error) {
|
|
try {
|
|
Util.trySpawn(['flatpak', 'kill', this.instance]);
|
|
} catch (pidError) {
|
|
logError(pidError, 'Failed to kill application');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const BackgroundAppsToggle = GObject.registerClass(
|
|
class BackgroundAppsToggle extends QuickToggle {
|
|
_init() {
|
|
super._init({
|
|
visible: false,
|
|
hasMenu: true,
|
|
// The background apps toggle looks like a flat menu, but doesn't
|
|
// have a separate menu button. Fake it with an arrow icon.
|
|
iconName: 'go-next-symbolic',
|
|
});
|
|
|
|
this.add_style_class_name('background-apps-quick-toggle');
|
|
|
|
this._box.set_child_above_sibling(this._icon, null);
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
|
|
this.menu.setHeader(
|
|
'background-app-ghost-symbolic',
|
|
C_('title', 'Background Apps'));
|
|
|
|
new BackgroundMonitorProxy(
|
|
Gio.DBus.session,
|
|
DBUS_NAME,
|
|
DBUS_OBJECT_PATH,
|
|
proxy => {
|
|
this._proxy = proxy;
|
|
proxy?.connect('g-properties-changed', () => this._sync());
|
|
this._sync();
|
|
},
|
|
null,
|
|
Gio.DBusProxyFlags.DO_NOT_AUTO_START);
|
|
|
|
this._listTitle = new PopupMenu.PopupMenuItem(
|
|
_('Apps known to be running without a window'),
|
|
{reactive: false});
|
|
this._listTitle.label.clutter_text.set({
|
|
line_wrap: true,
|
|
});
|
|
this.menu.addMenuItem(this._listTitle);
|
|
|
|
this._appsSection = new PopupMenu.PopupMenuSection();
|
|
this.menu.addMenuItem(this._appsSection);
|
|
|
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
this.menu.addSettingsAction(_('App Settings'),
|
|
'gnome-applications-panel.desktop');
|
|
|
|
this.connect('popup-menu', () => this.menu.open());
|
|
|
|
this.menu.connect('open-state-changed', () => this._syncVisibility());
|
|
Main.sessionMode.connect('updated', () => this._syncVisibility());
|
|
}
|
|
|
|
_syncVisibility() {
|
|
const {isLocked} = Main.sessionMode;
|
|
const nBackgroundApps = this._proxy?.BackgroundApps?.length;
|
|
// We cannot hide the quick toggle while the menu is open, otherwise
|
|
// the menu position goes bogus. We can't show it in locked sessions
|
|
// either
|
|
this.visible = !isLocked && (this.menu.isOpen || nBackgroundApps > 0);
|
|
}
|
|
|
|
_sync() {
|
|
this._syncVisibility();
|
|
|
|
if (!this._proxy)
|
|
return;
|
|
|
|
const {BackgroundApps: backgroundApps} = this._proxy;
|
|
|
|
const nBackgroundApps = backgroundApps?.length ?? 0;
|
|
this.title = nBackgroundApps === 0
|
|
? _('No Background Apps')
|
|
: ngettext(
|
|
'%d Background App',
|
|
'%d Background Apps',
|
|
nBackgroundApps).format(nBackgroundApps);
|
|
this._listTitle.visible = nBackgroundApps > 0;
|
|
|
|
this._appsSection.removeAll();
|
|
|
|
if (!backgroundApps)
|
|
return;
|
|
|
|
backgroundApps
|
|
.map(backgroundApp => {
|
|
const appId = backgroundApp.app_id.deepUnpack();
|
|
const app = this._appSystem.lookup_app(`${appId}.desktop`);
|
|
const message = backgroundApp.message?.deepUnpack();
|
|
const instance = backgroundApp.instance.deepUnpack();
|
|
|
|
return {app, message, instance};
|
|
})
|
|
.sort((a, b) => {
|
|
return a.app.get_name().localeCompare(b.app.get_name());
|
|
})
|
|
.forEach(backgroundApp => {
|
|
const {app, message, instance} = backgroundApp;
|
|
|
|
const item = new BackgroundAppMenuItem(app,
|
|
{instance, message});
|
|
this._appsSection.addMenuItem(item);
|
|
});
|
|
}
|
|
|
|
vfunc_clicked() {
|
|
this.menu.open();
|
|
}
|
|
});
|
|
|
|
var Indicator = GObject.registerClass(
|
|
class Indicator extends SystemIndicator {
|
|
_init() {
|
|
super._init();
|
|
|
|
this.quickSettingsItems.push(new BackgroundAppsToggle());
|
|
}
|
|
});
|