status/network: Make NMConnectionItem a menu item

Instead of the current radioItem/labelItem shenanigans, implement
a custom menu item with a :radio-mode property to switch between
the different presentations.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
This commit is contained in:
Florian Müllner 2022-08-02 12:34:18 +02:00 committed by Marge Bot
parent b63c6ac0ef
commit 9e3cb0b797

View File

@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported NMApplet */ /* exported NMApplet */
const { Clutter, Gio, GLib, GObject, Meta, NM, Polkit, St } = imports.gi; const {Atk, Clutter, Gio, GLib, GObject, Meta, NM, Polkit, St} = imports.gi;
const Signals = imports.misc.signals; const Signals = imports.misc.signals;
const Animation = imports.ui.animation; const Animation = imports.ui.animation;
@ -98,7 +98,25 @@ function launchSettingsPanel(panel, ...args) {
} }
} }
var NMConnectionItem = class extends Signals.EventEmitter { const NMConnectionItem = GObject.registerClass({
Properties: {
'radio-mode': GObject.ParamSpec.boolean('radio-mode', '', '',
GObject.ParamFlags.READWRITE,
false),
'is-active': GObject.ParamSpec.boolean('is-active', '', '',
GObject.ParamFlags.READABLE,
false),
'name': GObject.ParamSpec.string('name', '', '',
GObject.ParamFlags.READWRITE,
''),
'icon-name': GObject.ParamSpec.string('icon-name', '', '',
GObject.ParamFlags.READWRITE,
''),
},
Signals: {
'activation-failed': {},
},
}, class NMConnectionItem extends PopupMenu.PopupBaseMenuItem {
constructor(section, connection) { constructor(section, connection) {
super(); super();
@ -106,22 +124,30 @@ var NMConnectionItem = class extends Signals.EventEmitter {
this._connection = connection; this._connection = connection;
this._activeConnection = null; this._activeConnection = null;
this._buildUI(); this._label = new St.Label({
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
this.add_child(this._label);
this.label_actor = this._label;
this.connectObject(
'notify::radio-mode', () => this._sync(),
this);
this._sync(); this._sync();
} }
_buildUI() { get name() {
this.labelItem = new PopupMenu.PopupMenuItem(''); return this._connection.get_id();
this.labelItem.connect('activate', this._toggle.bind(this));
this.radioItem = new PopupMenu.PopupMenuItem(this._connection.get_id(), false);
this.radioItem.connect('activate', this._activate.bind(this));
} }
destroy() { get state() {
this._activeConnection?.disconnectObject(this); return this._activeConnection?.state ??
this.labelItem.destroy(); NM.ActiveConnectionState.DEACTIVATED;
this.radioItem.destroy(); }
get is_active() {
return this.state <= NM.ActiveConnectionState.ACTIVATED;
} }
updateForConnection(connection) { updateForConnection(connection) {
@ -132,30 +158,33 @@ var NMConnectionItem = class extends Signals.EventEmitter {
// Just to be safe, we set it here again // Just to be safe, we set it here again
this._connection = connection; this._connection = connection;
this.radioItem.label.text = connection.get_id(); this.notify('name');
this._sync(); this._sync();
this.emit('name-changed');
} }
getName() { _updateOrnament() {
return this._connection.get_id(); this.setOrnament(this.radio_mode && this.is_active
} ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
isActive() {
if (this._activeConnection == null)
return false;
return this._activeConnection.state <= NM.ActiveConnectionState.ACTIVATED;
} }
_sync() { _sync() {
let isActive = this.isActive(); if (this.radioMode) {
this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel(); this._label.text = this._connection.get_id();
this.radioItem.setOrnament(isActive ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE); this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
this.emit('icon-changed'); } else {
this._label.text = this.is_active
? _('Turn Off') : this._section.getConnectLabel();
this.accessible_role = Atk.Role.MENU_ITEM;
}
this._updateOrnament();
} }
_toggle() { activate() {
super.activate(Clutter.get_current_event());
if (this.radio_mode && this._activeConnection != null)
return; // only activate in radio mode
if (this._activeConnection == null) if (this._activeConnection == null)
this._section.activateConnection(this._connection); this._section.activateConnection(this._connection);
else else
@ -164,14 +193,8 @@ var NMConnectionItem = class extends Signals.EventEmitter {
this._sync(); this._sync();
} }
_activate() {
if (this._activeConnection == null)
this._section.activateConnection(this._connection);
this._sync();
}
_connectionStateChanged(_ac, _newstate, _reason) { _connectionStateChanged(_ac, _newstate, _reason) {
this.notify('is-active');
this._sync(); this._sync();
} }
@ -185,7 +208,7 @@ var NMConnectionItem = class extends Signals.EventEmitter {
this._sync(); this._sync();
} }
}; });
var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter { var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter {
constructor(client) { constructor(client) {
@ -199,12 +222,10 @@ var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter
this._connectionItems = new Map(); this._connectionItems = new Map();
this._connections = []; this._connections = [];
this._labelSection = new PopupMenu.PopupMenuSection(); this._section = new PopupMenu.PopupMenuSection();
this._radioSection = new PopupMenu.PopupMenuSection();
this.item = new PopupMenu.PopupSubMenuMenuItem('', true); this.item = new PopupMenu.PopupSubMenuMenuItem('', true);
this.item.menu.addMenuItem(this._labelSection); this.item.menu.addMenuItem(this._section);
this.item.menu.addMenuItem(this._radioSection);
this._client.connectObject('notify::connectivity', this._client.connectObject('notify::connectivity',
this._iconChanged.bind(this), this); this._iconChanged.bind(this), this);
@ -223,8 +244,8 @@ var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter
_sync() { _sync() {
let nItems = this._connectionItems.size; let nItems = this._connectionItems.size;
this._radioSection.actor.visible = nItems > 1; for (const item of this._connectionItems.values())
this._labelSection.actor.visible = nItems == 1; item.radio_mode = nItems > 1;
this.item.label.text = this._getStatus(); this.item.label.text = this._getStatus();
this.item.icon.icon_name = this._getMenuIcon(); this.item.icon.icon_name = this._getMenuIcon();
@ -275,8 +296,7 @@ var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter
this._connections.splice(pos, 1); this._connections.splice(pos, 1);
pos = Util.insertSorted(this._connections, connection, this._connectionSortFunction.bind(this)); pos = Util.insertSorted(this._connections, connection, this._connectionSortFunction.bind(this));
this._labelSection.moveMenuItem(item.labelItem, pos); this._section.moveMenuItem(item, pos);
this._radioSection.moveMenuItem(item.radioItem, pos);
item.updateForConnection(connection); item.updateForConnection(connection);
} }
@ -286,13 +306,12 @@ var NMConnectionSection = class NMConnectionSection extends Signals.EventEmitter
if (!item) if (!item)
return; return;
item.connect('icon-changed', () => this._iconChanged()); item.connect('notify::icon-name', () => this._iconChanged());
item.connect('activation-failed', () => this.emit('activation-failed')); item.connect('activation-failed', () => this.emit('activation-failed'));
item.connect('name-changed', this._sync.bind(this)); item.connect('notify::name', this._sync.bind(this));
let pos = Util.insertSorted(this._connections, connection, this._connectionSortFunction.bind(this)); let pos = Util.insertSorted(this._connections, connection, this._connectionSortFunction.bind(this));
this._labelSection.addMenuItem(item.labelItem, pos); this._section.addMenuItem(item, pos);
this._radioSection.addMenuItem(item.radioItem, pos);
this._connectionItems.set(connection.get_uuid(), item); this._connectionItems.set(connection.get_uuid(), item);
this._sync(); this._sync();
} }
@ -324,7 +343,8 @@ var NMDeviceItem = class NMDeviceItem extends NMConnectionSection {
this._deviceName = ''; this._deviceName = '';
this._autoConnectItem = this.item.menu.addAction(_("Connect"), this._autoConnect.bind(this)); this._autoConnectItem = this.item.menu.addAction(_("Connect"), this._autoConnect.bind(this));
this._deactivateItem = this._radioSection.addAction(_("Turn Off"), this.deactivateConnection.bind(this)); this._deactivateItem = this.item.menu.addAction(_('Turn Off'),
() => this.deactivateConnection());
this._client.connectObject( this._client.connectObject(
'notify::primary-connection', () => this._iconChanged(), 'notify::primary-connection', () => this._iconChanged(),
@ -404,7 +424,7 @@ var NMDeviceItem = class NMDeviceItem extends NMConnectionSection {
_sync() { _sync() {
let nItems = this._connectionItems.size; let nItems = this._connectionItems.size;
this._autoConnectItem.visible = nItems === 0; this._autoConnectItem.visible = nItems === 0;
this._deactivateItem.visible = this._device.state > NM.DeviceState.DISCONNECTED; this._deactivateItem.visible = this.radioMode && this.isActive;
if (this._activeConnection == null) { if (this._activeConnection == null) {
let activeConnection = this._device.active_connection; let activeConnection = this._device.active_connection;
@ -1396,20 +1416,39 @@ var NMWirelessDeviceItem = class extends Signals.EventEmitter {
} }
}; };
var NMVpnConnectionItem = class extends NMConnectionItem { const NMVpnConnectionItem = GObject.registerClass(
_buildUI() { class NMVpnConnectionItem extends NMConnectionItem {
this.labelItem = new PopupMenu.PopupMenuItem(''); constructor(section, connection) {
this.labelItem.connect('activate', this._toggle.bind(this)); super(section, connection);
this.radioItem = new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false); this._label.x_expand = true;
this.radioItem.connect('toggled', this._toggle.bind(this));
this._switch = new PopupMenu.Switch(this.is_active);
this.add_child(this._switch);
this.bind_property('radio-mode',
this._switch, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this.bind_property('is-active',
this._switch, 'state',
GObject.BindingFlags.SYNC_CREATE);
}
get name() {
return this._connection?.get_id() ?? '';
}
_updateOrnament() {
this.setOrnament(PopupMenu.Ornament.NONE);
} }
_sync() { _sync() {
let isActive = this.isActive(); super._sync();
this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel();
this.radioItem.setToggleState(isActive); if (this.radio_mode && this.is_active)
this.emit('icon-changed'); this.add_accessible_state(Atk.StateType.CHECKED);
else
this.remove_accessible_state(Atk.StateType.CHECKED);
} }
_connectionStateChanged() { _connectionStateChanged() {
@ -1421,21 +1460,21 @@ var NMVpnConnectionItem = class extends NMConnectionItem {
reason !== NM.ActiveConnectionStateReason.USER_DISCONNECTED) reason !== NM.ActiveConnectionStateReason.USER_DISCONNECTED)
this.emit('activation-failed'); this.emit('activation-failed');
this.emit('icon-changed'); this.notify('icon-name');
super._connectionStateChanged(); super._connectionStateChanged();
} }
getIndicatorIcon() { get icon_name() {
if (this._activeConnection) { switch (this.state) {
if (this._activeConnection.state < NM.ActiveConnectionState.ACTIVATED) case NM.ActiveConnectionState.ACTIVATING:
return 'network-vpn-acquiring-symbolic'; return 'network-vpn-acquiring-symbolic';
else case NM.ActiveConnectionState.ACTIVATED:
return 'network-vpn-symbolic'; return 'network-vpn-symbolic';
} else { default:
return ''; return 'network-vpn-disabled-symbolic';
} }
} }
}; });
var NMVpnSection = class extends NMConnectionSection { var NMVpnSection = class extends NMConnectionSection {
constructor(client) { constructor(client) {
@ -1464,8 +1503,8 @@ var NMVpnSection = class extends NMConnectionSection {
_getStatus() { _getStatus() {
let values = this._connectionItems.values(); let values = this._connectionItems.values();
for (let item of values) { for (let item of values) {
if (item.isActive()) if (item.is_active)
return item.getName(); return item.name;
} }
return _("VPN Off"); return _("VPN Off");
@ -1503,9 +1542,8 @@ var NMVpnSection = class extends NMConnectionSection {
getIndicatorIcon() { getIndicatorIcon() {
let items = this._connectionItems.values(); let items = this._connectionItems.values();
for (let item of items) { for (let item of items) {
let icon = item.getIndicatorIcon(); if (item.is_active)
if (icon) return item.icon_name;
return icon;
} }
return ''; return '';
} }