From 2e00e631c7def6d58bdb1eb0fa3254ae82a37574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 17 May 2017 19:13:50 +0200 Subject: [PATCH 1/6] extensions: Resurrect systemMonitor extension The extension was removed upstream because: - it hooks into the message tray that was removed - it was known to have performance issues - there are plenty of alternatives Those aren't good enough reasons for dropping it downstream as well though, so we need to bring it back ... This reverts commit c9a6421f362cd156cf731289eadc11f44f6970ac. --- extensions/systemMonitor/extension.js | 376 ++++++++++++++++++++++ extensions/systemMonitor/meson.build | 5 + extensions/systemMonitor/metadata.json.in | 11 + extensions/systemMonitor/stylesheet.css | 35 ++ meson.build | 1 + 5 files changed, 428 insertions(+) create mode 100644 extensions/systemMonitor/extension.js create mode 100644 extensions/systemMonitor/meson.build create mode 100644 extensions/systemMonitor/metadata.json.in create mode 100644 extensions/systemMonitor/stylesheet.css diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js new file mode 100644 index 0000000..7b09df0 --- /dev/null +++ b/extensions/systemMonitor/extension.js @@ -0,0 +1,376 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const GTop = imports.gi.GTop; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const St = imports.gi.St; +const Shell = imports.gi.Shell; + +const Main = imports.ui.main; +const Tweener = imports.ui.tweener; + +const Gettext = imports.gettext.domain('gnome-shell-extensions'); +const _ = Gettext.gettext; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); +const Convenience = Me.imports.convenience; + +const INDICATOR_UPDATE_INTERVAL = 500; +const INDICATOR_NUM_GRID_LINES = 3; + +const ITEM_LABEL_SHOW_TIME = 0.15; +const ITEM_LABEL_HIDE_TIME = 0.1; +const ITEM_HOVER_TIMEOUT = 300; + +const Indicator = new Lang.Class({ + Name: 'SystemMonitor.Indicator', + + _init: function() { + this._initValues(); + this.drawing_area = new St.DrawingArea({ reactive: true }); + this.drawing_area.connect('repaint', Lang.bind(this, this._draw)); + this.drawing_area.connect('button-press-event', function() { + let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop'); + app.open_new_window(-1); + return true; + }); + + this.actor = new St.Bin({ style_class: "extension-systemMonitor-indicator-area", + reactive: true, track_hover: true, + x_fill: true, y_fill: true }); + this.actor.add_actor(this.drawing_area); + + this._timeout = Mainloop.timeout_add(INDICATOR_UPDATE_INTERVAL, Lang.bind(this, function () { + this._updateValues(); + this.drawing_area.queue_repaint(); + return true; + })); + }, + + showLabel: function() { + if (this.label == null) + return; + + this.label.opacity = 0; + this.label.show(); + + let [stageX, stageY] = this.actor.get_transformed_position(); + + let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; + let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1; + + let labelWidth = this.label.width; + let labelHeight = this.label.height; + let xOffset = Math.floor((itemWidth - labelWidth) / 2) + + let x = stageX + xOffset; + + let node = this.label.get_theme_node(); + let yOffset = node.get_length('-y-offset'); + + let y = stageY - this.label.get_height() - yOffset; + + this.label.set_position(x, y); + Tweener.addTween(this.label, + { opacity: 255, + time: ITEM_LABEL_SHOW_TIME, + transition: 'easeOutQuad', + }); + }, + + setLabelText: function(text) { + if (this.label == null) + this.label = new St.Label({ style_class: 'extension-systemMonitor-indicator-label'}); + + this.label.set_text(text); + Main.layoutManager.addChrome(this.label); + this.label.hide(); + }, + + hideLabel: function () { + Tweener.addTween(this.label, + { opacity: 0, + time: ITEM_LABEL_HIDE_TIME, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, function() { + this.label.hide(); + }) + }); + }, + + destroy: function() { + Mainloop.source_remove(this._timeout); + + this.actor.destroy(); + if (this.label) + this.label.destroy(); + }, + + _initValues: function() { + }, + + _updateValues: function() { + }, + + _draw: function(area) { + let [width, height] = area.get_surface_size(); + let themeNode = this.actor.get_theme_node(); + let cr = area.get_context(); + + //draw the background grid + let color = themeNode.get_color(this.gridColor); + let gridOffset = Math.floor(height / (INDICATOR_NUM_GRID_LINES + 1)); + for (let i = 1; i <= INDICATOR_NUM_GRID_LINES; ++i) { + cr.moveTo(0, i * gridOffset + .5); + cr.lineTo(width, i * gridOffset + .5); + } + Clutter.cairo_set_source_color(cr, color); + cr.setLineWidth(1); + cr.setDash([4,1], 0); + cr.stroke(); + + //draw the foreground + + function makePath(values, reverse, nudge) { + if (nudge == null) { + nudge = 0; + } + //if we are going in reverse, we are completing the bottom of a chart, so use lineTo + if (reverse) { + cr.lineTo(values.length - 1, (1 - values[values.length - 1]) * height + nudge); + for (let k = values.length - 2; k >= 0; --k) { + cr.lineTo(k, (1 - values[k]) * height + nudge); + } + } else { + cr.moveTo(0, (1 - values[0]) * height + nudge); + for (let k = 1; k < values.length; ++k) { + cr.lineTo(k, (1 - values[k]) * height + nudge); + } + + } + } + + let renderStats = this.renderStats; + + // Make sure we don't have more sample points than pixels + renderStats.map(Lang.bind(this, function(k){ + let stat = this.stats[k]; + if (stat.values.length > width) { + stat.values = stat.values.slice(stat.values.length - width, stat.values.length); + } + })); + + for (let i = 0; i < renderStats.length; ++i) { + let stat = this.stats[renderStats[i]]; + // We outline at full opacity and fill with 40% opacity + let outlineColor = themeNode.get_color(stat.color); + let color = new Clutter.Color(outlineColor); + color.alpha = color.alpha * .4; + + // Render the background between us and the next level + makePath(stat.values, false); + // If there is a process below us, render the cpu between us and it, otherwise, + // render to the bottom of the chart + if (i == renderStats.length - 1) { + cr.lineTo(stat.values.length - 1, height); + cr.lineTo(0, height); + cr.closePath(); + } else { + let nextStat = this.stats[renderStats[i+1]]; + makePath(nextStat.values, true); + } + cr.closePath() + Clutter.cairo_set_source_color(cr, color); + cr.fill(); + + // Render the outline of this level + makePath(stat.values, false, .5); + Clutter.cairo_set_source_color(cr, outlineColor); + cr.setLineWidth(1.0); + cr.setDash([], 0); + cr.stroke(); + } + } +}); + +const CpuIndicator = new Lang.Class({ + Name: 'SystemMonitor.CpuIndicator', + Extends: Indicator, + + _init: function() { + this.parent(); + + this.gridColor = '-grid-color'; + this.renderStats = [ 'cpu-user', 'cpu-sys', 'cpu-iowait' ]; + + // Make sure renderStats is sorted as necessary for rendering + let renderStatOrder = {'cpu-total': 0, 'cpu-user': 1, 'cpu-sys': 2, 'cpu-iowait': 3}; + this.renderStats = this.renderStats.sort(function(a,b) { + return renderStatOrder[a] - renderStatOrder[b]; + }); + + this.setLabelText(_("CPU")); + }, + + _initValues: function() { + this._prev = new GTop.glibtop_cpu; + GTop.glibtop_get_cpu(this._prev); + + this.stats = { + 'cpu-user': {color: '-cpu-user-color', values: []}, + 'cpu-sys': {color: '-cpu-sys-color', values: []}, + 'cpu-iowait': {color: '-cpu-iowait-color', values: []}, + 'cpu-total': {color: '-cpu-total-color', values: []} + }; + }, + + _updateValues: function() { + let cpu = new GTop.glibtop_cpu; + let t = 0.0; + GTop.glibtop_get_cpu(cpu); + let total = cpu.total - this._prev.total; + let user = cpu.user - this._prev.user; + let sys = cpu.sys - this._prev.sys; + let iowait = cpu.iowait - this._prev.iowait; + let idle = cpu.idle - this._prev.idle; + + t += iowait / total; + this.stats['cpu-iowait'].values.push(t); + t += sys / total; + this.stats['cpu-sys'].values.push(t); + t += user / total; + this.stats['cpu-user'].values.push(t); + this.stats['cpu-total'].values.push(1 - idle / total); + + this._prev = cpu; + } +}); + +const MemoryIndicator = new Lang.Class({ + Name: 'SystemMonitor.MemoryIndicator', + Extends: Indicator, + + _init: function() { + this.parent(); + + this.gridColor = '-grid-color'; + this.renderStats = [ 'mem-user', 'mem-other', 'mem-cached' ]; + + // Make sure renderStats is sorted as necessary for rendering + let renderStatOrder = { 'mem-cached': 0, 'mem-other': 1, 'mem-user': 2 }; + this.renderStats = this.renderStats.sort(function(a,b) { + return renderStatOrder[a] - renderStatOrder[b]; + }); + + this.setLabelText(_("Memory")); + }, + + _initValues: function() { + this.mem = new GTop.glibtop_mem; + this.stats = { + 'mem-user': { color: "-mem-user-color", values: [] }, + 'mem-other': { color: "-mem-other-color", values: [] }, + 'mem-cached': { color: "-mem-cached-color", values: [] } + }; + }, + + _updateValues: function() { + GTop.glibtop_get_mem(this.mem); + + let t = this.mem.user / this.mem.total; + this.stats['mem-user'].values.push(t); + t += (this.mem.used - this.mem.user - this.mem.cached) / this.mem.total; + this.stats['mem-other'].values.push(t); + t += this.mem.cached / this.mem.total; + this.stats['mem-cached'].values.push(t); + } +}); + +const INDICATORS = [CpuIndicator, MemoryIndicator]; + +const Extension = new Lang.Class({ + Name: 'SystemMonitor.Extension', + + _init: function() { + Convenience.initTranslations(); + + this._showLabelTimeoutId = 0; + this._resetHoverTimeoutId = 0; + this._labelShowing = false; + }, + + enable: function() { + this._box = new St.BoxLayout({ style_class: 'extension-systemMonitor-container', + x_align: Clutter.ActorAlign.START, + x_expand: true }); + this._indicators = [ ]; + + for (let i = 0; i < INDICATORS.length; i++) { + let indicator = new (INDICATORS[i])(); + + indicator.actor.connect('notify::hover', Lang.bind(this, function() { + this._onHover(indicator); + })); + this._box.add_actor(indicator.actor); + this._indicators.push(indicator); + } + + this._boxHolder = new St.BoxLayout({ x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.START, + }); + let menuButton = Main.messageTray._messageTrayMenuButton.actor; + Main.messageTray.actor.remove_child(menuButton); + Main.messageTray.actor.add_child(this._boxHolder); + + this._boxHolder.add_child(this._box); + this._boxHolder.add_child(menuButton); + }, + + disable: function() { + this._indicators.forEach(function(i) { i.destroy(); }); + + let menuButton = Main.messageTray._messageTrayMenuButton.actor; + this._boxHolder.remove_child(menuButton); + Main.messageTray.actor.add_child(menuButton); + + this._box.destroy(); + this._boxHolder.destroy(); + }, + + _onHover: function (item) { + if (item.actor.get_hover()) { + if (this._showLabelTimeoutId == 0) { + let timeout = this._labelShowing ? 0 : ITEM_HOVER_TIMEOUT; + this._showLabelTimeoutId = Mainloop.timeout_add(timeout, + Lang.bind(this, function() { + this._labelShowing = true; + item.showLabel(); + return false; + })); + if (this._resetHoverTimeoutId > 0) { + Mainloop.source_remove(this._resetHoverTimeoutId); + this._resetHoverTimeoutId = 0; + } + } + } else { + if (this._showLabelTimeoutId > 0) + Mainloop.source_remove(this._showLabelTimeoutId); + this._showLabelTimeoutId = 0; + item.hideLabel(); + if (this._labelShowing) { + this._resetHoverTimeoutId = Mainloop.timeout_add(ITEM_HOVER_TIMEOUT, + Lang.bind(this, function() { + this._labelShowing = false; + return false; + })); + } + } + }, +}); + +function init() { + return new Extension(); +} diff --git a/extensions/systemMonitor/meson.build b/extensions/systemMonitor/meson.build new file mode 100644 index 0000000..48504f6 --- /dev/null +++ b/extensions/systemMonitor/meson.build @@ -0,0 +1,5 @@ +extension_data += configure_file( + input: metadata_name + '.in', + output: metadata_name, + configuration: metadata_conf +) diff --git a/extensions/systemMonitor/metadata.json.in b/extensions/systemMonitor/metadata.json.in new file mode 100644 index 0000000..fa75007 --- /dev/null +++ b/extensions/systemMonitor/metadata.json.in @@ -0,0 +1,11 @@ +{ + "shell-version": ["@shell_current@" ], + "uuid": "@uuid@", + "extension-id": "@extension_id@", + "settings-schema": "@gschemaname@", + "gettext-domain": "@gettext_domain@", + "original-author": "zaspire@rambler.ru", + "name": "SystemMonitor", + "description": "System monitor showing CPU and memory usage in the message tray.", + "url": "@url@" +} diff --git a/extensions/systemMonitor/stylesheet.css b/extensions/systemMonitor/stylesheet.css new file mode 100644 index 0000000..13f95ec --- /dev/null +++ b/extensions/systemMonitor/stylesheet.css @@ -0,0 +1,35 @@ +.extension-systemMonitor-container { + spacing: 5px; + padding-left: 5px; + padding-right: 5px; + padding-bottom: 10px; + padding-top: 10px; +} + +.extension-systemMonitor-indicator-area { + border: 1px solid #8d8d8d; + border-radius: 3px; + width: 100px; + /* message tray is 72px, so 20px padding of the container, + 2px of border, makes it 50px */ + height: 50px; + -grid-color: #575757; + -cpu-total-color: rgb(0,154,62); + -cpu-user-color: rgb(69,154,0); + -cpu-sys-color: rgb(255,253,81); + -cpu-iowait-color: rgb(210,148,0); + -mem-user-color: rgb(210,148,0); + -mem-cached-color: rgb(90,90,90); + -mem-other-color: rgb(205,203,41); + background-color: #1e1e1e; +} + +.extension-systemMonitor-indicator-label { + border-radius: 7px; + padding: 4px 12px; + background-color: rgba(0,0,0,0.9); + text-align: center; + -y-offset: 8px; + font-size: 9pt; + font-weight: bold; +} diff --git a/meson.build b/meson.build index 6a2fdf0..afc0133 100644 --- a/meson.build +++ b/meson.build @@ -48,6 +48,7 @@ all_extensions += [ 'dash-to-dock', 'native-window-placement', 'panel-favorites', + 'systemMonitor', 'top-icons', 'updates-dialog', 'user-theme' -- 2.32.0 From 59927edac1f40239d7926f0285249c933ea42caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 17 May 2019 22:55:48 +0000 Subject: [PATCH 2/6] systemMonitor: Modernise code - port to ES6 classes - replace Lang.bind() - replace Tweener - use standard align/expand properties - destructure imports - fix style issues (stray/missing spaces/semi-colons, indent, ...) --- extensions/systemMonitor/extension.js | 422 +++++++++++++------------- 1 file changed, 212 insertions(+), 210 deletions(-) diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js index 7b09df0..f7c6a4a 100644 --- a/extensions/systemMonitor/extension.js +++ b/extensions/systemMonitor/extension.js @@ -1,56 +1,57 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ -const Clutter = imports.gi.Clutter; -const GTop = imports.gi.GTop; -const Lang = imports.lang; -const Mainloop = imports.mainloop; -const St = imports.gi.St; -const Shell = imports.gi.Shell; +/* exported init */ +const { Clutter, GLib, GTop, Shell, St } = imports.gi; + +const ExtensionUtils = imports.misc.extensionUtils; const Main = imports.ui.main; -const Tweener = imports.ui.tweener; const Gettext = imports.gettext.domain('gnome-shell-extensions'); const _ = Gettext.gettext; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Convenience = Me.imports.convenience; - const INDICATOR_UPDATE_INTERVAL = 500; const INDICATOR_NUM_GRID_LINES = 3; -const ITEM_LABEL_SHOW_TIME = 0.15; -const ITEM_LABEL_HIDE_TIME = 0.1; +const ITEM_LABEL_SHOW_TIME = 150; +const ITEM_LABEL_HIDE_TIME = 100; const ITEM_HOVER_TIMEOUT = 300; -const Indicator = new Lang.Class({ - Name: 'SystemMonitor.Indicator', - - _init: function() { +const Indicator = class { + constructor() { this._initValues(); - this.drawing_area = new St.DrawingArea({ reactive: true }); - this.drawing_area.connect('repaint', Lang.bind(this, this._draw)); - this.drawing_area.connect('button-press-event', function() { + this._drawingArea = new St.DrawingArea({ + reactive: true, + x_expand: true, + y_expand: true, + }); + this._drawingArea.connect('repaint', this._draw.bind(this)); + this._drawingArea.connect('button-press-event', () => { let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop'); app.open_new_window(-1); return true; }); - this.actor = new St.Bin({ style_class: "extension-systemMonitor-indicator-area", - reactive: true, track_hover: true, - x_fill: true, y_fill: true }); - this.actor.add_actor(this.drawing_area); + this.actor = new St.Bin({ + style_class: 'extension-systemMonitor-indicator-area', + reactive: true, + track_hover: true, + }); + this.actor.add_actor(this._drawingArea); + + this.actor.connect('destroy', this._onDestroy.bind(this)); - this._timeout = Mainloop.timeout_add(INDICATOR_UPDATE_INTERVAL, Lang.bind(this, function () { - this._updateValues(); - this.drawing_area.queue_repaint(); - return true; - })); - }, + this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, + INDICATOR_UPDATE_INTERVAL, + () => { + this._updateValues(); + this._drawingArea.queue_repaint(); + return GLib.SOURCE_CONTINUE; + }); + } - showLabel: function() { - if (this.label == null) + showLabel() { + if (this.label === null) return; this.label.opacity = 0; @@ -58,12 +59,10 @@ const Indicator = new Lang.Class({ let [stageX, stageY] = this.actor.get_transformed_position(); - let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; - let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1; + let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; - let labelWidth = this.label.width; - let labelHeight = this.label.height; - let xOffset = Math.floor((itemWidth - labelWidth) / 2) + let labelWidth = this.label.width; + let xOffset = Math.floor((itemWidth - labelWidth) / 2); let x = stageX + xOffset; @@ -73,116 +72,113 @@ const Indicator = new Lang.Class({ let y = stageY - this.label.get_height() - yOffset; this.label.set_position(x, y); - Tweener.addTween(this.label, - { opacity: 255, - time: ITEM_LABEL_SHOW_TIME, - transition: 'easeOutQuad', - }); - }, + this.label.ease({ + opacity: 255, + duration: ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } - setLabelText: function(text) { - if (this.label == null) - this.label = new St.Label({ style_class: 'extension-systemMonitor-indicator-label'}); + setLabelText(text) { + if (this.label === null) { + this.label = new St.Label({ + style_class: 'extension-systemMonitor-indicator-label', + }); + } this.label.set_text(text); Main.layoutManager.addChrome(this.label); this.label.hide(); - }, - - hideLabel: function () { - Tweener.addTween(this.label, - { opacity: 0, - time: ITEM_LABEL_HIDE_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, function() { - this.label.hide(); - }) - }); - }, - - destroy: function() { - Mainloop.source_remove(this._timeout); + } + hideLabel() { + this.label.ease({ + opacity: 0, + duration: ITEM_LABEL_HIDE_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => this.label.hide(), + }); + } + + destroy() { this.actor.destroy(); - if (this.label) - this.label.destroy(); - }, + } - _initValues: function() { - }, + _onDestroy() { + GLib.source_remove(this._timeout); - _updateValues: function() { - }, + if (this.label) + this.label.destroy(); + } + + _initValues() { + } - _draw: function(area) { + _updateValues() { + } + + _draw(area) { let [width, height] = area.get_surface_size(); let themeNode = this.actor.get_theme_node(); let cr = area.get_context(); - //draw the background grid + // draw the background grid let color = themeNode.get_color(this.gridColor); let gridOffset = Math.floor(height / (INDICATOR_NUM_GRID_LINES + 1)); for (let i = 1; i <= INDICATOR_NUM_GRID_LINES; ++i) { - cr.moveTo(0, i * gridOffset + .5); - cr.lineTo(width, i * gridOffset + .5); + cr.moveTo(0, i * gridOffset + .5); + cr.lineTo(width, i * gridOffset + .5); } Clutter.cairo_set_source_color(cr, color); cr.setLineWidth(1); - cr.setDash([4,1], 0); + cr.setDash([4, 1], 0); cr.stroke(); - //draw the foreground + // draw the foreground - function makePath(values, reverse, nudge) { - if (nudge == null) { - nudge = 0; - } - //if we are going in reverse, we are completing the bottom of a chart, so use lineTo + function makePath(values, reverse, nudge = 0) { + // if we are going in reverse, we are completing the bottom of a chart, so use lineTo if (reverse) { cr.lineTo(values.length - 1, (1 - values[values.length - 1]) * height + nudge); - for (let k = values.length - 2; k >= 0; --k) { + for (let k = values.length - 2; k >= 0; --k) cr.lineTo(k, (1 - values[k]) * height + nudge); - } } else { cr.moveTo(0, (1 - values[0]) * height + nudge); - for (let k = 1; k < values.length; ++k) { + for (let k = 1; k < values.length; ++k) cr.lineTo(k, (1 - values[k]) * height + nudge); - } - } } let renderStats = this.renderStats; // Make sure we don't have more sample points than pixels - renderStats.map(Lang.bind(this, function(k){ + renderStats.forEach(k => { let stat = this.stats[k]; - if (stat.values.length > width) { + if (stat.values.length > width) stat.values = stat.values.slice(stat.values.length - width, stat.values.length); - } - })); + }); for (let i = 0; i < renderStats.length; ++i) { let stat = this.stats[renderStats[i]]; // We outline at full opacity and fill with 40% opacity let outlineColor = themeNode.get_color(stat.color); - let color = new Clutter.Color(outlineColor); - color.alpha = color.alpha * .4; + let fillColor = new Clutter.Color(outlineColor); + fillColor.alpha *= .4; // Render the background between us and the next level makePath(stat.values, false); // If there is a process below us, render the cpu between us and it, otherwise, // render to the bottom of the chart - if (i == renderStats.length - 1) { + if (i === renderStats.length - 1) { cr.lineTo(stat.values.length - 1, height); cr.lineTo(0, height); cr.closePath(); } else { - let nextStat = this.stats[renderStats[i+1]]; + let nextStat = this.stats[renderStats[i + 1]]; makePath(nextStat.values, true); } - cr.closePath() - Clutter.cairo_set_source_color(cr, color); + cr.closePath(); + Clutter.cairo_set_source_color(cr, fillColor); cr.fill(); // Render the outline of this level @@ -193,41 +189,43 @@ const Indicator = new Lang.Class({ cr.stroke(); } } -}); +}; -const CpuIndicator = new Lang.Class({ - Name: 'SystemMonitor.CpuIndicator', - Extends: Indicator, - - _init: function() { - this.parent(); +const CpuIndicator = class extends Indicator { + constructor() { + super(); this.gridColor = '-grid-color'; - this.renderStats = [ 'cpu-user', 'cpu-sys', 'cpu-iowait' ]; + this.renderStats = ['cpu-user', 'cpu-sys', 'cpu-iowait']; // Make sure renderStats is sorted as necessary for rendering - let renderStatOrder = {'cpu-total': 0, 'cpu-user': 1, 'cpu-sys': 2, 'cpu-iowait': 3}; - this.renderStats = this.renderStats.sort(function(a,b) { + let renderStatOrder = { + 'cpu-total': 0, + 'cpu-user': 1, + 'cpu-sys': 2, + 'cpu-iowait': 3, + }; + this.renderStats = this.renderStats.sort((a, b) => { return renderStatOrder[a] - renderStatOrder[b]; }); - this.setLabelText(_("CPU")); - }, + this.setLabelText(_('CPU')); + } - _initValues: function() { - this._prev = new GTop.glibtop_cpu; + _initValues() { + this._prev = new GTop.glibtop_cpu(); GTop.glibtop_get_cpu(this._prev); this.stats = { - 'cpu-user': {color: '-cpu-user-color', values: []}, - 'cpu-sys': {color: '-cpu-sys-color', values: []}, - 'cpu-iowait': {color: '-cpu-iowait-color', values: []}, - 'cpu-total': {color: '-cpu-total-color', values: []} - }; - }, - - _updateValues: function() { - let cpu = new GTop.glibtop_cpu; + 'cpu-user': { color: '-cpu-user-color', values: [] }, + 'cpu-sys': { color: '-cpu-sys-color', values: [] }, + 'cpu-iowait': { color: '-cpu-iowait-color', values: [] }, + 'cpu-total': { color: '-cpu-total-color', values: [] }, + }; + } + + _updateValues() { + let cpu = new GTop.glibtop_cpu(); let t = 0.0; GTop.glibtop_get_cpu(cpu); let total = cpu.total - this._prev.total; @@ -246,37 +244,34 @@ const CpuIndicator = new Lang.Class({ this._prev = cpu; } -}); +}; -const MemoryIndicator = new Lang.Class({ - Name: 'SystemMonitor.MemoryIndicator', - Extends: Indicator, - - _init: function() { - this.parent(); +const MemoryIndicator = class extends Indicator { + constructor() { + super(); this.gridColor = '-grid-color'; - this.renderStats = [ 'mem-user', 'mem-other', 'mem-cached' ]; + this.renderStats = ['mem-user', 'mem-other', 'mem-cached']; // Make sure renderStats is sorted as necessary for rendering let renderStatOrder = { 'mem-cached': 0, 'mem-other': 1, 'mem-user': 2 }; - this.renderStats = this.renderStats.sort(function(a,b) { + this.renderStats = this.renderStats.sort((a, b) => { return renderStatOrder[a] - renderStatOrder[b]; }); - this.setLabelText(_("Memory")); - }, + this.setLabelText(_('Memory')); + } - _initValues: function() { - this.mem = new GTop.glibtop_mem; + _initValues() { + this.mem = new GTop.glibtop_mem(); this.stats = { - 'mem-user': { color: "-mem-user-color", values: [] }, - 'mem-other': { color: "-mem-other-color", values: [] }, - 'mem-cached': { color: "-mem-cached-color", values: [] } - }; - }, + 'mem-user': { color: '-mem-user-color', values: [] }, + 'mem-other': { color: '-mem-other-color', values: [] }, + 'mem-cached': { color: '-mem-cached-color', values: [] }, + }; + } - _updateValues: function() { + _updateValues() { GTop.glibtop_get_mem(this.mem); let t = this.mem.user / this.mem.total; @@ -286,90 +281,97 @@ const MemoryIndicator = new Lang.Class({ t += this.mem.cached / this.mem.total; this.stats['mem-cached'].values.push(t); } -}); +}; const INDICATORS = [CpuIndicator, MemoryIndicator]; -const Extension = new Lang.Class({ - Name: 'SystemMonitor.Extension', - - _init: function() { - Convenience.initTranslations(); - - this._showLabelTimeoutId = 0; - this._resetHoverTimeoutId = 0; - this._labelShowing = false; - }, - - enable: function() { - this._box = new St.BoxLayout({ style_class: 'extension-systemMonitor-container', - x_align: Clutter.ActorAlign.START, - x_expand: true }); - this._indicators = [ ]; - - for (let i = 0; i < INDICATORS.length; i++) { - let indicator = new (INDICATORS[i])(); - - indicator.actor.connect('notify::hover', Lang.bind(this, function() { - this._onHover(indicator); - })); - this._box.add_actor(indicator.actor); - this._indicators.push(indicator); - } - - this._boxHolder = new St.BoxLayout({ x_expand: true, - y_expand: true, - x_align: Clutter.ActorAlign.START, - }); - let menuButton = Main.messageTray._messageTrayMenuButton.actor; - Main.messageTray.actor.remove_child(menuButton); - Main.messageTray.actor.add_child(this._boxHolder); - - this._boxHolder.add_child(this._box); - this._boxHolder.add_child(menuButton); - }, - - disable: function() { - this._indicators.forEach(function(i) { i.destroy(); }); - - let menuButton = Main.messageTray._messageTrayMenuButton.actor; - this._boxHolder.remove_child(menuButton); - Main.messageTray.actor.add_child(menuButton); - - this._box.destroy(); - this._boxHolder.destroy(); - }, - - _onHover: function (item) { +class Extension { + constructor() { + ExtensionUtils.initTranslations(); + + this._showLabelTimeoutId = 0; + this._resetHoverTimeoutId = 0; + this._labelShowing = false; + } + + enable() { + this._box = new St.BoxLayout({ + style_class: 'extension-systemMonitor-container', + x_align: Clutter.ActorAlign.START, + x_expand: true, + }); + this._indicators = []; + + for (let i = 0; i < INDICATORS.length; i++) { + let indicator = new INDICATORS[i](); + + indicator.actor.connect('notify::hover', () => { + this._onHover(indicator); + }); + this._box.add_actor(indicator.actor); + this._indicators.push(indicator); + } + + this._boxHolder = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.START, + }); + let menuButton = Main.messageTray._messageTrayMenuButton.actor; + Main.messageTray.actor.remove_child(menuButton); + Main.messageTray.actor.add_child(this._boxHolder); + + this._boxHolder.add_child(this._box); + this._boxHolder.add_child(menuButton); + } + + disable() { + this._indicators.forEach(i => i.destroy()); + + let menuButton = Main.messageTray._messageTrayMenuButton.actor; + this._boxHolder.remove_child(menuButton); + Main.messageTray.actor.add_child(menuButton); + + this._box.destroy(); + this._boxHolder.destroy(); + } + + _onHover(item) { if (item.actor.get_hover()) { - if (this._showLabelTimeoutId == 0) { - let timeout = this._labelShowing ? 0 : ITEM_HOVER_TIMEOUT; - this._showLabelTimeoutId = Mainloop.timeout_add(timeout, - Lang.bind(this, function() { - this._labelShowing = true; - item.showLabel(); - return false; - })); - if (this._resetHoverTimeoutId > 0) { - Mainloop.source_remove(this._resetHoverTimeoutId); - this._resetHoverTimeoutId = 0; - } + if (this._showLabelTimeoutId) + return; + + let timeout = this._labelShowing ? 0 : ITEM_HOVER_TIMEOUT; + this._showLabelTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, + timeout, + () => { + this._labelShowing = true; + item.showLabel(); + this._showLabelTimeoutId = 0; + return GLib.SOURCE_REMOVE; + }); + + if (this._resetHoverTimeoutId > 0) { + GLib.source_remove(this._resetHoverTimeoutId); + this._resetHoverTimeoutId = 0; } } else { if (this._showLabelTimeoutId > 0) - Mainloop.source_remove(this._showLabelTimeoutId); + GLib.source_remove(this._showLabelTimeoutId); this._showLabelTimeoutId = 0; item.hideLabel(); - if (this._labelShowing) { - this._resetHoverTimeoutId = Mainloop.timeout_add(ITEM_HOVER_TIMEOUT, - Lang.bind(this, function() { - this._labelShowing = false; - return false; - })); - } + if (!this._labelShowing) + return; + + this._resetHoverTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, + ITEM_HOVER_TIMEOUT, + () => { + this._labelShowing = false; + return GLib.SOURCE_REMOVE; + }); } - }, -}); + } +} function init() { return new Extension(); -- 2.32.0 From 71e275ba45b09c5f8c6ca5445a459196dc65474b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 26 May 2021 19:50:37 +0200 Subject: [PATCH 3/6] systemMonitor: Make label property private There is no good reason to use a public property, and the name will clash when we subclass St.Button. --- extensions/systemMonitor/extension.js | 35 ++++++++++++++------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js index f7c6a4a..bde25a1 100644 --- a/extensions/systemMonitor/extension.js +++ b/extensions/systemMonitor/extension.js @@ -19,6 +19,7 @@ const ITEM_HOVER_TIMEOUT = 300; const Indicator = class { constructor() { + this._label = null; this._initValues(); this._drawingArea = new St.DrawingArea({ reactive: true, @@ -51,28 +52,28 @@ const Indicator = class { } showLabel() { - if (this.label === null) + if (this._label === null) return; - this.label.opacity = 0; - this.label.show(); + this._label.opacity = 0; + this._label.show(); let [stageX, stageY] = this.actor.get_transformed_position(); let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; - let labelWidth = this.label.width; + let labelWidth = this._label.width; let xOffset = Math.floor((itemWidth - labelWidth) / 2); let x = stageX + xOffset; - let node = this.label.get_theme_node(); + let node = this._label.get_theme_node(); let yOffset = node.get_length('-y-offset'); - let y = stageY - this.label.get_height() - yOffset; + let y = stageY - this._label.get_height() - yOffset; - this.label.set_position(x, y); - this.label.ease({ + this._label.set_position(x, y); + this._label.ease({ opacity: 255, duration: ITEM_LABEL_SHOW_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, @@ -80,23 +81,23 @@ const Indicator = class { } setLabelText(text) { - if (this.label === null) { - this.label = new St.Label({ + if (this._label === null) { + this._label = new St.Label({ style_class: 'extension-systemMonitor-indicator-label', }); } - this.label.set_text(text); - Main.layoutManager.addChrome(this.label); - this.label.hide(); + this._label.set_text(text); + Main.layoutManager.addChrome(this._label); + this._label.hide(); } hideLabel() { - this.label.ease({ + this._label.ease({ opacity: 0, duration: ITEM_LABEL_HIDE_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => this.label.hide(), + onComplete: () => this._label.hide(), }); } @@ -107,8 +108,8 @@ const Indicator = class { _onDestroy() { GLib.source_remove(this._timeout); - if (this.label) - this.label.destroy(); + if (this._label) + this._label.destroy(); } _initValues() { -- 2.32.0 From b310c3a5b532a18af38390021daa332961e404ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 17 May 2017 19:31:58 +0200 Subject: [PATCH 4/6] systemMonitor: Move indicators to calendar The message tray joined the invisible choir, so we have to find a new home for the extension UI. The message list in the calendar drop-down looks like the best option, given that it replaced the old tray (and also took over the old keyboard shortcut to bring it up quickly). --- extensions/systemMonitor/extension.js | 106 +++++++++++------------- extensions/systemMonitor/stylesheet.css | 14 ---- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js index bde25a1..1fd01ab 100644 --- a/extensions/systemMonitor/extension.js +++ b/extensions/systemMonitor/extension.js @@ -2,10 +2,11 @@ /* exported init */ -const { Clutter, GLib, GTop, Shell, St } = imports.gi; +const { Clutter, GLib, GObject, GTop, Shell, St } = imports.gi; const ExtensionUtils = imports.misc.extensionUtils; const Main = imports.ui.main; +const MessageList = imports.ui.messageList; const Gettext = imports.gettext.domain('gnome-shell-extensions'); const _ = Gettext.gettext; @@ -17,30 +18,38 @@ const ITEM_LABEL_SHOW_TIME = 150; const ITEM_LABEL_HIDE_TIME = 100; const ITEM_HOVER_TIMEOUT = 300; -const Indicator = class { - constructor() { +const Indicator = GObject.registerClass({ + Signals: { + 'close': {}, + 'expanded': {}, + 'unexpanded': {}, + }, +}, class Indicator extends St.Button { + _init() { this._label = null; this._initValues(); this._drawingArea = new St.DrawingArea({ - reactive: true, x_expand: true, y_expand: true, }); this._drawingArea.connect('repaint', this._draw.bind(this)); - this._drawingArea.connect('button-press-event', () => { + + super._init({ + style_class: 'message message-content extension-systemMonitor-indicator-area', + child: this._drawingArea, + x_expand: true, + can_focus: true, + }); + + this.connect('clicked', () => { let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop'); app.open_new_window(-1); - return true; - }); - this.actor = new St.Bin({ - style_class: 'extension-systemMonitor-indicator-area', - reactive: true, - track_hover: true, + Main.overview.hide(); + Main.panel.closeCalendar(); }); - this.actor.add_actor(this._drawingArea); - this.actor.connect('destroy', this._onDestroy.bind(this)); + this.connect('destroy', this._onDestroy.bind(this)); this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, INDICATOR_UPDATE_INTERVAL, @@ -58,9 +67,9 @@ const Indicator = class { this._label.opacity = 0; this._label.show(); - let [stageX, stageY] = this.actor.get_transformed_position(); + let [stageX, stageY] = this.get_transformed_position(); - let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; + let itemWidth = this.allocation.x2 - this.allocation.x1; let labelWidth = this._label.width; let xOffset = Math.floor((itemWidth - labelWidth) / 2); @@ -73,6 +82,7 @@ const Indicator = class { let y = stageY - this._label.get_height() - yOffset; this._label.set_position(x, y); + this._label.get_parent().set_child_above_sibling(this._label, null); this._label.ease({ opacity: 255, duration: ITEM_LABEL_SHOW_TIME, @@ -101,8 +111,12 @@ const Indicator = class { }); } - destroy() { - this.actor.destroy(); + /* MessageList.Message boilerplate */ + canClose() { + return false; + } + + clear() { } _onDestroy() { @@ -120,7 +134,7 @@ const Indicator = class { _draw(area) { let [width, height] = area.get_surface_size(); - let themeNode = this.actor.get_theme_node(); + let themeNode = this.get_theme_node(); let cr = area.get_context(); // draw the background grid @@ -190,11 +204,12 @@ const Indicator = class { cr.stroke(); } } -}; +}); -const CpuIndicator = class extends Indicator { - constructor() { - super(); +const CpuIndicator = GObject.registerClass( +class CpuIndicator extends Indicator { + _init() { + super._init(); this.gridColor = '-grid-color'; this.renderStats = ['cpu-user', 'cpu-sys', 'cpu-iowait']; @@ -245,11 +260,12 @@ const CpuIndicator = class extends Indicator { this._prev = cpu; } -}; +}); -const MemoryIndicator = class extends Indicator { - constructor() { - super(); +const MemoryIndicator = GObject.registerClass( +class MemoryIndicator extends Indicator { + _init() { + super._init(); this.gridColor = '-grid-color'; this.renderStats = ['mem-user', 'mem-other', 'mem-cached']; @@ -282,7 +298,7 @@ const MemoryIndicator = class extends Indicator { t += this.mem.cached / this.mem.total; this.stats['mem-cached'].values.push(t); } -}; +}); const INDICATORS = [CpuIndicator, MemoryIndicator]; @@ -296,49 +312,27 @@ class Extension { } enable() { - this._box = new St.BoxLayout({ - style_class: 'extension-systemMonitor-container', - x_align: Clutter.ActorAlign.START, - x_expand: true, - }); - this._indicators = []; + this._section = new MessageList.MessageListSection(_('System Monitor')); for (let i = 0; i < INDICATORS.length; i++) { let indicator = new INDICATORS[i](); - indicator.actor.connect('notify::hover', () => { + indicator.connect('notify::hover', () => { this._onHover(indicator); }); - this._box.add_actor(indicator.actor); - this._indicators.push(indicator); + this._section.addMessage(indicator, false); } - this._boxHolder = new St.BoxLayout({ - x_expand: true, - y_expand: true, - x_align: Clutter.ActorAlign.START, - }); - let menuButton = Main.messageTray._messageTrayMenuButton.actor; - Main.messageTray.actor.remove_child(menuButton); - Main.messageTray.actor.add_child(this._boxHolder); - - this._boxHolder.add_child(this._box); - this._boxHolder.add_child(menuButton); + Main.panel.statusArea.dateMenu._messageList._addSection(this._section); + this._section.get_parent().set_child_at_index(this._section, 0); } disable() { - this._indicators.forEach(i => i.destroy()); - - let menuButton = Main.messageTray._messageTrayMenuButton.actor; - this._boxHolder.remove_child(menuButton); - Main.messageTray.actor.add_child(menuButton); - - this._box.destroy(); - this._boxHolder.destroy(); + this._section.destroy(); } _onHover(item) { - if (item.actor.get_hover()) { + if (item.get_hover()) { if (this._showLabelTimeoutId) return; diff --git a/extensions/systemMonitor/stylesheet.css b/extensions/systemMonitor/stylesheet.css index 13f95ec..978ac12 100644 --- a/extensions/systemMonitor/stylesheet.css +++ b/extensions/systemMonitor/stylesheet.css @@ -1,17 +1,4 @@ -.extension-systemMonitor-container { - spacing: 5px; - padding-left: 5px; - padding-right: 5px; - padding-bottom: 10px; - padding-top: 10px; -} - .extension-systemMonitor-indicator-area { - border: 1px solid #8d8d8d; - border-radius: 3px; - width: 100px; - /* message tray is 72px, so 20px padding of the container, - 2px of border, makes it 50px */ height: 50px; -grid-color: #575757; -cpu-total-color: rgb(0,154,62); @@ -21,7 +8,6 @@ -mem-user-color: rgb(210,148,0); -mem-cached-color: rgb(90,90,90); -mem-other-color: rgb(205,203,41); - background-color: #1e1e1e; } .extension-systemMonitor-indicator-label { -- 2.32.0 From 432f525336a5da1a545546ab40f882f44ac42bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 18 May 2017 16:20:07 +0200 Subject: [PATCH 5/6] systemMonitor: Handle clicks on section title While on 3.24.x only the event section still has a clickable title, it's a generic message list feature in previous versions. It's easy enough to support with a small subclass, so use that instead of the generic baseclass. Fixes: https://gitlab.gnome.org/GNOME/gnome-shell-extensions3 --- extensions/systemMonitor/extension.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js index 1fd01ab..57bdb51 100644 --- a/extensions/systemMonitor/extension.js +++ b/extensions/systemMonitor/extension.js @@ -300,6 +300,22 @@ class MemoryIndicator extends Indicator { } }); +const SystemMonitorSection = GObject.registerClass( +class SystemMonitorSection extends MessageList.MessageListSection { + _init() { + super._init(_('System Monitor')); + } + + _onTitleClicked() { + super._onTitleClicked(); + + let appSys = Shell.AppSystem.get_default(); + let app = appSys.lookup_app('gnome-system-monitor.desktop'); + if (app) + app.open_new_window(-1); + } +}); + const INDICATORS = [CpuIndicator, MemoryIndicator]; class Extension { @@ -312,7 +328,7 @@ class Extension { } enable() { - this._section = new MessageList.MessageListSection(_('System Monitor')); + this._section = new SystemMonitorSection(); for (let i = 0; i < INDICATORS.length; i++) { let indicator = new INDICATORS[i](); -- 2.32.0 From 657155f8f37a8d0ddf7c39dbff954d87202c681e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 18 May 2017 18:00:17 +0200 Subject: [PATCH 6/6] systemMonitor: Provide classic styling The indicator tooltips currently don't work out in classic mode (dark text on dark background), so provide some mode-specific style. Fixes: #4 --- extensions/systemMonitor/classic.css | 6 ++++++ extensions/systemMonitor/meson.build | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 extensions/systemMonitor/classic.css diff --git a/extensions/systemMonitor/classic.css b/extensions/systemMonitor/classic.css new file mode 100644 index 0000000..946863d --- /dev/null +++ b/extensions/systemMonitor/classic.css @@ -0,0 +1,6 @@ +@import url("stylesheet.css"); + +.extension-systemMonitor-indicator-label { + background-color: rgba(237,237,237,0.9); + border: 1px solid #a1a1a1; +} diff --git a/extensions/systemMonitor/meson.build b/extensions/systemMonitor/meson.build index 48504f6..b6548b1 100644 --- a/extensions/systemMonitor/meson.build +++ b/extensions/systemMonitor/meson.build @@ -3,3 +3,7 @@ extension_data += configure_file( output: metadata_name, configuration: metadata_conf ) + +if classic_mode_enabled + extension_data += files('classic.css') +endif -- 2.32.0