Add deferred work system
Previously we had various things watching for notify::mapped so we could be more lazy about updating non-visible actors, but it was fairly ad-hoc. The deferred work system unifies these callbacks, and also adds a timeout so that we don't delay changes arbitrarily; this way we avoid a storm of work if you stay out of the overview for a while, then go in. https://bugzilla.gnome.org/show_bug.cgi?id=603522
This commit is contained in:
parent
b03fa1ebf7
commit
d624db18c5
@ -236,7 +236,7 @@ BaseWellItem.prototype = {
|
|||||||
reactive: true });
|
reactive: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._rerenderGlow));
|
||||||
|
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
this.actor.set_child(box);
|
this.actor.set_child(box);
|
||||||
@ -262,8 +262,7 @@ BaseWellItem.prototype = {
|
|||||||
this._glowBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
this._glowBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
this._nameBox.add_actor(this._glowBox);
|
this._nameBox.add_actor(this._glowBox);
|
||||||
this._glowBox.lower(this._name);
|
this._glowBox.lower(this._name);
|
||||||
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
|
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._queueRerenderGlow));
|
||||||
this._rerenderGlow();
|
|
||||||
|
|
||||||
box.add(nameBox);
|
box.add(nameBox);
|
||||||
|
|
||||||
@ -319,18 +318,11 @@ BaseWellItem.prototype = {
|
|||||||
this.app.disconnect(this._appWindowChangedId);
|
this.app.disconnect(this._appWindowChangedId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMapped: function() {
|
_queueRerenderGlow: function() {
|
||||||
if (!this._queuedGlowRerender)
|
Main.queueDeferredWork(this._workId);
|
||||||
return;
|
|
||||||
this._queuedGlowRerender = false;
|
|
||||||
this._rerenderGlow();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_rerenderGlow: function() {
|
_rerenderGlow: function() {
|
||||||
if (!this.actor.mapped) {
|
|
||||||
this._queuedGlowRerender = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._glowBox.destroy_children();
|
this._glowBox.destroy_children();
|
||||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||||
let windows = this.app.get_windows();
|
let windows = this.app.get_windows();
|
||||||
@ -956,8 +948,7 @@ AppWell.prototype = {
|
|||||||
x_align: Big.BoxAlignment.CENTER });
|
x_align: Big.BoxAlignment.CENTER });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._pendingRedisplay = false;
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
|
||||||
|
|
||||||
this._grid = new WellGrid();
|
this._grid = new WellGrid();
|
||||||
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
||||||
@ -965,12 +956,9 @@ AppWell.prototype = {
|
|||||||
this._tracker = Shell.WindowTracker.get_default();
|
this._tracker = Shell.WindowTracker.get_default();
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
this._appSystem = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._redisplay));
|
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||||
|
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay));
|
this._tracker.connect('app-running-changed', Lang.bind(this, this._queueRedisplay));
|
||||||
this._tracker.connect('app-running-changed', Lang.bind(this, this._redisplay));
|
|
||||||
|
|
||||||
this._redisplay();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_appIdListToHash: function(apps) {
|
_appIdListToHash: function(apps) {
|
||||||
@ -980,20 +968,11 @@ AppWell.prototype = {
|
|||||||
return ids;
|
return ids;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMappedNotify: function() {
|
_queueRedisplay: function () {
|
||||||
let mapped = this.actor.mapped;
|
Main.queueDeferredWork(this._workId);
|
||||||
if (mapped && this._pendingRedisplay)
|
|
||||||
this._redisplay();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_redisplay: function () {
|
_redisplay: function () {
|
||||||
let mapped = this.actor.mapped;
|
|
||||||
if (!mapped) {
|
|
||||||
this._pendingRedisplay = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._pendingRedisplay = false;
|
|
||||||
|
|
||||||
this._grid.removeAll();
|
this._grid.removeAll();
|
||||||
|
|
||||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||||
|
@ -318,7 +318,7 @@ ErrorLog.prototype = {
|
|||||||
this.text = new St.Label();
|
this.text = new St.Label();
|
||||||
this.actor.add(this.text);
|
this.actor.add(this.text);
|
||||||
this.text.clutter_text.line_wrap = true;
|
this.text.clutter_text.line_wrap = true;
|
||||||
this.text.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||||
},
|
},
|
||||||
|
|
||||||
_formatTime: function(d){
|
_formatTime: function(d){
|
||||||
@ -331,8 +331,8 @@ ErrorLog.prototype = {
|
|||||||
+ pad(d.getUTCSeconds())+'Z'
|
+ pad(d.getUTCSeconds())+'Z'
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMappedNotify: function() {
|
_renderText: function() {
|
||||||
if (!(this.actor.mapped && Main._errorLogStack.length > 0))
|
if (!this.actor.mapped)
|
||||||
return;
|
return;
|
||||||
let text = this.text.text;
|
let text = this.text.text;
|
||||||
let stack = Main._getAndClearErrorStack();
|
let stack = Main._getAndClearErrorStack();
|
||||||
|
117
js/ui/main.js
117
js/ui/main.js
@ -425,3 +425,120 @@ function activateWindow(window, time) {
|
|||||||
window.activate(time);
|
window.activate(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - replace this timeout with some system to guess when the user might
|
||||||
|
// be e.g. just reading the screen and not likely to interact.
|
||||||
|
const DEFERRED_TIMEOUT_SECONDS = 20;
|
||||||
|
var _deferredWorkData = {};
|
||||||
|
// Work scheduled for some point in the future
|
||||||
|
var _deferredWorkQueue = [];
|
||||||
|
// Work we need to process before the next redraw
|
||||||
|
var _beforeRedrawQueue = [];
|
||||||
|
// Counter to assign work ids
|
||||||
|
var _deferredWorkSequence = 0;
|
||||||
|
var _deferredTimeoutId = 0;
|
||||||
|
|
||||||
|
function _runDeferredWork(workId) {
|
||||||
|
if (!_deferredWorkData[workId])
|
||||||
|
return;
|
||||||
|
let index = _deferredWorkQueue.indexOf(workId);
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_deferredWorkQueue.splice(index, 1);
|
||||||
|
_deferredWorkData[workId].callback();
|
||||||
|
if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
|
||||||
|
Mainloop.source_remove(_deferredTimeoutId);
|
||||||
|
_deferredTimeoutId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _runAllDeferredWork() {
|
||||||
|
while (_deferredWorkQueue.length > 0)
|
||||||
|
_runDeferredWork(_deferredWorkQueue[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _runBeforeRedrawQueue() {
|
||||||
|
for (let i = 0; i < _beforeRedrawQueue.length; i++) {
|
||||||
|
let workId = _beforeRedrawQueue[i];
|
||||||
|
_runDeferredWork(workId);
|
||||||
|
}
|
||||||
|
_beforeRedrawQueue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _queueBeforeRedraw(workId) {
|
||||||
|
_beforeRedrawQueue.push(workId);
|
||||||
|
if (_beforeRedrawQueue.length == 1) {
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
|
||||||
|
_runBeforeRedrawQueue();
|
||||||
|
return false;
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initializeDeferredWork:
|
||||||
|
* @actor: A #ClutterActor
|
||||||
|
* @callback: Function to invoke to perform work
|
||||||
|
*
|
||||||
|
* This function sets up a callback to be invoked when either the
|
||||||
|
* given actor is mapped, or after some period of time when the machine
|
||||||
|
* is idle. This is useful if your actor isn't always visible on the
|
||||||
|
* screen (for example, all actors in the overview), and you don't want
|
||||||
|
* to consume resources updating if the actor isn't actually going to be
|
||||||
|
* displaying to the user.
|
||||||
|
*
|
||||||
|
* Note that queueDeferredWork is called by default immediately on
|
||||||
|
* initialization as well, under the assumption that new actors
|
||||||
|
* will need it.
|
||||||
|
*
|
||||||
|
* Returns: A string work identifer
|
||||||
|
*/
|
||||||
|
function initializeDeferredWork(actor, callback, props) {
|
||||||
|
// Turn into a string so we can use as an object property
|
||||||
|
let workId = "" + (++_deferredWorkSequence);
|
||||||
|
_deferredWorkData[workId] = { 'actor': actor,
|
||||||
|
'callback': callback };
|
||||||
|
actor.connect('notify::mapped', function () {
|
||||||
|
if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
|
||||||
|
return;
|
||||||
|
_queueBeforeRedraw(workId);
|
||||||
|
});
|
||||||
|
actor.connect('destroy', function() {
|
||||||
|
let index = _deferredWorkQueue.indexOf(workId);
|
||||||
|
if (index >= 0)
|
||||||
|
_deferredWorkQueue.splice(index, 1);
|
||||||
|
delete _deferredWorkData[workId];
|
||||||
|
});
|
||||||
|
queueDeferredWork(workId);
|
||||||
|
return workId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* queueDeferredWork:
|
||||||
|
* @workId: work identifier
|
||||||
|
*
|
||||||
|
* Ensure that the work identified by @workId will be
|
||||||
|
* run on map or timeout. You should call this function
|
||||||
|
* for example when data being displayed by the actor has
|
||||||
|
* changed.
|
||||||
|
*/
|
||||||
|
function queueDeferredWork(workId) {
|
||||||
|
let data = _deferredWorkData[workId];
|
||||||
|
if (!data) {
|
||||||
|
global.logError("invalid work id ", workId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||||
|
_deferredWorkQueue.push(workId);
|
||||||
|
if (data.actor.mapped) {
|
||||||
|
_queueBeforeRedraw(workId);
|
||||||
|
return;
|
||||||
|
} else if (_deferredTimeoutId == 0) {
|
||||||
|
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
||||||
|
_runAllDeferredWork();
|
||||||
|
_deferredTimeoutId = 0;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user