diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 83a1f2eae..3515ba885 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -80,6 +80,7 @@
ui/shellDBus.js
ui/shellEntry.js
ui/shellMountOperation.js
+ ui/showOverviewAction.js
ui/slider.js
ui/switcherPopup.js
ui/tweener.js
diff --git a/js/ui/showOverviewAction.js b/js/ui/showOverviewAction.js
new file mode 100644
index 000000000..1b6c1f634
--- /dev/null
+++ b/js/ui/showOverviewAction.js
@@ -0,0 +1,66 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Signals = imports.signals;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Clutter = imports.gi.Clutter;
+
+const ShowOverviewAction = new Lang.Class({
+ Name: 'ShowOverviewAction',
+ Extends: Clutter.GestureAction,
+
+ _init : function() {
+ this.parent();
+ this.set_n_touch_points (3);
+ global.display.connect('grab-op-begin', Lang.bind(this, this.cancel));
+ global.display.connect('grab-op-end', Lang.bind(this, this.cancel));
+ },
+
+ vfunc_gesture_prepare : function(action, actor) {
+ return this.get_n_current_points() == this.get_n_touch_points();
+ },
+
+ _getBoundingRect : function(motion) {
+ let minX, minY, maxX, maxY;
+
+ for (let i = 0; i < this.get_n_current_points(); i++) {
+ let x, y;
+
+ if (motion == true) {
+ [x, y] = this.get_motion_coords(i);
+ } else {
+ [x, y] = this.get_press_coords(i);
+ }
+
+ if (i == 0) {
+ minX = maxX = x;
+ minY = maxY = y;
+ } else {
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x);
+ maxY = Math.max(maxY, y);
+ }
+ }
+
+ return new Meta.Rectangle({ x: minX,
+ y: minY,
+ width: maxX - minX,
+ height: maxY - minY });
+ },
+
+ vfunc_gesture_begin : function(action, actor) {
+ this._initialRect = this._getBoundingRect(false);
+ return true;
+ },
+
+ vfunc_gesture_end : function(action, actor) {
+ let rect = this._getBoundingRect(true);
+ let oldArea = this._initialRect.width * this._initialRect.height;
+ let newArea = rect.width * rect.height;
+ let areaDiff = newArea / oldArea;
+
+ this.emit('activated', areaDiff);
+ }
+});
+Signals.addSignalMethods(ShowOverviewAction.prototype);