diff --git a/appIcons.js b/appIcons.js index 1ccfbd6..f9cda61 100644 --- a/appIcons.js +++ b/appIcons.js @@ -110,12 +110,13 @@ var taskbarAppIcon = Utils.defineClass({ Extends: AppDisplay.AppIcon, ParentConstrParams: [[0, 'app'], [2]], - _init: function(appInfo, panel, iconParams, previewMenu) { + _init: function(appInfo, panel, iconParams, previewMenu, iconAnimator) { this.dtpPanel = panel; this._nWindows = 0; this.window = appInfo.window; this.isLauncher = appInfo.isLauncher; this._previewMenu = previewMenu; + this.iconAnimator = iconAnimator; this._timeoutsHandler = new Utils.TimeoutsHandler(); diff --git a/panelManager.js b/panelManager.js index ae4a6e3..ccd99c1 100755 --- a/panelManager.js +++ b/panelManager.js @@ -104,6 +104,8 @@ var dtpPanelManager = Utils.defineClass({ ); this._findPanelMenuButtons(p.panelBox).forEach(pmb => this._adjustPanelMenuButton(pmb, p.monitor, panelPosition)); + + p.taskbar.iconAnimator.start(); }); //in 3.32, BoxPointer now inherits St.Widget @@ -278,6 +280,8 @@ var dtpPanelManager = Utils.defineClass({ this.proximityManager.destroy(); this.allPanels.forEach(p => { + p.taskbar.iconAnimator.pause(); + this._findPanelMenuButtons(p.panelBox).forEach(pmb => { if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { pmb.menu._boxPointer._container.disconnect(pmb.menu._boxPointer._dtpGetPreferredHeightId); @@ -605,6 +609,76 @@ var dtpPanelManager = Utils.defineClass({ }, }); +// This class drives long-running icon animations, to keep them running in sync +// with each other. +var IconAnimator = Utils.defineClass({ + Name: 'DashToPanel.IconAnimator', + + _init: function(actor) { + this._count = 0; + this._started = false; + this._animations = { + dance: [], + }; + this._timeline = new Clutter.Timeline({ + duration: 3000, + repeat_count: -1, + }); + + /* Just use the construction property when no need to support 3.36 */ + if (this._timeline.set_actor) + this._timeline.set_actor(actor); + + this._timeline.connect('new-frame', () => { + const progress = this._timeline.get_progress(); + const danceRotation = progress < 1/6 ? 15*Math.sin(progress*24*Math.PI) : 0; + const dancers = this._animations.dance; + for (let i = 0, iMax = dancers.length; i < iMax; i++) { + dancers[i].rotation_angle_z = danceRotation; + } + }); + }, + + destroy: function() { + this._timeline.stop(); + this._timeline = null; + this._animations = null; + }, + + pause: function() { + if (this._started && this._count > 0) { + this._timeline.stop(); + } + this._started = false; + }, + + start: function() { + if (!this._started && this._count > 0) { + this._timeline.start(); + } + this._started = true; + }, + + addAnimation: function(target, name) { + this._animations[name].push(target); + if (this._started && this._count === 0) { + this._timeline.start(); + } + this._count++; + }, + + removeAnimation: function(target, name) { + const index = this._animations[name].indexOf(target); + if (index >= 0) { + this._animations[name].splice(index, 1); + this._count--; + if (this._started && this._count === 0) { + this._timeline.stop(); + } + } + } +}); + function newViewSelectorAnimateIn(oldPage) { if (oldPage) oldPage.hide(); @@ -842,4 +916,4 @@ function _newLookingGlassOpen() { this._resize(); this._oldOpen(); -} \ No newline at end of file +} diff --git a/progress.js b/progress.js index 1e6b43b..33cacff 100644 --- a/progress.js +++ b/progress.js @@ -176,6 +176,7 @@ var AppProgress = Utils.defineClass({ this._countVisible = false; this._progress = 0.0; this._progressVisible = false; + this._urgent = false; this.update(properties); }, @@ -231,6 +232,17 @@ var AppProgress = Utils.defineClass({ } }, + urgent: function() { + return this._urgent; + }, + + setUrgent: function(urgent) { + if (this._urgent != urgent) { + this._urgent = urgent; + this.emit('urgent-changed', this._urgent); + } + }, + setDBusName: function(dbusName) { if (this._dbusName != dbusName) { let oldName = this._dbusName; @@ -246,6 +258,7 @@ var AppProgress = Utils.defineClass({ this.setCountVisible(other.countVisible()); this.setProgress(other.progress()); this.setProgressVisible(other.progressVisible()) + this.setUrgent(other.urgent()); } else { for (let property in other) { if (other.hasOwnProperty(property)) { @@ -257,6 +270,8 @@ var AppProgress = Utils.defineClass({ this.setProgress(other[property].get_double()); } else if (property == 'progress-visible') { this.setProgressVisible(Me.settings.get_boolean('progress-show-bar') && other[property].get_boolean()); + } else if (property == 'urgent') { + this.setUrgent(other[property].get_boolean()); } else { // Not implemented yet } @@ -513,6 +528,7 @@ var ProgressIndicator = Utils.defineClass({ this.toggleNotificationBadge(false); this.setProgress(0); this.toggleProgressOverlay(false); + this.setUrgent(false); } }, @@ -547,6 +563,12 @@ var ProgressIndicator = Utils.defineClass({ (appProgress, value) => { this.toggleProgressOverlay(value); } + ], [ + appProgress, + 'urgent-changed', + (appProgress, value) => { + this.setUrgent(value) + } ]); this.setNotificationBadge(appProgress.count()); @@ -554,5 +576,23 @@ var ProgressIndicator = Utils.defineClass({ this.setProgress(appProgress.progress()); this.toggleProgressOverlay(appProgress.progressVisible()); + this._isUrgent = false; + }, + + setUrgent(urgent) { + const icon = this._source.icon._iconBin; + if (urgent) { + if (!this._isUrgent) { + icon.set_pivot_point(0.5, 0.5); + this._source.iconAnimator.addAnimation(icon, 'dance'); + this._isUrgent = true; + } + } else { + if (this._isUrgent) { + this._source.iconAnimator.removeAnimation(icon, 'dance'); + this._isUrgent = false; + } + icon.rotation_angle_z = 0; + } } }); diff --git a/taskbar.js b/taskbar.js index 60bcb17..801b5be 100644 --- a/taskbar.js +++ b/taskbar.js @@ -46,6 +46,7 @@ const Workspace = imports.ui.workspace; const Me = imports.misc.extensionUtils.getCurrentExtension(); const AppIcons = Me.imports.appIcons; const Panel = Me.imports.panel; +const PanelManager = Me.imports.panelManager; const Utils = Me.imports.utils; const WindowPreview = Me.imports.windowPreview; @@ -241,6 +242,8 @@ var taskbar = Utils.defineClass({ this._appSystem = Shell.AppSystem.get_default(); + this.iconAnimator = new PanelManager.IconAnimator(this.dtpPanel.panel.actor); + this._signalsHandler.add( [ this.dtpPanel.panel.actor, @@ -351,6 +354,8 @@ var taskbar = Utils.defineClass({ }, destroy: function() { + this.iconAnimator.destroy(); + this._signalsHandler.destroy(); this._signalsHandler = 0; @@ -549,7 +554,8 @@ var taskbar = Utils.defineClass({ showLabel: false, isDraggable: !Me.settings.get_boolean('taskbar-locked'), }, - this.previewMenu + this.previewMenu, + this.iconAnimator ); if (appIcon._draggable) {