From 95373b69b34f9756d2f05b19798b763d22aa5f0a Mon Sep 17 00:00:00 2001 From: Martin Rudalics Date: Mon, 11 Mar 2019 09:57:23 +0100 Subject: [PATCH] Rewrite minibuffer window resizing code * src/frame.c (resize_mini_frames): New variable. * src/window.c (resize_mini_window_apply): New function. (grow_mini_window, shrink_mini_window): Remove PIXELWISE argument. Call resize_mini_window_apply to apply changes. (Fresize_mini_window_internal): Call resize_mini_window_apply to apply changes. (Qwindow__resize_mini_frame): New symbol. * src/window.h (grow_mini_window, shrink_mini_window): Adjust external declarations. * src/xdisp.c (resize_mini_window): For minibuffer-only frames call 'window--resize-mini-frame' if resize_mini_frames is non-nil. Offload parts of logic to grow_mini_window and shrink_mini_window which are now called without the PIXELWISE argument. (Vresize_mini_windows): Mention 'resize-mini-frames' in doc-string. * lisp/cus-start.el (resize-mini-frames): Add customization support. * lisp/window.el (window--resize-mini-window): Simplify code. (window--resize-mini-frame): New function. * doc/lispref/minibuf.texi (Minibuffer Windows): Describe new option 'resize-mini-frames'. * etc/NEWS: Mention new option 'resize-mini-frames'. --- doc/lispref/minibuf.texi | 19 +++++ etc/NEWS | 5 ++ lisp/cus-start.el | 8 +- lisp/window.el | 8 +- src/frame.c | 13 +++ src/window.c | 180 ++++++++++++++++++--------------------- src/window.h | 4 +- src/xdisp.c | 92 +++++++------------- 8 files changed, 169 insertions(+), 160 deletions(-) diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index a2b6e145502..6c37fa92d6c 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -2402,6 +2402,25 @@ will not work. If you want to prevent resizing of minibuffer windows when displaying long messages, bind the @code{message-truncate-lines} variable instead (@pxref{Echo Area Customization}). +The option @code{resize-mini-windows} does not affect the behavior of +minibuffer-only frames (@pxref{Frame Layout}). The following option +allows to automatically resize such frames as well. + +@defopt resize-mini-frames +If this is @code{nil}, minibuffer-only frames are never resized +automatically. + +If this is a function, that function is called with the +minibuffer-only frame to be resized as sole argument. At the time +this function is called, the buffer of the minibuffer window of that +frame is the buffer whose contents will be shown the next time that +window is redisplayed. The function is expected to fit the frame to +the buffer in some appropriate way. + +Any other non-@code{nil} value means to resize minibuffer-only frames +by calling @code{fit-frame-to-buffer} (@pxref{Resizing Windows}). +@end defopt + @node Minibuffer Contents @section Minibuffer Contents diff --git a/etc/NEWS b/etc/NEWS index 3dc21dfe89b..410c1821ae9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1468,6 +1468,11 @@ return the total and body sizes of any window during last redisplay. See the section "(elisp) Window Hooks" in the Elisp manual for a detailed explanation of the new behavior. ++++ +*** New option 'resize-mini-frames'. +This option allows to automatically resize minibuffer-only frames +similarly to how minibuffer windows are resized on "normal" frames. + +++ ** New buffer display action alist entry 'dedicated'. Such an entry allows to specify the dedicated status of a window diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 44ce2929d66..baa05d0a89a 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -314,7 +314,13 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of (other :tag "hidden by keypress" 1)) "22.1") (make-pointer-invisible mouse boolean "23.2") - (menu-bar-mode frames boolean nil + (resize-mini-frames + frames (choice + (const :tag "Never" nil) + (const :tag "Fit frame to buffer" t) + (function :tag "User-defined function")) + "27.1") + (menu-bar-mode frames boolean nil ;; FIXME? ;; :initialize custom-initialize-default :set custom-set-minor-mode) diff --git a/lisp/window.el b/lisp/window.el index a8b65657a49..b769be06337 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -2753,7 +2753,7 @@ as small) as possible, but don't signal an error." ;; Sanitize DELTA. (cond ((<= (+ height delta) 0) - (setq delta (- (frame-char-height (window-frame window)) height))) + (setq delta (- (frame-char-height frame) height))) ((> delta min-delta) (setq delta min-delta))) @@ -3381,6 +3381,12 @@ routines." pixel-delta (/ pixel-delta (frame-char-height frame))))) +(defun window--resize-mini-frame (frame) + "Resize minibuffer-only frame FRAME." + (if (functionp resize-mini-frames) + (funcall resize-mini-frames frame) + (fit-frame-to-buffer frame))) + (defun window--sanitize-window-sizes (horizontal) "Assert that all windows on selected frame are large enough. If necessary and possible, make sure that every window on frame diff --git a/src/frame.c b/src/frame.c index c336369dbb5..46bdf222315 100644 --- a/src/frame.c +++ b/src/frame.c @@ -6079,6 +6079,19 @@ setting this variable does not change that frame's previous association. This variable is local to the current terminal and cannot be buffer-local. */); + DEFVAR_LISP ("resize-mini-frames", resize_mini_frames, + doc: /* Non-nil means resize minibuffer-only frames automatically. +If this is nil, do not resize minibuffer-only frames automatically. + +If this is a function, call that function with the minibuffer-only +frame that shall be resized as sole argument. The buffer of the root +window of that frame is the buffer whose text will be eventually shown +in the minibuffer window. + +Any other non-nil value means to resize minibuffer-only frames by +calling `fit-frame-to-buffer'. */); + resize_mini_frames = Qnil; + DEFVAR_LISP ("focus-follows-mouse", focus_follows_mouse, doc: /* Non-nil if window system changes focus when you move the mouse. You should set this variable to tell Emacs how your window manager diff --git a/src/window.c b/src/window.c index c498ae81cdb..ae039b76add 100644 --- a/src/window.c +++ b/src/window.c @@ -5168,118 +5168,111 @@ Signal an error when WINDOW is the only window on its frame. */) Resizing Mini-Windows ***********************************************************************/ -/* Grow mini-window W by DELTA lines, DELTA >= 0, or as much as we - can. */ +/** + * resize_mini_window_apply: + * + * Assign new window sizes after resizing a mini window W by DELTA + * pixels. No error checking performed. + */ +static void +resize_mini_window_apply (struct window *w, int delta) +{ + struct frame *f = XFRAME (w->frame); + Lisp_Object root = FRAME_ROOT_WINDOW (f); + struct window *r = XWINDOW (root); + + block_input (); + w->pixel_height = w->pixel_height + delta; + w->total_lines = w->pixel_height / FRAME_LINE_HEIGHT (f); + + window_resize_apply (r, false); + + w->pixel_top = r->pixel_top + r->pixel_height; + w->top_line = r->top_line + r->total_lines; + + /* Enforce full redisplay of the frame. */ + /* FIXME: Shouldn't some of the caller do it? */ + fset_redisplay (f); + adjust_frame_glyphs (f); + unblock_input (); +} + +/** + * grow_mini_window: + * + * Grow mini-window W by DELTA pixels. If DELTA is negative, this may + * shrink the minibuffer window to the minimum height to display one + * line of text. + */ void -grow_mini_window (struct window *w, int delta, bool pixelwise) +grow_mini_window (struct window *w, int delta) { struct frame *f = XFRAME (w->frame); - struct window *r; - Lisp_Object root, height; - int line_height, pixel_height; + int old_height = WINDOW_PIXEL_HEIGHT (w); + int min_height = FRAME_LINE_HEIGHT (f); eassert (MINI_WINDOW_P (w)); - eassert (delta >= 0); - if (delta > 0) - { - root = FRAME_ROOT_WINDOW (f); - r = XWINDOW (root); - height = call3 (Qwindow__resize_root_window_vertically, - root, make_fixnum (- delta), pixelwise ? Qt : Qnil); - if (FIXNUMP (height) && window_resize_check (r, false)) - { - block_input (); - window_resize_apply (r, false); + if (old_height + delta < min_height) + /* Never shrink mini-window to less than its minimum + height. */ + delta = old_height > min_height ? min_height - old_height : 0; - if (pixelwise) - { - pixel_height = min (-XFIXNUM (height), INT_MAX - w->pixel_height); - line_height = pixel_height / FRAME_LINE_HEIGHT (f); - } - else - { - line_height = min (-XFIXNUM (height), - ((INT_MAX - w->pixel_height) - / FRAME_LINE_HEIGHT (f))); - pixel_height = line_height * FRAME_LINE_HEIGHT (f); - } + if (delta != 0) + { + Lisp_Object root = FRAME_ROOT_WINDOW (f); + struct window *r = XWINDOW (root); + Lisp_Object grow; - /* Grow the mini-window. */ - w->pixel_top = r->pixel_top + r->pixel_height; - w->top_line = r->top_line + r->total_lines; - /* Make sure the mini-window has always at least one line. */ - w->pixel_height = max (w->pixel_height + pixel_height, - FRAME_LINE_HEIGHT (f)); - w->total_lines = max (w->total_lines + line_height, 1); - - /* Enforce full redisplay of the frame. */ - /* FIXME: Shouldn't window--resize-root-window-vertically do it? */ - fset_redisplay (f); - adjust_frame_glyphs (f); - unblock_input (); - } - else - error ("Failed to grow minibuffer window"); + FRAME_WINDOWS_FROZEN (f) = true; + grow = call3 (Qwindow__resize_root_window_vertically, + root, make_fixnum (- delta), Qt); + if (FIXNUMP (grow) && window_resize_check (r, false)) + resize_mini_window_apply (w, -XFIXNUM (grow)); } } -/* Shrink mini-window W to one line. */ +/** + * shrink_mini_window: + * + * Shrink mini-window W to the minimum height needed to display one + * line of text. + */ void -shrink_mini_window (struct window *w, bool pixelwise) +shrink_mini_window (struct window *w) { struct frame *f = XFRAME (w->frame); - struct window *r; - Lisp_Object root, delta; - EMACS_INT height, unit; + int delta = WINDOW_PIXEL_HEIGHT (w) - FRAME_LINE_HEIGHT (f); eassert (MINI_WINDOW_P (w)); - height = pixelwise ? w->pixel_height : w->total_lines; - unit = pixelwise ? FRAME_LINE_HEIGHT (f) : 1; - if (height > unit) + if (delta > 0) { - root = FRAME_ROOT_WINDOW (f); - r = XWINDOW (root); - delta = call3 (Qwindow__resize_root_window_vertically, - root, make_fixnum (height - unit), - pixelwise ? Qt : Qnil); - if (FIXNUMP (delta) && window_resize_check (r, false)) - { - block_input (); - window_resize_apply (r, false); - - /* Shrink the mini-window. */ - w->top_line = r->top_line + r->total_lines; - w->total_lines = 1; - w->pixel_top = r->pixel_top + r->pixel_height; - w->pixel_height = FRAME_LINE_HEIGHT (f); - /* Enforce full redisplay of the frame. */ - /* FIXME: Shouldn't window--resize-root-window-vertically do it? */ - fset_redisplay (f); - adjust_frame_glyphs (f); - unblock_input (); - } - /* If the above failed for whatever strange reason we must make a - one window frame here. The same routine will be needed when - shrinking the frame (and probably when making the initial - *scratch* window). For the moment leave things as they are. */ - else - error ("Failed to shrink minibuffer window"); + Lisp_Object root = FRAME_ROOT_WINDOW (f); + struct window *r = XWINDOW (root); + Lisp_Object grow; + + FRAME_WINDOWS_FROZEN (f) = false; + grow = call3 (Qwindow__resize_root_window_vertically, + root, make_fixnum (delta), Qt); + + if (FIXNUMP (grow) && window_resize_check (r, false)) + resize_mini_window_apply (w, -XFIXNUM (grow)); } } -DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini_window_internal, 1, 1, 0, - doc: /* Resize minibuffer window WINDOW. */) +DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, + Sresize_mini_window_internal, 1, 1, 0, + doc: /* Resize mini window WINDOW. */) (Lisp_Object window) { struct window *w = XWINDOW (window); struct window *r; struct frame *f; - int height; + int old_height, delta; - CHECK_WINDOW (window); + CHECK_LIVE_WINDOW (window); f = XFRAME (w->frame); if (!EQ (FRAME_MINIBUF_WINDOW (XFRAME (w->frame)), window)) @@ -5288,26 +5281,18 @@ DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini error ("Cannot resize a minibuffer-only frame"); r = XWINDOW (FRAME_ROOT_WINDOW (f)); - height = r->pixel_height + w->pixel_height; + old_height = r->pixel_height + w->pixel_height; + delta = XFIXNUM (w->new_pixel) - w->pixel_height; if (window_resize_check (r, false) && XFIXNUM (w->new_pixel) > 0 - && height == XFIXNUM (r->new_pixel) + XFIXNUM (w->new_pixel)) + && old_height == XFIXNUM (r->new_pixel) + XFIXNUM (w->new_pixel)) { - block_input (); - window_resize_apply (r, false); - - w->pixel_height = XFIXNAT (w->new_pixel); - w->total_lines = w->pixel_height / FRAME_LINE_HEIGHT (f); - w->pixel_top = r->pixel_top + r->pixel_height; - w->top_line = r->top_line + r->total_lines; + resize_mini_window_apply (w, delta); - fset_redisplay (f); - adjust_frame_glyphs (f); - unblock_input (); return Qt; } else - error ("Failed to resize minibuffer window"); + error ("Cannot resize mini window"); } /* Mark window cursors off for all windows in the window tree rooted @@ -8047,6 +8032,7 @@ syms_of_window (void) DEFSYM (Qwindow__resize_root_window, "window--resize-root-window"); DEFSYM (Qwindow__resize_root_window_vertically, "window--resize-root-window-vertically"); + DEFSYM (Qwindow__resize_mini_frame, "window--resize-mini-frame"); DEFSYM (Qwindow__sanitize_window_sizes, "window--sanitize-window-sizes"); DEFSYM (Qwindow__pixel_to_total, "window--pixel-to-total"); DEFSYM (Qsafe, "safe"); diff --git a/src/window.h b/src/window.h index d816bb1683d..b450173eb2f 100644 --- a/src/window.h +++ b/src/window.h @@ -1063,8 +1063,8 @@ extern Lisp_Object window_from_coordinates (struct frame *, int, int, extern void resize_frame_windows (struct frame *, int, bool, bool); extern void restore_window_configuration (Lisp_Object); extern void delete_all_child_windows (Lisp_Object); -extern void grow_mini_window (struct window *, int, bool); -extern void shrink_mini_window (struct window *, bool); +extern void grow_mini_window (struct window *, int); +extern void shrink_mini_window (struct window *); extern int window_relative_x_coord (struct window *, enum window_part, int); void run_window_change_functions (void); diff --git a/src/xdisp.c b/src/xdisp.c index 0af5e39dfb6..6d30afda6d8 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11259,15 +11259,10 @@ bool resize_mini_window (struct window *w, bool exact_p) { struct frame *f = XFRAME (w->frame); - bool window_height_changed_p = false; + int old_height = WINDOW_PIXEL_HEIGHT (w); eassert (MINI_WINDOW_P (w)); - /* By default, start display at the beginning. */ - set_marker_both (w->start, w->contents, - BUF_BEGV (XBUFFER (w->contents)), - BUF_BEGV_BYTE (XBUFFER (w->contents))); - /* Don't resize windows while redisplaying a window; it would confuse redisplay functions when the size of the window they are displaying changes from under them. Such a resizing can happen, @@ -11278,19 +11273,30 @@ resize_mini_window (struct window *w, bool exact_p) return false; /* Nil means don't try to resize. */ - if (NILP (Vresize_mini_windows) + if ((NILP (Vresize_mini_windows) + && (NILP (resize_mini_frames) || !FRAME_MINIBUF_ONLY_P (f))) || (FRAME_X_P (f) && FRAME_X_OUTPUT (f) == NULL)) return false; - if (!FRAME_MINIBUF_ONLY_P (f)) + /* By default, start display at the beginning. */ + set_marker_both (w->start, w->contents, + BUF_BEGV (XBUFFER (w->contents)), + BUF_BEGV_BYTE (XBUFFER (w->contents))); + + if (FRAME_MINIBUF_ONLY_P (f)) + { + if (!NILP (resize_mini_frames)) + safe_call1 (Qwindow__resize_mini_frame, WINDOW_FRAME (w)); + } + else { struct it it; - int total_height = (WINDOW_PIXEL_HEIGHT (XWINDOW (FRAME_ROOT_WINDOW (f))) - + WINDOW_PIXEL_HEIGHT (w)); + int old_height = WINDOW_PIXEL_HEIGHT (w); int unit = FRAME_LINE_HEIGHT (f); int height, max_height; struct text_pos start; struct buffer *old_current_buffer = NULL; + int windows_height = FRAME_WINDOWS_HEIGHT (f); if (current_buffer != XBUFFER (w->contents)) { @@ -11302,14 +11308,14 @@ resize_mini_window (struct window *w, bool exact_p) /* Compute the max. number of lines specified by the user. */ if (FLOATP (Vmax_mini_window_height)) - max_height = XFLOAT_DATA (Vmax_mini_window_height) * total_height; + max_height = XFLOAT_DATA (Vmax_mini_window_height) * windows_height; else if (FIXNUMP (Vmax_mini_window_height)) max_height = XFIXNUM (Vmax_mini_window_height) * unit; else - max_height = total_height / 4; + max_height = windows_height / 4; /* Correct that max. height if it's bogus. */ - max_height = clip_to_bounds (unit, max_height, total_height); + max_height = clip_to_bounds (unit, max_height, windows_height); /* Find out the height of the text in the window. */ if (it.line_wrap == TRUNCATE) @@ -11335,63 +11341,27 @@ resize_mini_window (struct window *w, bool exact_p) } else SET_TEXT_POS (start, BEGV, BEGV_BYTE); + SET_MARKER_FROM_TEXT_POS (w->start, start); if (EQ (Vresize_mini_windows, Qgrow_only)) { /* Let it grow only, until we display an empty message, in which case the window shrinks again. */ - if (height > WINDOW_PIXEL_HEIGHT (w)) - { - int old_height = WINDOW_PIXEL_HEIGHT (w); - - FRAME_WINDOWS_FROZEN (f) = true; - grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true); - window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height; - } - else if (height < WINDOW_PIXEL_HEIGHT (w) - && (exact_p || BEGV == ZV)) - { - int old_height = WINDOW_PIXEL_HEIGHT (w); - - FRAME_WINDOWS_FROZEN (f) = false; - shrink_mini_window (w, true); - window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height; - } - } - else - { - /* Always resize to exact size needed. */ - if (height > WINDOW_PIXEL_HEIGHT (w)) - { - int old_height = WINDOW_PIXEL_HEIGHT (w); - - FRAME_WINDOWS_FROZEN (f) = true; - grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true); - window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height; - } - else if (height < WINDOW_PIXEL_HEIGHT (w)) - { - int old_height = WINDOW_PIXEL_HEIGHT (w); - - FRAME_WINDOWS_FROZEN (f) = false; - shrink_mini_window (w, true); - - if (height) - { - FRAME_WINDOWS_FROZEN (f) = true; - grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true); - } - - window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height; - } + if (height > old_height) + grow_mini_window (w, height - old_height); + else if (height < old_height && (exact_p || BEGV == ZV)) + shrink_mini_window (w); } + else if (height != old_height) + /* Always resize to exact size needed. */ + grow_mini_window (w, height - old_height); if (old_current_buffer) set_buffer_internal (old_current_buffer); } - return window_height_changed_p; + return WINDOW_PIXEL_HEIGHT (w) != old_height; } @@ -33091,7 +33061,11 @@ A value of nil means don't automatically resize mini-windows. A value of t means resize them to fit the text displayed in them. A value of `grow-only', the default, means let mini-windows grow only; they return to their normal size when the minibuffer is closed, or the -echo area becomes empty. */); +echo area becomes empty. + +This variable does not affect resizing of the minibuffer window of +minibuffer-only frames. These are handled by 'resize-mini-frames' +only. */); /* Contrary to the doc string, we initialize this to nil, so that loading loadup.el won't try to resize windows before loading window.el, where some functions we need to call for this live. -- 2.39.2