gnome-shell/js/ui/wobbly.js
Jasper St. Pierre e7f82c66de wobbly
2014-09-03 10:58:01 -07:00

131 lines
3.7 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
function clampAbs(v, cap) {
if (v > cap)
v = cap;
if (v < -cap)
v = -cap;
return v;
}
const ActorWobbler = new Lang.Class({
Name: 'ActorWobbler',
_init: function(actor) {
this._actor = actor;
this._effect = new Shell.WobblyEffect();
this._actor.add_effect(this._effect);
this._actor._wobbler = this;
this._currentBend = 0;
this._currentHeightOffset = 0;
this._running = false;
this._allocationChangedId = this._actor.connect('allocation-changed', Lang.bind(this, this._allocationChanged));
this._timeline = new Clutter.Timeline({ duration: 100, repeat_count: -1 });
this._timeline.connect('new-frame', Lang.bind(this, this._newFrame));
this._timeline.start();
},
start: function() {
this._running = true;
},
stop: function() {
this._running = false;
},
_destroy: function() {
this._timeline.run_dispose();
this._timeline = null;
this._actor.disconnect(this._allocationChangedId);
this._actor.scale_y = 1.0;
this._actor.remove_effect(this._effect);
this._actor._wobbler = null;
},
_newFrame: function() {
this._step();
},
_step: function() {
const DAMPEN = 0.8;
this._currentBend *= DAMPEN;
if (Math.abs(this._currentBend) < 1)
this._currentBend = 0;
this._currentHeightOffset *= DAMPEN;
if (Math.abs(this._currentHeightOffset) < 1)
this._currentHeightOffset = 0;
// Cap the bend to a 100px shift.
const BEND_CAP = 50;
this._currentBend = clampAbs(this._currentBend, BEND_CAP);
this._effect.set_bend_x(this._currentBend);
// Cap the height change to 25px in either direction.
const HEIGHT_OFFSET_CAP = 25;
this._currentHeightOffset = clampAbs(this._currentHeightOffset, HEIGHT_OFFSET_CAP);
let [minHeight, natHeight] = this._actor.get_preferred_height(-1);
let scale = (natHeight + this._currentHeightOffset) / natHeight;
this._actor.scale_y = scale;
if (!this._running && this._currentBend == 0 && this._currentHeightOffset == 0)
this._destroy();
},
_allocationChanged: function(actor, box, flags) {
if (!this._running)
return;
if (this._oldX) {
let deltaX = box.x1 - this._oldX;
// Every 2px the user moves the window, we bend it by 1px.
this._currentBend -= deltaX / 2;
}
if (this._oldY) {
let deltaY = box.y1 - this._oldY;
// Every 2px the user moves the window, we scale it by 1px.
this._currentHeightOffset -= deltaY / 2;
}
this._oldX = box.x1;
this._oldY = box.y1;
},
});
const WobblyWindowManager = new Lang.Class({
Name: 'WobblyWindowManager',
_init: function() {
global.display.connect('grab-op-begin', Lang.bind(this, this._grabOpBegin));
global.display.connect('grab-op-end', Lang.bind(this, this._grabOpEnd));
},
_grabOpBegin: function(display, screen, window, op) {
if (op != Meta.GrabOp.MOVING)
return;
let actor = window.get_compositor_private();
if (!actor._wobbler)
new ActorWobbler(actor);
actor._wobbler.start();
},
_grabOpEnd: function(display, screen, window, op) {
if (!window)
return;
let actor = window.get_compositor_private();
if (actor._wobbler)
actor._wobbler.stop();
},
});