250 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			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);
 | |
|     }
 | |
| });
 | |
| 
 |