[gnome-shell] runDialog: popModal before running the command



commit a40daa3c22eb4a91d69a8554848c596d522f850a
Author: Dan Winship <danw gnome org>
Date:   Fri Mar 18 11:28:18 2011 -0400

    runDialog: popModal before running the command
    
    If you run a command from Alt+F2 that tries to get a server grab (eg,
    xmag), it will fail if it starts up before the run dialog is finished
    hiding.
    
    Additionally, the run dialog currently stays focused while it is
    fading out, potentially stealing keystrokes (or causing the user to
    accidentally launch two copies of a program).
    
    Change ModalDialog.close() to call popModal() immediately
    
    Add a ModalDialog.popModal method, and call that before running the
    RunDialog command. If the command succeeds, close the dialog as
    before. If it fails, call ModalDialog.pushModal() to put things back
    to normal before displaying the error.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=644857

 js/ui/modalDialog.js |   62 ++++++++++++++++++++++++++++++++++++++-----------
 js/ui/runDialog.js   |    9 +++++-
 2 files changed, 55 insertions(+), 16 deletions(-)
---
diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js
index 56bc1a2..cba0b72 100644
--- a/js/ui/modalDialog.js
+++ b/js/ui/modalDialog.js
@@ -39,6 +39,7 @@ ModalDialog.prototype = {
         params = Params.parse(params, { styleClass: null });
 
         this.state = State.CLOSED;
+        this._hasModal = false;
 
         this._group = new St.Group({ visible: false,
                                      x: 0,
@@ -46,6 +47,7 @@ ModalDialog.prototype = {
         Main.uiGroup.add_actor(this._group);
         global.focus_manager.add_group(this._group);
         this._initialKeyFocus = this._group;
+        this._savedKeyFocus = null;
 
         this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
 
@@ -60,12 +62,18 @@ ModalDialog.prototype = {
         this._group.add_actor(this._backgroundBin);
         this._lightbox.highlight(this._backgroundBin);
 
+        this._backgroundStack = new Shell.Stack();
+        this._backgroundBin.child = this._backgroundStack;
+
+        this._eventBlocker = new Clutter.Group({ reactive: true });
+        this._backgroundStack.add_actor(this._eventBlocker);
+
         this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
                                                 vertical:    true });
         if (params.styleClass != null) {
             this._dialogLayout.add_style_class_name(params.styleClass);
         }
-        this._backgroundBin.child = this._dialogLayout;
+        this._backgroundStack.add_actor(this._dialogLayout);
 
         this.contentLayout = new St.BoxLayout({ vertical: true });
         this._dialogLayout.add(this.contentLayout,
@@ -148,7 +156,6 @@ ModalDialog.prototype = {
         this._lightbox.show();
         this._group.opacity = 0;
         this._group.show();
-        this._initialKeyFocus.grab_key_focus();
         Tweener.addTween(this._group,
                          { opacity: 255,
                            time: OPEN_AND_CLOSE_TIME,
@@ -169,7 +176,7 @@ ModalDialog.prototype = {
         if (this.state == State.OPENED || this.state == State.OPENING)
             return true;
 
-        if (!Main.pushModal(this._group, timestamp))
+        if (!this.pushModal(timestamp))
             return false;
 
         this._fadeOpen();
@@ -180,14 +187,8 @@ ModalDialog.prototype = {
         if (this.state == State.CLOSED || this.state == State.CLOSING)
             return;
 
-        let needsPopModal;
-
-        if (this.state == State.OPENED || this.state == State.OPENING)
-            needsPopModal = true;
-        else
-            needsPopModal = false;
-
         this.state = State.CLOSING;
+        this.popModal(timestamp);
 
         Tweener.addTween(this._group,
                          { opacity: 0,
@@ -197,13 +198,46 @@ ModalDialog.prototype = {
                                function() {
                                    this.state = State.CLOSED;
                                    this._group.hide();
-
-                                   if (needsPopModal)
-                                       Main.popModal(this._group, timestamp);
                                })
                          });
     },
 
+    // Drop modal status without closing the dialog; this makes the
+    // dialog insensitive as well, so it needs to be followed shortly
+    // by either a close() or a pushModal()
+    popModal: function(timestamp) {
+        if (!this._hasModal)
+            return;
+
+        let focus = global.stage.key_focus;
+        if (focus && this._group.contains(focus))
+            this._savedKeyFocus = focus;
+        else
+            this._savedKeyFocus = null;
+        Main.popModal(this._group, timestamp);
+        global.gdk_screen.get_display().sync();
+        this._hasModal = false;
+
+        this._eventBlocker.raise_top();
+    },
+
+    pushModal: function (timestamp) {
+        if (this._hasModal)
+            return true;
+        if (!Main.pushModal(this._group, timestamp))
+            return false;
+
+        this._hasModal = true;
+        if (this._savedKeyFocus) {
+            this._savedKeyFocus.grab_key_focus();
+            this._savedKeyFocus = null;
+        } else
+            this._initialKeyFocus.grab_key_focus();
+
+        this._eventBlocker.lower_bottom();
+        return true;
+    },
+
     // This method is like close, but fades the dialog out much slower,
     // and leaves the lightbox in place. Once in the faded out state,
     // the dialog can be brought back by an open call, or the lightbox
@@ -222,6 +256,7 @@ ModalDialog.prototype = {
         if (this.state == State.FADED_OUT)
             return;
 
+        this.popModal(timestamp);
         Tweener.addTween(this._dialogLayout,
                          { opacity: 0,
                            time:    FADE_OUT_DIALOG_TIME,
@@ -229,7 +264,6 @@ ModalDialog.prototype = {
                            onComplete: Lang.bind(this,
                                function() {
                                    this.state = State.FADED_OUT;
-                                   Main.popModal(this._group, timestamp);
                                })
                          });
     }
diff --git a/js/ui/runDialog.js b/js/ui/runDialog.js
index 4279797..f239ec8 100644
--- a/js/ui/runDialog.js
+++ b/js/ui/runDialog.js
@@ -240,15 +240,20 @@ __proto__: ModalDialog.ModalDialog.prototype,
         this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
             let symbol = e.get_key_symbol();
             if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
+                this.popModal();
                 if (Shell.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK)
                     this._run(o.get_text(), true);
                 else
                     this._run(o.get_text(), false);
                 if (!this._commandError)
-                    this.close(global.get_current_time());
+                    this.close();
+                else {
+                    if (!this.pushModal())
+                        this.close();
+                }
             }
             if (symbol == Clutter.Escape) {
-                this.close(global.get_current_time());
+                this.close();
                 return true;
             }
             if (symbol == Clutter.slash) {



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