[gnome-shell] screenshot: move to a separate interface



commit 57bd43baf3734b7b0993781103e68893d5400387
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Mon Dec 10 10:26:31 2012 -0500

    screenshot: move to a separate interface
    
    Since we're breaking API already, take this as an occasion to use a
    separate interface for all the screenshot-related methods. The interface
    name is org.gnome.Shell.Screenshot.
    Internally, move all the related code to screenshot.js.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=688004

 js/Makefile.am      |    3 +-
 js/ui/flashspot.js  |   45 --------
 js/ui/screenshot.js |  281 +++++++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/selectArea.js |  134 ------------------------
 js/ui/shellDBus.js  |  143 +--------------------------
 5 files changed, 284 insertions(+), 322 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 7606f32..29187da 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -49,7 +49,6 @@ nobase_dist_js_DATA = 	\
 	ui/extensionSystem.js	\
 	ui/extensionDownloader.js \
 	ui/environment.js	\
-	ui/flashspot.js		\
 	ui/ibusCandidatePopup.js\
 	ui/grabHelper.js	\
 	ui/iconGrid.js		\
@@ -74,11 +73,11 @@ nobase_dist_js_DATA = 	\
 	ui/popupMenu.js		\
 	ui/remoteSearch.js	\
 	ui/runDialog.js		\
+	ui/screenshot.js	\
         ui/screenShield.js	\
 	ui/scripting.js		\
 	ui/search.js		\
 	ui/searchDisplay.js	\
