dot/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/shell/device.js

250 lines
7.0 KiB
JavaScript

'use strict';
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const St = imports.gi.St;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Extension = imports.misc.extensionUtils.getCurrentExtension();
// eslint-disable-next-line no-redeclare
const _ = gsconnect._;
const GMenu = Extension.imports.shell.gmenu;
const Tooltip = Extension.imports.shell.tooltip;
/**
* A battery widget with an icon, text percentage and time estimate tooltip
*/
var Battery = GObject.registerClass({
GTypeName: 'GSConnectShellDeviceBattery'
}, class Battery extends St.BoxLayout {
_init(params) {
super._init({
reactive: true,
style_class: 'gsconnect-device-battery',
track_hover: true
});
Object.assign(this, params);
// Percent Label
this.label = new St.Label({
y_align: Clutter.ActorAlign.CENTER
});
this.label.clutter_text.ellipsize = 0;
this.add_child(this.label);
// Battery Icon
this.icon = new St.Icon({
fallback_icon_name: 'battery-missing-symbolic',
icon_size: 16
});
this.add_child(this.icon);
// Battery Estimate
this.tooltip = new Tooltip.Tooltip({
parent: this,
text: this.battery_label
});
// Battery GAction
this._actionAddedId = this.device.action_group.connect(
'action-added',
this._onActionChanged.bind(this)
);
this._actionRemovedId = this.device.action_group.connect(
'action-removed',
this._onActionChanged.bind(this)
);
this._actionStateChangedId = this.device.action_group.connect(
'action-state-changed',
this._onStateChanged.bind(this)
);
this._onActionChanged(this.device.action_group, 'battery');
// Refresh when mapped
this._mappedId = this.connect('notify::mapped', this._sync.bind(this));
// Cleanup
this.connect('destroy', this._onDestroy);
}
_onActionChanged(action_group, action_name) {
if (action_name === 'battery') {
if (action_group.has_action('battery')) {
let value = action_group.get_action_state('battery');
let [charging, icon_name, level, time] = value.deepUnpack();
this.battery = {
Charging: charging,
IconName: icon_name,
Level: level,
Time: time
};
} else {
this.battery = null;
}
this._sync();
}
}
_onStateChanged(action_group, action_name, value) {
if (action_name === 'battery') {
let [charging, icon_name, level, time] = value.deepUnpack();
this.battery = {
Charging: charging,
IconName: icon_name,
Level: level,
Time: time
};
}
}
get battery_label() {
if (!this.battery) return null;
let {Charging, Level, Time} = this.battery;
if (Level === 100) {
// TRANSLATORS: When the battery level is 100%
return _('Fully Charged');
} else if (Time === 0) {
// TRANSLATORS: When no time estimate for the battery is available
// EXAMPLE: 42% (Estimating…)
return _('%d%% (Estimating…)').format(Level);
}
Time = Time / 60;
let minutes = Math.floor(Time % 60);
let hours = Math.floor(Time / 60);
if (Charging) {
// TRANSLATORS: Estimated time until battery is charged
// EXAMPLE: 42% (1:15 Until Full)
return _('%d%% (%d\u2236%02d Until Full)').format(
Level,
hours,
minutes
);
} else {
// TRANSLATORS: Estimated time until battery is empty
// EXAMPLE: 42% (12:15 Remaining)
return _('%d%% (%d\u2236%02d Remaining)').format(
Level,
hours,
minutes
);
}
}
_onDestroy(actor) {
actor.device.action_group.disconnect(actor._actionAddedId);
actor.device.action_group.disconnect(actor._actionRemovedId);
actor.device.action_group.disconnect(actor._actionStateChangedId);
actor.disconnect(actor._mappedId);
}
_sync() {
this.visible = (this.battery);
if (this.visible && this.mapped) {
this.icon.icon_name = this.battery.IconName;
this.label.text = (this.battery.Level > -1) ? `${this.battery.Level}%` : '';
this.tooltip.text = this.battery_label;
}
}
});
/**
* A PopupMenu used as an information and control center for a device
*/
var Menu = class Menu extends PopupMenu.PopupMenuSection {
constructor(params) {
super();
Object.assign(this, params);
this.actor.add_style_class_name('gsconnect-device-menu');
// Title
this._title = new PopupMenu.PopupSeparatorMenuItem(this.device.name);
this.addMenuItem(this._title);
// Title -> Name
this._title.label.style_class = 'gsconnect-device-name';
this._title.label.clutter_text.ellipsize = 0;
this.device.bind_property(
'name',
this._title.label,
'text',
GObject.BindingFlags.SYNC_CREATE
);
// Title -> Battery
this._battery = new Battery({device: this.device});
this._title.actor.add_child(this._battery);
// Actions
let actions;
if (this.menu_type === 'icon') {
actions = new GMenu.IconBox({
action_group: this.device.action_group,
model: this.device.menu
});
} else if (this.menu_type === 'list') {
actions = new GMenu.ListBox({
action_group: this.device.action_group,
model: this.device.menu
});
}
this.addMenuItem(actions);
}
isEmpty() {
return false;
}
};
/**
* An indicator representing a Device in the Status Area
*/
var Indicator = GObject.registerClass({
GTypeName: 'GSConnectDeviceIndicator'
}, class Indicator extends PanelMenu.Button {
_init(params) {
super._init(0.0, `${params.device.name} Indicator`, false);
Object.assign(this, params);
// Device Icon
this._icon = new St.Icon({
gicon: gsconnect.getIcon(this.device.icon_name),
style_class: 'system-status-icon gsconnect-device-indicator'
});
this.add_child(this._icon);
// Menu
let menu = new Menu({
device: this.device,
menu_type: 'icon'
});
this.menu.addMenuItem(menu);
}
update_icon(icon_name) {
this._icon.gicon = gsconnect.getIcon(icon_name);
}
});