gnome-shell/js/ui/accessDialog.js
Zander Brown 350cd296fa js: Stop using ClutterContainer API
These have been long deprecated over in clutter, and (via several
vtables) simply forward the call to the equivalent ClutterActor methods

Save ourselves the hassle and just use ClutterActor methods directly

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3010>
2023-11-10 20:19:13 +00:00

172 lines
5.5 KiB
JavaScript

import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Pango from 'gi://Pango';
import Shell from 'gi://Shell';
import St from 'gi://St';
import * as CheckBox from './checkBox.js';
import * as Dialog from './dialog.js';
import * as ModalDialog from './modalDialog.js';
import {loadInterfaceXML} from '../misc/fileUtils.js';
const RequestIface = loadInterfaceXML('org.freedesktop.impl.portal.Request');
const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access');
/** @enum {number} */
const DialogResponse = {
OK: 0,
CANCEL: 1,
CLOSED: 2,
};
const AccessDialog = GObject.registerClass(
class AccessDialog extends ModalDialog.ModalDialog {
_init(invocation, handle, title, description, body, options) {
super._init({styleClass: 'access-dialog'});
this._invocation = invocation;
this._handle = handle;
this._requestExported = false;
this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
for (let option in options)
options[option] = options[option].deepUnpack();
this._buildLayout(title, description, body, options);
}
_buildLayout(title, description, body, options) {
// No support for non-modal system dialogs, so ignore the option
// let modal = options['modal'] || true;
let denyLabel = options['deny_label'] || _('Deny');
let grantLabel = options['grant_label'] || _('Allow');
let choices = options['choices'] || [];
let content = new Dialog.MessageDialogContent({title, description});
this.contentLayout.add_child(content);
this._choices = new Map();
for (let i = 0; i < choices.length; i++) {
let [id, name, opts, selected] = choices[i];
if (opts.length > 0)
continue; // radio buttons, not implemented
let check = new CheckBox.CheckBox();
check.getLabelActor().text = name;
check.checked = selected === 'true';
content.add_child(check);
this._choices.set(id, check);
}
if (body) {
let bodyLabel = new St.Label({
text: body,
x_align: Clutter.ActorAlign.CENTER,
});
bodyLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
bodyLabel.clutter_text.line_wrap = true;
content.add_child(bodyLabel);
}
this.addButton({
label: denyLabel,
action: () => this._sendResponse(DialogResponse.CANCEL),
key: Clutter.KEY_Escape,
});
this.addButton({
label: grantLabel,
action: () => this._sendResponse(DialogResponse.OK),
});
}
open() {
if (!super.open())
return false;
let connection = this._invocation.get_connection();
this._requestExported = this._request.export(connection, this._handle);
return true;
}
CloseAsync(invocation, _params) {
if (this._invocation.get_sender() !== invocation.get_sender()) {
invocation.return_error_literal(
Gio.DBusError,
Gio.DBusError.ACCESS_DENIED,
'');
return;
}
this._sendResponse(DialogResponse.CLOSED);
}
_sendResponse(response) {
if (this._requestExported)
this._request.unexport();
this._requestExported = false;
let results = {};
if (response === DialogResponse.OK) {
for (let [id, check] of this._choices) {
let checked = check.checked ? 'true' : 'false';
results[id] = new GLib.Variant('s', checked);
}
}
// Delay actual response until the end of the close animation (if any)
this.connect('closed', () => {
this._invocation.return_value(
new GLib.Variant('(ua{sv})', [response, results]));
});
this.close();
}
});
export class AccessDialogDBus {
constructor() {
this._accessDialog = null;
this._windowTracker = Shell.WindowTracker.get_default();
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
Gio.DBus.session.own_name('org.gnome.Shell.Portal', Gio.BusNameOwnerFlags.REPLACE, null, null);
}
AccessDialogAsync(params, invocation) {
if (this._accessDialog) {
invocation.return_error_literal(
Gio.DBusError,
Gio.DBusError.LIMITS_EXCEEDED,
'Already showing a system access dialog');
return;
}
let [handle, appId, parentWindow_, title, description, body, options] = params;
// We probably want to use parentWindow and global.display.focus_window
// for this check in the future
if (appId && `${appId}.desktop` !== this._windowTracker.focus_app.id) {
invocation.return_error_literal(
Gio.DBusError,
Gio.DBusError.ACCESS_DENIED,
'Only the focused app is allowed to show a system access dialog');
return;
}
let dialog = new AccessDialog(
invocation, handle, title, description, body, options);
dialog.open();
dialog.connect('closed', () => (this._accessDialog = null));
this._accessDialog = dialog;
}
}