diff --git a/doc/emacs/windows.texi b/doc/emacs/windows.texi index a992f26fcdd..a4941435073 100644 --- a/doc/emacs/windows.texi +++ b/doc/emacs/windows.texi @@ -511,6 +511,7 @@ selected frame, and display the buffer in that new window. @vindex split-height-threshold @vindex split-width-threshold +@vindex split-window-preferred-direction The split can be either vertical or horizontal, depending on the variables @code{split-height-threshold} and @code{split-width-threshold}. These variables should have integer @@ -519,7 +520,9 @@ window's height, the split puts the new window below. Otherwise, if @code{split-width-threshold} is smaller than the window's width, the split puts the new window on the right. If neither condition holds, Emacs tries to split so that the new window is below---but only if the -window was not split before (to avoid excessive splitting). +window was not split before (to avoid excessive splitting). Whether +Emacs tries first to split vertically or horizontally, is +determined by the value of @code{split-window-preferred-direction}. @item Otherwise, display the buffer in a window previously showing it. diff --git a/etc/NEWS b/etc/NEWS index df1aff9213e..191b5235bde 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -226,6 +226,14 @@ It has been obsolete since Emacs 30.1. Use '(category . comint)' instead. Another user option 'display-tex-shell-buffer-action' has been removed too for which you can use '(category . tex-shell)'. ++++ +*** New user option 'split-window-preferred-direction'. +Users can now choose in which direction Emacs tries to split first: +vertical or horizontal. With this new setting, when the frame is in +landscape shape for instance, Emacs could split horizontally before +splitting vertically. The default setting preserves Emacs historical +behavior to try to split vertically first. + ** Frames +++ diff --git a/lisp/window.el b/lisp/window.el index 74bb2985254..ab15979baed 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -7394,20 +7394,64 @@ hold: (* 2 (max window-min-height (if mode-line-format 2 1)))))))))) +(defcustom split-window-preferred-direction 'vertical + "The first direction tried when Emacs needs to split a window. +This variable controls in which order `split-window-sensibly' will try to +split the window. That order specially matters when both dimensions of +the frame are long enough to be split according to +`split-width-threshold' and `split-height-threshold'. If this is set to +`vertical' (the default), `split-window-sensibly' tries to split +vertically first and then horizontally. If set to `horizontal' it does +the opposite. If set to `longest', the first direction tried +depends on the frame shape: in landscape orientation it will be like +`horizontal', but in portrait it will be like `vertical'. Basically, +the longest of the two dimension is split first. + +If both `split-width-threshold' and `split-height-threshold' cannot be +satisfied, it will fallback to split vertically. + +See `split-window-preferred-function' for more control of the splitting +strategy." + :type '(radio + (const :tag "Try to split vertically first" + vertical) + (const :tag "Try to split horizontally first" + horizontal) + (const :tag "Try to split along the longest edge first" + longest)) + :version "31.1" + :group 'windows) + +(defun window--try-vertical-split (window) + "Helper function for `split-window-sensibly'" + (when (window-splittable-p window) + (with-selected-window window + (split-window-below)))) + +(defun window--try-horizontal-split (window) + "Helper function for `split-window-sensibly'" + (when (window-splittable-p window t) + (with-selected-window window + (split-window-right)))) + (defun split-window-sensibly (&optional window) "Split WINDOW in a way suitable for `display-buffer'. -WINDOW defaults to the currently selected window. -If `split-height-threshold' specifies an integer, WINDOW is at -least `split-height-threshold' lines tall and can be split -vertically, split WINDOW into two windows one above the other and -return the lower window. Otherwise, if `split-width-threshold' -specifies an integer, WINDOW is at least `split-width-threshold' -columns wide and can be split horizontally, split WINDOW into two -windows side by side and return the window on the right. If this -can't be done either and WINDOW is the only window on its frame, -try to split WINDOW vertically disregarding any value specified -by `split-height-threshold'. If that succeeds, return the lower -window. Return nil otherwise. +The variable `split-window-preferred-direction' prescribes an order of +directions in which Emacs should try to split WINDOW. If that order +mandates starting with a vertical split, and `split-height-threshold' +specifies an integer that is at least as large a WINDOW's height, split +WINDOW into two windows one below the other and return the lower one. +If that order mandates starting with a horizontal split, and +`split-width-threshold' specifies an integer that is at least as large +as WINDOW's width, split WINDOW into two windows side by side and return +the one on the right. + +In either case, if the first attempt to split WINDOW fails, try to split +the window in the other direction in the same manner as described above. +If that attempt fails too, and WINDOW is the only window on its frame, +try splitting WINDOW into two windows, one below the other, disregarding +the value of `split-height-threshold' and return the window on the +bottom. By default `display-buffer' routines call this function to split the largest or least recently used window. To change the default @@ -7427,14 +7471,14 @@ Have a look at the function `window-splittable-p' if you want to know how `split-window-sensibly' determines whether WINDOW can be split." (let ((window (or window (selected-window)))) - (or (and (window-splittable-p window) - ;; Split window vertically. - (with-selected-window window - (split-window-below))) - (and (window-splittable-p window t) - ;; Split window horizontally. - (with-selected-window window - (split-window-right))) + (or (if (or + (eql split-window-preferred-direction 'horizontal) + (and (eql split-window-preferred-direction 'longest) + (> (frame-width) (frame-height)))) + (or (window--try-horizontal-split window) + (window--try-vertical-split window)) + (or (window--try-vertical-split window) + (window--try-horizontal-split window))) (and ;; If WINDOW is the only usable window on its frame (it is ;; the only one or, not being the only one, all the other @@ -7452,10 +7496,8 @@ split." frame nil 'nomini) t))) (not (window-minibuffer-p window)) - (let ((split-height-threshold 0)) - (when (window-splittable-p window) - (with-selected-window window - (split-window-below)))))))) + (let ((split-height-threshold 0)) + (window--try-vertical-split window)))))) (defun window--try-to-split-window (window &optional alist) "Try to split WINDOW.