diff options
Diffstat (limited to 'js/ui')
-rw-r--r-- | js/ui/windowManager.js | 3 | ||||
-rw-r--r-- | js/ui/wobbly.js | 130 |
2 files changed, 133 insertions, 0 deletions
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 101f9a8d7..397af8958 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -17,6 +17,7 @@ const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const Tweener = imports.ui.tweener; const WindowMenu = imports.ui.windowMenu; +const Wobbly = imports.ui.wobbly; const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; const MAXIMIZE_WINDOW_ANIMATION_TIME = 0.15; @@ -811,6 +812,8 @@ const WindowManager = new Lang.Class({ gesture = new AppSwitchAction(); gesture.connect('activated', Lang.bind(this, this._switchApp)); global.stage.add_action(gesture); + + this._wobblyWindows = new Wobbly.WobblyWindowManager(); }, _lookupIndex: function (windows, metaWindow) { diff --git a/js/ui/wobbly.js b/js/ui/wobbly.js new file mode 100644 index 000000000..ad53fc66f --- /dev/null +++ b/js/ui/wobbly.js @@ -0,0 +1,130 @@ +// -*- 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(); + }, +}); |