-	ui/selectArea.js	\
 	ui/shellDBus.js		\
 	ui/status/accessibility.js	\
 	ui/status/keyboard.js	\
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
new file mode 100644
index 0000000..12cf186
--- /dev/null
+++ b/js/ui/screenshot.js
@@ -0,0 +1,281 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const St = imports.gi.St;
+
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+
+const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
+<method name="ScreenshotArea">
+    <arg type="i" direction="in" name="x"/>
+    <arg type="i" direction="in" name="y"/>
+    <arg type="i" direction="in" name="width"/>
+    <arg type="i" direction="in" name="height"/>
+    <arg type="b" direction="in" name="flash"/>
+    <arg type="s" direction="in" name="filename"/>
+    <arg type="b" direction="out" name="success"/>
+    <arg type="s" direction="out" name="filename_used"/>
+</method>
+<method name="ScreenshotWindow">
+    <arg type="b" direction="in" name="include_frame"/>
+    <arg type="b" direction="in" name="include_cursor"/>
+    <arg type="b" direction="in" name="flash"/>
+    <arg type="s" direction="in" name="filename"/>
+    <arg type="b" direction="out" name="success"/>
+    <arg type="s" direction="out" name="filename_used"/>
+</method>
+<method name="Screenshot">
+    <arg type="b" direction="in" name="include_cursor"/>
+    <arg type="b" direction="in" name="flash"/>
+    <arg type="s" direction="in" name="filename"/>
+    <arg type="b" direction="out" name="success"/>
+    <arg type="s" direction="out" name="filename_used"/>
+</method>
+<method name="SelectArea">
+    <arg type="i" direction="out" name="x"/>
+    <arg type="i" direction="out" name="y"/>
+    <arg type="i" direction="out" name="width"/>
+    <arg type="i" direction="out" name="height"/>
+</method>
+<method name="FlashArea">
+    <arg type="i" direction="in" name="x"/>
+    <arg type="i" direction="in" name="y"/>
+    <arg type="i" direction="in" name="width"/>
+    <arg type="i" direction="in" name="height"/>
+</method>
+</interface>;
+
+const ScreenshotService = new Lang.Class({
+    Name: 'ScreenshotService',
+
+    _init: function() {
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
+
+        Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
+    },
+
+    _onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
+        if (flash && result) {
+            let flashspot = new Flashspot(area);
+            flashspot.fire();
+        }
+
+        let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
+        invocation.return_value(retval);
+    },
+
+    ScreenshotAreaAsync : function (params, invocation) {
+        let [x, y, width, height, flash, filename, callback] = params;
+        let screenshot = new Shell.Screenshot();
+        screenshot.screenshot_area (x, y, width, height, filename,
+                                Lang.bind(this, this._onScreenshotComplete,
+                                          flash, invocation));
+    },
+
+    ScreenshotWindowAsync : function (params, invocation) {
+        let [include_frame, include_cursor, flash, filename] = params;
+        let screenshot = new Shell.Screenshot();
+        screenshot.screenshot_window (include_frame, include_cursor, filename,
+                                  Lang.bind(this, this._onScreenshotComplete,
+                                            flash, invocation));
+    },
+
+    ScreenshotAsync : function (params, invocation) {
+        let [include_cursor, flash, filename] = params;
+        let screenshot = new Shell.Screenshot();
+        screenshot.screenshot(include_cursor, filename,
+                          Lang.bind(this, this._onScreenshotComplete,
+                                    flash, invocation));
+    },
+
+    SelectAreaAsync: function (params, invocation) {
+        let selectArea = new SelectArea();
+        selectArea.show();
+        selectArea.connect('finished', Lang.bind(this,
+            function(selectArea, areaRectangle) {
+                if (areaRectangle) {
+                    let retval = GLib.Variant.new('(iiii)',
+                        [areaRectangle.x, areaRectangle.y,
+                         areaRectangle.width, areaRectangle.height]);
+                    invocation.return_value(retval);
+                } else {
+                    invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
+                        "Operation was cancelled");
+                }
+            }));
+    },
+
+    FlashArea: function(x, y, width, height) {
+        let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
+        flashspot.fire();
+    }
+});
+
+const SelectArea = new Lang.Class({
+    Name: 'SelectArea',
+
+    _init: function() {
+        this._startX = -1;
+        this._startY = -1;
+        this._lastX = 0;
+        this._lastY = 0;
+
+        this._initRubberbandColors();
+
+        this._group = new St.Widget({ visible: false,
+                                      reactive: true,
+                                      x: 0,
+                                      y: 0 });
+        Main.uiGroup.add_actor(this._group);
+
+        this._group.connect('button-press-event',
+                            Lang.bind(this, this._onButtonPress));
+        this._group.connect('button-release-event',
+                            Lang.bind(this, this._onButtonRelease));
+        this._group.connect('key-press-event',
+                            Lang.bind(this, this._onKeyPress));
+        this._group.connect('motion-event',
+                            Lang.bind(this, this._onMotionEvent));
+
+        let constraint = new Clutter.BindConstraint({ source: global.stage,
+                                                      coordinate: Clutter.BindCoordinate.ALL });
+        this._group.add_constraint(constraint);
+
+        this._rubberband = new Clutter.Rectangle({ color: this._background,
+                                                   has_border: true,
+                                                   border_width: 1,
+                                                   border_color: this._border });
+        this._group.add_actor(this._rubberband);
+    },
+
+    show: function() {
+        if (!Main.pushModal(this._group) || this._group.visible)
+            return;
+
+        global.set_cursor(Shell.Cursor.CROSSHAIR);
+        this._group.visible = true;
+    },
+
+    _initRubberbandColors: function() {
+        function colorFromRGBA(rgba) {
+            return new Clutter.Color({ red: rgba.red * 255,
+                                       green: rgba.green * 255,
+                                       blue: rgba.blue * 255,
+                                       alpha: rgba.alpha * 255 });
+        }
+
+        let path = new Gtk.WidgetPath();
+        path.append_type(Gtk.IconView);
+
+        let context = new Gtk.StyleContext();
+        context.set_path(path);
+        context.add_class('rubberband');
+
+        this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
+        this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
+    },
+
+    _getGeometry: function() {
+        return { x: Math.min(this._startX, this._lastX),
+                 y: Math.min(this._startY, this._lastY),
+                 width: Math.abs(this._startX - this._lastX),
+                 height: Math.abs(this._startY - this._lastY) };
+    },
+
+    _onKeyPress: function(actor, event) {
+        if (event.get_key_symbol() == Clutter.Escape)
+            this._destroy(null, false);
+
+        return;
+    },
+
+    _onMotionEvent: function(actor, event) {
+        if (this._startX == -1 || this._startY == -1)
+            return false;
+
+        [this._lastX, this._lastY] = event.get_coords();
+        let geometry = this._getGeometry();
+
+        this._rubberband.set_position(geometry.x, geometry.y);
+        this._rubberband.set_size(geometry.width, geometry.height);
+
+        return false;
+    },
+
+    _onButtonPress: function(actor, event) {
+        [this._startX, this._startY] = event.get_coords();
+        this._rubberband.set_position(this._startX, this._startY);
+
+        return false;
+    },
+
+    _onButtonRelease: function(actor, event) {
+        this._destroy(this._getGeometry(), true);
+        return false;
+    },
+
+    _destroy: function(geometry, fade) {
+        Tweener.addTween(this._group,
+                         { opacity: 0,
+                           time: fade ? 0.2 : 0,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   Main.popModal(this._group);
+                                   this._group.destroy();
+                                   global.unset_cursor();
+
+                                   this.emit('finished', geometry);
+                               })
+                         });
+    }
+});
+Signals.addSignalMethods(SelectArea.prototype);
+
+const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
+
+const Flashspot = new Lang.Class({
+    Name: 'Flashspot',
+    Extends: Lightbox.Lightbox,
+
+    _init: function(area) {
+        this.parent(Main.uiGroup, { inhibitEvents: true,
+                                    width: area.width,
+                                    height: area.height });
+
+        this.actor.style_class = 'flashspot';
+        this.actor.set_position(area.x, area.y);
+    },
+
+    fire: function() {
+        this.actor.opacity = 0;
+        Tweener.addTween(this.actor,
+                         { opacity: 255,
+                           time: FLASHSPOT_ANIMATION_TIME,
+                           transition: 'linear',
+                           onComplete: Lang.bind(this, this._onFireShowComplete)
+                         });
+        this.actor.show();
+    },
+
+    _onFireShowComplete: function() {
+        Tweener.addTween(this.actor,
+                         { opacity: 0,
+                           time: FLASHSPOT_ANIMATION_TIME,
+                           transition: 'linear',
+                           onComplete: Lang.bind(this, function() {
+                               this.destroy();
+                           })
+                         });
+    }
+});
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index f2d91a9..97ffe41 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -9,9 +9,8 @@ const Config = imports.misc.config;
 const ExtensionSystem = imports.ui.extensionSystem;
 const ExtensionDownloader = imports.ui.extensionDownloader;
 const ExtensionUtils = imports.misc.extensionUtils;
