]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix handling of minibuffer-only child frames (Bug#33498)
authorMartin Rudalics <rudalics@gmx.at>
Tue, 5 Mar 2019 09:46:19 +0000 (10:46 +0100)
committerMartin Rudalics <rudalics@gmx.at>
Tue, 5 Mar 2019 09:46:19 +0000 (10:46 +0100)
* doc/lispref/frames.texi (Buffer Parameters): Describe how to
make a minibuffer-only child frame.
(Child Frames): Describe how minbuffer child frames are
deleted.
* src/frame.c (delete_frame): Handle deletion of minibuffer
child frames (Bug#33498).  In the course, fix reassigning of
'default-minibuffer-frame' with minibuffer-only frames.
* lisp/frame.el (frame-notice-user-settings): Handle creation of
initial minibuffer-only child frame.
(make-frame): Handle creation of frame with a minibuffer-only
child frame.

doc/lispref/frames.texi
etc/NEWS
lisp/frame.el
src/frame.c

index 820006a567527f8629ae2a8ff587d4c88381037e..9b3e02f4de03d4b21be4871fad05561f44efd4bc 100644 (file)
@@ -1884,6 +1884,12 @@ minibuffer window to @code{t} and vice-versa, or from @code{t} to
 @code{nil}.  If the parameter specifies a minibuffer window already,
 setting it to @code{nil} has no effect.
 
+The special value @code{child-frame} means to make a minibuffer-only
+child frame (@pxref{Child Frames}) whose parent becomes the frame
+created.  As if specified as @code{nil}, Emacs will set this parameter
+to the minibuffer window of the child frame but will not select the
+child frame after its creation.
+
 @vindex buffer-predicate@r{, a frame parameter}
 @item buffer-predicate
 The buffer-predicate function for this frame.  The function
@@ -3214,9 +3220,17 @@ top-level frame which also always appears on top of its parent
 window---the desktop's root window.  When a parent frame is iconified or
 made invisible (@pxref{Visibility of Frames}), its child frames are made
 invisible.  When a parent frame is deiconified or made visible, its
-child frames are made visible.  When a parent frame is about to be
-deleted (@pxref{Deleting Frames}), its child frames are recursively
-deleted before it.
+child frames are made visible.
+
+  When a parent frame is about to be deleted (@pxref{Deleting
+Frames}), its child frames are recursively deleted before it.  There
+is one exception to this rule: When the child frame serves as a
+surrogate minibuffer frame (@pxref{Minibuffers and Frames}) for
+another frame, it is retained until the parent frame has been deleted.
+If, at this time, no remaining frame uses the child frame as its
+minibuffer frame, Emacs will try to delete the child frame too.  If
+that deletion fails for whatever reason, the child frame is made a
+top-level frame.
 
   Whether a child frame can have a menu or tool bar is window-system or
 window manager dependent.  Most window-systems explicitly disallow menus
index fc4e2c57268b60628d4366ff11c4db7155bf6977..3e347b5318ac328e5ce4d798e376440837f97e83 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1166,6 +1166,11 @@ the 128...255 range, as expected.
 +++
 *** New command 'make-frame-on-monitor' makes a frame on the specified monitor.
 
++++
+*** New value of 'minibuffer' frame parameter 'child-frame'.
+This allows to create and parent immediately a minibuffer-only child
+frame when making a frame.
+
 \f
 * New Modes and Packages in Emacs 27.1
 
index d71a3fe5e8e10cb0966f5bc4b3ddcd7e70689e26..cdb2ac4af119f4d45084391114e1e367d02c8513 100644 (file)
@@ -316,10 +316,15 @@ there (in decreasing order of priority)."
   ;; want to use save-excursion here, because that may also try to set
   ;; the buffer of the selected window, which fails when the selected
   ;; window is the minibuffer.
-  (let ((old-buffer (current-buffer))
-       (window-system-frame-alist
-         (cdr (assq initial-window-system
-                    window-system-default-frame-alist))))
+  (let* ((old-buffer (current-buffer))
+        (window-system-frame-alist
+          (cdr (assq initial-window-system
+                     window-system-default-frame-alist)))
+         (minibuffer
+          (cdr (or (assq 'minibuffer initial-frame-alist)
+                  (assq 'minibuffer window-system-frame-alist)
+                  (assq 'minibuffer default-frame-alist)
+                  '(minibuffer . t)))))
 
     (when (and frame-notice-user-settings
               (null frame-initial-frame))
@@ -410,11 +415,7 @@ there (in decreasing order of priority)."
       ;; default-frame-alist in the parameters of the screen we
       ;; create here, so that its new value, gleaned from the user's
       ;; init file, will be applied to the existing screen.
-      (if (not (eq (cdr (or (assq 'minibuffer initial-frame-alist)
-                           (assq 'minibuffer window-system-frame-alist)
-                           (assq 'minibuffer default-frame-alist)
-                           '(minibuffer . t)))
-                  t))
+      (if (not (eq minibuffer t))
          ;; Create the new frame.
          (let (parms new)
            ;; MS-Windows needs this to avoid inflooping below.
@@ -442,7 +443,15 @@ there (in decreasing order of priority)."
                                parms
                                nil))
 
-           ;; Get rid of `reverse', because that was handled
+           (when (eq minibuffer 'child-frame)
+              ;; When the minibuffer shall be shown in a child frame,
+              ;; remove the 'minibuffer' parameter from PARMS.  It
+              ;; will get assigned by the usual routines to the child
+              ;; frame's root window below.
+              (setq parms (cons '(minibuffer)
+                               (delq (assq 'minibuffer parms) parms))))
+
+            ;; Get rid of `reverse', because that was handled
            ;; when we first made the frame.
            (setq parms (cons '(reverse) (delq (assq 'reverse parms) parms)))
 
@@ -465,7 +474,18 @@ there (in decreasing order of priority)."
            ;; the only frame with a minibuffer.  If it is, create a
            ;; new one.
            (or (delq frame-initial-frame (minibuffer-frame-list))
-               (make-initial-minibuffer-frame nil))
+                (and (eq minibuffer 'child-frame)
+                     ;; Create a minibuffer child frame and parent it
+                     ;; immediately.  Take any other parameters for
+                     ;; the child frame from 'minibuffer-frame-list'.
+                     (let* ((minibuffer-frame-alist
+                             (cons `(parent-frame . ,new) minibuffer-frame-alist)))
+                       (make-initial-minibuffer-frame nil)
+                       ;; With a minibuffer child frame we do not want
+                       ;; to select the minibuffer frame initially as
+                       ;; we do for standard minibuffer-only frames.
+                       (select-frame new)))
+                (make-initial-minibuffer-frame nil))
 
            ;; If the initial frame is serving as a surrogate
            ;; minibuffer frame for any frames, we need to wean them
@@ -795,7 +815,7 @@ the new frame according to its own rules."
              (t window-system)))
         (oldframe (selected-frame))
         (params parameters)
-        frame)
+        frame child-frame)
 
     (unless (get w 'window-system-initialized)
       (let ((window-system w))          ;Hack attack!
@@ -811,17 +831,44 @@ the new frame according to its own rules."
     (dolist (p default-frame-alist)
       (unless (assq (car p) params)
        (push p params)))
-    ;; Now make the frame.
-    (run-hooks 'before-make-frame-hook)
 
 ;;     (setq frame-size-history '(1000))
 
-    (setq frame (let ((window-system w)) ;Hack attack!
+    (when (eq (cdr (or (assq 'minibuffer params) '(minibuffer . t)))
+              'child-frame)
+      ;; If the 'minibuffer' parameter equals 'child-frame' make a
+      ;; frame without minibuffer first using the root window of
+      ;; 'default-minibuffer-frame' as its minibuffer window
+      (setq child-frame t)
+      (setq params (cons '(minibuffer)
+                         (delq (assq 'minibuffer params) params))))
+
+    ;; Now make the frame.
+    (run-hooks 'before-make-frame-hook)
+
+    (setq frame (let ((window-system w)) ; Hack attack!
                   (frame-creation-function params)))
+
+    (when child-frame
+      ;; When we want to equip the new frame with a minibuffer-only
+      ;; child frame, make that frame and reparent it immediately.
+      (setq child-frame
+            (make-frame
+             (append
+              `((display . ,display) (minibuffer . only)
+                (parent-frame . ,frame))
+              minibuffer-frame-alist)))
+      (when (frame-live-p child-frame)
+        ;; Have the 'minibuffer' parameter of our new frame refer to
+        ;; its child frame's root window.
+        (set-frame-parameter
+         frame 'minibuffer (frame-root-window child-frame))))
+
     (normal-erase-is-backspace-setup-frame frame)
-    ;; Inherit the original frame's parameters.
+    ;; Inherit original frame's parameters unless they are overridden
+    ;; by explicit parameters.
     (dolist (param frame-inherited-parameters)
-      (unless (assq param parameters)   ;Overridden by explicit parameters.
+      (unless (assq param parameters)
         (let ((val (frame-parameter oldframe param)))
           (when val (set-frame-parameter frame param val)))))
 
index 165ed4a4e52ed3ba309eb87e9da4ce73f6fe45d2..3d83dc0a0d8b0fcdb36a93c398b44fb3f2b5f89b 100644 (file)
@@ -1849,6 +1849,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
   Lisp_Object frames, frame1;
   int minibuffer_selected, is_tooltip_frame;
   bool nochild = !FRAME_PARENT_FRAME (f);
+  Lisp_Object minibuffer_child_frame = Qnil;
 
   if (!FRAME_LIVE_P (f))
     return Qnil;
@@ -1865,13 +1866,33 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
   /* Softly delete all frames with this frame as their parent frame or
      as their `delete-before' frame parameter value.  */
   FOR_EACH_FRAME (frames, frame1)
-    if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f
+    {
+      struct frame *f1 = XFRAME (frame1);
+
+      if (EQ (frame1, frame) || FRAME_TOOLTIP_P (f1))
+       continue;
+      else if (FRAME_PARENT_FRAME (f1) == f)
+       {
+         if (FRAME_HAS_MINIBUF_P (f1) && !FRAME_HAS_MINIBUF_P (f)
+             && EQ (FRAME_MINIBUF_WINDOW (f), FRAME_MINIBUF_WINDOW (f1)))
+           /* frame1 owns frame's minibuffer window so we must not
+              delete it here to avoid a surrogate minibuffer error.
+              Unparent frame1 and make it a top-level frame.  */
+           {
+             Fmodify_frame_parameters
+               (frame1, Fcons (Fcons (Qparent_frame, Qnil), Qnil));
+             minibuffer_child_frame = frame1;
+           }
+         else
+           delete_frame (frame1, Qnil);
+       }
+      else if (nochild
+              && EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame))
        /* Process `delete-before' parameter iff FRAME is not a child
           frame.  This avoids that we enter an infinite chain of mixed
           dependencies.  */
-       || (nochild
-           && EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame)))
-      delete_frame (frame1, Qnil);
+       delete_frame (frame1, Qnil);
+    }
 
   /* Does this frame have a minibuffer, and is it the surrogate
      minibuffer for any other frame?  */
@@ -2136,18 +2157,27 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
        {
          struct frame *f1 = XFRAME (frame1);
 
-         /* Consider only frames on the same kboard
-            and only those with minibuffers.  */
-         if (kb == FRAME_KBOARD (f1)
-             && FRAME_HAS_MINIBUF_P (f1))
+         /* Set frame_on_same_kboard to frame1 if it is on the same
+            keyboard.  Set frame_with_minibuf to frame1 if it also
+            has a minibuffer.  Leave the loop immediately if frame1
+            is also minibuffer-only.
+
+            Emacs 26 does _not_ set frame_on_same_kboard here when it
+            finds a minibuffer-only frame and subsequently fails to
+            set default_minibuffer_frame below.  Not a great deal and
+            never noticed since make_frame_without_minibuffer creates
+            a new minibuffer frame in that case (which can be a minor
+            annoyance though).  To consider for Emacs 26.3.  */
+         if (kb == FRAME_KBOARD (f1))
            {
-             frame_with_minibuf = frame1;
-             if (FRAME_MINIBUF_ONLY_P (f1))
-               break;
+             frame_on_same_kboard = frame1;
+             if (FRAME_HAS_MINIBUF_P (f1))
+               {
+                 frame_with_minibuf = frame1;
+                 if (FRAME_MINIBUF_ONLY_P (f1))
+                   break;
+               }
            }
-
-         if (kb == FRAME_KBOARD (f1))
-           frame_on_same_kboard = frame1;
        }
 
       if (!NILP (frame_on_same_kboard))
@@ -2180,7 +2210,46 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
       = Fcons (list3 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame),
               pending_funcalls);
   else
-      safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame);
+    safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame);
+
+  if (!NILP (minibuffer_child_frame))
+    /* If minibuffer_child_frame is non-nil, it was FRAME's minibuffer
+       child frame.  Delete it unless it's also the minibuffer frame
+       of another frame in which case we make sure it's visible.  */
+    {
+      struct frame *f1 = XFRAME (minibuffer_child_frame);
+
+      if (FRAME_LIVE_P (f1))
+       {
+         Lisp_Object window1 = FRAME_ROOT_WINDOW (f1);
+         Lisp_Object frame2;
+
+         FOR_EACH_FRAME (frames, frame2)
+           {
+             struct frame *f2 = XFRAME (frame2);
+
+             if (EQ (frame2, minibuffer_child_frame) || FRAME_TOOLTIP_P (f2))
+               continue;
+             else if (EQ (FRAME_MINIBUF_WINDOW (f2), window1))
+               {
+                 /* minibuffer_child_frame serves as minibuffer frame
+                    for at least one other frame - so make it visible
+                    and quit.  */
+                 if (!FRAME_VISIBLE_P (f1) && !FRAME_ICONIFIED_P (f1))
+                   Fmake_frame_visible (frame1);
+
+                 return Qnil;
+               }
+           }
+
+         /* No other frame found that uses minibuffer_child_frame as
+            minibuffer frame.  If FORCE is Qnoelisp or there are
+            other visible frames left, delete minibuffer_child_frame
+            since it presumably was used by FRAME only.  */
+         if (EQ (force, Qnoelisp) || other_frames (f1, false, !NILP (force)))
+           delete_frame (minibuffer_child_frame, Qnoelisp);
+       }
+    }
 
   return Qnil;
 }