2582d16ca7
Any symbols (including class properties) that should be visible outside the module it's defined in need to be defined as global. For now gjs still allows the access for 'const', but get rid of the warnings spill now by changing it. https://bugzilla.gnome.org/show_bug.cgi?id=785084
230 lines
6.3 KiB
JavaScript
230 lines
6.3 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/*
|
|
* Copyright 2011 Red Hat, Inc
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* In order for transformation animations to look good, they need to be
|
|
* incremental and have some order to them (e.g., fade out hidden items,
|
|
* then shrink to close the void left over). Chaining animations in this way can
|
|
* be error-prone and wordy using just Tweener callbacks.
|
|
*
|
|
* The classes in this file help with this:
|
|
*
|
|
* - Task. encapsulates schedulable work to be run in a specific scope.
|
|
*
|
|
* - ConsecutiveBatch. runs a series of tasks in order and completes
|
|
* when the last in the series finishes.
|
|
*
|
|
* - ConcurrentBatch. runs a set of tasks at the same time and completes
|
|
* when the last to finish completes.
|
|
*
|
|
* - Hold. prevents a batch from completing the pending task until
|
|
* the hold is released.
|
|
*
|
|
* The tasks associated with a batch are specified in a list at batch
|
|
* construction time as either task objects or plain functions.
|
|
* Batches are task objects, themselves, so they can be nested.
|
|
*
|
|
* These classes aren't specific to GDM, but were found to be unintuitive and so
|
|
* are not used elsewhere. These APIs may ultimately get dropped entirely and
|
|
* replaced by something else.
|
|
*/
|
|
|
|
const Lang = imports.lang;
|
|
const Signals = imports.signals;
|
|
|
|
var Task = new Lang.Class({
|
|
Name: 'Task',
|
|
|
|
_init: function(scope, handler) {
|
|
if (scope)
|
|
this.scope = scope;
|
|
else
|
|
this.scope = this;
|
|
|
|
this.handler = handler;
|
|
},
|
|
|
|
run: function() {
|
|
if (this.handler)
|
|
return this.handler.call(this.scope);
|
|
|
|
return null;
|
|
},
|
|
});
|
|
Signals.addSignalMethods(Task.prototype);
|
|
|
|
var Hold = new Lang.Class({
|
|
Name: 'Hold',
|
|
Extends: Task,
|
|
|
|
_init: function() {
|
|
this.parent(this, function () {
|
|
return this;
|
|
});
|
|
|
|
this._acquisitions = 1;
|
|
},
|
|
|
|
acquire: function() {
|
|
if (this._acquisitions <= 0)
|
|
throw new Error("Cannot acquire hold after it's been released");
|
|
this._acquisitions++;
|
|
},
|
|
|
|
acquireUntilAfter: function(hold) {
|
|
if (!hold.isAcquired())
|
|
return;
|
|
|
|
this.acquire();
|
|
let signalId = hold.connect('release', Lang.bind(this, function() {
|
|
hold.disconnect(signalId);
|
|
this.release();
|
|
}));
|
|
},
|
|
|
|
release: function() {
|
|
this._acquisitions--;
|
|
|
|
if (this._acquisitions == 0)
|
|
this.emit('release');
|
|
},
|
|
|
|
isAcquired: function() {
|
|
return this._acquisitions > 0;
|
|
}
|
|
});
|
|
Signals.addSignalMethods(Hold.prototype);
|
|
|
|
var Batch = new Lang.Class({
|
|
Name: 'Batch',
|
|
Extends: Task,
|
|
|
|
_init: function(scope, tasks) {
|
|
this.parent();
|
|
|
|
this.tasks = [];
|
|
|
|
for (let i = 0; i < tasks.length; i++) {
|
|
let task;
|
|
|
|
if (tasks[i] instanceof Task) {
|
|
task = tasks[i];
|
|
} else if (typeof tasks[i] == 'function') {
|
|
task = new Task(scope, tasks[i]);
|
|
} else {
|
|
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
|
|
}
|
|
|
|
this.tasks.push(task);
|
|
}
|
|
},
|
|
|
|
process: function() {
|
|
throw new Error('Not implemented');
|
|
},
|
|
|
|
runTask: function() {
|
|
if (!(this._currentTaskIndex in this.tasks)) {
|
|
return null;
|
|
}
|
|
|
|
return this.tasks[this._currentTaskIndex].run();
|
|
},
|
|
|
|
_finish: function() {
|
|
this.hold.release();
|
|
},
|
|
|
|
nextTask: function() {
|
|
this._currentTaskIndex++;
|
|
|
|
// if the entire batch of tasks is finished, release
|
|
// the hold and notify anyone waiting on the batch
|
|
if (this._currentTaskIndex >= this.tasks.length) {
|
|
this._finish();
|
|
return;
|
|
}
|
|
|
|
this.process();
|
|
},
|
|
|
|
_start: function() {
|
|
// acquire a hold to get released when the entire
|
|
// batch of tasks is finished
|
|
this.hold = new Hold();
|
|
this._currentTaskIndex = 0;
|
|
this.process();
|
|
},
|
|
|
|
run: function() {
|
|
this._start();
|
|
|
|
// hold may be destroyed at this point
|
|
// if we're already done running
|
|
return this.hold;
|
|
},
|
|
|
|
cancel: function() {
|
|
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
|
|
}
|
|
});
|
|
Signals.addSignalMethods(Batch.prototype);
|
|
|
|
var ConcurrentBatch = new Lang.Class({
|
|
Name: 'ConcurrentBatch',
|
|
Extends: Batch,
|
|
|
|
process: function() {
|
|
let hold = this.runTask();
|
|
|
|
if (hold) {
|
|
this.hold.acquireUntilAfter(hold);
|
|
}
|
|
|
|
// Regardless of the state of the just run task,
|
|
// fire off the next one, so all the tasks can run
|
|
// concurrently.
|
|
this.nextTask();
|
|
}
|
|
});
|
|
Signals.addSignalMethods(ConcurrentBatch.prototype);
|
|
|
|
var ConsecutiveBatch = new Lang.Class({
|
|
Name: 'ConsecutiveBatch',
|
|
Extends: Batch,
|
|
|
|
process: function() {
|
|
let hold = this.runTask();
|
|
|
|
if (hold && hold.isAcquired()) {
|
|
// This task is inhibiting the batch. Wait on it
|
|
// before processing the next one.
|
|
let signalId = hold.connect('release',
|
|
Lang.bind(this, function() {
|
|
hold.disconnect(signalId);
|
|
this.nextTask();
|
|
}));
|
|
return;
|
|
} else {
|
|
// This task finished, process the next one
|
|
this.nextTask();
|
|
}
|
|
}
|
|
});
|
|
Signals.addSignalMethods(ConsecutiveBatch.prototype);
|