-const Flashspot = imports.ui.flashspot;
 const Main = imports.ui.main;
-const SelectArea = imports.ui.selectArea;
+const Screenshot = imports.ui.screenshot;
 
 const GnomeShellIface = <interface name="org.gnome.Shell">
 <method name="Eval">
@@ -19,43 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
     <arg type="b" direction="out" name="success" />
     <arg type="s" direction="out" name="result" />
 </method>
-<method name="ScreenshotArea">
-    <arg type="i" direction="in" name="x"/>
-    <arg type="i" direction="in" name="y"/>
-    <arg type="i" direction="in" name="width"/>
-    <arg type="i" direction="in" name="height"/>
-    <arg type="b" direction="in" name="flash"/>
-    <arg type="s" direction="in" name="filename"/>
-    <arg type="b" direction="out" name="success"/>
-    <arg type="s" direction="out" name="filename_used"/>
-</method>
-<method name="ScreenshotWindow">
-    <arg type="b" direction="in" name="include_frame"/>
-    <arg type="b" direction="in" name="include_cursor"/>
-    <arg type="b" direction="in" name="flash"/>
-    <arg type="s" direction="in" name="filename"/>
-    <arg type="b" direction="out" name="success"/>
-    <arg type="s" direction="out" name="filename_used"/>
-</method>
-<method name="Screenshot">
-    <arg type="b" direction="in" name="include_cursor"/>
-    <arg type="b" direction="in" name="flash"/>
-    <arg type="s" direction="in" name="filename"/>
-    <arg type="b" direction="out" name="success"/>
-    <arg type="s" direction="out" name="filename_used"/>
-</method>
-<method name="SelectArea">
-    <arg type="i" direction="out" name="x"/>
-    <arg type="i" direction="out" name="y"/>
-    <arg type="i" direction="out" name="width"/>
-    <arg type="i" direction="out" name="height"/>
-</method>
-<method name="FlashArea">
-    <arg type="i" direction="in" name="x"/>
-    <arg type="i" direction="in" name="y"/>
-    <arg type="i" direction="in" name="width"/>
-    <arg type="i" direction="in" name="height"/>
-</method>
 <property name="Mode" type="s" access="read" />
 <property name="OverviewActive" type="b" access="readwrite" />
 <property name="ShellVersion" type="s" access="read" />
@@ -86,6 +48,7 @@ const GnomeShell = new Lang.Class({
         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
 
         this._extensionsSerivce = new GnomeShellExtensions();
+        this._screenshotService = new Screenshot.ScreenshotService();
     },
 
     /**
@@ -121,108 +84,6 @@ const GnomeShell = new Lang.Class({
         return [success, returnValue];
     },
 
-    _onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
-        if (flash && result) {
-            let flashspot = new Flashspot.Flashspot(area);
-            flashspot.fire();
-        }
-
-        let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
-        invocation.return_value(retval);
-    },
-
-    /**
-     * ScreenshotArea:
-     * @x: The X coordinate of the area
-     * @y: The Y coordinate of the area
-     * @width: The width of the area
-     * @height: The height of the area
-     * @flash: Whether to flash the area or not
-     * @filename: The filename for the screenshot
-     *
-     * Takes a screenshot of the passed in area and saves it
-     * in @filename as png image, it returns a boolean
-     * indicating whether the operation was successful or not.
-     * @filename can either be an absolute path or a basename, in
-     * which case the screenshot will be saved in the $XDG_PICTURES_DIR
-     * or the home directory if it doesn't exist.
-     *
-     */
-    ScreenshotAreaAsync : function (params, invocation) {
-        let [x, y, width, height, flash, filename, callback] = params;
-        let screenshot = new Shell.Screenshot();
-        screenshot.screenshot_area (x, y, width, height, filename,
-                                Lang.bind(this, this._onScreenshotComplete,
-                                          flash, invocation));
-    },
-
-    /**
-     * ScreenshotWindow:
-     * @include_frame: Whether to include the frame or not
-     * @include_cursor: Whether to include the cursor image or not
-     * @flash: Whether to flash the window area or not
-     * @filename: The filename for the screenshot
-     *
-     * Takes a screenshot of the focused window (optionally omitting the frame)
-     * and saves it in @filename as png image, it returns a boolean
-     * indicating whether the operation was successful or not.
-     * @filename can either be an absolute path or a basename, in
-     * which case the screenshot will be saved in the $XDG_PICTURES_DIR
-     * or the home directory if it doesn't exist.
-     *
-     */
-    ScreenshotWindowAsync : function (params, invocation) {
-        let [include_frame, include_cursor, flash, filename] = params;
-        let screenshot = new Shell.Screenshot();
-        screenshot.screenshot_window (include_frame, include_cursor, filename,
-                                  Lang.bind(this, this._onScreenshotComplete,
-                                            flash, invocation));
-    },
-
-    /**
-     * Screenshot:
-     * @filename: The filename for the screenshot
-     * @include_cursor: Whether to include the cursor image or not
-     * @flash: Whether to flash the screen or not
-     *
-     * Takes a screenshot of the whole screen and saves it
-     * in @filename as png image, it returns a boolean
-     * indicating whether the operation was successful or not.
-     * @filename can either be an absolute path or a basename, in
-     * which case the screenshot will be saved in the $XDG_PICTURES_DIR
-     * or the home directory if it doesn't exist.
-     *
-     */
-    ScreenshotAsync : function (params, invocation) {
-        let [include_cursor, flash, filename] = params;
-        let screenshot = new Shell.Screenshot();
-        screenshot.screenshot(include_cursor, filename,
-                          Lang.bind(this, this._onScreenshotComplete,
-                                    flash, invocation));
-    },
-
-    SelectAreaAsync: function (params, invocation) {
-        let selectArea = new SelectArea.SelectArea();
-        selectArea.show();
-        selectArea.connect('finished', Lang.bind(this,
-            function(selectArea, areaRectangle) {
-                if (areaRectangle) {
-                    let retval = GLib.Variant.new('(iiii)',
-                        [areaRectangle.x, areaRectangle.y,
-                         areaRectangle.width, areaRectangle.height]);
-                    invocation.return_value(retval);
-                } else {
-                    invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
-                        "Operation was cancelled");
-                }
-            }));
-    },
-
-    FlashArea: function(x, y, width, height) {
-        let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
-        flashspot.fire();
-    },
-
     Mode: global.session_mode,
 
     get OverviewActive() {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]